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

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

运行结果:

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

UICore部分

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

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

 namespace UICoreFramework
{
/// <summary>
/// Denotes an instance which can be executed.
/// </summary>
public interface IExecutable
{
bool CanExecute { get; }
void Execute();
} /// <summary>
/// Denotes an instance which can be checked.
/// </summary>
public interface ICheckable
{
bool IsCheckable { get; }
bool IsChecked { get; set; }
} /// <summary>
/// Denotes a small UI widget that can display and interact.
/// </summary>
public interface IPart : ILocalizableDisplay, IExecutable, IActivate, IDeactivate
{
string InputGestureText { get; set; } void OnAttached(); string Icon { get; set; } bool IsVisible { get; set; }
} /// <summary>
/// Concrete <see cref="IPart"/> which denotes a <see cref="System.Window.Controls.MenuItem"/> instance.
/// </summary>
public interface IMenuPart : IPart, ISeparaterPart, ICheckable
{ } /// <summary>
/// Denotes a <see cref="System.Window.Controls.Separater"/> instance.
/// </summary>
public interface ISeparaterPart
{
bool IsSeparator { get; }
} /// <summary>
/// Denotes a manager class that manage the <see cref="IPart"/>s.
/// </summary>
public interface IPartManager<T> where T : IPart
{
IObservableCollection<T> Items { get; }
} /// <summary>
/// Denotes a manager node that holds the <see cref="IObservableCollection"/> item.
/// </summary>
public interface IObservableParent<T>
{
IObservableCollection<T> Items { get; }
}
}

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控件。

 namespace UICoreFramework
{
/// <summary>
/// Concrete <see cref="IPartManager"/> with manages <see cref="IPart"/> items, it uses MEF to construct the <see cref="IPart"/>s.
/// </summary>
public class PartManager<T> : IPartManager<T>, IPartImportsSatisfiedNotification where T : IPart
{
#region Constructor public PartManager()
{
} #endregion #region Property [ImportMany]
protected T[] InternalItems { get; set; } #endregion #region Method protected virtual void ConfigParts()
{
if (InternalItems == null || InternalItems.Length == )
{
return;
} items.Clear();
items.AddRange(InternalItems);
} #endregion #region IPartManager Members private IObservableCollection<T> items = new BindableCollection<T>();
public IObservableCollection<T> Items
{
get { return items; }
} #endregion #region IPartImportsSatisfiedNotification Members public void OnImportsSatisfied()
{
ConfigParts();
} #endregion
} /// <summary>
/// Extends the <see cref="IPartManager"/> that support <see cref="IPartMetaData"/>.
/// </summary>
/// <typeparam name="T">IPart</typeparam>
/// <typeparam name="TMetadata">The type of the metadata.</typeparam>
public class PartManager<T, TMetadata> : IPartManager<T>, IPartImportsSatisfiedNotification
where T : IPart
where TMetadata : IPartMetaData
{
#region Field protected static readonly Func<TMetadata, string> BasePart;
protected static readonly Func<TMetadata, string> PreviousPart;
protected static readonly Func<TMetadata, string> NextPart; #endregion #region Constructor static PartManager()
{
var props = typeof(TMetadata).GetProperties();
BasePart = DynamicAccessEngine.GetPropertyDelegate<TMetadata, string>(props.FirstOrDefault(it => it.Name.Contains("Base")).Name);
PreviousPart = DynamicAccessEngine.GetPropertyDelegate<TMetadata, string>(props.FirstOrDefault(it => it.Name.Contains("Previous")).Name);
NextPart = DynamicAccessEngine.GetPropertyDelegate<TMetadata, string>(props.FirstOrDefault(it => it.Name.Contains("Next")).Name);
} public PartManager()
{
} #endregion #region Property [ImportMany]
protected Lazy<T, TMetadata>[] InternalItems
{
get;
set;
} #endregion #region Method protected virtual void ConfigParts()
{
if (InternalItems == null || InternalItems.Length == )
{
return;
} items.Clear(); //Sort items according to metadata's Base , Previous, Next value
SortItems();
} protected virtual void SortItems()
{
var items = InternalItems.Select((it) =>
{
return new OrderItem<T>()
{
Base = BasePart(it.Metadata),
Before = PreviousPart(it.Metadata),
After = NextPart(it.Metadata),
Value = it.Value
};
}).ToList(); var roots = SortAndAttachItems(items.Where(it => string.IsNullOrEmpty(it.Base)).ToList()); foreach (var item in items)
{
var baseItem = items.FirstOrDefault(it => string.Equals(it.Value.Name, item.Base));
if (baseItem != null)
{
baseItem.Children.Add(item);
}
} foreach (var item in roots)
{
SortItem(item);
} Items.AddRange(roots.Select(it => it.Value));
} private void SortItem(OrderItem<T> item)
{
if (item.Children.Count == )
{
return;
} //1. Child recursion.
foreach (var it in item.Children)
{
SortItem(it);
} //2. Sort
var sortedItems = SortAndAttachItems(item.Children); foreach (var it in sortedItems)
{
IObservableParent<T> parent = item.Value as IObservableParent<T>;
if (parent != null)
{
parent.Items.Add(it.Value);
}
}
} private List<OrderItem<T>> SortAndAttachItems(List<OrderItem<T>> items)
{
//1. Sort
var sortedItems = new List<OrderItem<T>>();
var unsortedItems = new List<OrderItem<T>>();
foreach (var newItem in items)
{
if (string.IsNullOrEmpty(newItem.Before) && string.IsNullOrEmpty(newItem.After))
{
sortedItems.Add(newItem);
}
else
{
unsortedItems.Add(newItem);
}
} while (unsortedItems.Count > )
{
List<OrderItem<T>> stillUnsortedItems = new List<OrderItem<T>>();
int startingCount = unsortedItems.Count;
foreach (var newItem in unsortedItems)
{
if (!string.IsNullOrEmpty(newItem.After))
{
var beforeItem = sortedItems.FirstOrDefault(it => it.Value.Name == newItem.After);
if (beforeItem != null)
{
sortedItems.Insert(sortedItems.IndexOf(beforeItem), newItem);
}
else
{
stillUnsortedItems.Add(newItem);
}
}
else
{
var afterItem = sortedItems.FirstOrDefault(it => it.Value.Name == newItem.Before);
if (afterItem != null)
{
int index = sortedItems.IndexOf(afterItem);
if (index == sortedItems.Count - )
{
sortedItems.Add(newItem);
}
else
{
sortedItems.Insert(index + , newItem);
}
}
else
{
stillUnsortedItems.Add(newItem);
}
}
}
if (startingCount == stillUnsortedItems.Count)
{
sortedItems.Add(stillUnsortedItems[]);
stillUnsortedItems.RemoveAt();
}
unsortedItems = stillUnsortedItems;
} //2. Call Attached method of IPart
sortedItems.Apply(o => o.Value.OnAttached()); return sortedItems;
} #endregion #region IPartManager Members private IObservableCollection<T> items = new BindableCollection<T>();
public IObservableCollection<T> Items
{
get { return items; }
} #endregion #region IPartImportsSatisfiedNotification Members public void OnImportsSatisfied()
{
ConfigParts();
} #endregion #region Private OrderItem Class private class OrderItem<U>
{
public string Base { get; set; }
public string Before { get; set; }
public string After { get; set; }
public U Value { get; set; } private List<OrderItem<U>> children = new List<OrderItem<U>>();
public List<OrderItem<U>> Children
{
get
{
return children;
}
}
} #endregion
}

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

