WPF:在运行时从App.xaml更改资源(颜色)

14

我正在尝试通过允许用户从“颜色选择器对话框”中选择颜色,并使用 DynamicResource 实时更改应用程序的样式来使我的应用程序更具可定制性。

如何更改位于 app.xaml 中的特定资源?


我尝试过像这样的代码,但没有成功(只是测试):

var colorDialog = new CustomControls.ColorPickerDialog();
var dResult = colorDialog.ShowDialog();
var x = Application.Current.Resources.Values.OfType<LinearGradientBrush>().First();
x = new LinearGradientBrush();
x.GradientStops.Add(new GradientStop(colorDialog.SelectedColor,1));

以下是app.xaml文件的摘录:

<Application.Resources>
    <LinearGradientBrush x:Key="HeaderBackground" StartPoint="0.5,0" EndPoint="0.5,1">
        <GradientStop Color="#82cb02" Offset="1"/>
        <GradientStop Color="#82cb01" Offset="0.2"/>
        <GradientStop Color="#629a01" Offset="0.5"/>
    </LinearGradientBrush>
</Application.Resources>

如何才能让应用程序具备这种形式的可定制性(基本上只是改变一些颜色)?


[更新]

我刚刚在之前的一个问题中找到了这个答案,并尝试了它,但我得到了与Petoj评论所提到的答案相同的InvalidOperationException异常。以下是答案中的示例代码:

Xaml:

<LinearGradientBrush x:Key="MainBrush" StartPoint="0,0.5" EndPoint="1,0.5" >
    <GradientBrush.GradientStops>
        <GradientStop Color="Blue" Offset="0" />
        <GradientStop Color="Black" Offset="1" />
    </GradientBrush.GradientStops>
</LinearGradientBrush>

C#:

LinearGradientBrush myBrush = FindResource("MainBrush") as LinearGradientBrush;
myBrush.GradientStops[0].Color = Colors.Red;
4个回答

17

看起来你想要做一些皮肤相关的操作?

我建议将资源定义在一个单独文件中的资源字典中。然后在代码中(在App.cs中加载默认值,然后在其他地方更改)可以这样加载资源:

//using System.Windows
ResourceDictionary dict = new ResourceDictionary();
dict.Source = new Uri("MyResourceDictionary.xaml", UriKind.Relative);

Application.Current.Resources.MergedDictionaries.Add(dict);

你也可以在App.xaml中定义默认资源字典并在代码中卸载它。

使用MergedDictionaries对象在运行时更改正在使用的字典。这对于快速更改整个界面非常有效。


2
请注意,MergedDictionaries 是一个容器。最近添加的 ResourceDictionary 中的资源会覆盖之前已有的。如果经常在不同字典间切换,从列表中移除之前的字典可能是有益的。(回答提到了“在代码中卸载”,但没有展示如何识别和移除它,这点需要注意。) - fadden

13

在运行时更改应用程序范围内的资源就像:

Application.Current.Resources("MainBackgroundBrush") = Brsh

关于 InvalidOperationException,我认为 WallStreet Programmer 是正确的。也许你不应该尝试修改现有的刷子,而是在代码中创建一个包含所有所需渐变停止的新刷子,然后将此新刷子分配给应用程序资源。

另一种改变某些 GradientStops 颜色的方法是将这些颜色定义为对应用程序范围内的 SolidColorBrush 的 DynamicResource 引用, 如下:

<LinearGradientBrush x:Key="MainBrush" StartPoint="0, 0.5" EndPoint="1, 0.5" >
<GradientBrush.GradientStops>
    <GradientStop Color="{DynamicResource FirstColor}" Offset="0" />
    <GradientStop Color="{DynamicResource SecondColor}" Offset="1" />
</GradientBrush.GradientStops>

然后使用

Application.Current.Resources["FirstColor"] = NewFirstColorBrsh
Application.Current.Resources["SecondColor"] = NewSecondColorBrsh

祝好运


这正是我想要的。 - Fᴀʀʜᴀɴ Aɴᴀᴍ
请注意,此代码在Silverlight中无法运行。DynamicResource不受支持。 - mbomb007

3
使用Clone()方法来制作刷子(或任何其他可冻结对象,如Storyboard)的深层副本,然后使用它:
LinearGradientBrush myBrush = FindResource("MainBrush") as LinearGradientBrush;
myBrush = myBrush.Clone();
myBrush.GradientStops[0].Color = Colors.Red;

@WallstreetProgrammer是正确的 - 所有应用程序级别的资源默认都是被冻结的。

这就是为什么你需要先克隆对象。


2

你遇到异常是因为你在试图修改一个被冻结的对象。所有应用级别的资源都会自动冻结,如果它们是可冻结的,而LinearGradientBrush就是可冻结的。如果你将它添加到更低的级别,比如窗口级别,它就能正常工作。


但是我不能将它添加到一个窗口中,因为这个样式适用于我项目中的每个窗口。 - Andreas Grech
这帮助我解决了问题。我将具有<GradientStop Color="{DynamicResource WindowBorderColor}" Offset="0.5"/>的LinearGradientBrush从App.xaml移动到了我的Window.Resources中,DynamicResource开始按照预期应用。谢谢。 - Alexandru Dicu

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