时隔1个月,2015/06/17走进新的环境。
最近一个星期在学习仿Word菜单栏的WPF实现方式,废话不多说,先看一下效果。
打开界面后,默认选中【市场A】,A对应的菜单栏,如上图,
选择【市场B】后讲改变菜单栏,和B相应的界面。
要实现上述的功能,要怎么解决?
实际上,每个界面都可以看成有三部分组成,顶部的DEV.RibbonControl,左侧的DEV.NavbarControl,和中间显示主要界面C部分。
NavBarControl中包含多个NavBarItem,当切换NavBarItem时,就加载相应的子界面到C处。但,除了MainWindow完整包含这几个部分外,其他子界面都不一定。
下面,就围绕这3个部分展开。
1、MainWindow.xaml
<dxr:DXRibbonWindow x:Class="WPFOptimizeTry.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:dx="http://schemas.devexpress.com/winfx/2008/xaml/core" xmlns:dxb="http://schemas.devexpress.com/winfx/2008/xaml/bars" xmlns:dxe="http://schemas.devexpress.com/winfx/2008/xaml/editors" xmlns:dxdo="http://schemas.devexpress.com/winfx/2008/xaml/docking" xmlns:dxn="http://schemas.devexpress.com/winfx/2008/xaml/navbar" xmlns:dxr="http://schemas.devexpress.com/winfx/2008/xaml/ribbon" xmlns:dxrt="http://schemas.devexpress.com/winfx/2008/xaml/ribbon/themekeys" xmlns:dxmvvm="http://schemas.devexpress.com/winfx/2008/xaml/mvvm" xmlns:dxwui="http://schemas.devexpress.com/winfx/2008/xaml/windowsui" xmlns:dxwuin="http://schemas.devexpress.com/winfx/2008/xaml/windowsui/navigation" xmlns:dxnt="http://schemas.devexpress.com/winfx/2008/xaml/navbar/themekeys" xmlns:local="clr-namespace:WPFOptimizeTry" xmlns:sys="clr-namespace:System;assembly=mscorlib" Title="优化尝试用例" Height="900" Width="1300" WindowStartupLocation="CenterScreen" UseLayoutRounding="True" DataContext="{dxmvvm:ViewModelSource Type=local:MainWindowViewModel}" Icon="pack://application:,,,/WPFOptimizeTry;component/demoicon.ico" > <dxmvvm:Interaction.Behaviors> <dxmvvm:CurrentWindowService /> <dx:DialogService/> </dxmvvm:Interaction.Behaviors> <dxb:BarManager Name="barManager"> <dxb:BarManager.Items> <dxr:RibbonGalleryBarItem x:Name="ribbonGalleryBarItem1"> <dxmvvm:Interaction.Behaviors> <dxr:RibbonGalleryItemThemeSelectorBehavior/> </dxmvvm:Interaction.Behaviors> <dxr:RibbonGalleryBarItem.Gallery> <dxb:Gallery ItemGlyphSize="30,24" HoverGlyphSize="48,48"/> </dxr:RibbonGalleryBarItem.Gallery> </dxr:RibbonGalleryBarItem> <dxb:BarCheckItem x:Name="layoutNormal" IsChecked="{Binding IsExpanded, ElementName=navPanelView, Mode=TwoWay}" Glyph="pack://application:,,,/WPFOptimizeTry;component/Images/normal.png" GlyphSize="Small"/> <dxb:BarCheckItem x:Name="layoutReading" IsChecked="{Binding IsExpanded, ElementName=navPanelView, Mode=TwoWay, Converter={StaticResource BooleanNegationConverter}}" Glyph="pack://application:,,,/WPFOptimizeTry;component/Images/reading.png" GlyphSize="Small"/> </dxb:BarManager.Items> <Grid> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition Height="*" /> <RowDefinition Height="Auto" /> </Grid.RowDefinitions> <dxr:RibbonControl RibbonStyle="Office2010" x:Name="ribbon" AllowCustomization="False"> <!--ApplicationMenu可以先忽略--> <dxr:RibbonControl.ApplicationMenu> <dxr:BackstageViewControl SelectedTabIndex="{Binding DefaultBackstatgeIndex, Mode=TwoWay}" IsOpen="{Binding IsBackstageOpen, Mode=TwoWay}"> <dxr:BackstageViewControl.Items> <dxr:BackstageTabItem Content="个人账户" > </dxr:BackstageTabItem> <dxr:BackstageTabItem Content="订单查询" IsEnabled="{Binding HasPrinting, Mode=OneWay}"> </dxr:BackstageTabItem> <dxr:BackstageButtonItem Content="授信查询" Command="{Binding ExitCommand}" /> </dxr:BackstageViewControl.Items> </dxr:BackstageViewControl> </dxr:RibbonControl.ApplicationMenu> <!--RibbonDefaultPageCategory 主界面默认显示的菜单,像Word里面可能先默认显示--> <dxr:RibbonDefaultPageCategory> <dxr:RibbonPage Caption="主页" > <dxr:RibbonPageGroup Caption="个人账户" ShowCaptionButton="False"> <dxb:BarButtonItem Content="修改" x:Name="btnModify" RibbonStyle="Large" Glyph="pack://application:,,,/WPFOptimizeTry;component/Images/WordProcessing.png" Command="{Binding ModifyCommand}" ></dxb:BarButtonItem> </dxr:RibbonPageGroup> <dxr:RibbonPageGroup Caption="XXXX" ShowCaptionButton="False"> <dxb:BarButtonItem Content="MMM" x:Name="btnM" RibbonStyle="Large" Glyph="pack://application:,,,/WPFOptimizeTry;component/Images/WordProcessing.png" Command="{Binding MoCommand}" ></dxb:BarButtonItem> </dxr:RibbonPageGroup> </dxr:RibbonPage> </dxr:RibbonDefaultPageCategory> </dxr:RibbonControl> <dxdo:DockLayoutManager Grid.Row="1" Margin="6"> <dxdo:LayoutGroup Caption="LayoutRoot"> <dxdo:LayoutPanel Caption="WPF Products" ItemWidth="Auto" AllowClose="False" ShowCaption="False" MaxWidth="183" Name="layoutPanel" AllowSizing="{Binding IsExpanded, ElementName=navPanelView}"> <dxn:NavBarControl SelectedItem="{Binding SelectedModuleInfo, Mode=TwoWay}" ItemsSource="{Binding Path=ModuleGroups}"> <dxmvvm:Interaction.Triggers> <dxmvvm:EventToCommand EventName="Loaded" Command="{Binding OnModulesLoadedCommand}" /> </dxmvvm:Interaction.Triggers> <dxn:NavBarControl.ItemStyle> <Style TargetType="dxn:NavBarGroup"> <Setter Property="Header" Value="{Binding Path=Title}" /> <Setter Property="ItemsSource" Value="{Binding Path=ModuleInfos}" /> <Setter Property="ItemStyle"> <Setter.Value> <Style TargetType="dxn:NavBarItem"> <Setter Property="IsSelected" Value="{Binding Path=IsSelected, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" /> <Setter Property="Content" Value="{Binding Path=Title}" /> <Setter Property="ImageSource" Value="{Binding Path=Icon}" /> <Setter Property="Command" Value="{Binding Path=ShowCommand}" /> <Setter Property="ImageSettings"> <Setter.Value> <dxn:ImageSettings Width="32" Height="32" Stretch="Uniform" StretchDirection="Both" /> </Setter.Value> </Setter> <Setter Property="LayoutSettings"> <Setter.Value> <dxn:LayoutSettings ImageDocking="Top" ImageHorizontalAlignment="Center" TextHorizontalAlignment="Center" ImageVerticalAlignment="Center" TextVerticalAlignment="Center" /> </Setter.Value> </Setter> </Style> </Setter.Value> </Setter> </Style> </dxn:NavBarControl.ItemStyle> <dxn:NavBarControl.View> <dxn:NavigationPaneView x:Name="navPanelView" IsOverflowPanelVisible="False" IsSplitterVisible="False" /> </dxn:NavBarControl.View> </dxn:NavBarControl> </dxdo:LayoutPanel> <dxdo:LayoutPanel AllowClose="False" AllowFloat="False" AllowHide="False" ShowCaption="False" ShowBorder="False" ShowCloseButton="False"> <dxwui:NavigationFrame x:Name="documentFrame" Navigating="OnDocumentFrameNavigating"> <dxwui:NavigationFrame.Resources> <Style TargetType="dxwui:PageAdornerControl"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="dxwui:PageAdornerControl"> <ContentPresenter Content="{TemplateBinding Content}" /> </ControlTemplate> </Setter.Value> </Setter> </Style> </dxwui:NavigationFrame.Resources> <dxmvvm:Interaction.Behaviors> <dxwuin:FrameNavigationService Frame="{Binding ElementName=documentFrame}" /> <dx:DXSplashScreenService SplashScreenType="{Binding SplashScreenType}" /> </dxmvvm:Interaction.Behaviors> </dxwui:NavigationFrame> </dxdo:LayoutPanel> </dxdo:LayoutGroup> </dxdo:DockLayoutManager> <dxr:RibbonStatusBarControl x:Name="statusBar" Grid.Row="2"> <dxr:RibbonStatusBarControl.RightItemLinks> <dxb:BarCheckItemLink BarItemName="layoutNormal"/> <dxb:BarCheckItemLink BarItemName="layoutReading"/> </dxr:RibbonStatusBarControl.RightItemLinks> </dxr:RibbonStatusBarControl> </Grid> </dxb:BarManager> </dxr:DXRibbonWindow>
MainWindow.cs
namespace WPFOptimizeTry { /// <summary> /// MainWindow.xaml 的交互逻辑 /// </summary> public partial class MainWindow : DXRibbonWindow { public virtual FrameworkElement BindContent { get; set; } public MainWindow() { InitializeComponent(); if (Height > SystemParameters.VirtualScreenHeight || Width > SystemParameters.VirtualScreenWidth) WindowState = WindowState.Maximized; DevExpress.Utils.About.UAlgo.Default.DoEventObject(DevExpress.Utils.About.UAlgo.kDemo, DevExpress.Utils.About.UAlgo.pWPF, this); }
void OnDocumentFrameNavigating(object sender, NavigatingEventArgs e) { if (e.Cancel) return;
//在Show命令中触发导航事件,在导航时加载子界面, Type type = Type.GetType(new MainWindow().GetType().Namespace + "." + e.Parameter.ToString(), true, true); var temp = Activator.CreateInstance(type); NavigationFrame frame = (sender as NavigationFrame); frame.Content = temp;
//到这里其实已经加载页面完毕,如果不添加SetMergeWith语句,会将子界面整个加载到C区,再与主界面合并,肉眼能够看到整个程序变化的过程。
//使用SetMergeWith可以使整个过度很平滑,会让你觉得一开始菜单就在菜单的位置。 FrameworkElement oldContent = (FrameworkElement)frame.Content; if (oldContent != null) { RibbonMergingHelper.SetMergeWith(oldContent, ribbon); RibbonMergingHelper.SetMergeStatusBarWith(oldContent, statusBar); }
//下面这句话不可缺少, e.Cancel = true; } } }
MainWindowViewModel.cs
namespace WPFOptimizeTry { public class MainWindowViewModel { public virtual IEnumerable<ModuleGroup> ModuleGroups { get; protected set; } public virtual ModuleInfo SelectedModuleInfo { get; set; } public virtual Type SplashScreenType { get; set; } public virtual int DefaultBackstatgeIndex { get; set; } public virtual bool HasPrinting { get; set; } public virtual bool IsBackstageOpen { get; set; } [Required] protected virtual ICurrentWindowService CurrentWindowService { get { return null; } } public MainWindowViewModel() { List<ModuleInfo> modules = new List<ModuleInfo>() { ViewModelSource.Create(() => new ModuleInfo("UCMarketA", this, "市场A")).SetIcon("GridContacts"), ViewModelSource.Create(() => new ModuleInfo("UCMarketB", this, "市场B")).SetIcon("GridTasks"), }; ModuleGroups = new ModuleGroup[] { new ModuleGroup("市场列表", modules) }; } public void Exit() { CurrentWindowService.Close(); } public void OnModulesLoaded() { if (SelectedModuleInfo == null) { SelectedModuleInfo = ModuleGroups.First().ModuleInfos.First(); SelectedModuleInfo.IsSelected = true; SelectedModuleInfo.Show(); } } }
//为NavBarControl构造数据源的类 public class ModuleGroup { public ModuleGroup(string _title, IEnumerable<ModuleInfo> _moduleInfos) { Title = _title; ModuleInfos = _moduleInfos; } public string Title { get; private set; } public IEnumerable<ModuleInfo> ModuleInfos { get; private set; } } //一个NavBarItem对应一个ModuleInfo public class ModuleInfo { ISupportServices Parent; public ModuleInfo(string _type, object parent, string _title) { Type = _type; this.Parent = (ISupportServices)parent; Title = _title; } public string Type { get; private set; } public virtual bool IsSelected { get; set; } public string Title { get; private set; } public virtual Uri Icon { get; set; } public ModuleInfo SetIcon(string icon) { this.Icon = AssemblyHelper.GetResourceUri(typeof(ModuleInfo).Assembly, string.Format("Images/{0}.png", icon)); return this; }
//选中按钮时触发导航事件,至于为什么,为了极大程度上的符合MVVM,这是我暂时能想到的解决问题的唯一办法。 public void Show(object parameter = null) { INavigationService navigationService = Parent.ServiceContainer.GetService<INavigationService>(); navigationService.Navigate(Type, Type, Parent); } } }
子界面,以B为例。
MarketB.xaml,子界面中有不同的菜单,所以需要在子界面写B的菜单RibbonControl.
<UserControl x:Class="WPFOptimizeTry.UCMarketB" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:dxe="http://schemas.devexpress.com/winfx/2008/xaml/editors" xmlns:dxg="http://schemas.devexpress.com/winfx/2008/xaml/grid" xmlns:dxb="http://schemas.devexpress.com/winfx/2008/xaml/bars" xmlns:dxr="http://schemas.devexpress.com/winfx/2008/xaml/ribbon" xmlns:dxlc="http://schemas.devexpress.com/winfx/2008/xaml/layoutcontrol" xmlns:dxmvvm="http://schemas.devexpress.com/winfx/2008/xaml/mvvm" xmlns:dx="http://schemas.devexpress.com/winfx/2008/xaml/core" xmlns:dxdo="http://schemas.devexpress.com/winfx/2008/xaml/docking" xmlns:local="clr-namespace:WPFOptimizeTry" DataContext="{dxmvvm:ViewModelSource Type=local:UCMarketBViewModel}" Height="300" Width="300"> <UserControl.Resources> <ResourceDictionary> <!--<local:ItemTypeToBooleanConverter x:Key="itemTypeToBooleanConverter"/>--> <Style x:Key="gridControlMVVMStyle" TargetType="{x:Type dxg:GridControl}"> <Setter Property="ItemsSource" Value="{Binding ItemsSource}"/> <Setter Property="ColumnsSource" Value="{Binding Columns}"/> <Setter Property="AutoExpandAllGroups" Value="True"/> <Setter Property="SelectedItem" Value="{Binding SelectedItem, Mode=TwoWay}"/> <Setter Property="FilterString" Value="{Binding FilterString, Mode=TwoWay}"/> </Style> <dxg:GridControl x:Key="printGridControl" > <dxg:GridControl.View> <dxg:TableView AutoWidth="True"/> </dxg:GridControl.View> <dxg:GridControl.GroupSummary> <dxg:GridSummaryItem SummaryType="Count"/> </dxg:GridControl.GroupSummary> </dxg:GridControl> </ResourceDictionary> </UserControl.Resources> <dxmvvm:Interaction.Behaviors> <dxmvvm:NotificationService UseWin8NotificationsIfAvailable="False" PredefinedNotificationTemplate="ShortHeaderAndLongText"/> </dxmvvm:Interaction.Behaviors> <Grid> <dxb:BarManager CreateStandardLayout="False"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <dxr:RibbonControl DockPanel.Dock="Top" RibbonStyle="Office2010"> <dxr:RibbonDefaultPageCategory> <dxr:RibbonPage Caption="B市场行情" MergeOrder="1"> <dxr:RibbonPageGroup Caption="报价行情"> <dxb:BarButtonItem Content="行情显示" x:Name="btnQuotationlShow" RibbonStyle="Large" Glyph="pack://application:,,,/WPFOptimizeTry;component/Images/Analytics.png" Command="{Binding QuotationlShowCommand}"></dxb:BarButtonItem> <dxb:BarButtonItem Content="报价成交" x:Name="btnQuotationDeal" RibbonStyle="Large" Glyph="pack://application:,,,/WPFOptimizeTry;component/Images/Tasks/Completed_32x32.png" Command="{Binding QuotationDealCommand}" ></dxb:BarButtonItem> </dxr:RibbonPageGroup> <dxr:RibbonPageGroup Caption="成交行情"> <dxb:BarButtonItem Content="成交行情" x:Name="btnDealShow" RibbonStyle="Large" Glyph="pack://application:,,,/WPFOptimizeTry;component/Images/WeatherMap.png" Command="{Binding Path=DealShowCommand}"></dxb:BarButtonItem> </dxr:RibbonPageGroup> </dxr:RibbonPage> </dxr:RibbonDefaultPageCategory> <dxr:RibbonPageCategory> <dxr:RibbonPage Caption="风控信息查询"> <dxr:RibbonPageGroup Caption="授信查询"></dxr:RibbonPageGroup> </dxr:RibbonPage> </dxr:RibbonPageCategory> </dxr:RibbonControl> <dxlc:LayoutControl Margin="0" Padding="0" Grid.Row="1"> <Grid Background="Pink" HorizontalAlignment="Center" VerticalAlignment="Center"> <TextBlock x:Name="blkB" Text="{Binding Path=BindMarketBContent,Mode=TwoWay}" Grid.Row="1" TextWrapping="Wrap" Foreground="Red" FontSize="36" /> </Grid> </dxlc:LayoutControl > <dxr:RibbonStatusBarControl> <dxr:RibbonStatusBarControl.LeftItemLinks> <dxb:BarStaticItemLink BarItemName="summaryCount"/> <dxb:BarItemLinkSeparator/> <dxb:BarButtonItemLink BarItemName="reminders"/> </dxr:RibbonStatusBarControl.LeftItemLinks> </dxr:RibbonStatusBarControl> </Grid> </dxb:BarManager> </Grid> </UserControl>
MarketB.cs
namespace WPFOptimizeTry { /// <summary> /// UCMarketB.xaml 的交互逻辑 /// </summary> public partial class UCMarketB : UserControl { public UCMarketB() { InitializeComponent(); }
} }
UCMarketBViewModel.cs
namespace WPFOptimizeTry { public class UCMarketBViewModel { public virtual string BindMarketBContent { get; set; } public UCMarketBViewModel() { } public void DealShow( ) { BindMarketBContent = "成交行情B"; } public void QuotationlShow() { BindMarketBContent = "行情显示"; } public void QuotationDeal() { BindMarketBContent = "成交"; } } }
综上,其实并不复杂,主要是写好各个界面、控件的布局,然后就是想好如何Show出界面。
由于刚刚学习,如有我哪里有不对或者你有更好的建议,欢迎讨论。