티스토리 뷰

WPF .NET

Telerik - DockingRegionAdapter

kaki104 2021. 2. 24. 10:00
반응형

Prism에서 Telerik의 Docking을 Region으로 이용해서 화면을 출력할 때 필요한 기능입니다. 기본적인 내용은 git에 올라와있는데, Prism 버전이 옛날 버전이라 최신(7.0 이상) 버전에서도 사용 가능하도록 수정했습니다.

telerik/xaml-sdk: The XAML SDK is an easy-to-use infrastructure with 1000+ developer focused examples for most of the Telerik WPF and Silverlight controls. (github.com)

 

telerik/xaml-sdk

The XAML SDK is an easy-to-use infrastructure with 1000+ developer focused examples for most of the Telerik WPF and Silverlight controls. - telerik/xaml-sdk

github.com

위 경로에서 Docking에 들어가시면, ShellPrism이라는 프로젝트를 참고하시면 되는데, 실행이 않되기 때문에 큰 도움이 되지는 않았습니다. 그리고, 여기서 만든 DockingRegionAdapter는 완성형 버전이 아니기 때문에 버그나 수정 사항이 있으면 알려주시면 수정 하도록 하겠습니다.

준비

소스에서 사용한 버전 정보

.Net 5 (.Net 3.1 이상)

Prism.DryIoc 8.0.0.1909 (Prism 7.0 이상)

Telerik.UI.for.Wpf.NetCore.Xaml.Trial 2021.1.119

DockingRegionAdapter는 .Net Framework에서도 사용 가능할 것으로 생각됩니다.

핵심

DockingRegionAdapter.cs

인프라지스틱스의 RegionAdapter를 참고로 작성한 DockingRegionAdapter입니다.

CreateRegion에 SingleActiveRegion 사용하는 것이 제일 중요합니다. 한번에 하나씩 Active가되는 형태입니다.

필요없는 코드 삭제했습니다. update 2021-03-22

using Prism.Regions;
using System.Windows;
using Telerik.Windows.Controls;

namespace TelerikPrismSample
{
    /// <summary>
    /// DockingRegionAdapter
    /// </summary>
    public class DockingRegionAdapter : RegionAdapterBase<RadDocking>
    {
        /// <summary>
        /// 생성자
        /// </summary>
        /// <param name="regionBehaviorFactory"></param>
        public DockingRegionAdapter(IRegionBehaviorFactory regionBehaviorFactory)
            : base(regionBehaviorFactory)
        {
        }
        /// <summary>
        /// 리즌과 컨트롤 연결
        /// </summary>
        /// <param name="region"></param>
        /// <param name="regionTarget"></param>
        protected override void Adapt(IRegion region, RadDocking regionTarget)
        {
            regionTarget.PanesSource = region.Views;
        }
        /// <summary>
        /// 비헤이비어 연결
        /// </summary>
        /// <param name="region"></param>
        /// <param name="regionTarget"></param>
        protected override void AttachBehaviors(IRegion region, RadDocking regionTarget)
        {
            base.AttachBehaviors(region, regionTarget);
            if (!region.Behaviors.ContainsKey(DockActivationRegionBehavior.BehaviorKey))
            {
                region.Behaviors.Add(DockActivationRegionBehavior.BehaviorKey,
                    new DockActivationRegionBehavior { HostControl = regionTarget });
            }
        }

        protected override IRegion CreateRegion()
        {
            //한번에 하나만 액티브가 되는 경우에 사용
            return new SingleActiveRegion();
        }
    }
}

DockActivationRegionBehavior.cs

Region에서 특정 뷰가 Active가될 때 어떤 동작을 할지를 지정합니다. RadDocking_Close는 직접 구현하셔야 합니다.

using Prism.Regions;
using Prism.Regions.Behaviors;
using System.Collections.Specialized;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using Telerik.Windows.Controls;

namespace TelerikPrismSample
{
    /// <summary>
    /// DockActivationRegionBehavior
    /// </summary>
    public class DockActivationRegionBehavior : RegionBehavior, IHostAwareRegionBehavior
    {
        public const string BehaviorKey = "DockActivationRegionBehavior";
        private RadDocking _hostControl;
        public DependencyObject HostControl
        {
            get => _hostControl;
            set => _hostControl = value as RadDocking;
        }

        protected override void OnAttach()
        {
            _hostControl.ActivePaneChanged += RadDocking_ActivePaneChanged;
            _hostControl.Close += RadDocking_Close;
            Region.ActiveViews.CollectionChanged += ActiveViews_CollectionChanged;
        }

        private void RadDocking_Close(object sender, Telerik.Windows.Controls.Docking.StateChangeEventArgs e)
        {
            //todo : 패인 닫힐때 처리 추가
        }