 //=================================================================================
//
// Copyright (C) 20013-2014
// All rights reserved
//
// description : 本系列于博客园首发,如果您想转载本博客,请注明出处,感谢支持
// created by Zengg
// http://www.cnblogs.com/01codeworld/
// Email:281365330@qq.com
//==================================================================================
namespace DemoApplication.Menu
{
[MenuPart(BaseMenu="插件")]
public class DockableTestPart : MenuPart
{
public DockableTestPart()
: base("工具栏")
{ }
public override void Execute()
{
IoC.Get<IDockScreenManager>()["工具栏"].Show();
}
}
} namespace DemoApplication.Menu
{
[MenuPart(BaseMenu="插件")]
public class DocTestViewPart : MenuPart
{
public DocTestViewPart()
: base("测试界面")
{ }
public override void Execute()
{
IoC.Get<IDockScreenManager>()["测试界面"].Show();
}
}
} namespace DemoApplication.Menu
{
[MenuPart(BaseMenu="插件")]
public class DocTest1ViewPart : MenuPart
{
public DocTest1ViewPart()
: base("测试插件2")
{ }
public override void Execute()
{
IoC.Get<IDockScreenManager>()["测试插件2"].Show();
}
}
}

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="" d:DesignWidth="">
<UserControl.Resources>
<avalonDock:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter"/>
</UserControl.Resources>
<Grid>
<Grid x:Name="layoutRoot">
<avalonDock:DockingManager DocumentsSource="{Binding Documents}" AnchorablesSource="{Binding DockableContents}" Grid.Row="" 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="" 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="" Orientation="Horizontal" >
<avalonDock:LayoutAnchorablePane > </avalonDock:LayoutAnchorablePane>
</avalonDock:LayoutAnchorablePaneGroup>
</avalonDock:LayoutPanel> <avalonDock:LayoutAnchorablePaneGroup DockWidth="" 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="" d:DesignWidth="">
<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="" />
<GradientStop Color="#F4F7FC" Offset="" />
</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="" Width="">
<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="" />
<GradientStop Color="#F4F7FC" Offset="" />
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</ResourceDictionary>
</Window.Resources>
<Grid Background="{StaticResource AvalonDock_ThemeAero_BaseColor1}" >
<Grid.RowDefinitions>
<RowDefinition Height=""/>
<RowDefinition Height="*"/>
<RowDefinition Height=""/>
</Grid.RowDefinitions>
<ContentControl x:Name="MenuContent" Margin="0,2,0,0" Grid.Row=""/>
<ContentControl x:Name="DockContent" Margin="0,2,0,0" Grid.Row=""/>
</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/

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

AvalonDock 2.0+Caliburn.Micro+MahApps.Metro实现Metro风格插件式系统(菜单篇)的更多相关文章

