为什么我可以更改在 Windows.Resources 而不是 Application.Resources 定义的资源属性

Why I can change a resource properties when it defined at Windows.Resources but not from Application.Resources

问题:

我注意到如果我在我的 Windows.Resources 中定义相同的资源,当我以 TwoWay 模式绑定到它们时,我可以对其属性进行一些更改,但如果我在 Application.Resources。我对 Static/Dynamic 资源有所了解,我认为这两个地方都应该是静态的!

问题:

当我们在 Windows.Resources 级别定义资源时,为什么我们可以对资源进行一些更改?在Windows.Resources中定义时是否初始化为DynamicResource?还是有其他原因!?

附带问题:当我们在 Application.Resources 中定义它时,有没有办法让它工作?

要检查的代码

在 Windows 级别定义一个资源并绑定到它并更改它的 属性 没有任何问题:

<Window x:Class="WpfApp1.MainWindow"
        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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp1"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    
    <Window.Resources>
        <SolidColorBrush x:Key="MyBrush" Color="GreenYellow"/>
    </Window.Resources>

    <StackPanel>
        <Button Background="{StaticResource MyBrush}" Content="Button 1" Margin="10"/>
        <Button Background="{StaticResource MyBrush}" Content="Button 2" Margin="10"/>
        <Slider Value="{Binding Source={StaticResource MyBrush}, Path=Opacity, Mode=TwoWay}"
                Maximum="1" TickFrequency="0.01"/>
    </StackPanel>
</Window>

但是如果我像这样将资源定义移动到 Application.Resources 中:

<Application x:Class="WpfApp1.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:WpfApp1"
             StartupUri="MainWindow.xaml">
    <Application.Resources>
        <SolidColorBrush x:Key="MyBrush" Color="GreenYellow"/>
    </Application.Resources>
</Application>

我在输出中得到这个异常 window:

System.Windows.Data Error: 8 : Cannot save value from target back to source. BindingExpression:Path=Opacity; DataItem='SolidColorBrush' (HashCode=37320431); target element is 'Slider' (Name=''); target property is 'Value' (type 'Double') InvalidOperationException:'System.InvalidOperationException: Cannot set a property on object '#FFADFF2F' because it is in a read-only state.

我认为这是正确的回答,因为上面的资源被定义为静态的。我试过这个技巧来解决上面的问题:

<Slider DataContext="{DynamicResource MyBrush}" Value="{Binding Path=Opacity, Mode=TwoWay}"
        Maximum="1" TickFrequency="0.01"/>

运气不好!我的主要问题仍然是为什么当我们在 Windows.Resources!?

使用资源时它的行为不同

搜索到现在才找到答案

我读了这些,但没有我要找的答案:

Changing SolidColorBrush#Color in resource dictionary failed: Property is readonly [duplicate]

一个SolidColorBrush is a type derived from Freezable,看它的继承链。

Object > DispatcherObject > DependencyObject > Freezable > Animatable > Brush > SolidColorBrush

A Freezable 是一种可以在运行时冻结的特殊类型,这使得它不可修改。

Defines an object that has a modifiable state and a read-only (frozen) state. Classes that derive from Freezable provide detailed change notification, can be made immutable, and can clone themselves.

InvalidOperationException 表明 SolidColorBrush 已经被冻结。

To make a Freezable unmodifiable, you call its Freeze method. When you freeze an object that contains freezable objects, those objects are frozen as well. [...] Once you call a freezable's Freeze method, it can no longer be modified. Attempting to modify a frozen object causes an InvalidOperationException to be thrown.

根据这个MSDN forum post,应用程序资源总是自动冻结。

-> Are Resources which are defined in the Application.Resources always frozen.

This is a by design side effect.

事实上,查看 ResourceDictionary 的参考源,您可以看到如果通过 Add 方法或索引器添加资源,则会调用 SealValue method。评论说明了一切。

This method

  1. Sets the InheritanceContext of the value to the dictionary's principal owner

  2. Seals the freezable/style/template that is to be placed in an App/Theme/Style/Template ResourceDictionary

因此,您的 SolidColorBrush 和任何其他可冻结对象在应用程序资源字典中自动冻结。这是设计使然,我想这可能是由于性能原因。

A Freezable provides a Changed event to notify observers of any modifications to the object. Freezing a Freezable can improve its performance, because it no longer needs to spend resources on change notifications. A frozen Freezable can also be shared across threads, while an unfrozen Freezable cannot.

作为对参考源的旁注:所有实现 ISealable 的类型都被密封在那里。在 documentation, there is no notion of Freezable implementing ISealable, but according to its reference source it does and it explicitly implements the Seal 方法中只需调用 Freeze.


关于您对静态和动态资源的理解,资源既不是定义为静态的也不是动态的,它们只是资源StaticResource and DynamicResource 只是定义您如何引用资源的标记扩展,这意味着它的 查找行为 .

有没有办法让你想要的行为与应用程序资源字典一起工作?我不这么认为,因为即使在合并的资源字典中,代码也会显式地冻结资源,所以定义一个单独的资源字典是行不通的,PresentationOptions:Freeze="False" 也行不通。因此,在不同的范围内定义资源似乎是唯一的选择。