Material Design in XAML 如何理解 DialogHost

  • Material Design in XAML 如何理解 DialogHost已关闭评论
  • 422 次浏览
  • A+
所属分类:.NET技术
摘要

首先,本文假设您已经使用 MDIX 设置了一个项目。此外,它推测您了解模型-视图-视图模型 (MVVM) 模式。但是,DialogHost也可以在没有 MVVM 的情况下使用。

首先,本文假设您已经使用 MDIX 设置了一个项目。此外,它推测您了解模型-视图-视图模型 (MVVM) 模式。但是,DialogHost也可以在没有 MVVM 的情况下使用。

小试牛刀DialogHost

Dialog是Material Design In XAML中非常重要且强大的一个控件,首先我们从一个简单的例子开始。

<Window x:Class="MaterilDesign.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:local="clr-namespace:MaterilDesign"          xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"         mc:Ignorable="d"         Title="MainWindow" Height="450" Width="800">      <Window.DataContext>         <local:MainWindowViewModel />     </Window.DataContext>      <materialDesign:DialogHost CloseOnClickAway="True">         <materialDesign:DialogHost.DialogContent>             <Grid Margin="16">                 <TextBlock Text="我的第一个DialogHost" FontSize="20"/>             </Grid>         </materialDesign:DialogHost.DialogContent>          <Button Command="{x:Static materialDesign:DialogHost.OpenDialogCommand}" Width="160" Content="打开" Margin="0,64,0,0"/>     </materialDesign:DialogHost> </Window>

然后在App.Xaml中加入Material Design的默认样式,并添加主题颜色,这里不明白为什么这么写的话,可以去看一下官方文档,或者等待后续文章的更新。

<Application x:Class="MaterilDesign.App"              xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"              xmlns:local="clr-namespace:MaterilDesign"               xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"              StartupUri="MainWindow.xaml">     <Application.Resources>         <ResourceDictionary>             <ResourceDictionary.MergedDictionaries>                 <ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Defaults.xaml" />                 <ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Defaults.xaml" />                 <materialDesign:BundledTheme BaseTheme="Inherit"  PrimaryColor="Blue"  SecondaryColor="Lime"                                              ColorAdjustment="{materialDesign:ColorAdjustment}" />             </ResourceDictionary.MergedDictionaries>         </ResourceDictionary>     </Application.Resources> </Application>

上边的代码运行起来就是下边这样一个非常简单的对话框了。

Material Design in XAML 如何理解 DialogHost

DialogHost 由三个单独的 UI 组件组成:宿主控件、覆盖层和对话框。宿主控件包含应将对话框放在其上的内容。通常,它放置在 XAML 的根目录附近,以便它涵盖所有内容。叠加层是覆盖主机控件内所有内容的变暗区域。最后,对话框本身包含要显示的内容。默认情况下,对话框显示在弹出窗口内部。由于弹出窗口是一个单独的窗口,因此对话框可以大于其父窗口。如果要将对话框限制到其父窗口,则可以将静态资源样式 MaterialDesignEmbeddedDialogHost 应用于 DialogHost。

 显示和关闭对话框

DialogHost 适用于同时使用 MVVM 和Winform风格的应用程序。因此,有几种方法可以显示对话框。你可以选择在应用中使用这些选项中的任意一种。

(1)使用路由命令

DialogHost提供了两个用于显示和隐藏对话框的路由命令;OpenDialogCommand 和 CloseDialogCommand。这是最简单的纯 XAML 选项,因为它只是在元素树上向上移动,直到遇到 DialogHost 控件。将其设置为 Command 属性,它将显示对话框。下边是打开和关闭的命令。

<Button Command="{x:Static materialDesign:DialogHost.OpenDialogCommand}"/> <Button Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}"/>

(2)绑定DialogHost的IsOpen 属性

为了对对话框的状态进行简单的编程控制,DialogHost 提供了一个 IsOpen 属性。切换此属性的状态可以控制对话框显示或隐藏。属性还可以与数据绑定配对,使其易于在 MVVM 体系结构中使用。下边代码涉及到使用Mvvm框架Prism,使用前需要首先安装Prism.Wpf

<materialDesign:DialogHost IsOpen="{Binding IsDialogOpen}">     <materialDesign:DialogHost.DialogContent>         <Grid Margin="16">             <TextBlock Text="我的第一个DialogHost" FontSize="20"/>         </Grid>     </materialDesign:DialogHost.DialogContent>     <Button Command="{Binding ShowDialogCommand}" Width="160" Content="打开" Margin="0,64,0,0"/> </materialDesign:DialogHost>

using Prism.Commands; using Prism.Mvvm;  namespace MaterilDesign {     public class MainWindowViewModel : BindableBase     {         private bool _IsDialogOpen;         public bool IsDialogOpen         {             get => _IsDialogOpen;             set => SetProperty(ref _IsDialogOpen, value);         }          public DelegateCommand ShowDialogCommand => new(OnShowDialog);          public MainWindowViewModel(){}          private void OnShowDialog()         {             IsDialogOpen = true;         }     } }

或者直接在按钮的点击事件中编写DialogHost.IsOpen = true;的代码,需要在xaml中定义控件的Name为DialogHost。也可以同样达到打开对话框的效果。

(3)关闭对话框

若要使对话框在用户单击叠加层时自动关闭,请将 CloseOnClickAway 属性设置为“True”。

<materialDesign:DialogHost CloseOnClickAway="True">     ...  </materialDesign:DialogHost>

