Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Override resource in XAML

I have following UserControl:

<UserControl x:Class="MyControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">

    <UserControl.Resources>
        <SolidColorBrush x:Key="ColorKey" Color="Orange"/>
    </UserControl.Resources>

    <Grid Background="{StaticResource ColorKey}">

    </Grid>
</UserControl>

and I use it like this:

<Window x:Class="MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:OverrideResource"
    Title="MainWindow" Height="350" Width="525">

    <Window.Resources>
        <SolidColorBrush x:Key="OtherColorKey" Color="Blue"/>
    </Window.Resources>

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

        <local:MyControl Grid.Row="0">
            <local:MyControl.Resources>
                <SolidColorBrush x:Key="ColorKey" Color="Red"/>
            </local:MyControl.Resources>
        </local:MyControl>

        <Grid Grid.Row="1">
            <Grid.Resources>
                <SolidColorBrush x:Key="OtherColorKey" Color="Green"/>
            </Grid.Resources>
            <Grid Background="{StaticResource OtherColorKey}"/>
        </Grid>
    </Grid>
</Window>

Overriding of resource OtherColorKey works like I expect; the grid has green Background. But I would like to override a Resource that is used inside the UserControl (ColorKey in my example). But I'm getting exception:

Item has already been added. Key in dictionary: 'ColorKey' Key being added: 'ColorKey'

This is just a simplified example, in reality I need it for a bit more complex task. I know that for examle DevExpress uses similar mechanism for customizing their controls (however they use not strings as keys, but objects derived from ResourceKey). But I'm unable to find simple working example to implement such thing on my own.

Thanks for your help.

like image 542
Stalker Avatar asked Mar 06 '13 09:03

Stalker


1 Answers

After reading your post, and reply to the first answer it seems you are writing an application that will require you to keep track of several styling elements. The best way to keep them organized and easily maintainable is to use a ResourceDictionary and reference it.

For me, I have a much easier time when I separate mine based on what they are defining, one for the Brushes and Colors and one for the ControlTemplates and Styles (if the project is extremely complicated, then it depends on several other factors - this is just for a relatively straightforward project).

Once these ResourceDictionary files have been defined, you would reference them in the App.xaml file so they can be applied throughout the application without having to always redefine a Brush or Template in the actual XAML of the page.

So, an example of how the App.xaml may look:

<Application
    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"
    mc:Ignorable="d"
    x:Class="MyColorBlock.App"
    StartupUri="MainWindow.xaml">
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="Resources/BrushesAndColors.xaml"/>
                <ResourceDictionary Source="Resources/StylesAndTemplates.xaml"/>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
</Application>

As stated, this is a very simplified example, so the source files are located in the same project the App.xaml is - just in the Resources folder. The BrushesAndColors.xaml is referenced before the StylesAndTemplates.xaml since the styles and templates defined depend on the colors you have defined.

For the ResourceDictionary BrushesAndColors.xaml:

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:OverrideResource">

        <SolidColorBrush x:Key="BlueBrush" Color="Blue"/>
        <SolidColorBrush x:Key="RedBrush" Color="Red"/>
        <SolidColorBrush x:Key="GreenBrush" Color="Green"/>

</ResourceDictionary>

The Brushes are the same as you defined, however they can now just be referenced by their Key throughout your application.

For the StylesAndTemplates.xaml:

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:OverrideResource">

    <!-- Adding the MergedDictionaries in this ResourceDictionary allows the control templates and styles to reference the colors and brushes defined in BrushesAndColors.xaml. Note: It is a relative link, both files in this example in the same folder "Resources"  -->
    <ResourceDictionary.MergedDictionaries>
        <ResourceDictionary Source="BrushesAndColors.xaml"/>
    </ResourceDictionary.MergedDictionaries>

    <Style x:key="MyControlStyle" TargetType="{x:Type local:MyControl}">
        <Setter Property="HorizontalAlignment" Value="Stretch"/>
        <Setter Property="VerticalAlignment" Value="Stretch"/>
        <Setter Property="Background" Value="{StaticResource RedBrush}"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:MyControl}">
                <!-- by giving the border background the value of {TemplateBinding Background} it is now set based on what the style's property has been defined as -->
                    <Border Background="{TemplateBinding Background}">
                        <TextBlock Text="Red Block Text"/>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>


    <!-- since the style is based on a predefined one it inherits that particular style's definition.
        However, the background color of this stylehas been redefined, and will use that background color
        on any control it is applied to instead-->

    <Style x:Key="MyControlStyleTwo" TargetType="{x:Type local:MyControl}" BasedOn="{StaticResource MyControlStyle}">
        <Setter Property="Background" Value="{StaticResource BlueBrush}"/>
    </Style>


</ResourceDictionary>

Out of habit, I still reference the ResourceDictionary that this one needs to make sure that the styles and templates defined can find the resources I reference when creating them.

The second style, I based it off of the first so I don't have to rewrite the entire thing - but I can simply change the color of it's background. So it will look like the first except with a different background color.

For the main window - which will change slightly:

<Window x:Class="MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:OverrideResource"
    Title="MainWindow" Height="350" Width="525">

    <Grid Background="{StaticResource BlueBrush}">
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

      <local:MyControl Style="{StaticResource MyControlStyle}" Grid.Row="0" />
        <local:MyControl Style="{StaticResource MyControlStyleTwo}" Grid.Row="1"/>

    </Grid>
</Window>

You no longer have to redefine any information pertaining to the UI of the application, keeping the XAML cleaner and easier to read.

It may not be the "easy fix" - but it will definitely make things much easier as you continue. It will also help anyone else if more than one person is working on the project. With everything defined in a centralized area, you can keep styles and colors consistent and no one is having to sift through different pages to find what style was used, they just reference the appropriate one.

This also allows for your control to be written only once, but by giving it different styles/templates you are able to change how the information is displayed - helping to keep the data and the display information separated :)

Hope this helps, sorry for the long post, but it was the only way I could think of to explain how I think you could best solve your problem (and keep it solved).

like image 158
trippedOverXaml Avatar answered Nov 04 '22 01:11

trippedOverXaml