Prism.Wpf框架中WindowStartupLocation的问题

220 阅读2分钟

一、出现场景

        版本:Prism.Wpf 8.1.97。在应用IDialogService,来显示弹窗时,主界面的ViewModel:

public class MainWindowViewModel : BindableBase
{
      private string _title = "Prism Application";
      private IDialogService _dialogService;
      public string Title
      {
          get { return _title; }
          set { SetProperty(ref _title, value); }
      }

      public ICommand ShowDialogTestCommand { get; private set; }
      public MainWindowViewModel(IDialogService dialogService)
      {
          _dialogService = dialogService;
          ShowDialogTestCommand = new DelegateCommand(ShowDialogTest);
      }

      private void ShowDialogTest()
      {
          _dialogService.ShowDialog(nameof(TestView));
      }
  }

主界面只放一个按钮并绑定ShowDialogTestCommand ,弹窗的View:

<UserControl
    x:Class="BlankApp1.Views.TestView"
    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:BlankApp1.Views"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:prism="http://prismlibrary.com/"
    Width="400"
    Height="400"
    mc:Ignorable="d">
    <prism:Dialog.WindowStyle>
        <Style TargetType="Window">
            <Setter Property="Width" Value="400" />
            <Setter Property="Height" Value="400" />
            <Setter Property="ResizeMode" Value="NoResize" />
            <Setter Property="WindowStyle" Value="None" />
            <Setter Property="AllowsTransparency" Value="True" />
            <Setter Property="prism:Dialog.WindowStartupLocation" Value="Manual" />
            <Setter Property="Top" Value="10" />
            <Setter Property="Left" Value="10" />
            <Setter Property="Background" Value="Yellow" />
        </Style>
    </prism:Dialog.WindowStyle>
    <Grid>
        <TextBlock Text="在主窗口弹窗" />
    </Grid>
</UserControl>

其中设置的 prism:Dialog.WindowStartupLocation" Value="Manual" Property="Top" Value="10" Property="Left" Value="10" 都不会生效,不管WindowStartupLocation设置为任何值,都显示在中间。


二、原因分析

       在没有提供自定义的Window时,Prism框架会默认生成一个Window包裹在UserControl外面,并将在xaml设置的Style赋值给这个Window,而这个Window在Prism中默认DialogWindow,关键源码如下:

//
// 摘要:
//     Configure Prism.Services.Dialogs.IDialogWindow properties.
//
// 参数:
//   window:
//     The hosting window.
//
//   dialogContent:
//     The dialog to show.
//
//   viewModel:
//     The dialog's ViewModel.
protected virtual void ConfigureDialogWindowProperties(IDialogWindow window, FrameworkElement dialogContent, IDialogAware viewModel)
{
    Style windowStyle = Dialog.GetWindowStyle(dialogContent);
    if (windowStyle != null)
    {
        window.Style = windowStyle;
    }

    window.Content = dialogContent;
    window.DataContext = viewModel;
    if (window.Owner == null)
    {
        window.Owner = Application.Current?.Windows.OfType<Window>().FirstOrDefault((Window x) => x.IsActive);
    }
}

       但prism:Dialog.WindowStartupLocation" Value="Manual"是类Dialog中的附加属性,并不是Window.Style中的依赖属性。
       在界面给其赋值为Manual后,源码中并没有将这个值赋值给window对象,导致window对象中的WindowStartupLocation还是默认值。那么为什么会在中间呢?Window的WindowStartupLocation默认值不是Manual吗?的确。但DialogWindow的WindowStartupLocation默认值是CenterOwner:

<Window x:Class="Prism.Services.Dialogs.DialogWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="{Binding Title}"
        WindowStartupLocation="CenterOwner">
    <Window.Style>
        <Style TargetType="{x:Type Window}" >
            <Setter Property="SizeToContent" Value="WidthAndHeight" />
        </Style>
    </Window.Style>

</Window>

所以无论怎么设置prism:Dialog.WindowStartupLocation" Value=“Manual”,位置总是CenterOwner。


三、解决方案

1、自定义Window,并传入。 2、改源码ConfigureDialogWindowProperties:

protected virtual void ConfigureDialogWindowProperties(IDialogWindow window, FrameworkElement dialogContent, IDialogAware viewModel)
{
    if (window is DialogWindow dialogWindow)
    {
        var windowStyle = Dialog.GetWindowStyle(dialogContent);
        if (windowStyle != null)
            dialogWindow.Style = windowStyle;

        var windowStartupLocation = Dialog.GetWindowStartupLocation(dialogContent);
        dialogWindow.WindowStartupLocation = windowStartupLocation;
        dialogWindow.Content = dialogContent;
        dialogWindow.DataContext = viewModel; //we want the host window and the dialog to share the same data context

        if (dialogWindow.Owner == null)
            dialogWindow.Owner = Application.Current?.Windows.OfType<Window>().FirstOrDefault(x => x.IsActive);

    }
}