4

I have the following style which removes data point and randomly generates a line color for my line series plots

<Style x:Key="LineDataPointStyle" 
        TargetType="ChartingToolkit:LineDataPoint">
    <Setter Property="Foreground" Value="DarkGreen"/>
    <Setter Property="IsTabStop" Value="False"/>
    <Setter Property="Width" Value="NaN"/>
    <Setter Property="Height" Value="NaN"/>
    <Setter Property="Background" 
            Value="{Binding RelativeSource={RelativeSource Self}, 
                            Converter={StaticResource ColorBrushConverter}}"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="ChartingToolkit:LineDataPoint">
                <Grid x:Name="Root" Opacity="0"/>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

where the converter is:

public class ColorToBrushConverter : IValueConverter
{
    public object Convert(object value, Type targetType, 
        object parameter, System.Globalization.CultureInfo culture)
    {
        return new SolidColorBrush(Utils.GenerateRandomColor());
    }

    public object ConvertBack(object value, Type targetType, 
        object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

This generate the line in a random color, but the legend is a different color; either being auto-generated by the library itself or it is also calling my converter through the style template.

Shown

How can I get the legend to print the correct color?

MoonKnight
  • 23,214
  • 40
  • 145
  • 277

2 Answers2

1

I do something similar, where I generate plots with changing colors, but these colors are randomly selected from a preferred list (I have a black background and some colors just don't work very well on top of black) . I set the color from code behind and I am not sure this is something you can do.

In your case I would try something like this:

//If you declare your style in a resource dictionary, get that resource first

ResourceDictionary resD = (ResourceDictionary)Application.LoadComponent(new Uri("ResourcesPlot\\ResourceDictionaryPlot.xaml", UriKind.Relative));

//The actual style


Style lineDataPointStyle= (Style)resD["LineDataPointStyle"];

//Set the color
lineDataPointStyle.Setters.Add(new Setter(BackgroundProperty, Utils.GenerateRandomColor()));

Hope this works.

Edit:

For the legend I use this (I have an extra checkbox for showing/hiding a certain plot)

<Style x:Key="CustomLegendItemStyle" TargetType="{x:Type chartingToolkit:LegendItem}">
        <Setter Property="IsTabStop" Value="False" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="chartingToolkit:LegendItem">
                    <StackPanel Orientation="Horizontal">
                        <CheckBox VerticalAlignment="Center" Margin="3" IsChecked="true" Checked="DisplaySeries_Checked" Unchecked="DisplaySeries_Unchecked"/>
                        <!--<Rectangle VerticalAlignment="Center" Width="8" Height="8" Fill="{DynamicResource MyBackgroundDiode1}" Stroke="{Binding BorderBrush}" StrokeThickness="1" Margin="5,5,5,5" />-->
                        <chartingToolkit:LegendItem VerticalAlignment="Center" Content="{TemplateBinding Content}" />

                    </StackPanel>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
Florin
  • 11
  • 2
  • Hi, I saw you edited the question... Did you try setting the color from code behind? – Florin Jan 25 '14 at 18:01
  • I tried it and your solution did not work. Moreover, I would like to avoid any code in my code behind. I think the solution here will be to bind the property that governs the Legends marker color to the line color of the plot. The problem is i do not know what propert of the Legend governs the color? – MoonKnight Jan 25 '14 at 18:56
  • I added a small edit to my answer. It was too long for a comment. – Florin Jan 27 '14 at 12:17
1

Note: is the answer to the question of Killercam, that has been asked here. Answer to this question is particularly suitable for his bounty, so at his request I publish it here.

In this answer, the Button control is used to demonstrate working with templates.

Part I. Binding in ControlTemplate

If you want to use Binding in a ControlTemplate, you should use following construction:

<ControlTemplate TargetType="{x:Type SomeControl}">
    <Rectangle Fill="{TemplateBinding Background}" />

Quoted from MSDN:

A TemplateBinding is an optimized form of a Binding for template scenarios, analogous to a Binding constructed with {Binding RelativeSource={RelativeSource TemplatedParent}}.

Notes about using TemplateBinding

TemplateBinding doesn’t work outside a template or outside its VisualTree property, so you can’t even use TemplateBinding inside a template’s trigger. Furthermore, TemplateBinding doesn’t work when applied to a Freezable (for mostly artificial reasons), for example - VisualBrush. In such cases it is possible to use Binding like this:

<FreezableControl Property="{Binding RelativeSource={RelativeSource TemplatedParent},
                                     Path=Background}" />

Also, you can always use an alternative for TemplateBinding:

<Rectangle Fill="{Binding RelativeSource={RelativeSource TemplatedParent},
                          Path=Background}" />

As another possibility, you can also try the following:

<Rectangle Fill="{Binding Background, 
                          RelativeSource={RelativeSource AncestorType={x:Type SomeControl}}, 
                          Path=Background}" />

