zoukankan      html  css  js  c++  java
  • AvalonDock 2.0+Caliburn.Micro+MahApps.Metro实现Metro风格插件式系统(菜单篇)

      这章主要说插件的菜单,可以说菜单是最核心的部分,前面我们已经实现了Document添加,现在主要就是生成具有层级关系的菜单,以及把菜单跟我们自定义的Document关联起来,也就是MenuPart->View->Model的关联,菜单的实现部分我也是网上参照别人的来实现的,由于代码比较多,我就抽一些重要的来说,其他的只能靠各位自己去体会了,不明白的可以照葫芦画瓢,这菜单部分可以直接抽出来用的,我们不需要明白它内部是怎么实现的,能用就行了,其实有些地方我也没有深入去了解,我们主要的任务是把现有的技术融合成一个可用的插件平台,技术的细节以后有时间再讨论。。。

      额外提示所有关于UI的操作都是以绑定的方式实现的,如果不熟悉WPF以及MVVM模式可能会难以理解    [BY Zengg]

    运行结果:

      可以看到我们已经能做到通过菜单来控制插件的显示以及关闭,插件平台已经初具雏形

    UICore部分

    涉及到核心的项目结构部分:

    IPart 定义了菜单的基本属性,主要的方法是 void Execute(),这是单击菜单后的执行方法, 之后我们得把Show出Document的动作写入Execute方法里面。

     1 namespace UICoreFramework
     2 {
     3     /// <summary>
     4     /// Denotes an instance which can be executed.
     5     /// </summary>
     6     public interface IExecutable
     7     {
     8         bool CanExecute { get; }
     9         void Execute();
    10     }
    11 
    12     /// <summary>
    13     /// Denotes an instance which can be checked.
    14     /// </summary>
    15     public interface ICheckable
    16     {
    17         bool IsCheckable { get; }
    18         bool IsChecked { get; set; }
    19     }
    20 
    21     /// <summary>
    22     /// Denotes a small UI widget that can display and interact.
    23     /// </summary>
    24     public interface IPart : ILocalizableDisplay, IExecutable, IActivate, IDeactivate
    25     {
    26         string InputGestureText { get; set; }
    27 
    28         void OnAttached();
    29         
    30         string Icon { get; set; }
    31 
    32         bool IsVisible { get; set; }
    33     }
    34 
    35     /// <summary>
    36     /// Concrete <see cref="IPart"/> which denotes a <see cref="System.Window.Controls.MenuItem"/> instance.
    37     /// </summary>
    38     public interface IMenuPart : IPart, ISeparaterPart, ICheckable
    39     {
    40 
    41     }
    42 
    43     /// <summary>
    44     /// Denotes a <see cref="System.Window.Controls.Separater"/> instance.
    45     /// </summary>
    46     public interface ISeparaterPart
    47     {
    48         bool IsSeparator { get; }
    49     }
    50 
    51     /// <summary>
    52     /// Denotes a manager class that manage the <see cref="IPart"/>s.
    53     /// </summary>
    54     public interface IPartManager<T> where T : IPart
    55     {
    56         IObservableCollection<T> Items { get; }
    57     }
    58 
    59     /// <summary>
    60     /// Denotes a manager node that holds the <see cref="IObservableCollection"/> item.
    61     /// </summary>
    62     public interface IObservableParent<T>
    63     {
    64         IObservableCollection<T> Items { get; }
    65     }
    66 }

    PartBase IPart的抽象类主要实现了IPart接口以及一些共同的抽象函数。

    namespace UICoreFramework
    {
        /// <summary>
        /// Base <see cref="IPart"/> class for various implementations of <see cref="IPart"/>.
        /// </summary>
        public abstract class PartBase : PropertyChangedBase, IPart
        {
            protected PartBase()
                :this(null)
            {
            }
    
            protected PartBase(string name)
            {
                DisplayName = name;
                this.Name = name ?? GetType().Name.Replace("Part", string.Empty);
                this.execute = ((i) => { });
                this.canExecute = (() => IsActive);
            }
    
            public PartBase(string name, System.Action<PartBase> execute, Func<bool> canExecute = null)
                : this(name)
            {
                this.execute = execute ?? ((i) => { });
                this.canExecute = canExecute ?? (() => IsActive);
            }
    
            private string name;
            public string Name
            {
                get { return name; }
                protected set { name = value; NotifyOfPropertyChange(() => Name); }
            }
    
            private string displayName;
            public string DisplayName
            {
                get { return displayName; }
                set { displayName = value; NotifyOfPropertyChange(() => DisplayName); }
            }
    
            private string icon;
            public string Icon
            {
                get { return icon; }
                set { icon = value; NotifyOfPropertyChange(() => Icon); }
            }
    
            private string inputGestureText;
            public string InputGestureText
            {
                get { return inputGestureText; }
                set { inputGestureText = value; NotifyOfPropertyChange(() => InputGestureText); }
            }
    
            private bool isVisible = true;
            public bool IsVisible
            {
                get { return isVisible; }
                set { isVisible = value; NotifyOfPropertyChange(() => IsVisible); }
            }
    
            public virtual void OnAttached()
            { }
    
            #region IExecutable
    
            private readonly System.Action<PartBase> execute;
            /// <summary>
            /// The action associated to the ActionItem
            /// </summary>
            public virtual void Execute()
            {
                this.execute(this);
            }
    
            private readonly Func<bool> canExecute;
            /// <summary>
            /// Calls the underlying canExecute function.
            /// </summary>
            public virtual bool CanExecute
            {
                get { return canExecute(); }
            }
            #endregion
    
            #region Activation & Deactivation
            public event EventHandler<ActivationEventArgs> Activated;
            public event EventHandler<DeactivationEventArgs> AttemptingDeactivation;
            public event EventHandler<DeactivationEventArgs> Deactivated;
    
            private bool isActive = true;
            public bool IsActive
            {
                get { return isActive; }
            }
    
            public void Activate()
            {
                if (IsActive)
                    return;
    
                isActive = true;
                OnActivate();
                if (Activated != null)
                    Activated(this, new ActivationEventArgs { WasInitialized = false });
                NotifyOfPropertyChange(() => CanExecute);
            }
            protected virtual void OnActivate() { }
    
            public virtual void Deactivate(bool close)
            {
                if (!IsActive)
                    return;
    
                if (AttemptingDeactivation != null)
                    AttemptingDeactivation(this, new DeactivationEventArgs { WasClosed = close });
    
                isActive = false;
                OnDeactivate(close);
                NotifyOfPropertyChange(() => CanExecute);
                if (Deactivated != null)
                    Deactivated(this, new DeactivationEventArgs { WasClosed = close });
            }
            protected virtual void OnDeactivate(bool close) { }
    
            #endregion
    
            #region IHandle<LanguageChangedEventArgs> Members
    
            //public void Handle(LanguageChangedMessage message)
            //{
            //    this.UpdateDisplayName();
            //}
    
            #endregion
    
        }
    }
    PartBase

    MenuPart 继承于PartBase,这个类跟界面的Menu.Item形成对应关系,也就是Menu.Item具有的重要属性MenuPart也基本会有,一个MenuPart等于一个Menu.Item,之后我们会把MenuPart的具体实例绑定到Menu.Item上,这样就实现了MenuPart和界面Menu.Item的关联。

    namespace UICoreFramework
    {
        /// <summary>
        /// A menu part for various implementations of <see cref="IMenuPart"/>.
        /// </summary>
        public class MenuPart : PartBase, IMenuPart, IObservableParent<IMenuPart>
        {
            public MenuPart()
                : base()
            {
            }
    
            public MenuPart(string name)
                : base(name)
            {
    
            }
    
            public MenuPart(string name, System.Action<PartBase> execute, Func<bool> canExecute = null)
                : base(name, execute, canExecute)
            {
            }
    
            private IObservableCollection<IMenuPart> items = new BindableCollection<IMenuPart>();
            IObservableCollection<IMenuPart> IObservableParent<IMenuPart>.Items
            {
                get { return items; }
            }
    
            #region ISeparaterPart Members
    
            private bool _isSeparator = false;
            public bool IsSeparator
            {
                get
                {
                    return _isSeparator;
                }
                protected set
                {
                    _isSeparator = value;
                    NotifyOfPropertyChange(() => IsSeparator);
                }
            }
    
            #endregion
    
            #region IMenuPart Members
    
            private bool _isCheckable = false;
            public bool IsCheckable
            {
                get
                {
                    return _isCheckable;
                }
                set
                {
                    _isCheckable = value;
                    NotifyOfPropertyChange(() => IsCheckable);
                }
            }
    
            private bool _isChecked = false;
            public bool IsChecked
            {
                get
                {
                    return _isChecked;
                }
                set
                {
                    _isChecked = value;
                    NotifyOfPropertyChange(() => IsChecked);
                }
            }
    
            #endregion
        }
    }
    MenuPart

    PartManager MenuPart的管理是由该类处理的,比如菜单的分层,排序等,该类对应着界面的Menu控件,之后会把PartManager绑定到Menu控件。

      1 namespace UICoreFramework
      2 {
      3     /// <summary>
      4     /// Concrete <see cref="IPartManager"/> with manages <see cref="IPart"/> items, it uses MEF to construct the <see cref="IPart"/>s.
      5     /// </summary>
      6     public class PartManager<T> : IPartManager<T>, IPartImportsSatisfiedNotification where T : IPart
      7     {
      8         #region Constructor
      9         
     10         public PartManager()
     11         {
     12         }
     13         
     14         #endregion
     15 
     16         #region Property
     17         
     18         [ImportMany]
     19         protected T[] InternalItems { get; set; }
     20         
     21         #endregion
     22 
     23         #region Method
     24         
     25         protected virtual void ConfigParts()
     26         {
     27             if (InternalItems == null || InternalItems.Length == 0)
     28             {
     29                 return;
     30             }
     31 
     32             items.Clear();
     33             items.AddRange(InternalItems);
     34         }
     35         
     36         #endregion
     37 
     38         #region IPartManager Members
     39 
     40         private IObservableCollection<T> items = new BindableCollection<T>();
     41         public IObservableCollection<T> Items
     42         {
     43             get { return items; }
     44         }
     45 
     46         #endregion
     47 
     48         #region IPartImportsSatisfiedNotification Members
     49 
     50         public void OnImportsSatisfied()
     51         {
     52             ConfigParts();
     53         }
     54 
     55         #endregion
     56     }
     57 
     58     /// <summary>
     59     /// Extends the <see cref="IPartManager"/> that support <see cref="IPartMetaData"/>.
     60     /// </summary>
     61     /// <typeparam name="T">IPart</typeparam>
     62     /// <typeparam name="TMetadata">The type of the metadata.</typeparam>
     63     public class PartManager<T, TMetadata> : IPartManager<T>, IPartImportsSatisfiedNotification
     64         where T : IPart
     65         where TMetadata : IPartMetaData
     66     {
     67         #region Field
     68         
     69         protected static readonly Func<TMetadata, string> BasePart;
     70         protected static readonly Func<TMetadata, string> PreviousPart;
     71         protected static readonly Func<TMetadata, string> NextPart;
     72         
     73         #endregion
     74 
     75         #region Constructor
     76         
     77         static PartManager()
     78         {
     79             var props = typeof(TMetadata).GetProperties();
     80             BasePart = DynamicAccessEngine.GetPropertyDelegate<TMetadata, string>(props.FirstOrDefault(it => it.Name.Contains("Base")).Name);
     81             PreviousPart = DynamicAccessEngine.GetPropertyDelegate<TMetadata, string>(props.FirstOrDefault(it => it.Name.Contains("Previous")).Name);
     82             NextPart = DynamicAccessEngine.GetPropertyDelegate<TMetadata, string>(props.FirstOrDefault(it => it.Name.Contains("Next")).Name);
     83         }
     84 
     85         public PartManager()
     86         {
     87         }
     88         
     89         #endregion
     90 
     91         #region Property
     92 
     93         [ImportMany]
     94         protected Lazy<T, TMetadata>[] InternalItems
     95         {
     96             get;
     97             set;
     98         }
     99         
    100         #endregion
    101 
    102         #region Method
    103         
    104         protected virtual void ConfigParts()
    105         {
    106             if (InternalItems == null || InternalItems.Length == 0)
    107             {
    108                 return;
    109             }
    110 
    111             items.Clear();
    112 
    113             //Sort items according to metadata's Base , Previous, Next value
    114             SortItems();
    115         }
    116 
    117         protected virtual void SortItems()
    118         {
    119             var items = InternalItems.Select((it) =>
    120             {
    121                 return new OrderItem<T>()
    122                 {
    123                     Base = BasePart(it.Metadata),
    124                     Before = PreviousPart(it.Metadata),
    125                     After = NextPart(it.Metadata),
    126                     Value = it.Value
    127                 };
    128             }).ToList();
    129 
    130             var roots = SortAndAttachItems(items.Where(it => string.IsNullOrEmpty(it.Base)).ToList());
    131 
    132             foreach (var item in items)
    133             {
    134                 var baseItem = items.FirstOrDefault(it => string.Equals(it.Value.Name, item.Base));
    135                 if (baseItem != null)
    136                 {
    137                     baseItem.Children.Add(item);
    138                 }
    139             }
    140 
    141             foreach (var item in roots)
    142             {
    143                 SortItem(item);
    144             }
    145 
    146             Items.AddRange(roots.Select(it => it.Value));
    147         }
    148 
    149         private void SortItem(OrderItem<T> item)
    150         {
    151             if (item.Children.Count == 0)
    152             {
    153                 return;
    154             }
    155 
    156             //1. Child recursion.
    157             foreach (var it in item.Children)
    158             {
    159                 SortItem(it);
    160             }
    161 
    162             //2. Sort
    163             var sortedItems = SortAndAttachItems(item.Children);
    164 
    165             foreach (var it in sortedItems)
    166             {
    167                 IObservableParent<T> parent = item.Value as IObservableParent<T>;
    168                 if (parent != null)
    169                 {
    170                     parent.Items.Add(it.Value);
    171                 }
    172             }
    173         }
    174 
    175         private List<OrderItem<T>> SortAndAttachItems(List<OrderItem<T>> items)
    176         {
    177             //1. Sort
    178             var sortedItems = new List<OrderItem<T>>();
    179             var unsortedItems = new List<OrderItem<T>>();
    180             foreach (var newItem in items)
    181             {
    182                 if (string.IsNullOrEmpty(newItem.Before) && string.IsNullOrEmpty(newItem.After))
    183                 {
    184                     sortedItems.Add(newItem);
    185                 }
    186                 else
    187                 {
    188                     unsortedItems.Add(newItem);
    189                 }
    190             }
    191 
    192             while (unsortedItems.Count > 0)
    193             {
    194                 List<OrderItem<T>> stillUnsortedItems = new List<OrderItem<T>>();
    195                 int startingCount = unsortedItems.Count;
    196                 foreach (var newItem in unsortedItems)
    197                 {
    198                     if (!string.IsNullOrEmpty(newItem.After))
    199                     {
    200                         var beforeItem = sortedItems.FirstOrDefault(it => it.Value.Name == newItem.After);
    201                         if (beforeItem != null)
    202                         {
    203                             sortedItems.Insert(sortedItems.IndexOf(beforeItem), newItem);
    204                         }
    205                         else
    206                         {
    207                             stillUnsortedItems.Add(newItem);
    208                         }
    209                     }
    210                     else
    211                     {
    212                         var afterItem = sortedItems.FirstOrDefault(it => it.Value.Name == newItem.Before);
    213                         if (afterItem != null)
    214                         {
    215                             int index = sortedItems.IndexOf(afterItem);
    216                             if (index == sortedItems.Count - 1)
    217                             {
    218                                 sortedItems.Add(newItem);
    219                             }
    220                             else
    221                             {
    222                                 sortedItems.Insert(index + 1, newItem);
    223                             }
    224                         }
    225                         else
    226                         {
    227                             stillUnsortedItems.Add(newItem);
    228                         }
    229                     }
    230                 }
    231                 if (startingCount == stillUnsortedItems.Count)
    232                 {
    233                     sortedItems.Add(stillUnsortedItems[0]);
    234                     stillUnsortedItems.RemoveAt(0);
    235                 }
    236                 unsortedItems = stillUnsortedItems;
    237             }
    238 
    239             //2. Call Attached method of IPart
    240             sortedItems.Apply(o => o.Value.OnAttached());
    241 
    242             return sortedItems;
    243         }
    244         
    245         #endregion
    246 
    247         #region IPartManager Members
    248 
    249         private IObservableCollection<T> items = new BindableCollection<T>();
    250         public IObservableCollection<T> Items
    251         {
    252             get { return items; }
    253         }
    254 
    255         #endregion
    256 
    257         #region IPartImportsSatisfiedNotification Members
    258 
    259         public void OnImportsSatisfied()
    260         {
    261             ConfigParts();
    262         }
    263 
    264         #endregion
    265 
    266         #region Private OrderItem Class
    267 
    268         private class OrderItem<U>
    269         {
    270             public string Base { get; set; }
    271             public string Before { get; set; }
    272             public string After { get; set; }
    273             public U Value { get; set; }
    274 
    275             private List<OrderItem<U>> children = new List<OrderItem<U>>();
    276             public List<OrderItem<U>> Children
    277             {
    278                 get
    279                 {
    280                     return children;
    281                 }
    282             }
    283         }
    284 
    285         #endregion
    286     }
    PartManager

    Util文件夹里的类是提供更方便的绑定方式,这里就不做过多解释

    DemoApplication 主程序部分结构:

    可以看到Menu放的就是我们的各个菜单,主要看一下AddinPart,这个就是我们从界面上看到有"插件"两个字的菜单

    AddinPart

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using UICoreFramework;
    //=================================================================================
    //
    //        Copyright (C) 20013-2014    
    //        All rights reserved
    //        
    //        description : 本系列于博客园首发,如果您想转载本博客,请注明出处,感谢支持  
    //        created by Zengg
    //        http://www.cnblogs.com/01codeworld/
    //        Email:281365330@qq.com
    //==================================================================================
    namespace DemoApplication.Menu
    {
        //public string MenuName { get; set; } //菜单名称
    
        //public string BaseMenu { get; set; }//父菜单名称
    
        //public string PreviousMenu { get; set; }//该入哪个菜单名的后面
    
        //public string NextMenu { get; set; }//下一个菜单是什么
    
        //PartManager会以注入的元数据做为依据进行菜单层级的排序
        [MenuPart(PreviousMenu = "工具")]
        public class AddinPart : MenuPart
        {
            public AddinPart()
                : base("插件")
            {
    
            }
        }
    }


    在AddinPart下面我们可以看到已经有了3个插件,工具栏(DockableContent类型),测试界面,测试插件2,

    这3个插件分别对应着

    DockableTestPart DocTestViewPart DocTest1ViewPart

     1 //=================================================================================
     2 //
     3 //        Copyright (C) 20013-2014    
     4 //        All rights reserved
     5 //        
     6 //        description : 本系列于博客园首发,如果您想转载本博客,请注明出处,感谢支持  
     7 //        created by Zengg
     8 //        http://www.cnblogs.com/01codeworld/
     9 //        Email:281365330@qq.com
    10 //==================================================================================
    11 namespace DemoApplication.Menu
    12 {
    13     [MenuPart(BaseMenu="插件")]
    14     public class DockableTestPart : MenuPart
    15     {
    16          public DockableTestPart()
    17             : base("工具栏")
    18         {
    19 
    20         }
    21         public override void Execute()
    22         {
    23             IoC.Get<IDockScreenManager>()["工具栏"].Show();
    24         }
    25     }
    26 }
    27 
    28 namespace DemoApplication.Menu
    29 {
    30     [MenuPart(BaseMenu="插件")]
    31     public class DocTestViewPart : MenuPart
    32     {
    33         public DocTestViewPart()
    34             : base("测试界面")
    35         {
    36 
    37         }
    38         public override void Execute()
    39         {
    40             IoC.Get<IDockScreenManager>()["测试界面"].Show();
    41         }
    42     }
    43 }
    44 
    45 namespace DemoApplication.Menu
    46 {
    47     [MenuPart(BaseMenu="插件")]
    48     public class DocTest1ViewPart : MenuPart
    49     {
    50         public DocTest1ViewPart()
    51             : base("测试插件2")
    52         {
    53 
    54         }
    55         public override void Execute()
    56         {
    57             IoC.Get<IDockScreenManager>()["测试插件2"].Show();
    58         }
    59     }
    60 }


    Execute执行的就是IDockScreen接口的Show方法,执行这个方法后对应的UI部件就会显示出来。

    IDockScreen接口增加部分

        /// <summary>
        /// 抽象出基本的Content类型,并实现属性通知接口,因为以后绑定到AvalonDock是有双向绑定的,这样我们要操作AvalonDock时
        /// 就可以直接操作实现该接口的属性,比如DisplayName的属性用于绑定到AvalonDock的LayoutItem的Title
        /// </summary>
        public interface IDockScreen
        {
            DockType Type { get; set; }
            DockSide Side { get; set; }
            ICommand CloseCommand { get; }//双向绑定AvalonDock的LayoutItem的CloseCommand命令
            string DisplayName { get; set; }
            bool IsActive { get; set; }//双向绑定AvalonDock的LayoutItem的IsActive属性
            bool IsSelected { get; set; }//双向绑定AvalonDock的LayoutItem的IsSelected属性
            void Show();//把控件显示出来
            void Close();//把控件关闭
        }


    接下来主要说到,怎么把IDockScreen类型和AvalonDock里的Document ,Anchorable进行双向绑定,我们来看一看DockView.xaml [BY Zengg]

    <UserControl x:Class="DemoApplication.Views.DockView"
                 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
                 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
                 xmlns:Micro="http://www.caliburnproject.org"
                 xmlns:UICore="clr-namespace:UICoreFramework;assembly=UICoreFramework"
                 mc:Ignorable="d" 
                 xmlns:avalonDock="http://avalondock.codeplex.com"
                 d:DesignHeight="300" d:DesignWidth="800">
        <UserControl.Resources>
            <avalonDock:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter"/>
        </UserControl.Resources>
        <Grid>
            <Grid x:Name="layoutRoot">
                <avalonDock:DockingManager DocumentsSource="{Binding Documents}" AnchorablesSource="{Binding DockableContents}"  Grid.Row="1" x:Name="dockManager"  AllowMixedOrientation="True"  >
                    <avalonDock:DockingManager.Theme>
                        <avalonDock:AeroTheme/>
                    </avalonDock:DockingManager.Theme>
                    <avalonDock:DockingManager.LayoutItemTemplate>
                        <DataTemplate>
                            <ContentControl Micro:View.Model="{Binding}" IsTabStop="False" />
                        </DataTemplate>
                    </avalonDock:DockingManager.LayoutItemTemplate>
                    <avalonDock:DockingManager.LayoutItemContainerStyleSelector>
                        <UICore:PanesStyleSelector>
                            <UICore:PanesStyleSelector.ToolStyle>
                                <Style TargetType="{x:Type avalonDock:LayoutAnchorableItem}">
                                    <Setter Property="Title" Value="{Binding Model.DisplayName}"/>
                                    <Setter Property="CloseCommand" Value="{Binding Model.CloseCommand}"></Setter>
                                    <Setter Property="IsActive" Value="{Binding Model.IsActive,Mode=TwoWay}"></Setter>
                                    <Setter Property="Visibility" Value="{Binding Model.Visibility,Mode=TwoWay, Converter={StaticResource BoolToVisibilityConverter}, ConverterParameter={x:Static Visibility.Hidden}}"></Setter>
                                </Style>
                                </UICore:PanesStyleSelector.ToolStyle>
                            <UICore:PanesStyleSelector.DocumentStyle>
                                <Style TargetType="{x:Type avalonDock:LayoutItem}">
                                    <Setter Property="Title" Value="{Binding Model.DisplayName}"/>
                                    <Setter Property="CloseCommand" Value="{Binding Model.CloseCommand}"></Setter>
                                    <Setter Property="IsActive" Value="{Binding Model.IsActive,Mode=TwoWay}"></Setter>
                                </Style>
                                </UICore:PanesStyleSelector.DocumentStyle>
                                    </UICore:PanesStyleSelector>
                    </avalonDock:DockingManager.LayoutItemContainerStyleSelector>
                    <avalonDock:LayoutRoot>
                        <avalonDock:LayoutPanel  Orientation="Horizontal"  >
                            <avalonDock:LayoutAnchorablePaneGroup DockWidth="200"    Orientation="Vertical"  >
                                <avalonDock:LayoutAnchorablePane  >
                                </avalonDock:LayoutAnchorablePane>
                            </avalonDock:LayoutAnchorablePaneGroup>
                            <avalonDock:LayoutPanel  Orientation="Vertical"  >
                                <avalonDock:LayoutDocumentPaneGroup Orientation="Horizontal">
                                    <avalonDock:LayoutDocumentPane   >
                                    </avalonDock:LayoutDocumentPane>
    
                                </avalonDock:LayoutDocumentPaneGroup>
                                <avalonDock:LayoutAnchorablePaneGroup DockHeight="100"   Orientation="Horizontal"  >
                                    <avalonDock:LayoutAnchorablePane  >
    
                                    </avalonDock:LayoutAnchorablePane>
                                </avalonDock:LayoutAnchorablePaneGroup>
                            </avalonDock:LayoutPanel>
    
                            <avalonDock:LayoutAnchorablePaneGroup DockWidth="200"    Orientation="Horizontal"  >
                                <avalonDock:LayoutAnchorablePane >
                                </avalonDock:LayoutAnchorablePane>
                            </avalonDock:LayoutAnchorablePaneGroup>
                        </avalonDock:LayoutPanel>
    
                    </avalonDock:LayoutRoot>
                </avalonDock:DockingManager>
            </Grid>
        </Grid>
    </UserControl>

    在 <avalonDock:DockingManager.LayoutItemContainerStyleSelector> </avalonDock:DockingManager.LayoutItemContainerStyleSelector>区域里的部分,就是对Document ,Anchorable两种类型进行双向绑定。

    Menu

    我们的View文件夹多出了MenuView.xaml,MenuViewModel两个文件,这就是我们的菜单,来看看PartManager是怎么绑定到MenuView.xaml的

    MenuView.xaml

    <UserControl x:Class="DemoApplication.Views.MenuView"
                 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
                 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
                 xmlns:fs="clr-namespace:UICoreFramework;assembly=UICoreFramework"
                 xmlns:Micro="http://www.caliburnproject.org"
                 mc:Ignorable="d" 
               d:DesignHeight="25" d:DesignWidth="200">
        <UserControl.Resources>
            <ResourceDictionary >
                <ResourceDictionary.MergedDictionaries>
                </ResourceDictionary.MergedDictionaries>
                <BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
                <fs:GenericBindingConverter x:Key ="GenericBindingConverter"/>
                    <LinearGradientBrush x:Key="AvalonDock_ThemeAero_BaseColor1"
                            StartPoint="0,0"
                            EndPoint="1,0">
            <LinearGradientBrush.GradientStops>
                <GradientStop Color="#EBEEFA" Offset="0" />
                <GradientStop Color="#F4F7FC" Offset="1" />
            </LinearGradientBrush.GradientStops>
        </LinearGradientBrush>
            </ResourceDictionary>
        </UserControl.Resources>
        <Grid  >
            <Menu Name="MainMenu"  Background="{x:Null}"  ItemsSource="{Binding Items}" >
                <Menu.ItemContainerStyle>
                    <Style TargetType="MenuItem" >
                        <Setter Property="Header" Value="{Binding DisplayName}" />
                        <Setter Property="IsCheckable" Value="{Binding IsCheckable}" />
                        <Setter Property="InputGestureText" Value="{Binding Path=InputGestureText}"/>
                        <Setter Property="IsChecked" Value="{Binding IsChecked, Mode=OneWay}" />
                        <Setter Property="ItemsSource" Value="{Binding RelativeSource={RelativeSource Self}, Path=DataContext, Converter={StaticResource GenericBindingConverter}, ConverterParameter=IObservableParent&lt;IMenuPart&gt;.Items}" />
                        <Setter Property="IsEnabled" Value="{Binding CanExecute}" />
                        <Setter Property="Visibility" Value="{Binding IsVisible, Converter={StaticResource BooleanToVisibilityConverter}}" />
                        <Setter Property="Micro:Message.Attach" Value="[Event Click] = [Action Execute()]" />
                        <Style.Triggers>
                            <DataTrigger Binding="{Binding Path=IsSeparator}" 
                                   Value="True">
                                <Setter Property="Template">
                                    <Setter.Value>
                                        <ControlTemplate TargetType="{x:Type MenuItem}">
                                            <Separator 
                             Style="{DynamicResource {x:Static MenuItem.SeparatorStyleKey}}"/>
                                        </ControlTemplate>
                                    </Setter.Value>
                                </Setter>
                            </DataTrigger>
                        </Style.Triggers>
                    </Style>
                </Menu.ItemContainerStyle>
            </Menu>
        </Grid>
    </UserControl>

    MenuViewModel

    //=================================================================================
    //
    //        Copyright (C) 20013-2014    
    //        All rights reserved
    //        
    //        description : 本系列于博客园首发,如果您想转载本博客,请注明出处,感谢支持  
    //        created by Zengg
    //        http://www.cnblogs.com/01codeworld/
    //        Email:281365330@qq.com
    //==================================================================================
    namespace DemoApplication.Views
    {
        [Export(typeof(IPartManager<IMenuPart>))]
        public class MenuViewModel : PartManager<IMenuPart, IMenuPartMetaData>
        {
            
        }
    }

    自定义菜单定义好了,我们要把它绑定到ShellView里面

    <MetrolControls:MetroWindow x:Class="DemoApplication.ShellView"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:MetrolControls="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"
            Title="ShellView" Height="500" Width="800">
        <Window.Resources>
            <ResourceDictionary>
                <ResourceDictionary.MergedDictionaries>
                    <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Colours.xaml"/>
                    <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Fonts.xaml"/>
                    <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Controls.xaml"/>
                    
                </ResourceDictionary.MergedDictionaries>
                <LinearGradientBrush x:Key="AvalonDock_ThemeAero_BaseColor1"
                            StartPoint="0,0"
                            EndPoint="1,0">
                    <LinearGradientBrush.GradientStops>
                        <GradientStop Color="#EBEEFA" Offset="0" />
                        <GradientStop Color="#F4F7FC" Offset="1" />
                    </LinearGradientBrush.GradientStops>
                </LinearGradientBrush>
            </ResourceDictionary>
        </Window.Resources>
        <Grid Background="{StaticResource AvalonDock_ThemeAero_BaseColor1}" >
            <Grid.RowDefinitions>
                <RowDefinition Height="30"/>
                <RowDefinition Height="*"/>
                <RowDefinition Height="30"/>
            </Grid.RowDefinitions>
            <ContentControl  x:Name="MenuContent" Margin="0,2,0,0" Grid.Row="0"/>
            <ContentControl  x:Name="DockContent" Margin="0,2,0,0" Grid.Row="1"/>
        </Grid>
    </MetrolControls:MetroWindow>

    MenuContent就是MenuView  [BY Zengg]

    ShellViewModel

    //=================================================================================
    //
    //        Copyright (C) 20013-2014    
    //        All rights reserved
    //        
    //        description : 本系列于博客园首发,如果您想转载本博客,请注明出处,感谢支持  
    //        created by Zengg
    //        http://www.cnblogs.com/01codeworld/
    //        Email:281365330@qq.com
    //==================================================================================
    namespace DemoApplication
    {
        [Export(typeof(IShell))]
        class ShellViewModel : IShell
        {
            readonly IWindowManager windowManager;
            [ImportingConstructor]
            public ShellViewModel(IWindowManager windowManager)
            {
                this.windowManager = windowManager;
    
            }
            /// <summary>
            /// DockView
            /// </summary>
            [Import]
            public IDockScreenManager DockContent { get; set; }
    
            [Import]
            public IPartManager<IMenuPart> MenuContent { get; set; }
        }
    }


      大致的流程就是这样,我也说不了多细致毕竟也有一定的代码量,一个个来说不知道得写多久,大家自己研究源码吧!涉及到的基本知识:WPF的数据绑定,MVVM,MEF,如果明白这些基本知识,理解代码应该不难,我也是边更新代码边写博客,这只是给大家提供一个思路,如果想要更完善得大家自己去扩展完善,如果发现有错误的地方请及时告知。。非常感谢

    章节预告:

    4:寻找插件并加载插件

    5:多语言化

    6:换肤功能

    第三部分源码:http://pan.baidu.com/share/link?shareid=859524889&uk=554439928

    如果您看了本篇博客,觉得对您有所收获,请点击右下角的 [推荐]

    如果您想转载本博客,请注明出处

    如果您对本文有意见或者建议,欢迎留言

    感谢您的阅读,请关注我的后续博客

    作者:Zengg 

    出处:http://www.cnblogs.com/01codeworld/

    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

  • 相关阅读:
    Django Rest framework 之 节流
    Django Rest framework 之 权限
    Django Rest framework 之 认证
    url参数和字典的相互转化
    Ajax之跨域请求
    爬虫之爬取B站关键字
    Django之ModelForm组件
    C语言逻辑运算符顺序
    2.4.4 N-S流程图表示法
    2.4.3 三种基本结构和改进的流程图
  • 原文地址:https://www.cnblogs.com/01codeworld/p/3159785.html
Copyright © 2011-2022 走看看