在WPF图表工具包中更改图例显示的颜色。

4
我有以下样式,可以移除数据点并为我的线系列图表随机生成颜色。
<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>

转换器的位置:

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();
    }
}

这会以随机颜色生成线条,但图例是不同的颜色;它可能是由库自动生成的,也可能通过样式模板调用我的转换器。如何使图例打印正确的颜色?
2个回答

1
注意:这是回答问题“Killercam”在此处提出的答案。这个问题的答案特别适合他的赏金,所以应他的要求,我在这里发布了它。
在这个答案中,按钮(Button)控件被用来演示如何使用模板。 第一部分. 控件模板中的绑定 如果你想在控件模板(ControlTemplate)中使用数据绑定(Binding),你应该使用以下结构:
<ControlTemplate TargetType="{x:Type SomeControl}">
    <Rectangle Fill="{TemplateBinding Background}" />

引用自MSDN:

TemplateBinding是模板场景下的一种优化形式的绑定,类似于使用{Binding RelativeSource={RelativeSource TemplatedParent}}构建的绑定。

使用TemplateBinding的注意事项

TemplateBinding不能在模板之外或其VisualTree属性之外使用,因此甚至不能在模板的触发器中使用TemplateBinding。此外,对于Freezable(主要是人为原因),例如-VisualBrush,TemplateBinding不起作用。在这种情况下,可以像这样使用绑定:

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

此外,您始终可以使用替代方案来替代TemplateBinding:
<Rectangle Fill="{Binding RelativeSource={RelativeSource TemplatedParent},
                          Path=Background}" />

作为另一种可能性,您也可以尝试以下方法:
<Rectangle Fill="{Binding Background, 
                          RelativeSource={RelativeSource AncestorType={x:Type SomeControl}}, 
                          Path=Background}" />

第二部分. 关于您的版本的说明

在您的情况下,这可能会导致ControlTemplate中名称的冲突,因为您已经在使用Binding背景作为Border。因此,请删除此Binding以使用Border,或者使用其他属性,例如Tagattached依赖属性来绑定背景颜色。

使用示例

不使用ChartingToolkit控件,而是以Button控件为基础,因为这更容易演示这些样式的想法。

解决方案1: 使用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>

输出

enter image description here

Rectangle中,设置两种颜色:默认为Rectangle,标签为Border。我认为这不是一个好的解决方案,原因如下:

  • 如果需要为边框和矩形设置不同的值,例如:背景、边框厚度、边框笔刷等,则一个Tag是不够的。

  • 必须清楚地说明一个名称属性的用途,而一个名称"Tag"并没有什么意义。

由此可以得出这些缺点,我们应该找到一种替代方案,作为替代方案,我使用了一个带有附加依赖属性的扩展类。

扩展类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>

输出

enter image description here

第三部分. 为依赖属性设置值
当您创建并注册您的附加依赖属性时,您必须声明Set和Get方法来与其配合使用:
public static void SetRectangleBackground(DependencyObject DepObject, Brush value)
{
    DepObject.SetValue(RectangleBackgroundProperty, value);
}

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

那么与它们一起工作的步骤如下:

Set

ButtonExt.SetRectangleBackground(MyButton, Brushes.Red);

Get

Brush MyBrush = ButtonExt.GetRectangleBackground(MyButton);

但在我们的情况下,情况并不简单。当我使用附加的依赖属性时,更新值的问题并不存在。但在我们的情况下,该属性位于模板中,在我的情况下没有更新 Button。我尝试在绑定和属性声明中设置 Mode=TwoWayUpdateSourceTrigger=PropertyChanged,以及 GetBindingExpression().UpdateTarget(),但都无效。
请注意,对于属性设置一个新值,并且来自模板的通知没有表明已经更新了该属性。也许我错了,你会成功,或者它是特意这样做的,例如为了避免内存泄漏。
在任何情况下,最好不要直接更新依赖属性,而是将其绑定到 Model 的属性,并在 ViewModel 中设置值。
示例:
<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}" />

其中RectBackgroundRectBorderBrush实现了INotifyPropertyChanged接口。

作为替代方案,在这种情况下,不要使用依赖属性,而是使用DataTemplate来控制。 DataTemplate非常适合MVVM,非常灵活和动态。

例如,使用DataTemplate工作,您可以查看我的答案:

创建可重用的动态视图

一个ViewModel用于UserControl和Window或单独的ViewModels


1
我会做类似的事情,使用不同的颜色来生成图表,但这些颜色是从一个首选列表中随机选择的(我的背景是黑色的,有些颜色在黑色上面并不很好看)。我从后台代码设置颜色,我不确定这是否是你可以做到的。
在你的情况下,我会尝试这样做:
//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));


资源字典resD等于应用程序加载组件(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()));

希望这个有效。 编辑: 对于图例,我使用这个(我有一个额外的复选框用于显示/隐藏某个图表)。
<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
我尝试了一下,你的解决方案并没有起作用。此外,我想避免在我的代码后台中添加任何代码。我认为这里的解决方案将是将控制图例标记颜色的属性绑定到绘图线的颜色上。问题是我不知道图例控件中哪个属性控制颜色? - MoonKnight
我在我的答案中添加了一个小编辑。它太长了,不能作为评论。 - Florin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接