7.Prism框架之对话框服务

文章目录

      • 一. 目标
      • 二. 技能介绍
        • ① 什么是Dialog?
        • ② Prism中Dialog的实现方式
        • ③ Dialog使用案例一 (修改器)
        • ④ Dialog使用案例2(异常显示窗口)

一. 目标

  • 1. 什么是Dialog?
  • 2. 传统的Dialog如何实现?
  • 3. Prism中Dialog实现方式
  • 4. 使用Dialog实现一个异常信息弹出框

二. 技能介绍

① 什么是Dialog?

Dialog通常是一种特殊类型的窗口,用于和用户进行交互,一般是用来从用户那里接收数据,显示信息,或者是允许用户执行特定任务.窗口分为两种一种是模态窗口(Modal Window),一种是非模态窗口(Non-Modal WIndow)

  • 模态窗口
    模态窗口就是阻止和其他的控件进行交互的窗口,必须是处理完这个窗口之后,才能和其他的窗口进行交互,这个窗口脚模态窗口.一般用于比较重要的交互,比如保存文件,确认删除等.传统显示的是时候使用ShowDialog(),有返回值.

  • 非模态窗口
    可以在不关闭这个窗口的情况下和其他的控件进行交互,一般适用于不需要立即决策或者响应的情况,如工具箱,提示信息等.传统显示的时候使用Show(),无返回值.

模态窗口的例子

namespace DialogSimple.ViewModels
{public class MainWindowViewModel{public MainWindowViewModel(){ShowDialogCommand = new Command(DoShowDialog);}private void DoShowDialog(object modalType){if (modalType is string modal){switch (modal){case "Modal":var modalDialog = new ModalDialog();bool? result = modalDialog.ShowDialog();if (result == true){System.Diagnostics.Debug.WriteLine("模态对话框返回值为true,执行保存操作!");}else{System.Diagnostics.Debug.WriteLine("模态对话框返回值为false,不执行保存操作!");}break;}}}public Command ShowDialogCommand { get; private set; }}
}

这里注意一个问题就是这里的result是一个窗口的返回值,为bool?类型,这个返回值是怎么返回的呢?

这个返回值是通过窗口的视图类中的一个DialogResult属性,可以在事件处理的时候设置其返回值.

namespace DialogSimple
{/// <summary>/// ModalDialog.xaml 的交互逻辑/// </summary>public partial class ModalDialog : Window{public ModalDialog(){InitializeComponent();}private void BtnOk_Click(object sender, RoutedEventArgs e){DialogResult = true;}private void BtnCancel_Click(object sender, RoutedEventArgs e){DialogResult = false;}}
}

现在我有一个疑问,就是这里我们窗口的按钮实现的方式比较简单,如果比较复杂,比如按钮的处理逻辑放到对应的ViewModel中去处理的时候,这个时候是没有DialogResult属性呢,如果设置其返回值呢?注意事件本质上还是一个委托,其实可以当成是一个回调或者是钩子来使用.

