一、出现场景
版本: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);
}
}