Part II. Notes about your version

In your case, this may cause a conflict of names in the ControlTemplate, because you already are using Binding background is for Border. Therefore, remove it this Binding for a Border, or use another property, such as Tag or attached dependency property for binding Background color.

Example of using

Instead ChartingToolkit controls, took as a basis Button control, because it's easier to demonstrate the idea of ​​this styles.

Solution 1: using Tag

<Window.Resources>
    <Style x:Key="TestButtonStyle" TargetType="{x:Type Button}">
        <Setter Property="BorderThickness" Value="0" />
        <Setter Property="IsTabStop" Value="False" />

        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type Button}">
                    <!-- Here we are set Tag for Border Background -->
                    <Border Background="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Tag}" 
                            BorderThickness="{TemplateBinding BorderThickness}">

                        <Grid>
                            <Rectangle Width="24" 
                                       Height="24" 
                                       Fill="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Background}" 
                                       Stroke="{TemplateBinding BorderBrush}" />

                            <ContentPresenter Content="{TemplateBinding Content}" 
                                              HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                                              VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
                        </Grid>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</Window.Resources>

<Grid>
    <Button Name="TestButton"
            Style="{StaticResource TestButtonStyle}"
            Content="Test"
            HorizontalContentAlignment="Center"
            VerticalContentAlignment="Center"
            Tag="Green"
            Background="Aquamarine"
            Width="100"
            Height="100" />
</Grid>

Output

enter image description here

Here for Rectangle, set two colors: default for Rectangle, in Tag for Border. I do not find this a good solution, and here's why:

  • If a Border and Rectangle need to set different values, such as: Background, BorderThickness, BorderBrush, etc. one Tag is not enough.

  • With one name property must be clearly its purpose, one name "Tag" us to nothing says.

Of these disadvantages can be concluded that we should find an alternative, as an alternative I use a extender-class with the attached dependency properties.

Extender class ButtonExt.cs

public static class ButtonExt
{
    #region RectangleBackground Property

    public static readonly DependencyProperty RectangleBackgroundProperty;

    public static void SetRectangleBackground(DependencyObject DepObject, Brush value)
    {
        DepObject.SetValue(RectangleBackgroundProperty, value);
    }

    public static Brush GetRectangleBackground(DependencyObject DepObject)
    {
        return (Brush)DepObject.GetValue(RectangleBackgroundProperty);
    }

    #endregion

    #region RectangleBorderBrush Property

    public static readonly DependencyProperty RectangleBorderBrushProperty;

    public static void SetRectangleBorderBrush(DependencyObject DepObject, Brush value)
    {
        DepObject.SetValue(RectangleBorderBrushProperty, value);
    }

    public static Brush GetRectangleBorderBrush(DependencyObject DepObject)
    {
        return (Brush)DepObject.GetValue(RectangleBorderBrushProperty);
    }

    #endregion       

    #region Button Constructor

