整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          NET Core 3 WPF MVVM框架 Prism系列之導航系統

          文將介紹如何在.NET Core3環境下使用MVVM框架Prism基于區域Region的導航系統

          在講解Prism導航系統之前,我們先來看看一個例子,我在之前的demo項目創建一個登錄界面:

          我們看到這里是不是一開始想象到使用WPF帶有的導航系統,通過Frame和Page進行頁面跳轉,然后通過導航日志的GoBack和GoForward實現后退和前進,其實這是通過使用Prism的導航框架實現的,下面我們來看看如何在Prism的MVVM模式下實現該功能

          一.區域導航#

          我們在上一篇介紹了Prism的區域管理,而Prism的導航系統也是基于區域的,首先我們來看看如何在區域導航

          1.注冊區域#

          LoginWindow.xaml:

          Copy<Window x:Class="PrismMetroSample.Shell.Views.Login.LoginWindow"
                  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                  xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
                  xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
                  xmlns:local="clr-namespace:PrismMetroSample.Shell.Views.Login"
                  xmlns:region="clr-namespace:PrismMetroSample.Infrastructure.Constants;assembly=PrismMetroSample.Infrastructure"
                  mc:Ignorable="d"
                  xmlns:prism="http://prismlibrary.com/"
                  xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
                   Height="600" Width="400" prism:ViewModelLocator.AutoWireViewModel="True" ResizeMode="NoResize" WindowStartupLocation="CenterScreen"
                  Icon="pack://application:,,,/PrismMetroSample.Infrastructure;Component/Assets/Photos/Home, homepage, menu.png" >
              <i:Interaction.Triggers>
                  <i:EventTrigger EventName="Loaded">
                      <i:InvokeCommandAction Command="{Binding LoginLoadingCommand}"/>
                  </i:EventTrigger>
              </i:Interaction.Triggers>
              <Grid>
                  <ContentControl prism:RegionManager.RegionName="{x:Static region:RegionNames.LoginContentRegion}" Margin="5"/>
              </Grid>
          </Window>
          

          2.注冊導航#

          App.cs:

          Copy  protected override void RegisterTypes(IContainerRegistry containerRegistry)
            {
                  containerRegistry.Register<IMedicineSerivce, MedicineSerivce>();
                  containerRegistry.Register<IPatientService, PatientService>();
                  containerRegistry.Register<IUserService, UserService>();
          
                  //注冊全局命令
                  containerRegistry.RegisterSingleton<IApplicationCommands, ApplicationCommands>();
                  containerRegistry.RegisterInstance<IFlyoutService>(Container.Resolve<FlyoutService>());
          
                  //注冊導航
                  containerRegistry.RegisterForNavigation<LoginMainContent>();
                  containerRegistry.RegisterForNavigation<CreateAccount>();
            }
          

          3.區域導航#

          LoginWindowViewModel.cs:

          Copypublic class LoginWindowViewModel:BindableBase
          {
          
              private readonly IRegionManager _regionManager;
              private readonly IUserService _userService;
              private DelegateCommand _loginLoadingCommand;
              public DelegateCommand LoginLoadingCommand =>
                      _loginLoadingCommand ?? (_loginLoadingCommand = new DelegateCommand(ExecuteLoginLoadingCommand));
          
              void ExecuteLoginLoadingCommand()
              {
                  //在LoginContentRegion區域導航到LoginMainContent
                  _regionManager.RequestNavigate(RegionNames.LoginContentRegion, "LoginMainContent");
          
                   Global.AllUsers = _userService.GetAllUsers();
              }
          
              public LoginWindowViewModel(IRegionManager regionManager, IUserService userService)
              {
                    _regionManager = regionManager;
                    _userService = userService;            
              }
          
          }
          

          LoginMainContentViewModel.cs:

          Copypublic class LoginMainContentViewModel : BindableBase
          {
              private readonly IRegionManager _regionManager;
          
              private DelegateCommand _createAccountCommand;
              public DelegateCommand CreateAccountCommand =>
                      _createAccountCommand ?? (_createAccountCommand = new DelegateCommand(ExecuteCreateAccountCommand));
          
              //導航到CreateAccount
              void ExecuteCreateAccountCommand()
              {
                   Navigate("CreateAccount");
              }
          
              private void Navigate(string navigatePath)
              {
                   if (navigatePath != null)
                        _regionManager.RequestNavigate(RegionNames.LoginContentRegion, navigatePath);
                  
              }
          
          
              public LoginMainContentViewModel(IRegionManager regionManager)
              {
                   _regionManager = regionManager;
              }
          
           }
          

          效果如下:

          這里我們可以看到我們調用RegionMannager的RequestNavigate方法,其實這樣看不能很好的說明是基于區域的做法,如果將換成下面的寫法可能更好理解一點:

          Copy   //在LoginContentRegion區域導航到LoginMainContent
            _regionManager.RequestNavigate(RegionNames.LoginContentRegion, "LoginMainContent");
          

          換成

          Copy //在LoginContentRegion區域導航到LoginMainContent
           IRegion region = _regionManager.Regions[RegionNames.LoginContentRegion];
           region.RequestNavigate("LoginMainContent");
          

          其實RegionMannager的RequestNavigate源碼也是大概實現也是大概如此,就是去調Region的RequestNavigate的方法,而Region的導航是實現了一個INavigateAsync接口:

          Copypublic interface INavigateAsync
          {
             void RequestNavigate(Uri target, Action<NavigationResult> navigationCallback);
             void RequestNavigate(Uri target, Action<NavigationResult> navigationCallback, NavigationParameters navigationParameters);
              
          }   
          

          我們可以看到有RequestNavigate方法三個形參:

          • target:表示將要導航的頁面Uri
          • navigationCallback:導航后的回調方法
          • navigationParameters:導航傳遞參數(下面會詳解)

          那么我們將上述加上回調方法:

          Copy //在LoginContentRegion區域導航到LoginMainContent
           IRegion region = _regionManager.Regions[RegionNames.LoginContentRegion];
           region.RequestNavigate("LoginMainContent", NavigationCompelted);
          
           private void NavigationCompelted(NavigationResult result)
           {
               if (result.Result==true)
               {
                   MessageBox.Show("導航到LoginMainContent頁面成功");
               }
               else
               {
                   MessageBox.Show("導航到LoginMainContent頁面失敗");
               }
           }
          

          效果如下:

          二.View和ViewModel參與導航過程#

          1.INavigationAware#

          我們經常在兩個頁面之間導航需要處理一些邏輯,例如,LoginMainContent頁面導航到CreateAccount頁面時候,LoginMainContent退出頁面的時刻要保存頁面數據,導航到CreateAccount頁面的時刻處理邏輯(例如獲取從LoginMainContent頁面的信息),Prism的導航系統通過一個INavigationAware接口:

          Copy    public interface INavigationAware : Object
              {
                  Void OnNavigatedTo(NavigationContext navigationContext);
          
                  Boolean IsNavigationTarget(NavigationContext navigationContext);
          
                  Void OnNavigatedFrom(NavigationContext navigationContext);
              }
          
          • OnNavigatedFrom:導航之前觸發,一般用于保存該頁面的數據
          • OnNavigatedTo:導航后目的頁面觸發,一般用于初始化或者接受上頁面的傳遞參數
          • IsNavigationTarget:True則重用該View實例,Flase則每一次導航到該頁面都會實例化一次

          我們用代碼來演示這三個方法:

          LoginMainContentViewModel.cs:

          Copypublic class LoginMainContentViewModel : BindableBase, INavigationAware
          {
               private readonly IRegionManager _regionManager;
          
               private DelegateCommand _createAccountCommand;
               public DelegateCommand CreateAccountCommand =>
                      _createAccountCommand ?? (_createAccountCommand = new DelegateCommand(ExecuteCreateAccountCommand));
          
               void ExecuteCreateAccountCommand()
               {
                   Navigate("CreateAccount");
               }
          
               private void Navigate(string navigatePath)
               {
                   if (navigatePath != null)
                      _regionManager.RequestNavigate(RegionNames.LoginContentRegion, navigatePath);
               }
          
               public LoginMainContentViewModel(IRegionManager regionManager)
               {
                    _regionManager = regionManager;
               }
          
               public bool IsNavigationTarget(NavigationContext navigationContext)
               {            
                    return true;
               }
          
               public void OnNavigatedFrom(NavigationContext navigationContext)
               {
                    MessageBox.Show("退出了LoginMainContent");
               }
          
               public void OnNavigatedTo(NavigationContext navigationContext)
               {
                    MessageBox.Show("從CreateAccount導航到LoginMainContent");
               }
           }
          

          CreateAccountViewModel.cs:

          Copypublic class CreateAccountViewModel : BindableBase,INavigationAware
          {
               private DelegateCommand _loginMainContentCommand;
               public DelegateCommand LoginMainContentCommand =>
                      _loginMainContentCommand ?? (_loginMainContentCommand = new DelegateCommand(ExecuteLoginMainContentCommand));
          
               void ExecuteLoginMainContentCommand()
               {
                   Navigate("LoginMainContent");
               }
          
               public CreateAccountViewModel(IRegionManager regionManager)
               {
                   _regionManager = regionManager;
               }
          
               private void Navigate(string navigatePath)
               {
                  if (navigatePath != null)
                      _regionManager.RequestNavigate(RegionNames.LoginContentRegion, navigatePath);
               }
          
               public bool IsNavigationTarget(NavigationContext navigationContext)
               {
                   return true;
               }
          
               public void OnNavigatedFrom(NavigationContext navigationContext)
               {
                   MessageBox.Show("退出了CreateAccount");
               }
          
               public void OnNavigatedTo(NavigationContext navigationContext)
               {
                   MessageBox.Show("從LoginMainContent導航到CreateAccount");
               }
          
           }
          

          效果如下:

          修改IsNavigationTarget為false:

          Copypublic class LoginMainContentViewModel : BindableBase, INavigationAware
          {
               public bool IsNavigationTarget(NavigationContext navigationContext)
               {            
                    return false;
               }
          }
          
          public class CreateAccountViewModel : BindableBase,INavigationAware
          {
               public bool IsNavigationTarget(NavigationContext navigationContext)
               {
                   return false;
               }
           }
          
          

          效果如下:

          我們會發現LoginMainContent和CreateAccount頁面的數據不見了,這是因為第二次導航到頁面的時候當IsNavigationTarget為false時,View將會重新實例化,導致ViewModel也重新加載,因此所有數據都清空了

          2.IRegionMemberLifetime#

          同時,Prism還可以通過IRegionMemberLifetime接口的KeepAlive布爾屬性控制區域的視圖的生命周期,我們在上一篇關于區域管理器說到,當視圖添加到區域時候,像ContentControl這種單獨顯示一個活動視圖,可以通過RegionActivateDeactivate方法激活和失效視圖,像ItemsControl這種可以同時顯示多個活動視圖的,可以通過RegionAddRemove方法控制增加活動視圖和失效視圖,而當視圖的KeepAlivefalseRegionActivate另外一個視圖時,則該視圖的實例則會去除出區域,為什么我們不在區域管理器講解該接口呢?因為當導航的時候,同樣的是在觸發了RegionActivateDeactivate,當有IRegionMemberLifetime接口時則會觸發RegionAddRemove方法,這里可以去看下Prism的RegionMemberLifetimeBehavior源碼

          我們將LoginMainContentViewModel實現IRegionMemberLifetime接口,并且把KeepAlive設置為false,同樣的將IsNavigationTarget設置為true

          LoginMainContentViewModel.cs:

          Copypublic class LoginMainContentViewModel : BindableBase, INavigationAware,IRegionMemberLifetime
          {
          
               public bool KeepAlive => false;
               
               private readonly IRegionManager _regionManager;
          
               private DelegateCommand _createAccountCommand;
               public DelegateCommand CreateAccountCommand =>
                      _createAccountCommand ?? (_createAccountCommand = new DelegateCommand(ExecuteCreateAccountCommand));
          
               void ExecuteCreateAccountCommand()
               {
                   Navigate("CreateAccount");
               }
          
               private void Navigate(string navigatePath)
               {
                   if (navigatePath != null)
                      _regionManager.RequestNavigate(RegionNames.LoginContentRegion, navigatePath);
               }
          
               public LoginMainContentViewModel(IRegionManager regionManager)
               {
                    _regionManager = regionManager;
               }
          
               public bool IsNavigationTarget(NavigationContext navigationContext)
               {            
                    return true;
               }
          
               public void OnNavigatedFrom(NavigationContext navigationContext)
               {
                    MessageBox.Show("退出了LoginMainContent");
               }
          
               public void OnNavigatedTo(NavigationContext navigationContext)
               {
                    MessageBox.Show("從CreateAccount導航到LoginMainContent");
               }
           }
          

          效果如下:

          我們會發現跟沒實現IRegionMemberLifetime接口和IsNavigationTarget設置為false情況一樣,當KeepAlivefalse時,通過斷點知道,重新導航回LoginMainContent頁面時不會觸發IsNavigationTarget方法,因此可以

          知道判斷順序是:KeepAlive -->IsNavigationTarget

          3.IConfirmNavigationRequest#

          Prism的導航系統還支持再導航前允許是否需要導航的交互需求,這里我們在CreateAccount注冊完用戶后尋問是否需要導航回LoginMainContent頁面,代碼如下:

          CreateAccountViewModel.cs:

          Copypublic class CreateAccountViewModel : BindableBase, INavigationAware,IConfirmNavigationRequest
          {
               private DelegateCommand _loginMainContentCommand;
               public DelegateCommand LoginMainContentCommand =>
                      _loginMainContentCommand ?? (_loginMainContentCommand = new DelegateCommand(ExecuteLoginMainContentCommand));
              
               private DelegateCommand<object> _verityCommand;
               public DelegateCommand<object> VerityCommand =>
                      _verityCommand ?? (_verityCommand = new DelegateCommand<object>(ExecuteVerityCommand));
          
               void ExecuteLoginMainContentCommand()
               {
                   Navigate("LoginMainContent");
               }
          
               public CreateAccountViewModel(IRegionManager regionManager)
               {
                   _regionManager = regionManager;
               }
          
               private void Navigate(string navigatePath)
               {
                  if (navigatePath != null)
                      _regionManager.RequestNavigate(RegionNames.LoginContentRegion, navigatePath);
               }
          
               public bool IsNavigationTarget(NavigationContext navigationContext)
               {
                   return true;
               }
          
               public void OnNavigatedFrom(NavigationContext navigationContext)
               {
                   MessageBox.Show("退出了CreateAccount");
               }
          
               public void OnNavigatedTo(NavigationContext navigationContext)
               {
                   MessageBox.Show("從LoginMainContent導航到CreateAccount");
               }
              
               //注冊賬號
               void ExecuteVerityCommand(object parameter)
               {
                   if (!VerityRegister(parameter))
                   {
                          return;
                   }
                   MessageBox.Show("注冊成功!");
                   LoginMainContentCommand.Execute();
               }
              
               //導航前詢問
               public void ConfirmNavigationRequest(NavigationContext navigationContext, Action<bool> continuationCallback)
               {
                   var result = false;
                   if (MessageBox.Show("是否需要導航到LoginMainContent頁面?", "Naviagte?",MessageBoxButton.YesNo) ==MessageBoxResult.Yes)
                   {
                       result = true;
                   }
                   continuationCallback(result);
                }
           }
          

          效果如下:

          三.導航期間傳遞參數#

          Prism提供NavigationParameters類以幫助指定和檢索導航參數,在導航期間,可以通過訪問以下方法來傳遞導航參數:

          • INavigationAware接口的IsNavigationTargetOnNavigatedFromOnNavigatedTo方法中IsNavigationTargetOnNavigatedFromOnNavigatedTo中形參NavigationContext對象的NavigationParameters屬性
          • IConfirmNavigationRequest接口的ConfirmNavigationRequest形參NavigationContext對象的NavigationParameters屬性
          • 區域導航的INavigateAsync接口的RequestNavigate方法賦值給其形參navigationParameters
          • 導航日志IRegionNavigationJournal接口CurrentEntry屬性的NavigationParameters類型的Parameters屬性(下面會介紹導航日志)

          這里我們CreateAccount頁面注冊完用戶后詢問是否需要用當前注冊用戶來作為登錄LoginId,來演示傳遞導航參數,代碼如下:

          CreateAccountViewModel.cs(修改代碼部分):

          Copyprivate string _registeredLoginId;
          public string RegisteredLoginId
          {
              get { return _registeredLoginId; }
              set { SetProperty(ref _registeredLoginId, value); }
          }
          
          public bool IsUseRequest { get; set; }
          
          void ExecuteVerityCommand(object parameter)
          {
              if (!VerityRegister(parameter))
              {
                  return;
              }
              this.IsUseRequest = true;
              MessageBox.Show("注冊成功!");
              LoginMainContentCommand.Execute();
          }   
          
          public void ConfirmNavigationRequest(NavigationContext navigationContext, Action<bool> continuationCallback)
          {
              if (!string.IsNullOrEmpty(RegisteredLoginId) && this.IsUseRequest)
              {
                   if (MessageBox.Show("是否需要用當前注冊的用戶登錄?", "Naviagte?", MessageBoxButton.YesNo) == MessageBoxResult.Yes)
                   {
                        navigationContext.Parameters.Add("loginId", RegisteredLoginId);
                   }
              }
              continuationCallback(true);
          }
          

          LoginMainContentViewModel.cs(修改代碼部分):

          Copypublic void OnNavigatedTo(NavigationContext navigationContext)
          {
               MessageBox.Show("從CreateAccount導航到LoginMainContent");
                      
               var loginId= navigationContext.Parameters["loginId"] as string;
               if (loginId!=null)
               {
                   this.CurrentUser = new User() { LoginId=loginId};
               }
                      
           }
          

          效果如下:

          四.導航日志#

          Prism導航系統同樣的和WPF導航系統一樣,都支持導航日志,Prism是通過IRegionNavigationJournal接口來提供區域導航日志功能,

          Copy    public interface IRegionNavigationJournal
              {
                  bool CanGoBack { get; }
          
                  bool CanGoForward { get; }
          
                  IRegionNavigationJournalEntry CurrentEntry {get;}
          
                  INavigateAsync NavigationTarget { get; set; }
          
                  void GoBack();
          
                  void GoForward();
          
                  void RecordNavigation(IRegionNavigationJournalEntry entry, bool persistInHistory);
          
                  void Clear();
              }
          

          我們將在登錄界面接入導航日志功能,代碼如下:

          LoginMainContent.xaml(前進箭頭代碼部分):

          Copy<TextBlock Width="30" Height="30" HorizontalAlignment="Right" Text="" FontWeight="Bold" FontFamily="pack://application:,,,/PrismMetroSample.Infrastructure;Component/Assets/Fonts/#iconfont" FontSize="30" Margin="10" Visibility="{Binding IsCanExcute,Converter={StaticResource boolToVisibilityConverter}}">
                <i:Interaction.Triggers>
                     <i:EventTrigger EventName="MouseLeftButtonDown">
                           <i:InvokeCommandAction Command="{Binding GoForwardCommand}"/>
                      </i:EventTrigger>
                </i:Interaction.Triggers>
                <TextBlock.Style>
                     <Style TargetType="TextBlock">
                          <Style.Triggers>
                              <Trigger Property="IsMouseOver" Value="True">
                                    <Setter Property="Background" Value="#F9F9F9"/>
                              </Trigger>
                           </Style.Triggers>
                      </Style>
                </TextBlock.Style>
           </TextBlock>
          

          BoolToVisibilityConverter.cs:

          Copypublic class BoolToVisibilityConverter : IValueConverter
          {
              public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
              {
                    if (value==null)
                    {
                        return DependencyProperty.UnsetValue;
                    }
                    var isCanExcute = (bool)value;
                    if (isCanExcute)
                    {
                        return Visibility.Visible;
                    }
                    else
                    {
                        return Visibility.Hidden;
                    }
              }
          
              public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
              {
                      throw new NotImplementedException();
              }
           }
          

          LoginMainContentViewModel.cs(修改代碼部分):

          CopyIRegionNavigationJournal _journal;
          
          private DelegateCommand<PasswordBox> _loginCommand;
          public DelegateCommand<PasswordBox> LoginCommand =>
                      _loginCommand ?? (_loginCommand = new DelegateCommand<PasswordBox>(ExecuteLoginCommand, CanExecuteGoForwardCommand));
          
          private DelegateCommand _goForwardCommand;
          public DelegateCommand GoForwardCommand =>
                      _goForwardCommand ?? (_goForwardCommand = new DelegateCommand(ExecuteGoForwardCommand));
          
          private void ExecuteGoForwardCommand()
          {
              _journal.GoForward();
          }
          
          private bool CanExecuteGoForwardCommand(PasswordBox passwordBox)
          {
              this.IsCanExcute=_journal != null && _journal.CanGoForward;
              return true;
          }
          
          public void OnNavigatedTo(NavigationContext navigationContext)
          {
               //MessageBox.Show("從CreateAccount導航到LoginMainContent");
               _journal = navigationContext.NavigationService.Journal;
          
               var loginId= navigationContext.Parameters["loginId"] as string;
               if (loginId!=null)
               {
                          this.CurrentUser = new User() { LoginId=loginId};
               }
               LoginCommand.RaiseCanExecuteChanged();
          }
          
          

          CreateAccountViewModel.cs(修改代碼部分):

          CopyIRegionNavigationJournal _journal;
          
          private DelegateCommand _goBackCommand;
          public DelegateCommand GoBackCommand =>
                      _goBackCommand ?? (_goBackCommand = new DelegateCommand(ExecuteGoBackCommand));
          
          void ExecuteGoBackCommand()
          {
               _journal.GoBack();
          }
          
           public void OnNavigatedTo(NavigationContext navigationContext)
           {
               //MessageBox.Show("從LoginMainContent導航到CreateAccount");
               _journal = navigationContext.NavigationService.Journal;
           }
          

          效果如下:

          選擇退出導航日志#

          如果不打算將頁面在導航過程中不加入導航日志,例如LoginMainContent頁面,可以通過實現IJournalAware并從PersistInHistory()返回false

          Copy    public class LoginMainContentViewModel : IJournalAware
              {
                  public bool PersistInHistory() => false;
              }   
          

          五.小結:#

          prism的導航系統可以跟wpf導航并行使用,這是prism官方文檔也支持的,因為prism的導航系統是基于區域的,不依賴于wpf,不過更推薦于單獨使用prism的導航系統,因為在MVVM模式下更靈活,支持依賴注入,通過區域管理器能夠更好的管理視圖View,更能適應復雜應用程序需求,wpf導航系統不支持依賴注入模式,也依賴于Frame元素,而且在導航過程中也是容易強依賴View部分,下一篇將會講解Prism的對話框服務

          六.源碼#

          ?最后,附上整個demo的源代碼:PrismDemo源碼


          作者: RyzenAdorer

          出處:https://www.cnblogs.com/ryzen/p/12703914.html

          信公眾號:Dotnet9,網站:Dotnet9,問題或建議:請網站留言, 如果對您有所幫助:歡迎贊賞。

          .NET CORE(C#) WPF 抽屜式菜單

          閱讀導航

          1. 本文背景
          2. 代碼實現
          3. 本文參考
          4. 源碼

          1. 本文背景

          使用簡單動畫實現抽屜式菜單

          2. 代碼實現

          使用 .NET CORE 3.1 創建名為 “AnimatedColorfulMenu” 的WPF模板項目,添加1個Nuget庫:MaterialDesignThemes,版本為最新預覽版3.1.0-ci948。

          解決方案主要文件目錄組織結構:

          • AnimatedColorfulMenuApp.xamlMainWindow.xaml

          2.1 引入樣式

          文件【App.xaml】,在 StartupUri 中設置啟動的視圖【MainWindow.xaml】,并在【Application.Resources】節點增加 MaterialDesignThemes庫的樣式文件:

          <Application.Resources>
              <ResourceDictionary>
                  <ResourceDictionary.MergedDictionaries>
                      <ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Light.xaml" />
                      <ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Defaults.xaml" />
                      <ResourceDictionary Source="pack://application:,,,/MaterialDesignColors;component/Themes/Recommended/Primary/MaterialDesignColor.Blue.xaml" />
                      <ResourceDictionary Source="pack://application:,,,/MaterialDesignColors;component/Themes/Recommended/Accent/MaterialDesignColor.Indigo.xaml" />
                  </ResourceDictionary.MergedDictionaries>
              </ResourceDictionary>
          </Application.Resources>

          2.2 演示窗體布局

          文件【MainWindow.xaml】,代碼不多,主要看左側菜單,啟動時,菜單在顯示窗體左側-150位置;點擊展開菜單,使用簡單的動畫,慢慢呈現在顯示窗體左側,源碼如下:

          <Window x:Class="AnimatedColorfulMenu.MainWindow"
                  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                  xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
                  xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
                  xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
                  mc:Ignorable="d" Height="600" Width="1080" ResizeMode="NoResize" 
                  WindowStartupLocation="CenterScreen" WindowStyle="None">
              <Window.Resources>
                  <Storyboard x:Key="CloseMenu">
                      <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.X)" Storyboard.TargetName="GridMenu">
                          <EasingDoubleKeyFrame KeyTime="0" Value="150"/>
                          <EasingDoubleKeyFrame KeyTime="0:0:0.5" Value="0"/>
                      </DoubleAnimationUsingKeyFrames>
                      <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="GridBackground">
                          <EasingDoubleKeyFrame KeyTime="0" Value="1"/>
                          <EasingDoubleKeyFrame KeyTime="0:0:0.5" Value="0"/>
                      </DoubleAnimationUsingKeyFrames>
                  </Storyboard>
                  <Storyboard x:Key="OpenMenu">
                      <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.X)" Storyboard.TargetName="GridMenu">
                          <EasingDoubleKeyFrame KeyTime="0" Value="0"/>
                          <EasingDoubleKeyFrame KeyTime="0:0:0.5" Value="150"/>
                      </DoubleAnimationUsingKeyFrames>
                      <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="GridBackground">
                          <EasingDoubleKeyFrame KeyTime="0" Value="0"/>
                          <EasingDoubleKeyFrame KeyTime="0:0:0.5" Value="1"/>
                      </DoubleAnimationUsingKeyFrames>
                  </Storyboard>
              </Window.Resources>
              <Window.Triggers>
                  <EventTrigger RoutedEvent="ButtonBase.Click" SourceName="ButtonClose">
                      <BeginStoryboard x:Name="CloseMenu_BeginStoryboard" Storyboard="{StaticResource CloseMenu}"/>
                  </EventTrigger>
                  <EventTrigger RoutedEvent="ButtonBase.Click" SourceName="ButtonOpen">
                      <BeginStoryboard Storyboard="{StaticResource OpenMenu}"/>
                  </EventTrigger>
              </Window.Triggers>
              <Grid>
                  <Grid x:Name="GridBackground" Background="#55313131" Opacity="0"/>
                  <Button x:Name="ButtonOpen" HorizontalAlignment="Left" VerticalAlignment="Top" Background="{x:Null}" BorderBrush="{x:Null}" Width="30" Height="30" Padding="0">
                      <materialDesign:PackIcon Kind="Menu" Foreground="#FF313131"/>
                  </Button>
                  <!--左側抽屜菜單,默認在顯示窗體之外,點擊菜單圖標再通過簡單的動畫呈現出來-->
                  <Grid x:Name="GridMenu" Width="150" HorizontalAlignment="Left" Margin="-150 0 0 0" Background="White" RenderTransformOrigin="0.5,0.5">
                      <Grid.RenderTransform>
                          <TransformGroup>
                              <ScaleTransform/>
                              <SkewTransform/>
                              <RotateTransform/>
                              <TranslateTransform/>
                          </TransformGroup>
                      </Grid.RenderTransform>
                      <StackPanel>
                          <Image Height="140" Source="https://img.dotnet9.com/logo-foot.png" Stretch="Fill"/>
                          <ListView Foreground="#FF313131" FontFamily="Champagne & Limousines" FontSize="18">
                              <ListViewItem Height="45" Padding="0">
                                  <StackPanel Orientation="Horizontal" Margin="10 0">
                                      <materialDesign:PackIcon Kind="Recycle" Width="20" Height="20" Foreground="Gray" Margin="5" VerticalAlignment="Center"/>
                                      <TextBlock Text="回收" Margin="10"/>
                                  </StackPanel>
                              </ListViewItem>
                              <ListViewItem Height="45" Padding="0">
                                  <StackPanel Orientation="Horizontal" Margin="10 0">
                                      <materialDesign:PackIcon Kind="HelpCircleOutline" Width="20" Height="20" Foreground="#FFF08033" Margin="5" VerticalAlignment="Center"/>
                                      <TextBlock Text="幫助" Margin="10"/>
                                  </StackPanel>
                              </ListViewItem>
                              <ListViewItem Height="45" Padding="0">
                                  <StackPanel Orientation="Horizontal" Margin="10 0">
                                      <materialDesign:PackIcon Kind="Lightbulb" Width="20" Height="20" Foreground="Green" Margin="5" VerticalAlignment="Center"/>
                                      <TextBlock Text="發送反饋" Margin="10"/>
                                  </StackPanel>
                              </ListViewItem>
                              <ListViewItem Height="45" Padding="0">
                                  <StackPanel Orientation="Horizontal" Margin="10 0">
                                      <materialDesign:PackIcon Kind="Heart" Width="20" Height="20" Foreground="#FFD41515" Margin="5" VerticalAlignment="Center"/>
                                      <TextBlock Text="推薦" Margin="10"/>
                                  </StackPanel>
                              </ListViewItem>
                              <ListViewItem Height="45" Padding="0">
                                  <StackPanel Orientation="Horizontal" Margin="10 0">
                                      <materialDesign:PackIcon Kind="StarCircle" Width="20" Height="20" Foreground="#FFE6A701" Margin="5" VerticalAlignment="Center"/>
                                      <TextBlock Text="溢價認購" Margin="10"/>
                                  </StackPanel>
                              </ListViewItem>
                              <ListViewItem Height="45" Padding="0">
                                  <StackPanel Orientation="Horizontal" Margin="10 0">
                                      <materialDesign:PackIcon Kind="Settings" Width="20" Height="20" Foreground="#FF0069C1" Margin="5" VerticalAlignment="Center"/>
                                      <TextBlock Text="設置" Margin="10"/>
                                  </StackPanel>
                              </ListViewItem>
                          </ListView>
                      </StackPanel>
                      <Button x:Name="ButtonClose" HorizontalAlignment="Right" VerticalAlignment="Top" Background="{x:Null}" Foreground="#CCC" BorderBrush="{x:Null}" Width="30" Height="30" Padding="0">
                          <materialDesign:PackIcon Kind="Close"/>
                      </Button>
                  </Grid>
              </Grid>
          </Window>

          3.本文參考

          1. 視頻一:C# WPF Material Design UI: Animated Colorful Navigation Drawer,配套源碼:AnimatedColorfulMenu。
          2. C# WPF開源控件庫《MaterialDesignInXAML》

          4.源碼

          效果圖實現代碼在文中已經全部給出,可直接Copy,按解決方案目錄組織代碼文件即可運行。


          除非注明,文章均由 Dotnet9 整理發布,歡迎轉載。

          轉載請注明本文地址:https://dotnet9.com/7397.html

          歡迎掃描下方二維碼關注 Dotnet9 的微信公眾號,本站會及時推送最新技術文章


          時間如流水,只能流去不流回!

          點擊《【閱讀原文】》,本站還有更多技術類文章等著您哦!!!

          此刻順便為我點個《【再看】》可好?

          信公眾號:Dotnet9,網站:Dotnet9,問題或建議:請網站留言, 如果對您有所幫助:歡迎贊賞。

          .NET CORE(C#) WPF 值得推薦的動畫菜單設計

          閱讀導航

          1. 本文背景
          2. 代碼實現
          3. 本文參考
          4. 源碼

          1. 本文背景

          YouTube上老外的一個設計,站長覺得不錯,分享給大家作為參考,抽屜菜單的動畫做的非常不錯。

          運行起始界面:



          站長運行操作一遍,錄制了動畫大家看看:



          2. 代碼實現

          使用 .NET CORE 3.1 創建名為 “AnimatedMenu” 的WPF模板項目,添加1個Nuget庫:MaterialDesignThemes,版本為最新預覽版3.1.0-ci948。

          解決方案主要文件目錄組織結構:

          • AnimatedMenu
          • App.xaml
          • MainWindow.xaml
          • MainWindow.xaml.cs

          2.1 引入樣式

          文件【App.xaml】,在 StartupUri 中設置啟動的視圖【MainWindow.xaml】,并在【Application.Resources】節點增加 MaterialDesignThemes庫的樣式文件:

          <Application.Resources>
              <ResourceDictionary>
                  <ResourceDictionary.MergedDictionaries>
                      <ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Dark.xaml" />
                      <ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Defaults.xaml" />
                      <ResourceDictionary Source="pack://application:,,,/MaterialDesignColors;component/Themes/Recommended/Primary/MaterialDesignColor.Blue.xaml" />
                      <ResourceDictionary Source="pack://application:,,,/MaterialDesignColors;component/Themes/Recommended/Accent/MaterialDesignColor.Blue.xaml" />
                  </ResourceDictionary.MergedDictionaries>
              </ResourceDictionary>
          </Application.Resources>

          2.2 演示窗體

          文件【MainWindow.xaml】,布局代碼、動畫代碼都在此文件中,源碼如下:

          <Window x:Class="AnimatedMenu.MainWindow"
                  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                  xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
                  xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
                  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                  xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
                  mc:Ignorable="d" MouseLeftButtonDown="MoveWindow_MouseLeftButtonDown"
                  Height="600" Width="1024" WindowStyle="None" WindowStartupLocation="CenterScreen">
              <WindowChrome.WindowChrome>
                  <WindowChrome CaptionHeight="0"/>
              </WindowChrome.WindowChrome>
              <Window.Resources>
                  <Storyboard x:Key="OpenMenu">
                      <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.X)" Storyboard.TargetName="GridMain">
                          <EasingDoubleKeyFrame KeyTime="0" Value="0"/>
                          <EasingDoubleKeyFrame KeyTime="0:0:0.5" Value="250"/>
                      </DoubleAnimationUsingKeyFrames>
                      <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.Y)" Storyboard.TargetName="GridMain">
                          <EasingDoubleKeyFrame KeyTime="0" Value="0"/>
                          <EasingDoubleKeyFrame KeyTime="0:0:0.5" Value="50"/>
                      </DoubleAnimationUsingKeyFrames>
                      <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.X)" Storyboard.TargetName="StackPanelMenu">
                          <EasingDoubleKeyFrame KeyTime="0" Value="0"/>
                          <EasingDoubleKeyFrame KeyTime="0:0:0.5" Value="250"/>
                      </DoubleAnimationUsingKeyFrames>
                      <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.X)" Storyboard.TargetName="listViewItem">
                          <EasingDoubleKeyFrame KeyTime="0" Value="-250"/>
                          <EasingDoubleKeyFrame KeyTime="0:0:0.5" Value="-250"/>
                          <EasingDoubleKeyFrame KeyTime="0:0:0.7" Value="0"/>
                      </DoubleAnimationUsingKeyFrames>
                      <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.X)" Storyboard.TargetName="listViewItem1">
                          <EasingDoubleKeyFrame KeyTime="0" Value="-250"/>
                          <EasingDoubleKeyFrame KeyTime="0:0:0.5" Value="-250"/>
                          <EasingDoubleKeyFrame KeyTime="0:0:0.9" Value="0"/>
                      </DoubleAnimationUsingKeyFrames>
                      <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.X)" Storyboard.TargetName="listViewItem2">
                          <EasingDoubleKeyFrame KeyTime="0" Value="-250"/>
                          <EasingDoubleKeyFrame KeyTime="0:0:0.5" Value="-250"/>
                          <EasingDoubleKeyFrame KeyTime="0:0:1.1" Value="0"/>
                      </DoubleAnimationUsingKeyFrames>
                      <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.X)" Storyboard.TargetName="listViewItem3">
                          <EasingDoubleKeyFrame KeyTime="0" Value="-250"/>
                          <EasingDoubleKeyFrame KeyTime="0:0:0.5" Value="-250"/>
                          <EasingDoubleKeyFrame KeyTime="0:0:1.3" Value="0"/>
                      </DoubleAnimationUsingKeyFrames>
                      <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.X)" Storyboard.TargetName="listViewItem4">
                          <EasingDoubleKeyFrame KeyTime="0" Value="-250"/>
                          <EasingDoubleKeyFrame KeyTime="0:0:0.5" Value="-250"/>
                          <EasingDoubleKeyFrame KeyTime="0:0:1.5" Value="0"/>
                      </DoubleAnimationUsingKeyFrames>
                      <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)" Storyboard.TargetName="button">
                          <EasingDoubleKeyFrame KeyTime="0" Value="0"/>
                          <EasingDoubleKeyFrame KeyTime="0:0:0.5" Value="0"/>
                          <EasingDoubleKeyFrame KeyTime="0:0:1.5" Value="1"/>
                      </DoubleAnimationUsingKeyFrames>
                      <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)" Storyboard.TargetName="button">
                          <EasingDoubleKeyFrame KeyTime="0" Value="0"/>
                          <EasingDoubleKeyFrame KeyTime="0:0:0.5" Value="0"/>
                          <EasingDoubleKeyFrame KeyTime="0:0:1.5" Value="1"/>
                      </DoubleAnimationUsingKeyFrames>
                  </Storyboard>
                  <Storyboard x:Key="CloseMenu">
                      <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.X)" Storyboard.TargetName="GridMain">
                          <EasingDoubleKeyFrame KeyTime="0" Value="250"/>
                          <EasingDoubleKeyFrame KeyTime="0:0:0.5" Value="0"/>
                      </DoubleAnimationUsingKeyFrames>
                      <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.Y)" Storyboard.TargetName="GridMain">
                          <EasingDoubleKeyFrame KeyTime="0" Value="50"/>
                          <EasingDoubleKeyFrame KeyTime="0:0:0.5" Value="0"/>
                      </DoubleAnimationUsingKeyFrames>
                      <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.X)" Storyboard.TargetName="StackPanelMenu">
                          <EasingDoubleKeyFrame KeyTime="0" Value="250"/>
                          <EasingDoubleKeyFrame KeyTime="0:0:0.5" Value="0"/>
                      </DoubleAnimationUsingKeyFrames>
                  </Storyboard>
              </Window.Resources>
              <Window.Triggers>
                  <EventTrigger RoutedEvent="ButtonBase.Click" SourceName="ButtonOpenMenu">
                      <BeginStoryboard Storyboard="{StaticResource OpenMenu}"/>
                  </EventTrigger>
                  <EventTrigger RoutedEvent="ButtonBase.Click" SourceName="ButtonCloseMenu">
                      <BeginStoryboard x:Name="CloseMenu_BeginStoryboard" Storyboard="{StaticResource CloseMenu}"/>
                  </EventTrigger>
              </Window.Triggers>
              <Grid Background="#FF3580BF">
                  <StackPanel x:Name="StackPanelMenu" Width="250" HorizontalAlignment="Left" Margin="-250 0 0 0" RenderTransformOrigin="0.5,0.5">
                      <StackPanel.RenderTransform>
                          <TransformGroup>
                              <ScaleTransform/>
                              <SkewTransform/>
                              <RotateTransform/>
                              <TranslateTransform/>
                          </TransformGroup>
                      </StackPanel.RenderTransform>
                      <StackPanel Orientation="Horizontal" VerticalAlignment="Top" Height="100" HorizontalAlignment="Center">
                          <Button Style="{StaticResource MaterialDesignFloatingActionMiniAccentButton}" Background="{x:Null}" BorderBrush="{x:Null}" Padding="0" Width="50" Height="50" Margin="10">
                              <materialDesign:PackIcon Kind="Settings" Width="40" Height="40"/>
                          </Button>
                          <Button x:Name="button" Style="{StaticResource MaterialDesignFloatingActionMiniAccentButton}" BorderBrush="{x:Null}" Padding="0" Width="80" Height="80" Margin="10" RenderTransformOrigin="0.5,0.5">
                              <Button.RenderTransform>
                                  <TransformGroup>
                                      <ScaleTransform/>
                                      <SkewTransform/>
                                      <RotateTransform/>
                                      <TranslateTransform/>
                                  </TransformGroup>
                              </Button.RenderTransform>
                              <Button.Background>
                                  <ImageBrush ImageSource="https://img.dotnet9.com/logo.png" Stretch="UniformToFill"/>
                              </Button.Background>
                          </Button>
                          <Button Style="{StaticResource MaterialDesignFloatingActionMiniAccentButton}" Background="{x:Null}" BorderBrush="{x:Null}" Padding="0" Width="50" Height="50" Margin="10">
                              <materialDesign:PackIcon Kind="InformationOutline" Width="40" Height="40"/>
                          </Button>
                      </StackPanel>
                      <ListView>
                          <ListViewItem x:Name="listViewItem" Height="60" RenderTransformOrigin="0.5,0.5">
                              <ListViewItem.RenderTransform>
                                  <TransformGroup>
                                      <ScaleTransform/>
                                      <SkewTransform/>
                                      <RotateTransform/>
                                      <TranslateTransform/>
                                  </TransformGroup>
                              </ListViewItem.RenderTransform>
                              <StackPanel Orientation="Horizontal" Margin="10 0">
                                  <materialDesign:PackIcon Kind="Home" Width="30" Height="30" VerticalAlignment="Center" Margin="5"/>
                                  <TextBlock Text="主頁" Margin="10" VerticalAlignment="Center"/>
                              </StackPanel>
                          </ListViewItem>
                          <ListViewItem x:Name="listViewItem1" Height="60" RenderTransformOrigin="0.5,0.5">
                              <ListViewItem.RenderTransform>
                                  <TransformGroup>
                                      <ScaleTransform/>
                                      <SkewTransform/>
                                      <RotateTransform/>
                                      <TranslateTransform/>
                                  </TransformGroup>
                              </ListViewItem.RenderTransform>
                              <StackPanel Orientation="Horizontal" Margin="10 0">
                                  <materialDesign:PackIcon Kind="AccountSearch" Width="30" Height="30" VerticalAlignment="Center" Margin="5"/>
                                  <TextBlock Text="搜索" Margin="10" VerticalAlignment="Center"/>
                              </StackPanel>
                          </ListViewItem>
                          <ListViewItem x:Name="listViewItem2" Height="60" RenderTransformOrigin="0.5,0.5">
                              <ListViewItem.RenderTransform>
                                  <TransformGroup>
                                      <ScaleTransform/>
                                      <SkewTransform/>
                                      <RotateTransform/>
                                      <TranslateTransform/>
                                  </TransformGroup>
                              </ListViewItem.RenderTransform>
                              <StackPanel Orientation="Horizontal" Margin="10 0">
                                  <materialDesign:PackIcon Kind="Wechat" Width="30" Height="30" VerticalAlignment="Center" Margin="5"/>
                                  <TextBlock Text="微信" Margin="10" VerticalAlignment="Center"/>
                              </StackPanel>
                          </ListViewItem>
                          <ListViewItem x:Name="listViewItem3" Height="60" RenderTransformOrigin="0.5,0.5">
                              <ListViewItem.RenderTransform>
                                  <TransformGroup>
                                      <ScaleTransform/>
                                      <SkewTransform/>
                                      <RotateTransform/>
                                      <TranslateTransform/>
                                  </TransformGroup>
                              </ListViewItem.RenderTransform>
                              <StackPanel Orientation="Horizontal" Margin="10 0">
                                  <materialDesign:PackIcon Kind="Qqchat" Width="30" Height="30" VerticalAlignment="Center" Margin="5"/>
                                  <TextBlock Text="QQ" Margin="10" VerticalAlignment="Center"/>
                              </StackPanel>
                          </ListViewItem>
                          <ListViewItem x:Name="listViewItem4" Height="60" RenderTransformOrigin="0.5,0.5">
                              <ListViewItem.RenderTransform>
                                  <TransformGroup>
                                      <ScaleTransform/>
                                      <SkewTransform/>
                                      <RotateTransform/>
                                      <TranslateTransform/>
                                  </TransformGroup>
                              </ListViewItem.RenderTransform>
                              <StackPanel Orientation="Horizontal" Margin="10 0">
                                  <materialDesign:PackIcon Kind="Facebook" Width="30" Height="30" VerticalAlignment="Center" Margin="5"/>
                                  <TextBlock Text="臉書" Margin="10" VerticalAlignment="Center"/>
                              </StackPanel>
                          </ListViewItem>
                      </ListView>
                  </StackPanel>
                  <Grid x:Name="GridMain" Background="#FFFBFBFB" Width="1024" RenderTransformOrigin="0.5,0.5">
                      <Grid.RenderTransform>
                          <TransformGroup>
                              <ScaleTransform/>
                              <SkewTransform/>
                              <RotateTransform/>
                              <TranslateTransform/>
                          </TransformGroup>
                      </Grid.RenderTransform>
                      <Grid.ColumnDefinitions>
                          <ColumnDefinition Width="*"/>
                          <ColumnDefinition Width="250"/>
                      </Grid.ColumnDefinitions>
          
                      <Grid Grid.Column="1" Background="#FF3580BF">
                          <Image Height="150" VerticalAlignment="Top" Source="https://dotnet9.com/wp-content/uploads/2017/04/About-Header.jpg" Stretch="UniformToFill"/>
                          <Ellipse Height="100" Width="100" VerticalAlignment="Top" HorizontalAlignment="Right" Margin="20 100" Stroke="White">
                              <Ellipse.Fill>
                                  <ImageBrush ImageSource="https://img.dotnet9.com/logo.png" Stretch="UniformToFill"/>
                              </Ellipse.Fill>
                          </Ellipse>
                          <TextBlock Text="Dotnet9" Foreground="White" FontSize="28" FontFamily="Nirmala UI Semilight" Margin="10 100" VerticalAlignment="Top"/>
                          <StackPanel Margin="0 150">
                              <Grid Height="60" Margin="20 50 20 0">
                                  <Grid.ColumnDefinitions>
                                      <ColumnDefinition Width="*"/>
                                      <ColumnDefinition Width="*"/>
                                  </Grid.ColumnDefinitions>
                                  <Grid.RowDefinitions>
                                      <RowDefinition Height="30"/>
                                      <RowDefinition Height="30"/>
                                  </Grid.RowDefinitions>
                                  <TextBlock Text="追隨者" VerticalAlignment="Bottom" Foreground="#FFFBFBFB" Margin="5,0,5,5"/>
                                  <TextBlock Text="1.5K" VerticalAlignment="Top" Foreground="#FFFBFBFB" Grid.Row="1" Margin="10 0"/>
          
                                  <TextBlock Text="跟隨" VerticalAlignment="Bottom" Foreground="#FFFBFBFB" Margin="5,0,5,5" Grid.Column="1"/>
                                  <TextBlock Text="2.3K" VerticalAlignment="Top" Foreground="#FFFBFBFB" Grid.Row="1" Margin="10 0" Grid.Column="1"/>
                              </Grid>
          
                              <TextBlock TextWrapping="Wrap" Margin="10" Foreground="#FFFBFBFB" FontSize="14">
                                  <Run Text="網名:沙漠盡頭的狼"/>
                                  <LineBreak/>
                                  <LineBreak/>
                                  <Run Text="網站:https://dotnet9.com"/>
                                  <LineBreak/>
                                  <Run Text="    Dotnet9的博客 - 一個熱衷于互聯網分享精神的個人博客站點"/>
                                  <LineBreak/>
                                  <LineBreak/>
                                  <Run Text="座右銘:時間如流水,只能流去不流回。"/>
                              </TextBlock>
                          </StackPanel>
                      </Grid>
                      <Grid>
                          <Grid.RowDefinitions>
                              <RowDefinition Height="40"/>
                              <RowDefinition Height="50"/>
                              <RowDefinition Height="*"/>
                              <RowDefinition Height="*"/>
                              <RowDefinition Height="*"/>
                          </Grid.RowDefinitions>
                          <Grid.ColumnDefinitions>
                              <ColumnDefinition Width="*"/>
                              <ColumnDefinition Width="*"/>
                              <ColumnDefinition Width="*"/>
                          </Grid.ColumnDefinitions>
          
                          <Button x:Name="ButtonCloseMenu" Style="{StaticResource MaterialDesignFloatingActionMiniAccentButton}" Width="30" Height="30" Padding="0" Background="{x:Null}" BorderBrush="{x:Null}" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="5" Click="ButtonCloseMenu_Click" Visibility="Collapsed">
                              <materialDesign:PackIcon Kind="Menu" Foreground="#FF3580BF"/>
                          </Button>
                          <Button x:Name="ButtonOpenMenu" Style="{StaticResource MaterialDesignFloatingActionMiniAccentButton}" Width="30" Height="30" Padding="0" Background="{x:Null}" BorderBrush="{x:Null}" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="5" Click="ButtonOpenMenu_Click">
                              <materialDesign:PackIcon Kind="Menu" Foreground="#FF3580BF"/>
                          </Button>
          
          
                          <TextBlock Text="照片" Foreground="#FF3580BF" FontSize="30" FontWeight="Bold" Margin="5" Grid.Row="1"/>
          
                          <Grid Margin="5" Grid.Row="2" Grid.Column="0">
                              <Grid.Effect>
                                  <DropShadowEffect BlurRadius="20" Color="#FFEEEEEE" ShadowDepth="1"/>
                              </Grid.Effect>
                              <Image Source="https://dotnet9.com/wp-content/uploads/2019/12/wpf.png" Stretch="UniformToFill"/>
                              <StackPanel Orientation="Horizontal" Margin="5" HorizontalAlignment="Right" VerticalAlignment="Bottom">
                                  <materialDesign:PackIcon Kind="Heart" Foreground="#FFFBFBFB"/>
                                  <TextBlock Text="25" Foreground="#FFFBFBFB"/>
                              </StackPanel>
                          </Grid>
                          <Grid Margin="5" Grid.Row="2" Grid.Column="1">
                              <Grid.Effect>
                                  <DropShadowEffect BlurRadius="20" Color="#FFEEEEEE" ShadowDepth="1"/>
                              </Grid.Effect>
                              <Image Source="https://dotnet9.com/wp-content/uploads/2019/12/winform.png" Stretch="UniformToFill"/>
                              <StackPanel Orientation="Horizontal" Margin="5" HorizontalAlignment="Right" VerticalAlignment="Bottom">
                                  <materialDesign:PackIcon Kind="Heart" Foreground="#FFFBFBFB"/>
                                  <TextBlock Text="50" Foreground="#FFFBFBFB"/>
                              </StackPanel>
                          </Grid>
                          <Grid Margin="5" Grid.Row="2" Grid.Column="2">
                              <Grid.Effect>
                                  <DropShadowEffect BlurRadius="20" Color="#FFEEEEEE" ShadowDepth="1"/>
                              </Grid.Effect>
                              <Image Source="https://dotnet9.com/wp-content/uploads/2019/12/asp-net-core.png" Stretch="UniformToFill"/>
                              <StackPanel Orientation="Horizontal" Margin="5" HorizontalAlignment="Right" VerticalAlignment="Bottom">
                                  <materialDesign:PackIcon Kind="Heart" Foreground="#FFFBFBFB"/>
                                  <TextBlock Text="18" Foreground="#FFFBFBFB"/>
                              </StackPanel>
                          </Grid>
                          <Grid Margin="5" Grid.Row="3" Grid.Column="0">
                              <Grid.Effect>
                                  <DropShadowEffect BlurRadius="20" Color="#FFEEEEEE" ShadowDepth="1"/>
                              </Grid.Effect>
                              <Image Source="https://img.dotnet9.com/Xamarin.Forms.png" Stretch="UniformToFill"/>
                              <StackPanel Orientation="Horizontal" Margin="5" HorizontalAlignment="Right" VerticalAlignment="Bottom">
                                  <materialDesign:PackIcon Kind="Heart" Foreground="#FFFBFBFB"/>
                                  <TextBlock Text="32" Foreground="#FFFBFBFB"/>
                              </StackPanel>
                          </Grid>
                          <Grid Margin="5" Grid.Row="3" Grid.Column="1">
                              <Grid.Effect>
                                  <DropShadowEffect BlurRadius="20" Color="#FFEEEEEE" ShadowDepth="1"/>
                              </Grid.Effect>
                              <Image Source="https://dotnet9.com/wp-content/uploads/2019/12/front-end.png" Stretch="UniformToFill"/>
                              <StackPanel Orientation="Horizontal" Margin="5" HorizontalAlignment="Right" VerticalAlignment="Bottom">
                                  <materialDesign:PackIcon Kind="Heart" Foreground="#FFFBFBFB"/>
                                  <TextBlock Text="32" Foreground="#FFFBFBFB"/>
                              </StackPanel>
                          </Grid>
                      </Grid>
                  </Grid>
                  <StackPanel Orientation="Horizontal" VerticalAlignment="Top" Height="40" HorizontalAlignment="Right" Margin="10">
                      <Button Style="{StaticResource MaterialDesignFloatingActionMiniAccentButton}" Width="30" Height="30" Padding="0" Background="{x:Null}" BorderBrush="{x:Null}">
                          <materialDesign:PackIcon Kind="Bell"/>
                      </Button>
                      <Button x:Name="ButtonClose" Style="{StaticResource MaterialDesignFloatingActionMiniAccentButton}" Width="30" Height="30" Padding="0" Background="{x:Null}" BorderBrush="{x:Null}" Click="ButtonClose_Click">
                          <materialDesign:PackIcon Kind="Close"/>
                      </Button>
                  </StackPanel>
              </Grid>
          </Window>

          簡單說明下:

          1. "StackPanelMenu" 作為左側菜單容器,默認是顯示在窗體外,距離窗體左邊緣-250,點擊左上角菜單按鈕圖標可控制此容器的顯示與隱藏,注:菜單開關由兩個按鈕組成 "ButtonOpenMenu" 和 "ButtonCloseMenu"。
          2. 左側菜單項使用 "ListView" 進行布局,實際開發需要運用模板,使用MVVM做成動態菜單,方便擴展。
          3. 中間的5張演示照片,也和2類似。直接使用Grid進行的布局,實際上都需要做成模板。
          4. 抽屜菜單動畫見 Window.Resouces 中的動畫代碼,展開抽屜菜單動畫是 "OpenMenu",左側菜單向右、向下移動,右側展示區域及個人信息概況界面同時也是向右、向下移動;關閉抽屜菜單動畫是 "CloseMenu",動畫移動方向與展開時相反(說的是廢話),這段動畫代碼值得好好學習、復用。

          文件【MainWindow.xaml.cs】,后臺關閉窗體、抽屜菜單按鈕切換、窗體移動等事件處理:

          private void ButtonClose_Click(object sender, RoutedEventArgs e)
          {
              Application.Current.Shutdown();
          }
          
          private void ButtonOpenMenu_Click(object sender, RoutedEventArgs e)
          {
              ButtonOpenMenu.Visibility = Visibility.Collapsed;
              ButtonCloseMenu.Visibility = Visibility.Visible;
          }
          
          private void ButtonCloseMenu_Click(object sender, RoutedEventArgs e)
          {
              ButtonOpenMenu.Visibility = Visibility.Visible;
              ButtonCloseMenu.Visibility = Visibility.Collapsed;
          }
          
          private void MoveWindow_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
          {
              DragMove();
          }

          代碼已全部奉上...

          3.本文參考

          1. 視頻一:C# WPF Material Design UI: Animated Menu,配套源碼:AnimatedMenu1。
          2. C# WPF開源控件庫《MaterialDesignInXAML》

          4.源碼

          效果圖實現代碼在文中已經全部給出,站長方便演示,文中的圖片使用的本站外鏈圖片,代碼可直接Copy,按解決方案目錄組織代碼文件即可運行。

          演示Demo下載


          除非注明,文章均由 Dotnet9 整理發布,歡迎轉載。轉載請注明本文地址:https://dotnet9.com/7669.html歡迎掃描下方二維碼關注 Dotnet9 的微信公眾號,本站會及時推送最新技術文章


          時間如流水,只能流去不流回!

          點擊《【閱讀原文】》,本站還有更多技術類文章等著您哦!!!


          主站蜘蛛池模板: AA区一区二区三无码精片| 日韩人妻一区二区三区蜜桃视频| 无人码一区二区三区视频| 国产福利视频一区二区| 麻豆精品人妻一区二区三区蜜桃| 精品aⅴ一区二区三区| 无码一区二区波多野结衣播放搜索| 国产福利电影一区二区三区久久老子无码午夜伦不| 国产精品视频一区二区三区无码| 日韩综合无码一区二区| 国产一区二区不卡老阿姨| 无码人妻精品一区二区三区在线| 亚洲无线码一区二区三区| 国产在线一区二区综合免费视频| 色综合一区二区三区| 久久久久99人妻一区二区三区| 少妇人妻精品一区二区三区| www一区二区www免费| 国产伦精品一区二区三区四区| 亚洲AV无码一区二区三区牛牛| 国产自产在线视频一区| 中文字幕一区二区三区在线播放 | 久久久久久免费一区二区三区 | 国产欧美色一区二区三区| 久久久老熟女一区二区三区| 国模吧一区二区三区| 色噜噜AV亚洲色一区二区| 国产精品乱码一区二区三区| 黑人一区二区三区中文字幕| 日韩视频一区二区在线观看| 亚洲av色香蕉一区二区三区| 亚洲欧美日韩中文字幕一区二区三区| 99久久精品国产免看国产一区| 国产天堂在线一区二区三区| 国产精品一区二区av| 精品亚洲av无码一区二区柚蜜| 尤物精品视频一区二区三区 | tom影院亚洲国产一区二区| 久久免费精品一区二区| 国产精品丝袜一区二区三区| 国产视频一区二区在线播放|