从ResourceDictionary设置WindowStartupLocation会引发XamlParseException异常

11

当我尝试在ResourceDictionary中通过Setter设置WindowStartupLocation属性时,我会收到一个XamlParseException错误:

“Set property 'System.Windows.Setter.Property' threw an exception.' 行号 'x' 和行位置 'y'。

内部异常是ArgumentNullException

值不能为 null。参数名: property。

我的资源字典样式如下:

<Style TargetType="Window" x:Key="WindowStyle">
    <Setter Property="SizeToContent" Value="WidthAndHeight" />
    <Setter Property="ResizeMode" Value="CanMinimize" />
    <Setter Property="WindowStartupLocation" Value="CenterOwner" />
</Style>

问题并不在于使用 ResourceDictionary,因为当我移除 WindowStartupLocation 属性时,引用该样式的窗口上的另外两个属性 (SizeToContentResizeMode) 都被设置为预期值:

<Window x:Class="WpfApplication1.MyWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Style="{DynamicResource WindowStyle}">
    <Window.Resources>
        <ResourceDictionary Source="MyResourceDictionary.xaml" />
    </Window.Resources>
</Window>

有人遇到这个问题吗?这是WPF的一个bug/限制吗?

P.S. 我知道这个问题类似于从资源字典设置窗口启动位置,但在那个问题中没有提供足够的信息,因此一直未解决。

3个回答

15

问题在于WindowStartupLocation不是一个依赖属性,因此您无法在样式设置器中设置它。查看ILSpy,Setter调用了

CheckValidProperty(DependencyProperty property)

并抛出 NullArgumentException 异常。

由于 WindowStartupLocation 只是一个公共语言运行时 (CLR) 属性,因此无法以此方式设置它。

但是,你仍然可以使用 ResourceDictionary:

<Application.Resources>
    <ResourceDictionary>
        <Style x:Key="WindowStyle" TargetType="Window">
            <Setter Property="SizeToContent" Value="WidthAndHeight" />
            <Setter Property="ResizeMode" Value="CanMinimize" />
        </Style>
        <WindowStartupLocation x:Key="WSL">CenterOwner</WindowStartupLocation>
    </ResourceDictionary>
</Application.Resources>

<Window x:Class="WpfApplication7.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"            
        WindowStartupLocation="{StaticResource WSL}"
        Style="{StaticResource WindowStyle}" />

那么你可以直接从XAML(使用<Window WindowStartupLocation="CenterOwner" />语法)设置CLR(非依赖项)属性,但不能从资源字典中设置,对吗? - User3810621
不完全是这样。您可以直接在XAML中设置属性,但仍然可以使用资源字典,只是不能使用样式。也就是说,以下内容有效: <Application.Resources> <ResourceDictionary> <Style x:Key="WindowStyle" TargetType="Window"> <Setter Property="SizeToContent" Value="WidthAndHeight" /> <Setter Property="ResizeMode" Value="CanMinimize" /> CenterOwner - Andrew Jones
谢谢,非常有帮助!不幸的是,我不能使用StaticResource,因为我需要从Window引用ResourceDictionary(我正在开发一个Office插件,它没有WPFApplication类)。但是您的建议非常适合在Windows应用程序中集中默认样式。 - User3810621

9

WindowStartupLocation 是一个CLR属性,可以在ILSpy中看到:

[DefaultValue(WindowStartupLocation.Manual)]
public WindowStartupLocation WindowStartupLocation
{
    get
    {
        this.VerifyContextAndObjectState();
        this.VerifyApiSupported();
        return this._windowStartupLocation;
    }

    set
    {
        this.VerifyContextAndObjectState();
        this.VerifyApiSupported();

        if (!Window.IsValidWindowStartupLocation(value))
        {
            throw new InvalidEnumArgumentException("value", (int)value, typeof(WindowStartupLocation));
        }

        this._windowStartupLocation = value;
    }
}

在样式设置器中,只能指定依赖属性。解决此问题的方法有两种:
  • 继承类Window,并使用依赖属性WindowStartupLocation创建您的类

  • 创建一个附加属性类型,取决于WindowStartupLocation,并在PropertyChanged中定义逻辑。

第一种方法很繁琐,因为需要为一个属性重新定义类。第二种方法是首选方法,将会成为附加行为,但我将称之为PropertyExtension
以下是完整代码:
namespace YourProject.PropertiesExtension
{
    public static class WindowExt
    {
        public static readonly DependencyProperty WindowStartupLocationProperty;

        public static void SetWindowStartupLocation(DependencyObject DepObject, WindowStartupLocation value)
        {
            DepObject.SetValue(WindowStartupLocationProperty, value);
        }

        public static WindowStartupLocation GetWindowStartupLocation(DependencyObject DepObject)
        {
            return (WindowStartupLocation)DepObject.GetValue(WindowStartupLocationProperty);
        }

        static WindowExt() 
        {            
            WindowStartupLocationProperty = DependencyProperty.RegisterAttached("WindowStartupLocation",
                                                      typeof(WindowStartupLocation),
                                                      typeof(WindowExt),
                                                      new UIPropertyMetadata(WindowStartupLocation.Manual, OnWindowStartupLocationChanged));
        }

        private static void OnWindowStartupLocationChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            Window window = sender as Window; 

            if (window != null) 
            {
                window.WindowStartupLocation = GetWindowStartupLocation(window);
            }
        }
    }
}

使用示例:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:PropertiesExtension="clr-namespace:YourProject.PropertiesExtension">

    <Style TargetType="{x:Type Window}">            
        <Setter Property="PropertiesExtension:WindowExt.WindowStartupLocation" Value="CenterScreen" />
        <Setter Property="Width" Value="723" />
        <Setter Property="Height" Value="653" />
        <Setter Property="Title" Value="MainWindow title string" />    
    </Style>
</ResourceDictionary>

脱帽致敬!这是一个完美的解决方案 :) - Moumit

0

如果你想将 WindowStartupLocation 设置为固定值,并且正在从 Window 派生自己的类(这是典型的),那么你可以在构造函数中设置它。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接