以上打开关闭对话框的方法都需要在Xaml中首先创建DialogHost对象,下边说明如何通过代码动态创建打开和关闭DialogHost

(4)动态创建对话框。

为了更好地控制对话框,DialogHost 类上有几个静态的 Show 方法。所有这些方法都返回 Tasks,并应用作异步方法。对话框的内容在 XAML 中指定,然后在前面的示例中显示或隐藏。使用 Show 方法时,必须传递对话框的内容。这允许创建动态对话框。

var dialogContent = new TextBlock {    Text = "动态对话框!",    Margin = new Thickness(20) }; await MaterialDesignThemes.Wpf.DialogHost.Show(dialogContent);

通过上边这种方法也可以打开一个对话框,但是如果需要在代码中创建对话框UI。更好的办法是为对话框的UI创建一个数据模板,并将数据对象作为对话框的内容传递,在数据模板中绑定数据。

<Window x:Class="MaterilDesign.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:local="clr-namespace:MaterilDesign"          xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"         mc:Ignorable="d"         Title="MainWindow" Height="450" Width="800">      <Window.DataContext>         <local:MainWindowViewModel />     </Window.DataContext>      <materialDesign:DialogHost CloseOnClickAway="True">         <Button Command="{Binding ShowDialogCommand}" Width="160" Content="打开" Margin="0,64,0,0"/>         <materialDesign:DialogHost.DialogContentTemplate>             <DataTemplate DataType="local:Person">                 <StackPanel Margin="20">                     <TextBlock Text="{Binding Name}" />                     <TextBlock Text="{Binding Age}" />                 </StackPanel>             </DataTemplate>         </materialDesign:DialogHost.DialogContentTemplate>     </materialDesign:DialogHost>      </Window>

using MaterialDesignThemes.Wpf; using Prism.Commands; using Prism.Mvvm;  namespace MaterilDesign {     public class MainWindowViewModel : BindableBase     {         public DelegateCommand ShowDialogCommand => new(OnShowDialog);          public MainWindowViewModel(){}          private async void OnShowDialog()         {             var person = new Person             {                 Name = "测试",                 Age = 12,             };             await DialogHost.Show(person);         }     }      public class Person     {         public string? Name { get; set; }         public int? Age { get; set; }     } }

Show 方法还包括在打开或关闭对话框时调用的回调委托的重载。您还可以直接在 DialogHost 上注册 DialogOpen 和 DialogClosed 事件。这些回调的事件参数包含一个 DialogSession 对象。此会话对象可以更新已显示的对话的内容或关闭可见对话框。

using MaterialDesignThemes.Wpf; using Prism.Commands; using Prism.Mvvm;  namespace MaterilDesign {     public class MainWindowViewModel : BindableBase     {         private DialogSession? _dialogOpeneSession;          public DelegateCommand ShowDialogCommand => new(OnShowDialog);          public MainWindowViewModel(){}          private async void OnShowDialog()         {             var person = new Person             {                 Name = "测试",                 Age = 12,             };             await DialogHost.Show(person, new DialogOpenedEventHandler((object sender, DialogOpenedEventArgs args) =>              {                  // 将Session赋值给私有字段,方便其他方法对内容进行更行操作                  _dialogOpeneSession = args.Session;                  var newPerson = new Person                  {                      Name = "张三",                      Age = 12,                  };                   // 更新Session内容                  _dialogOpeneSession.UpdateContent(newPerson);                   //关闭对话框,将_dialogOpeneSession置为null,避免错误应用                  _dialogOpeneSession.Close();                  _dialogOpeneSession = null;              }),new DialogClosingEventHandler((object sender,DialogClosingEventArgs args) =>              {                  // 获取Session中的内容,将结果推送到有需要的地方,获取返回值的一种方法                  // 关闭事件              }));         }     }      public class Person     {         public string? Name { get; set; }         public int? Age { get; set; }     } }

最后,还有用于传递对话标识符的其他重载。如果只有一个 DialogHost 实例,则 Show 方法将自动使用它。但是,在具有多个 DialogHost 实例的情况下,必须指定一个对话框标识符。此唯一 ID 标识要使用的对话框主机控件。

<materialDesign:DialogHost Identifier="MyDialogHost"> ... </materialDesign:DialogHost>

await MaterialDesignThemes.Wpf.DialogHost.Show(person, "MyDialogHost");

(5)返回值

使用静态 Show 方法显示的对话框也可以返回结果值。这些结果值可以是所需的对象。

object result = await MaterialDesignThemes.Wpf.DialogHost.Show(...); //处理返回的结果值

根据对话框的关闭方式,有几种方法可以指定返回值。

  • 如果使用对话框关闭命令,请在使用该命令的同一元素上设置 CommandParameter。
  • 如果使用 CloseOnClickAway,请在 DialogHost 上设置 CloseOnClickAwayParameter。
  • 如果使用对话框会话,请将参数传递给 Close 方法。
  • 属性不支持传递结果。必须使用其他方法之一。

尽管 XAML 对话主机是 MDIX 库中最强大的控件之一,但它也是最容易被误解的控件之一。但是,只需稍加努力,它就可以使在应用程序中使用“模式”对话框变得轻而易举。它可以改善用户界面的美感并简化用户体验。

 

下一篇将说明在实际生产中DialogHost遇到的两个问题及解决方案。

 

声明,本文在Material Design in XAML – How to Make Sense of the DialogHost基础之上进行了扩展撰写。

涉及使用软件框架版本如下:

Mvvm框架 Prism8+

UI框架MaterialDesignThemes4.4.0

Net版本 5.0