        /// <summary>
        /// 액티브뷰가 변경될때
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ActiveViews_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            if (e.Action == NotifyCollectionChangedAction.Add)
            {
                //are we dealing with a view
                FrameworkElement frameworkElement = e.NewItems[0] as FrameworkElement;
                if (frameworkElement != null)
                {
                    RadPane pane = _hostControl.Panes.FirstOrDefault(p => p.Content == frameworkElement);
                    if (pane != null)
                    {
                        pane.IsActive = true;
                    }
                }
                else
                {
                    //must be a viewmodel
                    object viewModel = e.NewItems[0];
                    RadPane contentPane = GetContentPaneFromFromViewModel(viewModel);
                    if (contentPane != null)
                    {
                        contentPane.IsActive = true;
                    }
                }
            }
        }
        /// <summary>
        /// 뷰모델에서 패인찾음
        /// </summary>
        /// <param name="viewModel"></param>
        /// <returns></returns>
        private RadPane GetContentPaneFromFromViewModel(object viewModel)
        {
            System.Collections.Generic.IEnumerable<RadPane> panes = _hostControl.Panes.OfType<RadPane>();
            foreach (RadPane contentPane in panes)
            {
                if (contentPane.DataContext == viewModel)
                {
                    return contentPane;
                }
            }
            return null;
        }
        /// <summary>
        /// RadDocking Active
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void RadDocking_ActivePaneChanged(object sender, Telerik.Windows.Controls.Docking.ActivePangeChangedEventArgs e)
        {
            if (e.OldPane != null)
            {
                RadPane item = e.OldPane;
                //ContentPane을 직접 다루고 있습니까?
                if (Region.Views.Contains(item) && Region.ActiveViews.Contains(item))
                {
                    Region.Deactivate(item);
                }
                else
                {
                    //이제 주입 된 뷰가 있는지 확인
                    ContentControl contentControl = item;
                    if (contentControl != null)
                    {
                        object injectedView = contentControl.Content;
                        if (Region.Views.Contains(injectedView) && Region.ActiveViews.Contains(injectedView))
                        {
                            Region.Deactivate(injectedView);
                        }
                    }
                }
            }

            if (e.NewPane != null)
            {
                RadPane item = e.NewPane;

                //ContentPane을 직접 다루고 있습니까?
                if (Region.Views.Contains(item) && !Region.ActiveViews.Contains(item))
                {
                    Region.Activate(item);
                }
                else
                {
                    //이제 주입 된 뷰가 있는지 확인
                    ContentControl contentControl = item;
                    if (contentControl != null)
                    {
                        object injectedView = contentControl.Content;
                        if (Region.Views.Contains(injectedView) && !Region.ActiveViews.Contains(injectedView))
                        {
                            Region.Activate(injectedView);
                        }
                    }
                }
            }
        }
    }
}

ShellDockingPanesFactory.cs

RadDocking 컨트롤에 pane이 추가될 때 사용되는 곳으로 어떤 위치에 추가할지를 지정하게 됩니다.

using System.Linq;
using Telerik.Windows.Controls;
using Telerik.Windows.Controls.Docking;

namespace TelerikPrismSample
{
    public class ShellDockingPanesFactory : DockingPanesFactory
    {
        protected override void AddPane(RadDocking radDocking, RadPane pane)
        {
            var paneModel = pane as IPaneModel;
            if (paneModel != null && !(pane is RadPane))
            {
                RadPaneGroup group = null;
                switch (paneModel.Position)
                {
                    case DockState.DockedRight:
                        group = radDocking.SplitItems.ToList()
                            .FirstOrDefault(i => i.Control.Name == "rightGroup") as RadPaneGroup;
                        if (group != null)
                        {
                            group.Items.Add(pane);
                        }
                        return;
                    case DockState.DockedBottom:
                        group = radDocking.SplitItems.ToList()
                            .FirstOrDefault(i => i.Control.Name == "bottomGroup") as RadPaneGroup;
                        if (group != null)
                        {
                            group.Items.Add(pane);
                        }
                        return;
                    case DockState.DockedLeft:
                        group = radDocking.SplitItems.ToList()
                            .FirstOrDefault(i => i.Control.Name == "leftGroup") as RadPaneGroup;
                        if (group != null)
                        {
                            group.Items.Add(pane);
                        }
                        return;
                    case DockState.FloatingDockable:
                        var fdSplitContainer = radDocking.GeneratedItemsFactory.CreateSplitContainer();
                        group = radDocking.GeneratedItemsFactory.CreatePaneGroup();
                        fdSplitContainer.Items.Add(group);
                        group.Items.Add(pane);
                        radDocking.Items.Add(fdSplitContainer);
                        pane.MakeFloatingDockable();
                        return;
                    case DockState.FloatingOnly:
                        var foSplitContainer = radDocking.GeneratedItemsFactory.CreateSplitContainer();
                        group = radDocking.GeneratedItemsFactory.CreatePaneGroup();
                        foSplitContainer.Items.Add(group);
                        group.Items.Add(pane);
                        radDocking.Items.Add(foSplitContainer);
                        pane.MakeFloatingOnly();
                        return;
                    case DockState.DockedTop:
                    default:
                        return;
                }
            }
            else
            {
                var hostGroup = radDocking.SplitItems.ToList()
                    .FirstOrDefault(i => i.Control.Name == "hostGroup") as RadPaneGroup;
                if (hostGroup != null)
                {
                    pane.Header = $"{pane.Content.GetType().Name}";
                    hostGroup.Items.Add(pane);
                    return;
                }
            }

            base.AddPane(radDocking, pane);
        }
    }
}

화면

화면을 완전히 분리해서 다른 창처럼 사용하는 것도 가능합니다.

마침

많은 소스를 등록했었는데, 한번 날려먹어서 핵심 소스만 올리는 것으로 변경했습니다.

전체 소스는 Git을 참고하시면 될 것 같습니다.

kaki104/TelerikPrismSample (github.com)

반응형
댓글