  1. AvalonDock 2.0+Caliburn.Micro+MahApps.Metro实现Metro风格插件式系统(一)

    随着IOS7由之前UI的拟物化设计变为如今的扁平化设计,也许扁平化的时代要来了,当然我们是不是该吐槽一下,苹果什么时候也开始跟风了,自GOOGLE和微软界面扁平化过后,苹果也加入了这一队伍. Aval ...

  2. AvalonDock 2.0+Caliburn.Micro+MahApps.Metro实现Metro风格插件式系统(二)

    上次已经建立了可运行的基本框架,这篇就说说怎么把我们自定义的View自动加载并添加到AvalonDock里面,AvalonDock里有3种类型的UI部件,Document, DockableConte ...

  3. Caliburn.Micro 杰的入门教程2 ,了解Data Binding 和 Events(翻译)

    Caliburn.Micro 杰的入门教程1(翻译)Caliburn.Micro 杰的入门教程2 ,了解Data Binding 和 Events(翻译)Caliburn.Micro 杰的入门教程3, ...

  4. 从0到1:使用Caliburn.Micro(WPF和MVVM)开发简单的计算器

    从0到1:使用Caliburn.Micro(WPF和MVVM)开发简单的计算器 之前时间一直在使用Caliburn.Micro这种应用了MVVM模式的WPF框架做开发,是时候总结一下了. Calibu ...