namespace DialogSimple.ViewModels
{public class ModalDialogViewModel{public event Action<bool>? RequestClose;public ModalDialogViewModel(){OkCommand = new Command(DoOkClick);CancelCommand = new Command(DoCancelClick);}private void DoCancelClick(object obj){// 1. 处理其他逻辑// 2. 调用窗口关闭事件,传递参数为falseRequestClose?.Invoke(false);}private void DoOkClick(object obj){// 1. 处理其他逻辑// 2. 调用窗口关闭事件,传递参数为trueRequestClose?.Invoke(true);}public Command OkCommand { get; private set; }public Command CancelCommand { get; private set; }}
}

然后在UI程序中,去注册这个事件

namespace DialogSimple
{/// <summary>/// ModalDialog.xaml 的交互逻辑/// </summary>public partial class ModalDialog : Window{public ModalDialog(){InitializeComponent();DataContext = new ModalDialogViewModel();(DataContext as ModalDialogViewModel)!.RequestClose += ModalDialog_RequestClose;}private void ModalDialog_RequestClose(bool result){DialogResult = result;}}
}

这里有一个疑问就是为什么我没有关闭这个窗口,当我们点击确定和取消按钮的时候,窗口就关闭了呢,其实这里的原因是因为当在WPF应用中如果设置了DialogResult的属性之后,无论是设置的true还是false,它都会去关闭窗口

注意:

我们可能还记得MVVM框架的设计原则,UI的逻辑一般都是在UI的模块代码中去完成,但是上面的弹窗部分是在ViewModel中实现的,并且在ViewModel中操作了UI视图的创建,这在实践上不是一个很好的实践,那么如何将这个创建弹窗的逻辑放到UI代码中去的也就是放到View中去呢,其中一种方式和上面的类似,就是通过事件.每次请求窗口的时候,调用事件,然后UI代码注册这个事件,完成对应的操作.

namespace DialogSimple.ViewModels
{public class MainWindowViewModel{public event Action<object>? RequestShowDialog;public MainWindowViewModel(){ShowDialogCommand = new Command(DoShowDialog);}private void DoShowDialog(object modalType){if (modalType is string modal){switch (modal){case "Modal":RequestShowDialog?.Invoke("Modal");break;}}}public Command ShowDialogCommand { get; private set; }}
}

然后在View中去做具体的窗口弹出处理

 public partial class MainWindow : Window{public MainWindow(){InitializeComponent();DataContext = new MainWindowViewModel();(DataContext as MainWindowViewModel)!.RequestShowDialog += MainWindow_RequestShowDialog;}private void MainWindow_RequestShowDialog(object obj){if (obj is string windowType){switch (windowType){case "Modal":var result = new ModalDialog().ShowDialog();if (result == true){System.Diagnostics.Debug.WriteLine("返回true,进行保存操作");}else{System.Diagnostics.Debug.WriteLine("返回false,取消保存操作");}break;}}}}

非模态窗口的例子

非模态窗口就是把ShowDialog换成Show()就OK了,并且非模态窗口是没有返回值的.

② Prism中Dialog的实现方式

Prism对话框Dialog的实现主要分为三步

  • 1. 创建对话框(UserControl)
  • 2. 注册对话框(并绑定其关联的ViewModel)
  • 3. 使用IDialogService来显示对话框,如果有参数并传递参数

第一步: 创建对话框用户控件,注意这里只能是UserControl,不能是Window

<UserControl x:Class="PrismDialogSimple.Views.ModalDialog"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:local="clr-namespace:PrismDialogSimple.Views"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800"><Grid><Grid><Grid.RowDefinitions><RowDefinition Height="50" /><RowDefinition /><RowDefinition Height="100" /></Grid.RowDefinitions><TextBlock Text="模态对话框演示"FontSize="16"Margin="10"HorizontalAlignment="Left" /><TextBlock Grid.Row="1"Text="确认要进行保存吗"FontSize="18"HorizontalAlignment="Center" VerticalAlignment="Center" /><StackPanel Grid.Row="2" HorizontalAlignment="Right" Orientation="Horizontal"><Button Width="100" Height="30"Content="确定"Command="{Binding OkCommand}"CommandParameter="" /><Button Width="100" Height="30"Content="取消"Command="{Binding CancelCommand}"CommandParameter=""Margin="10,0" /></StackPanel></Grid></Grid>
</UserControl>

第二步:注册对话框

注意种类关联的ViewModel,就是和对话框关联的ViewModel必须是实现了IDialogAware接口才可以.

namespace PrismDialogSimple.ViewModels
{public class ModalDialogViewModel : BindableBase, IDialogAware{public string Title => throw new NotImplementedException();public event Action<IDialogResult> RequestClose;public bool CanCloseDialog(){throw new NotImplementedException();}public void OnDialogClosed(){throw new NotImplementedException();}public void OnDialogOpened(IDialogParameters parameters){throw new NotImplementedException();}}
}

注册对话框的代码

protected override void RegisterTypes(IContainerRegistry containerRegistry)
{// 1. 注册导航视图和视图模型containerRegistry.RegisterForNavigation<MainWindow, MainWindowViewModel>();// 2. 注册对话框和对话框模型containerRegistry.RegisterDialog<ModalDialog, ModalDialogViewModel>();
}

第三步: 使用IDialogService显示对话框

在显示对话框之前,我们要把对话框的ViewModel中实现的那些接口来解释一下,并进行一些简单的实现,不然等下在弹出窗口的时候回报错.下面具体看看这个IDialogAware都实现了什么东西?

 public class ModalDialogViewModel : BindableBase, IDialogAware{public string Title => throw new NotImplementedException();public event Action<IDialogResult> RequestClose;public bool CanCloseDialog(){throw new NotImplementedException();}public void OnDialogClosed(){throw new NotImplementedException();}public void OnDialogOpened(IDialogParameters parameters){throw new NotImplementedException();}}
  • Title

我们首先要明白一点就是我们弹出的窗口是一个UserContorl控件,它其实是在一个Window窗口中的,这个窗口呢是Prism框架来进行创建和管理的,这个窗口呢,绑定了一个标题,就是这个Title,所以如果你只是在初始化的时候就一个标题,那么就这样简单的赋值就行了,如果你想要在运行过程中更改这个标题,你还要实现它的通知属性才行.

 private string title;public string Title{get { return title; }set{title = value;RaisePropertyChanged();}}
  • RequestClose()事件

用于从ViewModel触发对话框的关闭.是的ViewModel能够控制视图的关闭,而不必依赖视图层的代码.
这样做的好处就是保持了视图和业务逻辑的分离,符合MVVM设计原则.

  • CanCloseDialog()方法

CanCloseDialog()方法用于确定对话框是否可以关闭.这可以基于某些判断,比如表单是否填写完毕或者是否满足特定的业务逻辑来控制对话框是否可以关闭.返回true表示可以关闭对话框,返回false表示不允许关闭对话框.

  • OnDialogClosed()方法

关闭时调用,可以用于执行一些清理操作或者其他逻辑的地方,如资源释放或者状态重置.

  • OnDialogOpened()方法

在对话框打开时调用,可以在这里处理对话框打开的初始化工作,如基于传入的参数设置初始状态或者加载数据.

关于对话框的返回值怎么传递的问题?当你需要从对话框返回数据到调用它的地方的时候,你可以在RequestClose事件时,传递一个
DialogResult实例,并通过DialogResult的构造函数或者属性来设置返回值.

private void DoCancel()
{RequestClose?.Invoke(new DialogResult(ButtonResult.Cancel, null));
}private void DoOk()
{RequestClose?.Invoke(new DialogResult(ButtonResult.OK, null));
}

然后在弹出窗口的那里对结果进行获取,并进行判断

private void DoShowDialog(string modelType)
{switch (modelType){case "Modal":_dialogService.ShowDialog(nameof(ModalDialog), result =>{if (result.Result == ButtonResult.OK){System.Diagnostics.Debug.WriteLine("点击了OK,进行保存操作.");}else{System.Diagnostics.Debug.WriteLine("点击了取消,不进行保存操作.");}});break;}
}
③ Dialog使用案例一 (修改器)

我们做一个案例,界面上有两个按钮,弹出来的是同一个弹窗,但是弹窗的功能稍微有点不同,一个是名称修改,一个是电话修改.这个案例不重要,主要是运用之前的知识. 修改之后,如果点击了确定按钮,要把这个新的数据返回给调用者,调用者那里显示这个新的值.

主界面操作按钮

<Window x:Class="DialogUsedSimple01.Views.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:prism="http://prismlibrary.com/"Title="{Binding Title}"Width="1000" Height="800"prism:ViewModelLocator.AutoWireViewModel="True"><Grid><StackPanel Margin="50"><Button Width="100" Height="35"Content="修改名称"Command="{Binding ModifyNameCommand}" /><Button Width="100" Height="35"Content="修改电话"Command="{Binding ModifyPhoneCommand}"Margin="0,10" /></StackPanel></Grid>
</Window>

注册view和viewModel

namespace DialogUsedSimple01
{/// <summary>/// Interaction logic for App.xaml/// </summary>public partial class App{protected override Window CreateShell(){return Container.Resolve<MainWindow>();}protected override void RegisterTypes(IContainerRegistry containerRegistry){// 1. 注册导航视图containerRegistry.RegisterForNavigation<MainWindow, MainWindowViewModel>();// 2.注册弹框containerRegistry.RegisterDialog<DialogSimpleDemo, DialogSimpleViewModel>();}}
}

弹框视图

<UserControl x:Class="DialogUsedSimple01.Views.DialogSimpleDemo"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:local="clr-namespace:DialogUsedSimple01.Views"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800"Width="500" Height="300"><Grid><StackPanel Margin="10" Orientation="Horizontal"><Label Width="60" Height="30"Content="{Binding TextShow}"FontSize="16"VerticalAlignment="Center" /><TextBox Width="200" Height="30"Text="{Binding TextInput}"Margin="10,0"VerticalContentAlignment="Center" /></StackPanel><StackPanel Margin="10"HorizontalAlignment="Right" VerticalAlignment="Bottom"Orientation="Horizontal"><Button Width="100" Content="确认" Command="{Binding OkCommand}" /><Button Width="100"Content="取消"Command="{Binding CancelCommand}"Margin="10,0" /></StackPanel></Grid>
</UserControl>

弹框视图对应的ViewModel

namespace DialogUsedSimple01.ViewModels
{public class DialogSimpleViewModel : BindableBase, IDialogAware{public DialogSimpleViewModel(){OkCommand = new DelegateCommand(DoOK);CancelCommand = new DelegateCommand(DoCancel);}private void DoCancel(){RequestClose?.Invoke(new DialogResult(ButtonResult.Cancel, new DialogParameters($"resVal={TextInput}")));}private void DoOK(){RequestClose?.Invoke(new DialogResult(ButtonResult.OK, new DialogParameters($"resVal={TextInput}")));}public DelegateCommand OkCommand { get; private set; }public DelegateCommand CancelCommand { get; private set; }private string title;public string Title{get { return title; }set{title = value;RaisePropertyChanged();}}private string textShow;public string TextShow{get { return textShow; }set{textShow = value;RaisePropertyChanged();}}private string textInput;public string TextInput{get { return textInput; }set{textInput = value;RaisePropertyChanged();}}public event Action<IDialogResult> RequestClose;public bool CanCloseDialog(){return true;}public void OnDialogClosed(){}public void OnDialogOpened(IDialogParameters parameters){var parameter = parameters.GetValue<string>("modifyType");if (parameter == "Name"){Title = "名称修改器";TextShow = "新名称:";}else if (parameter == "Phone"){Title = "电话修改器";TextShow = "新电话:";}}}
}

主视图对应的ViewModel

namespace DialogUsedSimple01.ViewModels
{public class MainWindowViewModel : BindableBase{private IDialogService _dialogService;private string _title = "弹窗示例1";public string Title{get { return _title; }set { SetProperty(ref _title, value); }}public MainWindowViewModel(IDialogService dialogService){_dialogService = dialogService;ModifyNameCommand = new DelegateCommand(DoModifyName);ModifyPhoneCommand = new DelegateCommand(DoModifyPhone);}private void DoModifyPhone(){_dialogService.ShowDialog(nameof(DialogSimpleDemo), new DialogParameters($"modifyType=Phone"), result =>{string newVal = result.Parameters.GetValue<string>("resVal");if (result.Result == ButtonResult.OK){System.Diagnostics.Debug.WriteLine($"确认成功,修改后的电话为:{newVal}");}else{System.Diagnostics.Debug.WriteLine($"取消修改");}});}private void DoModifyName(){_dialogService.ShowDialog(nameof(DialogSimpleDemo), new DialogParameters($"modifyType=Name"), result =>{string newVal = result.Parameters.GetValue<string>("resVal");if (result.Result == ButtonResult.OK){System.Diagnostics.Debug.WriteLine($"确认成功,修改后名字为: {newVal}");}else{System.Diagnostics.Debug.WriteLine($"取消修改");}});}public DelegateCommand ModifyNameCommand { get; private set; }public DelegateCommand ModifyPhoneCommand { get; private set; }}
}
④ Dialog使用案例2(异常显示窗口)

我们平时看到的异常,比如下面这些,都分别代表什么意思呢?
在这里插入图片描述

  • 1. Data

  • 类型: IDictionary

  • 描述: 获取一个键值对集合,可以在其中添加自定义的异常处理信息.这使得你可以将额外的数据和异常对象关联,便于异常处理时获取更多的上下文信息.

  • 2. HResult

  • 类型: int

  • 描述: 获取或者设置HResult(一个由返回值标识的整数),它是一个从底层COM组件中映射过来的错误代码.通常是一个固定的很大的负数.

  • 3. HelpLink

  • 类型: string

  • 描述: 获取或者设置异常处理相关文档的链接地址或者URI,通常是文件路径或者是网页路径,可以提供更多的异常相关的帮助

  • 4. InnerException

  • 类型: Exception

  • 描述: 内部异常,也就是说异常是可以嵌套的,当我们在处理异常A的过程中,这个异常A是因为异常B导致的,也就是说抛出的异常A在异常B的Catch逻辑里,那么我称最开始出现的异常B为异常A的内部异常,说白了就是我们捕获的时候可能最后只捕获了异常A,但是异常A发生的时候其实先发生了异常B,异常B就是异常A的内部异常.

try
{string content= File.ReadAllText(filePath);
}
catch(BException ex)
{throw new AException();
}

上面的BException就是AException的内部异常.

  • 5. Message

  • 类型: string

  • 描述: 获取描述当前异常的消息.这是一个人类可以读取的错误描述,通常用于提示异常原因.

  • 6. Source

  • 类型: string

  • 描述: 获取或者设置引发当前异常的应用程序或者对象的名称.默认情况下,这通常是引发异常的程序集的名称.

  • 7. StackTrace

  • 类型: string

  • 描述: 获取调用堆栈上的当前发生异常的时候的方法调用序列包括异常发生的地方,对于调试和定位问题非常重要.

  • 8. TargetSite

  • 类型: MethodBase

  • 描述: 获取引发当前异常的方法.这个返回一个MethodBase对象,包含有关异常源头的方法的信息,如方法名,拥有类等.

个人想法

我现在是这样想的,当出现异常的时候,我首先是弹出一个框,这个框先弹出来的异常信息,上面有几个按钮,一个是查看详情,一个是打开打开异常日志目录,一个是关闭,点击查看详情的时候弹出一个框显示这个异常的详细信息,详细信息就是上面介绍的这些内容,这里要注意一点就是如果有内部异常,内部异常也要现实出来.然后这个详情页就只有两个按钮,一个是复制,一个是关闭

第一步: 创建异常显示页面,只显示异常的Message消息

<UserControl x:Class="ExceptionShowDialogDemo.Dialogs.ErrorShowDialog"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:local="clr-namespace:ExceptionShowDialogDemo.Dialogs"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"xmlns:prism="http://prismlibrary.com/"mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800"><!--设置Prism对话框所在窗口的全局属性1) 窗口显示的区域,父窗口的中心2) 窗口是否可以被用户调整大小,NoResize不可以3) 窗口是否在任务栏中显示, False,不显示,一般模态窗口和一些简单的操作不显示在任务栏4) 设置窗口的大小自动调整以适应其内容宽高--><prism:Dialog.WindowStyle><Style TargetType="Window"><Setter Property="prism:Dialog.WindowStartupLocation" Value="CenterOwner" /><Setter Property="ResizeMode" Value="NoResize" /><Setter Property="ShowInTaskbar" Value="False" /><Setter Property="SizeToContent" Value="WidthAndHeight" /></Style></prism:Dialog.WindowStyle><UserControl.Resources><Style TargetType="Button"><Setter Property="Height" Value="35" /><Setter Property="VerticalContentAlignment" Value="Center" /></Style></UserControl.Resources><DockPanel MinWidth="700" MinHeight="300"Background="#EEE"LastChildFill="False"><StackPanel Orientation="Horizontal"><Label><TextBlock Text="{Binding Message}"Foreground="#333"FontSize="16"TextWrapping="Wrap" /></Label></StackPanel><StackPanel DockPanel.Dock="Bottom"Margin="20"HorizontalAlignment="Right"Orientation="Horizontal"><Button Width="100" Content="查看详情" Command="{Binding ViewDetailCommand}" /><Button Width="100" Content="关闭" Command="{Binding CloseDialogCommand}" /></StackPanel></DockPanel>
</UserControl>

其对应的ViewModel

namespace ExceptionShowDialogDemo.ViewModels
{public class ErrorShowDialogViewModel : BindableBase, IDialogAware{private readonly IDialogService _dialogService;public ErrorShowDialogViewModel(IDialogService dialogService){_dialogService = dialogService;ViewDetailCommand = new DelegateCommand(DoViewDetail);OpenErrorDirCommand = new DelegateCommand(DoOpenErrorDir);CloseDialogCommand = new DelegateCommand(DoCloseDialog);}private void DoCloseDialog(){RequestClose?.Invoke(new DialogResult());}private void DoOpenErrorDir(){}private void DoViewDetail(){DialogParameters parameters = new DialogParameters(){{"Title","异常详情页" },{"Exception",ExceptionDetail }};_dialogService.ShowDialog(nameof(ExceptionDetailDialog), parameters, result => { });}public DelegateCommand ViewDetailCommand { get; private set; }public DelegateCommand OpenErrorDirCommand { get; private set; }public DelegateCommand CloseDialogCommand { get; private set; }private string title;public string Title{get { return title; }set{title = value;RaisePropertyChanged();}}private string message;public string Message{get { return message; }set{message = value;RaisePropertyChanged();}}public Exception ExceptionDetail { get; set; }public event Action<IDialogResult> RequestClose;public bool CanCloseDialog(){return true;}public void OnDialogClosed(){}public void OnDialogOpened(IDialogParameters parameters){var title = parameters.GetValue<string>("Title");var exception = parameters.GetValue<Exception>("Exception");if (!string.IsNullOrEmpty(title) && exception != null){Title = title;ExceptionDetail = exception; // 异常赋值Message = exception.Message;}}}
}

注册和模拟异常代码app.cs中

namespace ExceptionShowDialogDemo
{/// <summary>/// Interaction logic for App.xaml/// </summary>public partial class App{public App(){Startup += App_Startup;DispatcherUnhandledException += App_DispatcherUnhandledException;}private void App_DispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e){var dialogService = Container.Resolve<IDialogService>();DialogParameters parameters = new DialogParameters{{ "Title", "未捕获异常提示框" },{ "Exception", e.Exception }};dialogService.ShowDialog(nameof(ErrorShowDialog), parameters, result => { });}private void App_Startup(object sender, StartupEventArgs e){}protected override Window CreateShell(){return Container.Resolve<MainWindow>();}protected override void OnStartup(StartupEventArgs e){base.OnStartup(e);try{throw new ArgumentException("数据库操作异常");}catch (Exception ex){ex.Data.Add("DataBaseName", "TableA");ex.Data.Add("HappenTime", DateAndTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));ex.HelpLink = "Https//www.baidu.com";throw new Exception("外部异常", ex);}}protected override void RegisterTypes(IContainerRegistry containerRegistry){containerRegistry.RegisterForNavigation<MainWindow, MainWindowViewModel>();containerRegistry.RegisterDialog<ErrorShowDialog, ErrorShowDialogViewModel>();containerRegistry.RegisterDialog<ExceptionDetailDialog, ExceptionDetailDialogViewModel>();}}
}

点击详情页弹出的另外一个详情页窗口

<UserControl x:Class="ExceptionShowDialogDemo.Dialogs.ExceptionDetailDialog"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:converter="clr-namespace:ExceptionShowDialogDemo.ValueConverters"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:local="clr-namespace:ExceptionShowDialogDemo.Dialogs"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"xmlns:prism="http://prismlibrary.com/"mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800"><prism:Dialog.WindowStyle><Style TargetType="Window"><Setter Property="prism:Dialog.WindowStartupLocation" Value="CenterOwner" /><Setter Property="ResizeMode" Value="NoResize" /><Setter Property="ShowInTaskbar" Value="False" /><Setter Property="SizeToContent" Value="WidthAndHeight" /></Style></prism:Dialog.WindowStyle><UserControl.Resources><converter:GetTypeConverter x:Key="GetTypeConverter" /><converter:NotNullConverter x:Key="NotNullConverter" /><Style TargetType="Button"><Setter Property="Height" Value="35" /><Setter Property="Width" Value="100" /><Setter Property="VerticalContentAlignment" Value="Center" /></Style></UserControl.Resources><DockPanel MinWidth="700" MinHeight="300"Background="#EEE"LastChildFill="False"><StackPanel DockPanel.Dock="Top" Margin="10" Orientation="Horizontal"><Label VerticalContentAlignment="Center"><TextBlock Text="{Binding ExceptionShow.Message}" Foreground="#333" TextWrapping="Wrap" /></Label></StackPanel><!--Width = "*"  和 Width 什么都不写有什么区别?1) Width = * 宽度占用剩余的部分,使用Width="*"2) 如果什么都不写表示这个列的宽度会根据内容自动适应--><Grid Margin="10"><Grid.ColumnDefinitions><ColumnDefinition Width="250" /><ColumnDefinition Width="auto" /><ColumnDefinition Width="*" /></Grid.ColumnDefinitions><ListView Background="#EEE"Padding="5,0"ItemsSource="{Binding Exceptions}"SelectedValue="{Binding ExceptionSelected, Mode=TwoWay}"><ListView.ItemTemplate><DataTemplate><!--绑定当前上下文,其中的Bingding Path=. 可以省略,直接写成Binding Convereter={StaticResource GetTypeConverter}--><TextBlock Text="{Binding Path=., Mode=OneWay, Converter={StaticResource GetTypeConverter}}" /></DataTemplate></ListView.ItemTemplate></ListView><!--  Grid之间的分隔线  --><GridSplitter Grid.Column="1"Width="2"Background="#888"Margin="10,0" /><ScrollViewer Grid.Column="2"HorizontalScrollBarVisibility="Auto" TextBlock.FontSize="13"VerticalScrollBarVisibility="Hidden"><StackPanel><Label><StackPanel Orientation="Horizontal"><TextBlock Text="{Binding ExceptionSelected, Converter={StaticResource GetTypeConverter}}" FontSize="18" FontWeight="Bold" /><TextBlock Text="(异常类型)" FontSize="18" FontWeight="Bold" /></StackPanel></Label><Label Content="Message(异常信息)"FontWeight="Bold"Margin="0,5,0,0"Visibility="{Binding ExceptionSelected.Message, Converter={StaticResource NotNullConverter}}" /><Label Content="{Binding ExceptionSelected.Message}" FontSize="12" Visibility="{Binding ExceptionSelected.Message, Converter={StaticResource NotNullConverter}}" /><Label Content="Stack Trace(方法调用序列)"FontWeight="Bold"Margin="0,5,0,0"Visibility="{Binding ExceptionSelected.StackTrace, Converter={StaticResource NotNullConverter}}" /><Label Content="{Binding ExceptionSelected.StackTrace}" Margin="0,5,0,0" /><Label Content="Target Site(异常发生方法)"FontWeight="Bold"Margin="0,5,0,0"Visibility="{Binding ExceptionSelected.TargetSite, Converter={StaticResource NotNullConverter}}" /><Label Content="{Binding ExceptionSelected.TargetSite}" Visibility="{Binding ExceptionSelected.TargetSite, Converter={StaticResource NotNullConverter}}" /><!--每个Exception对象都有一个Data属性,这是一个IDictionary接口的实例,用于存储定义的键值数据.如果你有一个数据库连接异常,你想知道是哪个数据库连接异常,你可以把这个信息添加到Data中去.--><Label Content="Data(异常附加信息)"FontWeight="Bold"Margin="0,5,0,0"Visibility="{Binding ExceptionSelected.Data, Converter={StaticResource NotNullConverter}}" /><ItemsControl ItemsSource="{Binding ExceptionSelected.Data}" Visibility="{Binding ExceptionSelected.Data, Converter={StaticResource NotNullConverter}}"><ItemsControl.ItemTemplate><DataTemplate><Label><TextBlock FontSize="12" FontStyle="Italic"><Run Text="{Binding Key, Mode=OneWay}" /><Run Text=" = " /><Run Text="{Binding Value, Mode=OneWay}" /></TextBlock></Label></DataTemplate></ItemsControl.ItemTemplate></ItemsControl><!--获取或者设置导致错误的应用程序的名称,通常是引发异常的应用程序的名称--><Label Content="Source(异常程序集)"FontWeight="Bold"Margin="0,5,0,0"Visibility="{Binding ExceptionSelected.Source, Converter={StaticResource NotNullConverter}}" /><Label Content="{Binding ExceptionSelected.Source}" Visibility="{Binding ExceptionSelected.Source, Converter={StaticResource NotNullConverter}}" /><Label Content="Help Link(异常帮助链接)"FontWeight="Bold"Margin="0,5,0,0"Visibility="{Binding ExceptionSelected.HelpLink, Converter={StaticResource NotNullConverter}}" /><Label Content="{Binding ExceptionSelected.HelpLink}" Visibility="{Binding ExceptionSelected.HelpLink, Converter={StaticResource NotNullConverter}}" /><Label Content="HResult(异常错误代码)"FontWeight="Bold"Margin="0,10,0,0"Visibility="{Binding ExceptionSelected.HResult, Converter={StaticResource NotNullConverter}}" /><Label Content="{Binding ExceptionSelected.HResult}" Visibility="{Binding ExceptionSelected.HResult, Converter={StaticResource NotNullConverter}}" /></StackPanel></ScrollViewer></Grid><StackPanel DockPanel.Dock="Bottom"Margin="10"HorizontalAlignment="Right"Orientation="Horizontal"><Button Content="关闭" Command="{Binding CloseDialogCommand}" /></StackPanel></DockPanel>
</UserControl>

异常详情页对应的ViewModel

namespace ExceptionShowDialogDemo.ViewModels
{public class ExceptionDetailDialogViewModel : BindableBase, IDialogAware{public ExceptionDetailDialogViewModel(){CloseDialogCommand = new DelegateCommand(DoCloseDialog);}private void DoCloseDialog(){RequestClose?.Invoke(new DialogResult());}public DelegateCommand CloseDialogCommand { get; private set; }private string title;public string Title{get { return title; }set{title = value;RaisePropertyChanged();}}private Exception exceptionSelected;public Exception ExceptionSelected{get { return exceptionSelected; }set{exceptionSelected = value;RaisePropertyChanged();}}/// <summary>/// 异常列表,包括发生异常的内部异常./// </summary>private ObservableCollection<Exception> exceptions = new ObservableCollection<Exception>();public ObservableCollection<Exception> Exceptions{get { return exceptions; }set{exceptions = value;RaisePropertyChanged();}}public event Action<IDialogResult> RequestClose;public bool CanCloseDialog(){return true;}public void OnDialogClosed(){}public void OnDialogOpened(IDialogParameters parameters){Exceptions.Clear();var title = parameters.GetValue<string>("Title");var exception = parameters.GetValue<Exception>("Exception");if (!string.IsNullOrEmpty(title)){Title = title;}while (exception != null){Exceptions.Add(exception);// 当没有内部异常的时候exception.InnerException的返回值为nullexception = exception.InnerException;}ExceptionSelected = Exceptions.FirstOrDefault();}}
}

使用到的两个类型转换器

namespace ExceptionShowDialogDemo.ValueConverters
{public class GetTypeConverter : IValueConverter{/// <summary>/// 当数据从绑定源(后台的一个数据模型)传递给绑定目标(例如一个控件)时,Converter方法会被调用./// </summary>/// <param name="value">要转换的值(绑定的数据源)</param>/// <param name="targetType">转换的目标类型,UI控件属性承载的类型,比如Text和Content的目标类型就是string</param>/// <param name="parameter">一个可选参数,可以在XAML中提供,你可以使用这个参数来影响转换的行为</param>/// <param name="culture">用于转换的文化信息,用来进行一些区域特性的转换,例如日期和字符串</param>/// <returns></returns>public object Convert(object value, Type targetType, object parameter, CultureInfo culture){var res = value?.GetType() ?? targetType;// 如果value为null,就返回targetType,如果是textBox或者是textBlock就返回stringreturn res;}// 当数据从绑定目标传递给绑定源的时候,就是从UI控件的值到后台的值的时候.ConvertBack会被调用.// 其实就是相当于控件上的值发生了变化,会调用这个方法,而我们使用了Bingding.DoNothing,表示不作转换// 什么都不做./// <summary>/// /// </summary>/// <param name="value">从绑定目标(UI控件)传递过来的值</param>/// <param name="targetType">绑定源的数据类型</param>/// <param name="parameter"></param>/// <param name="culture"></param>/// <returns></returns>public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture){return Binding.DoNothing;}}
}
namespace ExceptionShowDialogDemo.ValueConverters
{public class NotNullConverter : IValueConverter{public object Convert(object value, Type targetType, object parameter, CultureInfo culture){bool res = value != null;switch (value){case ICollection collection:res = collection.Count > 0;break;case string str:res = !string.IsNullOrWhiteSpace(str);break;case int i:res = i != 0;break;case double d:res = Math.Abs(d) > 0.00001;break;}// 看看是否需要 反转结果,根据paraeter的值来判读昂if ((parameter is bool inverse || parameter is string s && bool.TryParse(s, out inverse)) && inverse)res = !res;if (targetType == typeof(Visibility))return res ? Visibility.Visible : Visibility.Collapsed;return res;}public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture){return Binding.DoNothing;}}
}

最后实现的效果,出现异常的第一个弹窗
在这里插入图片描述
点击查看详情的弹窗
在这里插入图片描述
总结:

这个小案例还是用到了不少的知识点,但是还是有可以优化的地方,因为这里又在ViewModel中操作了UI,最好的实践方式是将弹窗这里封装成为一个服务,然后在服务里操作窗口,后续再介绍

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/1773.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Proxyman Premium for Mac:网络调试利器,开发者首选!

Proxyman Premium for Mac是一款功能强大的网络调试和分析工具&#xff0c;专为开发者和测试人员打造。这款软件以其出色的性能和丰富的功能&#xff0c;帮助用户在网络开发和调试过程中更有效地分析和拦截网络请求&#xff0c;进行必要的修改和重发&#xff0c;从而进行更深度…

【Linux基础】Linux基础概念

目录 前言 浅谈什么是文件&#xff1f; Linux下目录结构的认识及路径 目录结构 路径 家目录 什么是递归式的删除 重定向 输出重定向&#xff1a; 追加重定向&#xff1a; 输入重定向&#xff1a; 命令行管道 shell外壳 为什么需要shell外壳&#xff1f; shell外壳…

使用FPGA实现超前进位加法器

介绍 前面已经向大家介绍过8位逐位进位加法器了&#xff0c;今天向大家介绍4位超前进位加法器。 对于逐位进位加法器来说&#xff0c;计算任意一位的加法运算时&#xff0c;必须等到低位的加法运算结束送来进位才能运行。这种加法器结构简单&#xff0c;但是运算慢。 对于超…

NFT卡牌质押分红模式开发技术讲解分析

近年来&#xff0c;随着加密货币市场的快速发展&#xff0c;NFT&#xff08;Non-Fungible Token&#xff0c;非同质化代币&#xff09;作为一种独特的数字资产形式备受关注。其中&#xff0c;NFT卡牌质押分红模式是一种创新的应用场景&#xff0c;为用户提供了一种参与和投资的…

【Qt 学习笔记】Qt常用控件 | 按钮类控件 | Check Box的使用及说明

博客主页&#xff1a;Duck Bro 博客主页系列专栏&#xff1a;Qt 专栏关注博主&#xff0c;后期持续更新系列文章如果有错误感谢请大家批评指出&#xff0c;及时修改感谢大家点赞&#x1f44d;收藏⭐评论✍ Qt常用控件 | 按钮类控件 | Check Box的使用及说明 文章编号&#xff…

arduino使用光电开关控制电动平台回零

1.接线介绍 注意&#xff1a;NPN和PNP型光电开关的接线方式不同 首先&#xff0c;得在arduino上设置一个数字量接口&#xff0c;例如&#xff1a; #define PHOTO_SWITCH_PIN 53 // 假设光电开关连接到数字引脚53 然后根据你的情况把白色的或者黑色的线接到该引脚上&#x…

PCIe系统阻抗控制85还是100的验证

高速先生成员--周伟 还记得上次的文章&#xff0c;PCIe阻抗控制&#xff0c;85ohm和100ohm哪个好&#xff0c;文章里面只讲到目前的主要问题&#xff0c;但没有给出具体怎么解决这个问题&#xff0c;今天我们就通过无源仿真的方式来聊聊上次那个问题的最终解决方案。 目前我们看…

千锤百炼算法系列之动态规划

题外话 这段时间,我必须把算法弄明白 这篇直接讲解动态规划所有细节! 前面那篇 千锤百炼之每日算法(一)-CSDN博客 也有关于动态规划的讲解,也非常详细 很简单,我成尊不就是了?!!! 正题 动态规划 这里我们主要是让大家明白什么是动态规划,怎么用动态规划解题 我就不用…

IEC104协议

1. 简介 IEC104规约是一个广泛应用于电力、城市轨道交通等行业的国际标准。 2. 术语解释 遥脉 (电度量)&#xff1a; 是指对现场某装置所发出的脉冲信号进行周期累计的一种远程计数操作。 其实&#xff0c;遥脉也可以看成是被具体规定了采用脉冲计数作为测量方法的一种遥测…

【软考经验分享】软考-中级-嵌入式备考

这里写目录标题 教辅用书嵌入式系统设计师考试大纲嵌入式系统设计师教程嵌入式系统设计师5天修炼嵌入式系统设计师考前冲刺100题 刷题软件希赛网软考真题 视频教程希赛网王道-计组计网 教辅用书 嵌入式系统设计师考试大纲 50页左右&#xff0c;内容为罗列一些考点&#xff0c…

信息化工作人员必备常识3——nslookup命令【用来诊断域名(DNS) 基础结构的信息以及用来查看域名和IP的对应关系】

[TOC](信息化工作人员必备常识3——nslookup命令【用来诊断域名(DNS) 基础结构的信息以及用来查看域名和IP的对应关系】) 前言 信息化相关的工作人员&#xff0c;无论你是开发人员、产品经理、CIO领导&#xff0c;你只要是在做信息化相关的工作&#xff0c;有些基础知识和操作…

C#版Facefusion:让你的脸与世界融为一体!-05 人脸增强

C#版Facefusion&#xff1a;让你的脸与世界融为一体&#xff01;-05 人脸增强 目录 说明 效果 模型信息 项目 代码 下载 说明 C#版Facefusion一共有如下5个步骤&#xff1a; 1、使用yoloface_8n.onnx进行人脸检测 2、使用2dfan4.onnx获取人脸关键点 3、使用arcface_w60…

基于MNIST的手写数字识别

上次我们基于CIFAR-10训练一个图像分类器&#xff0c;梳理了一下训练模型的全过程&#xff0c;并且对卷积神经网络有了一定的理解&#xff0c;我们再在GPU上搭建一个手写的数字识别cnn网络&#xff0c;加深巩固一下 步骤 加载数据集定义神经网络定义损失函数训练网络测试网络 …

小扎万字深度访谈:最强开源大模型Llama 3发布,Meta的AGI路径和开源哲学

今天Meta发布了史上最强开源大模型Llama 3&#xff0c;一口气发布了 8B 和 70B 2个预训练和指令微调模型&#xff0c;对比同级别的参数模型&#xff0c;性能上均达到了最佳。 此外&#xff0c;Meta还发布了基于Llama 3的AI助手Meta AI&#xff0c;可以在Facebook、Instagram、W…

一举颠覆Transformer!最新Mamba结合方案刷新多个SOTA,单张GPU即可处理140k

还记得前段时间爆火的Jamba吗&#xff1f; Jamba是世界上第一个生产级的Mamba大模型&#xff0c;它将基于结构化状态空间模型 (SSM) 的 Mamba 模型与 transformer 架构相结合&#xff0c;取两种架构之长&#xff0c;达到模型质量和效率兼得的效果。 在吞吐量和效率等关键衡量指…

基于函数计算FC3.0 部署AI数字绘画stable-diffusion自定义模型

基于函数计算FC3.0 部署AI数字绘画stable-diffusion自定义模型 部署AI数字绘画stable-diffusion曲线救国授权github账号 部署ffmpeg-app-v3总结 在讲述了函数计算FC3.0和函数计算FC2.0的操作界面UI改版以及在函数管理、函数执行引擎、自定义域名、函数授权及弹性伸缩规则方面进…

【管理咨询宝藏82】麦肯锡某化工企业战略咨询报告

本报告首发于公号“管理咨询宝藏”&#xff0c;如需阅读完整版报告内容&#xff0c;请查阅公号“管理咨询宝藏”。 【管理咨询宝藏82】麦肯锡某化工企业战略咨询报告 【格式】PPT版本&#xff0c;可以编辑 【关键词】战略咨询、MBB、业务规划 【核心观点】 - 打造面向客户的…

ROS2 仿真学习02 Gazebo导入官方示例模型

1.下载模型 git clone https://gitee.com/bingda-robot/gazebo_models.git将gazebo_models拖到到.gazebo当中&#xff08;如果没看到.gazebo文件请按住CTRLh&#xff09; 2.添加模型到gazebo的Insert 这就将官方示例的模型都导入到Gazebo 了 随便试试一个模型

SLS 查询新范式:使用 SPL 对日志进行交互式探索

作者&#xff1a;无哲 引言 在构建现代数据和业务系统的过程中&#xff0c;可观测性已经变得至关重要&#xff0c;日志服务&#xff08;SLS&#xff09;为 Log/Trace/Metric 数据提供了大规模、低成本、高性能的一站式平台服务&#xff0c;并提供数据采集、加工、投递、分析、…

海外平台运营为什么需要静态住宅IP?

在世界经济高度全球化的今天&#xff0c;许多企业家和电子商务卖家纷纷转向海外平台进行业务扩展。像亚马逊、eBay这样的跨国电商平台为卖家提供了巨大的机会&#xff0c;来接触到世界各地的顾客。然而&#xff0c;在这些平台上成功运营&#xff0c;尤其是维持账号的健康和安全…