views:

35

answers:

2

This is quite obscure, I may just be missing something extremely simple.

Scenario 1

Lets say I create a gradient brush, like this in my <Window.Resources> section:

<LinearGradientBrush x:Key="GridRowSelectedBackBrushGradient" StartPoint="0,0" EndPoint="0,1">
    <GradientStop Color="#404040" Offset="0.0" />
    <GradientStop Color="#404040" Offset="0.5" />
    <GradientStop Color="#000000" Offset="0.6" />
    <GradientStop Color="#000000" Offset="1.0" />
</LinearGradientBrush>

Then much later on, I want to override the HighlightBrushKey for a DataGrid. I have basically done it like this (horrible);

<LinearGradientBrush x:Key="{x:Static SystemColors.HighlightBrushKey}"
                     GradientStops="{Binding Source={StaticResource GridRowSelectedBackBrushGradient}, Path=GradientStops}"
                     StartPoint="{Binding Source={StaticResource GridRowSelectedBackBrushGradient}, Path=StartPoint}"
                     EndPoint="{Binding Source={StaticResource GridRowSelectedBackBrushGradient}, Path=EndPoint}" />

This is obviously not the most slick way of referencing a resource. I also came up with the following problem, which is almost identical.

Scenario 2

Say I created two colors in my <Window.Resources> markup, like so:

<SolidColorBrush x:Key="DataGridRowBackgroundBrush" Color="#EAF2FB" />
<SolidColorBrush x:Key="DataGridRowBackgroundAltBrush" Color="#FFFFFF" />

Then later on, I want to supply them in an Array, which feeds the ConverterParameter on a Binding so I can supply the custom Converter with my static resource instances:

<Setter Property="Background">
    <Setter.Value>
        <Binding RelativeSource="{RelativeSource Mode=Self}" 
                 Converter="{StaticResource BackgroundBrushConverter}">
            <Binding.ConverterParameter>
                <x:Array Type="{x:Type Brush}">
                    <SolidColorBrush Color="{Binding Source={StaticResource DataGridRowBackgroundBrush}, Path=Color}" />
                    <SolidColorBrush Color="{Binding Source={StaticResource DataGridRowBackgroundAltBrush}, Path=Color}" />
                </x:Array>
            </Binding.ConverterParameter>
        </Binding>
    </Setter.Value>
</Setter>

What I've done is attempt to rereference an existing resource, but in my efforts I've actually recreated the resource, and bound the properties so they match. Again, this is not ideal.

Because I've now hit this problem at least twice, is there a better way?

Thanks, Tom

A: 

The markup you're working with doesn't go up far enough. You don't create a LinearGradientBrush, your first example: you just reference the resource. For example:

<DataGrid HighlightBrushKey="{StaticResource GridRowSelectedBackBrushGradient}"  ....

In your second example, I'd say that you want to declare the array as a resource:

<x:Array Type="{x:Type Brush}" x:Key="MyArray">
  <SolidColorBrush Color="#EAF2FB" />
  <SolidColorBrush Color="#FFFFFF" />
</x:Array>

And then you can use

<Binding RelativeSource="{RelativeSource Mode=Self}" 
         Converter="{StaticResource BackgroundBrushConverter}"
         ConverterParameter="{Staticresource MyArray}" />
Dan Puzey
Thanks Dan! However, there is no property on the DataGrid called HighlightBrushKey, it must be allocated in the DataGrid.Resources section overriding the x:Static system brush.I considered defining the array as a resource itself, but that requires defining the same SolidColorBrush resources twice, rather than referencing my existing SolidColorBrushes I created earlier on.What I'm looking for probably doesn't exist, it'd almost be a <InsertResource Source="{StaticResource myBrush}" /> type structure.I might just have to get over this and move on!
Tom
+1  A: 

I was wondering when someone was going to ask about this.

What you want to do in Scenario 1 is to effectively give a single resource an "alias." This is easily done by markup that seems obvious only after you see it. Suppose we have this in our App.xaml or somewhere:

<ResourceDictionary>
  <LinearGradientBrush x:Key="GridRowSelectedBackBrushGradient" ... />
</ResourceDictionary>

To include an alias in another ResourceDictionary, just:

<ResourceDictionary>
  <StaticResourceExtension x:Key="{x:Static SystemColors.HighlightBrushKey}"
                           ResourceKey="GridRowSelectedBackBrushGradient" />
</ResourceDictionary>

This looks up the brush object in the first ResourceDictionary and adds the same object to the second ResourceDictionary under a new key. This also works within a single ResourceDictionary.

For your Scenario 2 the solution is just as simple:

<Binding.ConverterParameter>
  <x:Array Type="{x:Type Brush}">
    <StaticResourceExtension ResourceKey="DataGridRowBackgroundBrush" />
    <StaticResourceExtension ResourceKey="DataGridRowBackgroundAltBrush" />
  </x:Array>
</Binding.ConverterParameter>

Again, the actual Brush objects found via the ResourceKey are added directly to the Brush[] array. No new Brush is created.

I think we're all so used to using StaticResourceExtension with markup extension syntax (eg {StaticResource Xyz}) that it's easy to forget that it can also be used with regular element syntax as well.

Ray Burns
Yes, Ray that's totally it! Let me faff about with my xaml for a bit and I'll let you know how I get on. As far as I'm concerned, this is the answer! Thanks!
Tom
Ok, as Window.Resources is itself a ResourceDictionary, I've simply replaced my botched resource duplicates with StaticResourceExtension and it just works. This is magic, thank you very much.
Tom