時隔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出界面。
由於剛剛學習,如有我哪里有不對或者你有更好的建議,歡迎討論。