    static ButtonExt()
    {
        #region RectangleBackground

        PropertyMetadata BrushPropertyMetadata = new PropertyMetadata(Brushes.Transparent);

        RectangleBackgroundProperty = DependencyProperty.RegisterAttached("RectangleBackground",
                                                            typeof(Brush),
                                                            typeof(ButtonExt),
                                                            BrushPropertyMetadata);

        #endregion

        #region RectangleBorderBrush

        RectangleBorderBrushProperty = DependencyProperty.RegisterAttached("RectangleBorderBrush",
                                                            typeof(Brush),
                                                            typeof(ButtonExt),
                                                            BrushPropertyMetadata);

        #endregion
    }

    #endregion
}

MainWindow.xaml

<Window.Resources>
    <Style x:Key="TestButtonExtensionStyle" TargetType="{x:Type Button}">
        <Setter Property="Width" Value="80" />
        <Setter Property="Height" Value="80" />
        <Setter Property="Background" Value="Green" />
        <Setter Property="BorderBrush" Value="Pink" />
        <Setter Property="BorderThickness" Value="4" />

        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type Button}">
                    <Border Background="{TemplateBinding Background}" 
                            BorderBrush="{TemplateBinding BorderBrush}"
                            BorderThickness="{TemplateBinding BorderThickness}">

                        <Grid>
                            <Rectangle Fill="{TemplateBinding PropertiesExtension:ButtonExt.RectangleBackground}" 
                                       Stroke="{TemplateBinding PropertiesExtension:ButtonExt.RectangleBorderBrush}"
                                       Width="30" 
                                       Height="30" />

                            <ContentPresenter Content="{TemplateBinding Content}" 
                                              HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                                              VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
                        </Grid>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</Window.Resources>

<Grid>
    <Button Style="{StaticResource TestButtonExtensionStyle}"
            PropertiesExtension:ButtonExt.RectangleBackground="Aquamarine"
            PropertiesExtension:ButtonExt.RectangleBorderBrush="Black"
            Content="Test" />
</Grid>

Output

enter image description here

Part III. Setting values for dependency properties

When you create and register your attached dependency property, you must declare the Set and Get methods to work with him:

public static void SetRectangleBackground(DependencyObject DepObject, Brush value)
{
    DepObject.SetValue(RectangleBackgroundProperty, value);
}

public static Brush GetRectangleBackground(DependencyObject DepObject)
{
    return (Brush)DepObject.GetValue(RectangleBackgroundProperty);
}

Then work with them will be as follows:

Set

ButtonExt.SetRectangleBackground(MyButton, Brushes.Red);

Get

Brush MyBrush = ButtonExt.GetRectangleBackground(MyButton);

But in our case, it's not so simple. When I used the attached dependency property problems in updating values ​​were not. But in our case, the property is in the template, and in my case there was no update Button. I tried to set Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, in Binding and in the property declaration, GetBindingExpression().UpdateTarget(), but it was useless.

Note that for the property setting a new value, and notification from the template is not, that the property has been updated. Maybe I'm wrong, and you have will work, or maybe it's made specifically, for example to avoid memory leaks.

In any case, it is better not to update the dependency property directly, and bind to it the property of the Model and in the ViewModel to set the value.

Example:

<Button Style="{StaticResource TestButtonExtensionStyle}"
        adp:ButtonExt.RectangleBackground="{Binding Path=Model.RectBackground,
                                                    Mode=TwoWay, 
                                                    UpdateSourceTrigger=PropertyChanged}"
        adp:ButtonExt.RectangleBorderBrush="{Binding Path=Model.RectBorderBrush,
                                                     Mode=TwoWay, 
                                                     UpdateSourceTrigger=PropertyChanged}" />

where RectBackground and RectBorderBrush implement the INotifyPropertyChanged interface.

As an alternative in this case, do not use dependency properties and use the DataTemplate for the control. DataTemplate ideal for MVVM, very flexible and dynamic.

For example, work with DataTemplate, you can see my answers:

Make (create) reusable dynamic Views

One ViewModel for UserControl and Window or separate ViewModels

Community
  • 1
  • 1
Anatoliy Nikolaev
  • 22,370
  • 15
  • 69
  • 68