【问题标题】:Change the Color Displayed in WPF Charting Toolkit Legend更改 WPF 图表工具包图例中显示的颜色
【发布时间】:2014-01-22 20:32:16
【问题描述】:

我有以下样式,它删除数据点并为我的线系列图随机生成线条颜色

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

这会生成随机颜色的线条,但图例是不同的颜色;要么由库本身自动生成,要么也通过样式模板调用我的转换器。

如何让图例打印正确的颜色?

【问题讨论】:

    标签: c# wpf mvvm charts


    【解决方案1】:

    注意:是Killercam 的问题的答案,已被问到here。这个问题的答案特别适合他的赏金,所以应他的要求,我在这里发布。

    在此答案中,Button 控件用于演示如何使用模板。

    Part I. Binding in ControlTemplate

    如果你想在 ControlTemplate 中使用 Binding,你应该使用以下构造:

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

    引用自MSDN

    TemplateBinding 是针对模板场景的 Binding 优化形式,类似于使用 {Binding RelativeSource={RelativeSource TemplatedParent}}. 构造的 Binding

    Notes about using TemplateBinding

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

    <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}" />
    

    Part II. Notes about your version

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

    Example of using

    而不是ChartingToolkit控件,以Button控件为基础,因为这样更容易展示这种风格的思想。

    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

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

    • 如果一个Border和Rectangle需要设置不同的值,比如:Background、BorderThickness、BorderBrush等,一个Tag是不够的。

    • 一个名字属性必须明确其用途,一个名字“标签”我们无话可说。

    可以得出结论,我们应该找到一个替代方案,作为替代方案,我使用具有 attached 依赖属性的扩展器类。

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

    Part III. Setting values for dependency properties

    当你创建和注册你的 attached 依赖属性时,你必须声明 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。我尝试在Binding和属性声明中设置Mode=TwoWayUpdateSourceTrigger=PropertyChangedGetBindingExpression().UpdateTarget(),但是没有用。

    请注意,对于设置 new 值的属性,并且没有来自模板的通知,表明该属性已更新。也许我错了,你会工作,或者它是专门制作的,例如避免内存泄漏。

    无论如何,最好不要直接更新依赖属性,将ModelViewModel中的属性绑定到它上面来设置值。

    例子:

    <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,你可以看到我的答案:

    Make (create) reusable dynamic Views

    One ViewModel for UserControl and Window or separate ViewModels

    【讨论】:

      【解决方案2】:

      我做了类似的事情,我生成颜色变化的图,但是这些颜色是从首选列表中随机选择的(我有黑色背景,有些颜色在黑色上效果不太好)。我从后面的代码中设置了颜色,我不确定这是否可以做到。

      在你的情况下,我会尝试这样的事情:

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

      希望这行得通。

      编辑:

      对于我使用这个的图例(我有一个额外的复选框来显示/隐藏某个情节)

      <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>
      

      【讨论】:

      • 您好,我看到您编辑了问题...您是否尝试从后面的代码中设置颜色?
      • 我试过了,你的解决方案不起作用。此外,我想避免在我的代码后面的任何代码。我认为这里的解决方案是将控制图例标记颜色的属性绑定到绘图的线条颜色。问题是我不知道 Legend 的什么属性控制颜色?
      • 我对我的答案添加了一个小编辑。评论太长了。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-10-26
      • 1970-01-01
      • 1970-01-01
      • 2019-03-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多