  5. WPF +MVVM(Caliburn.Micro)项目框架

    最近做了一个软件,这个软件不是网站,但是与HTML,AJAX等技术密切相关,也不是只有单纯的数据库增删改查,还涉及到线程协调,比较复杂的文本处理…… 这样的软件,用OA,ERP的框架显然是不合适的,因 ...

  6. Caliburn.Micro学习笔记(一)----引导类和命名匹配规则

    Caliburn.Micro学习笔记目录 用了几天时间看了一下开源框架Caliburn.Micro 这是他源码的地址http://caliburnmicro.codeplex.com/ 文档也写的很详 ...

  7. Caliburn.Micro学习笔记(二)----Actions

    Caliburn.Micro学习笔记目录 上一篇已经简单说了一下引导类和简单的控件绑定 我的上一个例子里的button自动匹配到ViewModel事件你一定感觉很好玩吧 今天说一下它的Actions, ...

  8. Xamarin 的 MVVM 之 Caliburn.Micro

    约定 Caliburn.Micro 以下简称 CMXamarin.Form 以下简称 XF 摘要CM 当前已释出 3.0 beta 版https://github.com/Caliburn-Micro ...

  9. 开源框架Caliburn.Micro

    Caliburn.Micro学习笔记----引导类和命名匹配规则   用了几天时间看了一下开源框架Caliburn.Micro 这是他源码的地址http://caliburnmicro.codeple ...

随机推荐

  1. 日历控件table布局

    作为初学者,一开始就接触div+css ,所以说实话,我并不怎么喜欢table布局,一般逃避. 先上这次的效果图: 看到这个图,第一次用table布局没实现,原因是给tr加下边框失效.当时没找到原因, ...

  2. IOS小知识纪录

    1.scrollView缩放 #import "ViewController.h" @interface ViewController () <UIScrollViewDel ...

  3. Win7 服务优化个人单机版

    我的PC设备比较旧了,为了系统能流畅点,不必要的服务就不开启了.然而,服务那么多,每次重装,都要从头了解一下一边,浪费时间. 个人在网络上收集信息并结合自己的摸索,整理如下,以备查找. 服务名称  显 ...

  4. iOS6定位服务编程详解

    现在的移动设备很多都提供定位服务,使用iOS系统的iPhone.iPod Touch和iPad都可以提供位置服务,iOS设备能提供3种不同途径进行定位:Wifi, 蜂窝式移动电话基站, GPS卫星 i ...

  5. Connected_Component Labelling(联通区域标记算法) C++实现

    // Connected-Component Labelling.cpp : 定义控制台应用程序的入口点.//如有是使用,请务必注明引用出处网站:http://www.cnblogs.com/Amat ...

  6. 【学习】Windows PE文件学习(一:导出表)

    今天做了一个读取PE文件导出表的小程序,用来学习. 参考了<Windows PE权威指南>一书. 首先, PE文件的全称是Portable Executable,可移植的可执行的文件,常见 ...

  7. Using Git Submodules

    NOTE: Following content is directly reprinted from http://patrickward.com/2013/01/09/using-git-submo ...

  8. 【风马一族_Android】代码英语之二 布局文件的Android各个参数

    布局文件的Android各个参数 第一类:属性值 true或者 false           android:layout _center Hrizontal 水平居中     android:la ...

  9. BootStrap简介及应用要点

    BootStrap简介 BootStrap是基于HTML.CSS和JavaScript的框架,使你只需要写简单的代码就可以很快的搭建一个还不错的前端框架,他是后端程序员的福音,使他们只需要专注业务逻辑 ...

  10. 【个人】IIS Express 配置

    <!-- 查看URL访问控制列表: netsh http show urlacl 添加URL访问控制: netsh http add urlacl url=http://myhostname:8 ...