且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

如何绑定到WPF中的另一个控件属性

更新时间: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内容}
                                              的Horizo​​ntalAlignment ={TemplateBinding Horizo​​ntalContentAlignment}
                                              VerticalAlignment ={TemplateBinding VerticalContentAlignment}/>
                        < /网格和GT;
                    < /边框>
                < /控件模板>
            < /Setter.Value>
        < /二传手>
    < /样式和GT;
< /Window.Resources><网格和GT;
    <按钮名称=TestButton
            风格={StaticResource的TestButtonStyle}
            CONTENT =测试
            Horizo​​ntalContentAlignment =中心
            VerticalContentAlignment =中心
            标签=绿色
            背景=海蓝宝石
            WIDTH =100
            HEIGHT =100/>
< /网格和GT;

输出

如何绑定到WPF中的另一个控件属性

下面的长方形,设置两个颜色:默认为长方形,在标签为边境。我不觉得这是一个很好的解决方案,这里的原因:


  • 如果边框和矩形需要设置不同的值,如:背景,了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内容}
                                              的Horizo​​ntalAlignment ={TemplateBinding Horizo​​ntalContentAlignment}
                                              VerticalAlignment ={TemplateBinding VerticalContentAlignment}/>
                        < /网格和GT;
                    < /边框>
                < /控件模板>
            < /Setter.Value>
        < /二传手>
    < /样式和GT;
< /Window.Resources><网格和GT;
    <按钮样式={StaticResource的TestButtonExtensionStyle}
            PropertiesExtension:ButtonExt.RectangleBackground =海蓝宝石
            PropertiesExtension:ButtonExt.RectangleBorderBrush =黑
            CONTENT =测试/>
< /网格和GT;

输出

如何绑定到WPF中的另一个控件属性

第三部分。对于依赖属性设定值

当您创建和注册您的连接的依赖项属性,必须声明的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 the LineSeries and the Lineseries 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 the TestSuiteLegendItemStyle style so that it is the same color as the LineSeries as set by the LineDataPointStyle defined above?*

Thanks for your time.


Edit. I have tried setting a DependencyProperty that holds the Background 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 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

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

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