如何为 ConverterParameters 启用动态换肤

发布于 2025-01-01 15:00:45 字数 3116 浏览 2 评论 0原文

当某些需要修改外观的项目不支持 DynamicResourceExtention 类型的值时,启用 WPF 应用程序动态外观的最佳方法是什么?特别是,我们的问题是 ConverterParameters 需要 StaticResourceExtentions。

这是使用 Visual Studio 2008 和 WPF 3.5 的 ConverterParameters 的情况。

我们有一个自定义转换器,它接受一个值和一个参数,然后简单地返回它们的乘积。 非常简单,工作正常,我们将它用于各种任务,包括设置一些窗口元素大小。例如,传递值“Source={x:Static SystemParameters.PrimaryScreenHeight}”和参数“0.1”使我们能够将元素的高度设置为屏幕高度的 1/10。

  Height="{Binding Source={x:Static SystemParameters.PrimaryScreenHeight}, 
                   Converter={StaticResource PctConverter}, 
                   ConverterParameter=0.1}"

其中 PctConverter 是我们的自定义转换器的资源引用。那里没问题。

现在我们希望通过提取 ConverterParameter 并将其放入单独的资源中来动态地为应用程序设置外观。例如,在某些外观中,我们可能希望元素高度为屏幕高度的 0.1,而在其他外观中,元素高度为屏幕高度的 0.25。最初,我们认为只需将 ConverterParameter 设置为 DynamicResource,但这不受支持,因此我们必须使用 StaticResourceExtension 来设置它,如下所示:

  Height="{Binding Source={x:Static SystemParameters.PrimaryScreenHeight}, 
           Converter={StaticResource PctConverter}, 
           ConverterParameter={StaticResource OurElementHeightParameter}}"

其中 OurElementHeightParameter 在单独的 ResourceDictionary(称为 MainResource.xaml)中定义,如下所示:

<sys:Double x:Key="OurElementHeightParameter">0.1</sys:Double>

(其中命名空间定义为 xmlns:sys="clr-namespace:System; assembly=mscorlib"。)

就提取 CustomParameter 而言,这工作正常,但它仍然还没有使我们能够通过即时交换皮肤来更改 ConverterParameter。

经过更多研究后,特别是以下文章

如何分配wpf资源转其他资源标签

使用一种颜色作为另一种颜色的静态资源进行换肤

别名资源

我们认为现在需要做的是采取我们的StaticResourceExtention 并使用资源别名在幕后动态设置其值。

为了做到这一点,我们用以下两个

<sys:Double x:Key="SkinnedHeightRatio">0.1</sys:Double>
<StaticResourceExtension x:Key="OurElementHeightParameter" ResourceKey="SkinnedHeightRatio" />

工作正常的资源替换了之前的 OurElementHeightParameter 资源,产生了相同的结果。

当一切正常时,我们认为将 SkinnedHeightRatio 资源放置在单独的 ResourceDictionary(称为 Skin.xaml)中并将其与原始 MainResource.xaml ResourceDictionary 合并,这将是一个简单的问题,我们将获得我们想要的动态换肤。

但是,一旦我们将 0.1 提取到另一个 ResourceDictionary,我们就会遇到如下构建错误:

未知构建错误, '索引超出范围。必须为非负数且小于集合的大小。

更奇怪的是,如果我们将上面的两个资源保留在同一个 ResourceDictionary 中,并通过在它们之间放置另一个随机资源来分隔它们,例如

<sys:Double x:Key="SkinnedHeightRatio">0.1</sys:Double>
<Thickness x:Key="SomeRandomResource" >5</Thickness>
<StaticResourceExtension x:Key="OurElementHeightParameter" ResourceKey="SkinnedHeightRatio" />

,OurElementHeightParameter 指向它正上方的 SomeRandomResource,而不是 在其 ResourceKey 属性 (SkinnedHeightRatio) 中指定的资源,仅在其上方 2 行... 在本例中,传递给转换器的参数是 Thickness SomeRandomResource。

一切都非常令人困惑,让我们觉得我们完全找错了方向。那么我们错在哪里呢?

如果有人需要重现问题的应用程序的完整代码,我可以将其发布。

任何指示都非常感激。

What's the best way to enable dynamic skinning of a WPF Application when some items requiring skin modification do not support values of type DynamicResourceExtention? In particular, our problem is that ConverterParameters require StaticResourceExtentions.

Here's our situation with ConverterParameters Using Visual Studio 2008 and WPF 3.5.

We have a custom converter which takes a value and a parameter and simply returns their product.
Very simple, works fine, and we use it for various tasks, including setting some window element sizes. For example, passing a value of "Source={x:Static SystemParameters.PrimaryScreenHeight}" and a parameter of "0.1" enables us to set an element's height to exactly 1/10 of the screen height.

  Height="{Binding Source={x:Static SystemParameters.PrimaryScreenHeight}, 
                   Converter={StaticResource PctConverter}, 
                   ConverterParameter=0.1}"

where PctConverter is a resource reference to our custom converter. No problem there.

Now we want to skin the application dynamically, by extracting the ConverterParameter and putting it in a seperate resource. For example, we might want the element height to be 0.1 of the screen height in some skins, and say 0.25 of the screen height in others. Initially we thought we'd simply set the ConverterParameter to a DynamicResource, but this is not supported, so we have to set it using a StaticResourceExtension like this:

  Height="{Binding Source={x:Static SystemParameters.PrimaryScreenHeight}, 
           Converter={StaticResource PctConverter}, 
           ConverterParameter={StaticResource OurElementHeightParameter}}"

where OurElementHeightParameter is defined in a seperate ResourceDictionary (call it MainResource.xaml) as follows:

<sys:Double x:Key="OurElementHeightParameter">0.1</sys:Double>

(where namespace is defined as xmlns:sys="clr-namespace:System;assembly=mscorlib".)

This works fine, as far as extracting the CustomParameter is concerned, but it still hasn't enabled us to change our ConverterParameter by swapping skins on the fly.

After researching this some more, in particular the following articles

How to assign wpf resources to other resource tags

Skinning using a color as staticresource for another color

Aliasing resources

what we think we need to do now is take our StaticResourceExtention and set its value dynamically behind the scenes using resource aliases.

Trying to do this, we replaced the previous OurElementHeightParameter resource with the following two resources

<sys:Double x:Key="SkinnedHeightRatio">0.1</sys:Double>
<StaticResourceExtension x:Key="OurElementHeightParameter" ResourceKey="SkinnedHeightRatio" />

which works fine, producing an identical result.

When that worked okay, we thought it would be a simple matter of placing the SkinnedHeightRatio resource in a seperate ResourceDictionary (call it Skin.xaml) and merging that with the original MainResource.xaml ResourceDictionary and we would have the dynamic skinning we are after.

But, as soon as we extract <sys:Single x:Key="SkinnedHeightRatio">0.1</sys:Single> to another ResourceDictionary we encounter build error as follows:

Unknown build error, 'Index was out of range. Must be non-negative and less than the size of the collection.'

Even more strange is that if we keep the two resources above in the same ResourceDictionary and just seperate them by putting another random resource between them, for example

<sys:Double x:Key="SkinnedHeightRatio">0.1</sys:Double>
<Thickness x:Key="SomeRandomResource" >5</Thickness>
<StaticResourceExtension x:Key="OurElementHeightParameter" ResourceKey="SkinnedHeightRatio" />

then the OurElementHeightParameter points to the SomeRandomResource directly above it and not the
resource specified in its ResourceKey property (SkinnedHeightRatio) which is only 2 lines above it...
In this case, the parameter passed to the converter is the Thickness SomeRandomResource.

All very confusing, and makes us think we are barking up the wrong tree completely. So where are we going wrong?

If anyone needs full code for an application reproducing the problem, I can post it up.

Any pointers greatly appreciated.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

浅笑轻吟梦一曲 2025-01-08 15:00:45

创建多值转换器并为其绑定到两个值可能会更简单。

It might be simpler to create a multi-value converter and bind to two values for it.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文