我有以下样式删除数据点并随机生成我的线系列图的线颜色
<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();
}
}
这会以随机颜色生成线条,但图例是不同的颜色;既可以由库本身自动生成,也可以通过样式模板调用我的转换器.
如何让图例打印出正确的颜色?
最佳答案 注意:是Killercam问题的答案,已被问到
here.这个问题的答案特别适合他的赏金,所以应他的要求我在这里发表.
在这个答案中,Button控件用于演示如何使用模板.
第一部分.在ControlTemplate中绑定
如果要在ControlTemplate中使用Binding,则应使用以下构造:
<ControlTemplate TargetType="{x:Type SomeControl}">
<Rectangle Fill="{TemplateBinding Background}" />
引自MSDN
:
A
TemplateBinding
is an optimized form of a Binding for template scenarios, analogous to a Binding constructed with{Binding RelativeSource={RelativeSource TemplatedParent}}.
关于使用TemplateBinding的注意事项
TemplateBinding在模板外或VisualTree属性之外不起作用,因此您甚至无法在模板的触发器中使用TemplateBinding.此外,当应用于Freezable时,TemplateBinding不起作用(主要是出于人为原因),例如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}" />
第二部分.有关您的版本的说明
在您的情况下,这可能会导致ControlTemplate中的名称冲突,因为您已经使用Binding背景是为Border.因此,删除它为边框绑定,或使用其他属性,如标记或附加依赖属性绑定背景颜色.
使用示例
取而代之的是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>
产量
对于Rectangle,设置两种颜色:Rectangle的默认值,Tag for Border.我觉得这不是一个好的解决方案,原因如下:
>如果Border和Rectangle需要设置不同的值,例如:Background,BorderThickness,BorderBrush等,则一个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>
产量
第三部分.设置依赖项属性的值
创建并注册附加的依赖项属性时,必须声明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);
}
然后与他们合作将如下:
组
ButtonExt.SetRectangleBackground(MyButton, Brushes.Red);
得到
Brush MyBrush = ButtonExt.GetRectangleBackground(MyButton);
但就我们而言,并非如此简单.当我使用附加的依赖属性问题时,更新值不是.但在我们的例子中,属性在模板中,在我的情况下没有更新Button.我试图在Binding和属性声明中设置Mode = TwoWay,UpdateSourceTrigger = 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}" />
其中RectBackground和RectBorderBrush实现INotifyPropertyChanged接口.
作为这种情况的替代方法,不要使用依赖项属性并使用DataTemplate作为控件. DataTemplate是MVVM的理想选择,非常灵活和动态.
例如,使用DataTemplate,您可以看到我的答案:
Make (create) reusable dynamic Views
One ViewModel for UserControl and Window or separate ViewModels