Prism系列之如何使用弹窗

自定义弹窗窗口

基础弹窗

  • 先定义一个窗口类 CustomDialog,并实现 IDialogWindow 接口,xaml 代码部分不需要修改。

     public partial class CustomDialog : Window, IDialogWindow
     {
         public CustomDialog()
         {
             InitializeComponent();
         }
    
         public IDialogResult Result { get; set; }
     }
    
  • 随后在 App.xaml.cs 中注册

    protected override void RegisterTypes(IContainerRegistry containerRegistry)
    {
        containerRegistry.RegisterDialog<GreetingView>();
        containerRegistry.RegisterDialogWindow<CustomDialog>();
    }
    
  • 接下来正常使用弹窗服务即可,以下是运行效果,为了确认真的使用了 CustomDialog, 我在 CustomDialog.xaml 中设置了 Background = "Red"

注册多个自定义弹窗

  • 如果我们的项目里不止一个自定义弹窗模板,根据不同的需求使用不同的弹窗,那么我们应该如何做?

  • 先再定义一个窗口类 MessageVDialog,代码与 CustomDialog 一致,不同的是将背景改为 Green,并且在 App.xaml.cs 中注册

    protected override void RegisterTypes(IContainerRegistry containerRegistry)
    {
        containerRegistry.RegisterDialog<GreetingView>();
        containerRegistry.RegisterDialogWindow<CustomDialog>();
        containerRegistry.RegisterDialogWindow<MessageVDialog>(); // 最后一个注册的DialogWindow将作为默认弹窗模板
    }
    
  • 运行效果:

    • 如果在 App.xaml.cs 将注册顺序颠倒一下,先注册 MessageVDialog 后注册 CustomDialog ,那么将默认使用 CustomDialog

那么如何在使用弹窗的时候指定特定的弹窗模板?

  • 官方文档 中没有说明

  • 问了ChatGPT,给了一堆复杂的配置,说需要自定义实现 DialogService,没有尝试(大概率是个错误的答案)

  • 查看源码有没有解决方案,还真找到了答案,在 DialogService.ShowDialog 中发现了一个参数: KnownDialogParameters.WindowName = "windowName"

  • 于是我反过来问GPT WindowName 参数有没有效果,给我的回答是没有用

  • 纠错GPT,GPT采纳了建议

  • 偏题了,回过头来看下如何传入参数

    // App.xaml.cs
    protected override void RegisterTypes(IContainerRegistry containerRegistry)
    {
        containerRegistry.RegisterDialog<GreetingView>();
        containerRegistry.RegisterDialogWindow<CustomDialog>("CustomDialog");
        containerRegistry.RegisterDialogWindow<MessageVDialog>(); // 最后一个注册的DialogWindow将作为默认弹窗模板
    }
    
    // MainViewModel.cs
    DialogParameters para = new DialogParameters()
    {
        { KnownDialogParameters.WindowName, "CustomDialog"}, // 如果注释该行,则使用 MessageVDialog 模板
    };
    DialogService.ShowDialog(nameof(GreetingView), para);
    

自定义弹窗模板的内容

  • 之前注册了 MessageVDialog,但是显示的弹窗的内容全部是 GreetingView 界面,现在想要在此基础上添加 OKCancel 按钮,如下图所示

  • 经过测试发现,不管 MessageDialog.xaml 中写了什么内容,最终都不会呈现在界面上(chatgpt还给了一系列的错误方案,例如 放一个 名为 PART_Host 的ContentControl)。

  • 遇到解决不了的问题还是看源码确认下:

    public void ShowDialog(string name, IDialogParameters parameters, DialogCallback callback)
    {
        parameters ??= new DialogParameters();
        var isModal = parameters.TryGetValue<bool>(KnownDialogParameters.ShowNonModal, out var show) ? !show : true;
        var windowName = parameters.TryGetValue<string>(KnownDialogParameters.WindowName, out var wName) ? wName : null;
    
        IDialogWindow dialogWindow = CreateDialogWindow(windowName);
        ConfigureDialogWindowEvents(dialogWindow, callback);
        ConfigureDialogWindowContent(name, dialogWindow, parameters);
    
        ShowDialogWindow(dialogWindow, isModal);
    }
    
    protected virtual void ConfigureDialogWindowContent(string dialogName, IDialogWindow window, IDialogParameters parameters)
    {
        ConfigureDialogWindowProperties(window, dialogContent, viewModel); 
    }
     
    protected virtual void ConfigureDialogWindowProperties(IDialogWindow window, FrameworkElement dialogContent, IDialogAware viewModel)
    {
        var windowStyle = Dialog.GetWindowStyle(dialogContent);
        if (windowStyle != null)
            window.Style = windowStyle;
    
        window.Content = dialogContent; // 
        window.DataContext = viewModel; //we want the host window and the dialog to share the same data context
    
        if (window.Owner == null)
            window.Owner = Application.Current?.Windows.OfType<Window>().FirstOrDefault(x => x.IsActive);
    }
    
  • 从以上代码可以看出 window.Content = dialogContent; ,直接将window的 Content 替换,所以不管我们在 .xaml 中写了什么内容,都会被替换。

  • 那么只能从模板上想办法了,直接修改 MessageVDialog 的模板

    <Window.Template>
        <ControlTemplate TargetType="Window">
            <Border Background="White">
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition />
                        <RowDefinition Height="Auto" />
                    </Grid.RowDefinitions>
    
                    <ContentControl Content="{TemplateBinding Content}" />
    
                    <StackPanel Orientation="Horizontal"
                                HorizontalAlignment="Right"
                                Grid.Row="1"
                                Margin="20">
                        <Button Content="OK"
                                Width="100"
                                Click="OKButton_Click" />
    
                        <Button Content="Cancel"
                                Margin="20 0 0 0"
                                Width="100"
                                Click="CancelButton_Click" />
                    </StackPanel>
                </Grid>
            </Border>
        </ControlTemplate>
    </Window.Template>
    
    private void OKButton_Click(object sender, RoutedEventArgs e)
    {
        Result = new DialogResult(ButtonResult.OK);
        this.DialogResult = true;
    }
    
    private void CancelButton_Click(object sender, RoutedEventArgs e)
    {
        Result = new DialogResult(ButtonResult.Cancel);
        this.DialogResult = false;
    }
    
  • 运行效果:

posted on 2025-04-14 20:12  baby-jie  阅读(134)  评论(0)    收藏  举报