简体   繁体   English

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

[英]How to Bind to Another Control Property in WPF

I am using the WPF Charting ToolKit via the namespaces: 我通过命名空间使用WPF Charting ToolKit:

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. 我有一个图表控件,每次进行Lineseries绘图时我会生成一个随机颜色。 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 LineSeries是通过定义的

<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 . 现在,这样可以正常工作,但图例标记显示的颜色与我为LineSeries生成的随机颜色不同。 I want the same color to be displayed for the legend item for the LineSeries and the Lineseries itself. 我想为LineSeriesLineseries本身的图例项目显示相同的颜色。 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? 我的问题是如何在TestSuiteLegendItemStyle样式中“绑定” Rectangle.Fill ,以便它与LineDataPointStyle定义的LineDataPointStyle设置的LineSeries颜色相同? * *

Thanks for your time. 谢谢你的时间。


Edit. 编辑。 I have tried setting a DependencyProperty that holds the Background color of my plot as suggested below via 我已经尝试设置一个DependencyProperty ,它保存我的绘图的Background颜色,如下所示

<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 其中依赖属性(DP)定义为

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 然后我会通过此DP来设置图例背景

<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使用Binding ,则应使用以下构造:

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

Quoted from MSDN : 引自MSDN

A TemplateBinding is an optimized form of a Binding for template scenarios, analogous to a Binding constructed with {Binding RelativeSource={RelativeSource TemplatedParent}}. TemplateBinding是模板场景的绑定的优化形式,类似于使用{Binding RelativeSource={RelativeSource TemplatedParent}}.构造的{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. TemplateBinding在模板外或VisualTree属性之外不起作用,因此您甚至无法在模板的触发器中使用TemplateBinding。 Furthermore, TemplateBinding doesn't work when applied to a Freezable (for mostly artificial reasons), for example - VisualBrush . 此外,当应用于Freezable时,TemplateBinding不起作用(主要是出于人为原因),例如VisualBrush In such cases it is possible to use Binding like this: 在这种情况下,可以像这样使用Binding:

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

Also, you can always use an alternative for TemplateBinding : 此外,您始终可以使用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. 在您的情况下,这可能会导致ControlTemplate的名称冲突,因为您已经使用Binding背景是为Border。 Therefore, remove it this Binding for a Border , or use another property, such as Tag or attached dependency property for binding Background color. 因此,删除它为Border 绑定 ,或使用其他属性,如Tag附加依赖属性绑定背景颜色。

Example of using

Instead ChartingToolkit controls, took as a basis Button control, because it's easier to demonstrate the idea of ​​this styles. 取而代之的是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

在此输入图像描述

Here for Rectangle , set two colors: default for Rectangle , in Tag for Border . 对于Rectangle ,设置两种颜色: Rectangle默认值,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. 如果Border和Rectangle需要设置不同的值,例如:Background,BorderThickness,BorderBrush等,则一个Tag是不够的。

  • 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 扩展程序类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: 创建并注册附加的依赖项属性时,必须声明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);
}

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 . 但在我们的例子中,属性在模板中,在我的情况下没有更新Button I tried to set Mode=TwoWay , UpdateSourceTrigger=PropertyChanged , in Binding and in the property declaration, GetBindingExpression().UpdateTarget() , but it was useless. 我试图在Binding和属性声明中设置Mode=TwoWayUpdateSourceTrigger=PropertyChangedGetBindingExpression().UpdateTarget() ,但它没用。

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. 在任何情况下,最好不要直接更新依赖属性,并将Model的属性绑定到它,并在ViewModel中设置值。

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. 其中RectBackgroundRectBorderBrush实现INotifyPropertyChanged接口。

As an alternative in this case, do not use dependency properties and use the DataTemplate for the control. 作为这种情况的替代方法,不要使用依赖项属性并使用DataTemplate作为控件。 DataTemplate ideal for MVVM, very flexible and dynamic. DataTemplate是MVVM的理想选择,非常灵活和动态。

For example, work with DataTemplate , you can see my answers: 例如,使用DataTemplate ,您可以看到我的答案:

Make (create) reusable dynamic Views 创建(创建)可重用的动态视图

One ViewModel for UserControl and Window or separate ViewModels 一个用于UserControl和Window的ViewModel或单独的ViewModel

Set x:Name on LineSeries control: 在LineSeries控件上设置x:Name

<ChartingToolkit:LineSeries x:Name="lineSeries"/>

Then you can bind in TestSuiteLegendItemStyle via binding using ElementName : 然后,您可以使用ElementName通过绑定绑定TestSuiteLegendItemStyle

<Rectangle Fill="{Binding Background, ElementName=lineSeries}"/>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM