更新时间:2022-10-21 11:29:12
第一部分在ControlTemplate中绑定
如果你想使用的绑定的在控件模板
,你应该使用下面的结构:
<的ControlTemplate的TargetType ={X:类型SomeControl}>
<矩形填充={TemplateBinding背景}/>
MSDN报价
:
A
TemplateBinding
是有约束力的模板方案的优化形式,类似于绑定与构建{绑定的RelativeSource = {的RelativeSource TemplatedParent}}
块引用>的
有关使用Notes TemplateBinding
的
TemplateBinding
不模板之外或其财产的VisualTree外出打工,所以你甚至不能使用TemplateBinding模板的触发器内。此外,当应用到可冻结(大多为人工原因)TemplateBinding不起作用,例如 -的VisualBrush
。在这种情况下,有可能使用绑定是这样的:< FreezableControl属性={绑定的RelativeSource = {的RelativeSource TemplatedParent},
路径=背景}/>此外,您还可以随时使用一种替代
TemplateBinding
:<矩形填充={绑定的RelativeSource = {的RelativeSource TemplatedParent},
路径=背景}/>作为另一种可能性,也可以尝试以下方法:
<矩形填充={结合的背景,
的RelativeSource = {的RelativeSource AncestorType = {X:类型SomeControl}},
路径=背景}/>
第二部分。关于你的版本注释
在你的情况,这可能会导致在
控件模板
,因为你已经在使用绑定的背景是边境名称的冲突。因此,将其删除这个的绑定的一个边框
,或使用其他属性,如标签
或附的依赖项属性绑定背景颜色。的
使用示例
的而不是
ChartingToolkit
控制,采取了以此为基础按钮
控件,因为它更容易证明的想法这种风格。
解决方法1:使用标签
< Window.Resources>
<风格X:键=TestButtonStyle的TargetType ={X:类型按钮}>
< setter属性=了borderThicknessVALUE =0/>
< setter属性=IsTabStopVALUE =FALSE/> < setter属性=模板>
< Setter.Value>
<的ControlTemplate的TargetType ={X:类型按钮}>
<! - 为边框背景这里我们设置标签 - >
<边框背景={绑定的RelativeSource = {的RelativeSource TemplatedParent},路径=标签}
了borderThickness ={TemplateBinding了borderThickness}> <网格和GT;
<矩形宽度=24
身高=24
填写={绑定的RelativeSource = {的RelativeSource TemplatedParent},路径=背景}
行程={TemplateBinding BorderBrush}/> <内容presenter CONTENT ={TemplateBinding内容}
的HorizontalAlignment ={TemplateBinding HorizontalContentAlignment}
VerticalAlignment ={TemplateBinding VerticalContentAlignment}/>
< /网格和GT;
< /边框>
< /控件模板>
< /Setter.Value>
< /二传手>
< /样式和GT;
< /Window.Resources><网格和GT;
<按钮名称=TestButton
风格={StaticResource的TestButtonStyle}
CONTENT =测试
HorizontalContentAlignment =中心
VerticalContentAlignment =中心
标签=绿色
背景=海蓝宝石
WIDTH =100
HEIGHT =100/>
< /网格和GT;
输出
下面的
长方形
,设置两个颜色:默认为长方形
,在标签为边境
。我不觉得这是一个很好的解决方案,这里的原因:
如果边框和矩形需要设置不同的值,如:背景,了borderThickness,BorderBrush等有一个
标签
是远远不够的。使用一个名称属性必须明确其目的,一个名字变量我们什么都不说。
这些缺点可以得出结论,我们应该找到一个替代,作为一种替代我使用了一个扩展级用的连接的依赖属性。
Extender类
ButtonExt.cs
公共静态类ButtonExt
{
#区域RectangleBackground物业 公共静态只读的DependencyProperty RectangleBackgroundProperty; 公共静态无效SetRectangleBackground(DependencyObject的DepObject,刷值)
{
DepObject.SetValue(RectangleBackgroundProperty,值);
} 公共静电毛刷GetRectangleBackground(DependencyObject的DepObject)
{
返回(刷)DepObject.GetValue(RectangleBackgroundProperty);
} #endregion #区域RectangleBorderBrush物业 公共静态只读的DependencyProperty RectangleBorderBrushProperty; 公共静态无效SetRectangleBorderBrush(DependencyObject的DepObject,刷值)
{
DepObject.SetValue(RectangleBorderBrushProperty,值);
} 公共静电毛刷GetRectangleBorderBrush(DependencyObject的DepObject)
{
返回(刷)DepObject.GetValue(RectangleBorderBrushProperty);
} #endregion #区域构造按钮 静态ButtonExt()
{
#区域RectangleBackground PropertyMetadata BrushPropertyMetadata =新PropertyMetadata(Brushes.Transparent); RectangleBackgroundProperty = DependencyProperty.RegisterAttached(RectangleBackground
typeof运算(刷)
typeof运算(ButtonExt)
BrushPropertyMetadata); #endregion #区域RectangleBorderBrush RectangleBorderBrushProperty = DependencyProperty.RegisterAttached(RectangleBorderBrush
typeof运算(刷)
typeof运算(ButtonExt)
BrushPropertyMetadata); #endregion
} #endregion
}
MainWindow.xaml
< Window.Resources>
<风格X:键=TestButtonExtensionStyle的TargetType ={X:类型按钮}>
< setter属性=宽度值=80/>
< setter属性=高度值=80/>
< setter属性=背景值=绿色/>
< setter属性=BorderBrushVALUE =粉红/>
< setter属性=了borderThicknessVALUE =4/> < setter属性=模板>
< Setter.Value>
<的ControlTemplate的TargetType ={X:类型按钮}>
<边框背景={TemplateBinding背景}
BorderBrush ={TemplateBinding BorderBrush}
了borderThickness ={TemplateBinding了borderThickness}> <网格和GT;
<矩形填充={TemplateBinding PropertiesExtension:ButtonExt.RectangleBackground}
行程={TemplateBinding PropertiesExtension:ButtonExt.RectangleBorderBrush}
宽度=30
高度=30/> <内容presenter CONTENT ={TemplateBinding内容}
的HorizontalAlignment ={TemplateBinding HorizontalContentAlignment}
VerticalAlignment ={TemplateBinding VerticalContentAlignment}/>
< /网格和GT;
< /边框>
< /控件模板>
< /Setter.Value>
< /二传手>
< /样式和GT;
< /Window.Resources><网格和GT;
<按钮样式={StaticResource的TestButtonExtensionStyle}
PropertiesExtension:ButtonExt.RectangleBackground =海蓝宝石
PropertiesExtension:ButtonExt.RectangleBorderBrush =黑
CONTENT =测试/>
< /网格和GT;
输出
第三部分。对于依赖属性设定值
当您创建和注册您的连接的依赖项属性,必须声明的set和get方法与他合作:
公共静态无效SetRectangleBackground(DependencyObject的DepObject,刷值)
{
DepObject.SetValue(RectangleBackgroundProperty,值);
}公共静电毛刷GetRectangleBackground(DependencyObject的DepObject)
{
返回(刷)DepObject.GetValue(RectangleBackgroundProperty);
}然后与他们合作将是如下:
的
设置
的ButtonExt.SetRectangleBackground(用作MyButton,Brushes.Red);
的
获取
的刷MyBrush = ButtonExt.GetRectangleBackground(用作MyButton);
但在我们的例子中,它不是那么简单。当我用在更新附加依赖属性值的问题没有。但是,在我们的例子中,属性模板,并在我的情况有没有更新
按钮
。我试图设置模式=双向
,UpdateSourceTrigger = PropertyChanged的
,在结合和财产申报,GetBindingEx pression()。UpdateTarget()
,但它是无用的。请注意,对于财产设置的新的的值,并从模板通知不是,该财产已更新。也许我错了,你必须将工作,或者也许它专门制作,例如以避免内存泄漏。
在任何情况下,***不要直接更新依赖属性,而
模型的属性绑定到它
,并在视图模型
来设置的值。例如:
<按钮样式={StaticResource的TestButtonExtensionStyle}
ADP:ButtonExt.RectangleBackground ={绑定路径= Model.RectBackground,
模式=双向,
UpdateSourceTrigger =的PropertyChanged}
ADP:ButtonExt.RectangleBorderBrush ={绑定路径= Model.RectBorderBrush,
模式=双向,
UpdateSourceTrigger =的PropertyChanged}/>其中,
RectBackground
和RectBorderBrush
实施INotifyPropertyChanged的
接口对于这种情况下的替代,不使用依赖属性和使用
的DataTemplate
的控制。的DataTemplate
理想的MVVM,非常灵活和动态的。例如,用
的DataTemplate
的工作,你可以看到我的回答:One视图模型的用户控件和窗口或单独的ViewModels
I am using the WPF Charting ToolKit via the namespaces:
xmlns:ChartingToolkit="clr-namespace:System.Windows.Controls.DataVisualization.Charting;assembly=System.Windows.Controls.DataVisualization.Toolkit" xmlns:VisualizationToolkit="clr-namespace:System.Windows.Controls.DataVisualization;assembly=System.Windows.Controls.DataVisualization.Toolkit"
I have a chart control in which I generate a random color for each time a
Lineseries
plot is undertaken. I remove the data point markers and colorize using the following style<Style x:Key="LineDataPointStyle" TargetType="ChartingToolkit:LineDataPoint"> <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>
The
LineSeries
is defined via<ChartingToolkit:LineSeries Title="{Binding DisplayName}" AnimationSequence="FirstToLast" SnapsToDevicePixels="True" DataPointStyle="{StaticResource LineDataPointStyle}" ItemsSource="{Binding Data}" IndependentValueBinding="{Binding X}" DependentValueBinding="{Binding Y}"/>
Now, this works fine but the legend markers display a different color to the random one I generate for the
LineSeries
. I want the same color to be displayed for the legend item for theLineSeries
and theLineseries
itself. So, I have styled the legend item as below<Style x:Key="TestSuiteLegendItemStyle" TargetType="{x:Type ChartingToolkit:LegendItem}"> <Setter Property="IsTabStop" Value="False"/> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type ChartingToolkit:LegendItem}"> <Border Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}"> <StackPanel Orientation="Horizontal"> <Rectangle Width="8" Height="8" Fill="{Binding Background}" Stroke="{Binding BorderBrush}" StrokeThickness="1" Margin="0,0,3,0" /> <VisualizationToolkit:Title Content="{TemplateBinding Content}" /> </StackPanel> </Border> </ControlTemplate> </Setter.Value> </Setter> </Style>
My question is how can I 'bind' the
Rectangle.Fill
in theTestSuiteLegendItemStyle
style so that it is the same color as theLineSeries
as set by theLineDataPointStyle
defined above?*Thanks for your time.
Edit. I have tried setting a
DependencyProperty
that holds theBackground
color of my plot as suggested below via<Style x:Key="LineDataPointStyle" TargetType="ChartingToolkit:LineDataPoint"> ... <Setter Property="Background" Value="{Binding RelativeSource={RelativeSource Self}, Converter={StaticResource ColorBrushConverter}}"/> ... </Style>
Where the I have ammended the converter (as marked) so that I can store the randomly generated background color and then use this in my legend
public class ColorToBrushConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { Brush b = new SolidColorBrush(Utils.GenerateRandomColor()); MultiChartExtensions.BackgroundBrushProperty = b; <= how to set the dependency property? return b; } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { throw new NotImplementedException(); } }
where the Dependency Property (DP) is defined as
public static readonly DependencyProperty BackgroundBrushProperty; public static void SetBackgroundBrush(DependencyObject DepObject, Brush value) { DepObject.SetValue(BackgroundBrushProperty, value); } public static Brush GetBackgroundBrush(DependencyObject DepObject) { return (Brush)DepObject.GetValue(BackgroundBrushProperty); }
I would then look to set the legend background via this DP via
<Style x:Key="TestSuiteLegendItemStyle" TargetType="{x:Type ChartingToolkit:LegendItem}"> <Setter Property="IsTabStop" Value="False"/> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type ChartingToolkit:LegendItem}"> <Border Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}"> <StackPanel Orientation="Horizontal"> <Rectangle Width="8" Height="8" Fill="{Binding MultiChart:MultiChartExtensions.BackgroundBrushProperty}" Stroke="{Binding BorderBrush}" StrokeThickness="1" Margin="0,0,3,0" /> <VisualizationToolkit:Title Content="{TemplateBinding Content}" /> </StackPanel> </Border> </ControlTemplate> </Setter.Value> </Setter> </Style>
Any help with this would be appreciated...
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 aBorder
, or use another property, such asTag
or attached dependency property for binding Background color.
Example of using
Instead
ChartingToolkit
controls, took as a basisButton
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
Here for
Rectangle
, set two colors: default forRectangle
, in Tag forBorder
. 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
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 setMode=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 theViewModel
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
andRectBorderBrush
implement theINotifyPropertyChanged
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