22

Is there a way to redefine/alias an existing SolidColorBrush (or any other resource, actually)?

Case in point: I have a brush in an external ResourceDictionary that I want to reference by my own key instead of the original key. I don't want to be dependent on the external reference, since the actual brush is prone to change in the future.

Inferis
  • 4,582
  • 5
  • 37
  • 47

3 Answers3

15
<Window x:Class="WpfApplication1.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="300" Width="300">
    <Window.Resources>
        <SolidColorBrush x:Key="SomeExternalResource">Red</SolidColorBrush>
    </Window.Resources>
    <Grid>
        <Grid.Resources>
            <StaticResourceExtension ResourceKey="SomeExternalResource" x:Key="SomeAliasedResource"/>
        </Grid.Resources>

        <Border Background="{StaticResource SomeAliasedResource}"/>
    </Grid>
</Window>

I don't want to be dependent on the external reference, since the actual brush is prone to change in the future.

You'll still be dependent on the external resource, just not in as many places.

Kent Boogaart
  • 175,602
  • 35
  • 392
  • 393
  • the only limitation to this is that you can't reference both the original and the alias if used within the same resource dictionary, as they will override each other. i am assuming this applies to multiple dictionaries you merge at application scope. – Paul Stovell Mar 11 '09 at 12:54
  • Judging by the way the question is worded, he doesn't want to reference the original - only his alias. Referencing the original would defeat the purpose of creating the alias. – Kent Boogaart Mar 11 '09 at 13:07
  • 5
    This is NOT working for me. I get <>. – Alan Baljeu Feb 02 '14 at 17:49
6

I have an update to Ruedi's solution. This works for resources both within the same resource dictionary and anywhere within the application.

public class Alias : MarkupExtension
{
    public string ResourceKey { get; set; }

    public Alias()
    {

    }
    public Alias(string resourceKey)
    {
        ResourceKey = resourceKey;
    }

    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        return _ProvideLocalValue(serviceProvider) ?? _ProvideApplicationValue();
    }

    private object _ProvideLocalValue(IServiceProvider serviceProvider)
    {
        var rootObjectProvider = (IRootObjectProvider)
            serviceProvider.GetService(typeof(IRootObjectProvider));
        if (rootObjectProvider == null) return null;
        var dictionary = rootObjectProvider.RootObject as IDictionary;
        if (dictionary == null) return null;
        return dictionary.Contains(ResourceKey) ? dictionary[ResourceKey] : null;
    }

    private object _ProvideApplicationValue()
    {
        var value = Application.Current.TryFindResource(ResourceKey);
        return value;
    }
}

The usage is similar to above, but the key is to use Alias as the markup extension where used, not StaticResource. Somewhere in the application resource stack, define the resources...

<Application x:Class="WPF.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:system="clr-namespace:System;assembly=mscorlib"
             xmlns:wpf="clr-namespace:WPF"
             StartupUri="MainWindow.xaml">
    <Application.Resources>
        <system:String x:Key="Text">Display some text.</system:String>
        <system:String x:Key="Other">4</system:String>
        <wpf:Alias x:Key="Alias" ResourceKey="Text"/>
        <wpf:Alias x:Key="Second" ResourceKey="Other"/>
    </Application.Resources>
</Application>

And wherever you're referencing the aliases...

<Window x:Class="WPF.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:wpf="clr-namespace:WPF"
        Title="MainWindow" Height="150" Width="300">
    <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
        <TextBlock Text="{wpf:Alias Alias}"/>
        <TextBlock Text="{wpf:Alias Second}"/>
    </StackPanel>
</Window>

My solution required referencing strings, but it works for any object you want to alias.

Community
  • 1
  • 1
gregsdennis
  • 7,218
  • 3
  • 38
  • 71
4

You can try to tuse the StaticResourceExtension, but in global resource dictionaries this does not work (strange compiler and runtime errors):

<ResourceDictionary 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" >

    <SolidColorBrush x:Key="StatusColor_OK" Color="#ff32a248" />
    <StaticResourceExtension
        x:Key="AliasKey"
        ResourceKey="StatusColor_Error" />
</ResourceDictionary>

To overcome this problem, I created the following class:

/// <summary>
/// Defines an Alias for an existing resource. Very similar to 
/// <see cref="StaticResourceExtension"/>, but it works in
///  ResourceDictionaries
/// </summary>
public class Alias: System.Windows.Markup.MarkupExtension
{
    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        IRootObjectProvider rootObjectProvider = (IRootObjectProvider)
            serviceProvider.GetService(typeof (IRootObjectProvider));
        if (rootObjectProvider == null) return null;
        IDictionary dictionary =  rootObjectProvider.RootObject as IDictionary;
        if (dictionary == null) return null;
        return dictionary[ResourceKey];
    }


    public object ResourceKey { get; set; }
}

Usage:

<ResourceDictionary 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" >

    <SolidColorBrush x:Key="StatusColor_OK" Color="#ff32a248" />
    <Alias
        x:Key="AliasKey"
        ResourceKey="StatusColor_Error" />
</ResourceDictionary>
  • 1
    If I try to use the alias somewhere...e.g. where MyOtherColor is an alias for a Color with key MyColor, then it complains and says "Invalid resource type: expected type is 'Color', actual type is 'Alias'". – Stephen Drew Sep 11 '12 at 18:21
  • To clarify, it does work if the alias is in a resource dictionary, and the alias is used outside that dictionary. It cannot be used within the same dictionary however. – Stephen Drew Sep 11 '12 at 18:25
  • And spurious errors are raised and disappear as per my original comment. If I run the app successfully, then edit the XAML by adding whitespace, a compiler error appears. I build the project, it is still there. I build again, it disappears. It's very frustrating because your idea looked so promising! – Stephen Drew Sep 11 '12 at 18:26
  • I have a resource dictionary for colors and another for individual design element resources that alias the color resources. All is well until I try to use a design element resource in a form. Then I get compiler errors. Using this Alias class is a pain but it works. – blearyeye Jan 02 '17 at 22:42