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

Moo*_*ght 4 c# wpf charts binding mvvm

我通过命名空间使用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"
Run Code Online (Sandbox Code Playgroud)

我有一个图表控件,在每次Lineseries绘图时我会生成一个随机颜色.我删除数据点标记并使用以下样式着色

<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>
Run Code Online (Sandbox Code Playgroud)

通过LineSeries定义

<ChartingToolkit:LineSeries Title="{Binding DisplayName}" 
                            AnimationSequence="FirstToLast"
                            SnapsToDevicePixels="True" 
                            DataPointStyle="{StaticResource LineDataPointStyle}"
                            ItemsSource="{Binding Data}"
                            IndependentValueBinding="{Binding X}"
                            DependentValueBinding="{Binding Y}"/>
Run Code Online (Sandbox Code Playgroud)

现在,这工作正常,但图例标记显示的颜色与我为其生成的随机颜色不同LineSeries.我希望为它LineSeries和它Lineseries自己的图例项目显示相同的颜色.所以,我将传说项目设置如下

<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>
Run Code Online (Sandbox Code Playgroud)

我的问题是我怎么能"捆绑"的Rectangle.FillTestSuiteLegendItemStyle风格,使其作为相同的颜色LineSeries由设置LineDataPointStyle上面定义的?*

谢谢你的时间.


编辑.我已经尝试设置一个DependencyProperty保存Background我的情节颜色,如下所示

<Style x:Key="LineDataPointStyle" 
        TargetType="ChartingToolkit:LineDataPoint">
    ...
    <Setter Property="Background" 
            Value="{Binding RelativeSource={RelativeSource Self}, 
                            Converter={StaticResource ColorBrushConverter}}"/>
    ...
</Style>
Run Code Online (Sandbox Code Playgroud)

我已经修改了转换器(标记为),以便我可以存储随机生成的背景颜色,然后在我的图例中使用它

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

其中依赖属性(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);
}
Run Code Online (Sandbox Code Playgroud)

然后我会通过此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>
Run Code Online (Sandbox Code Playgroud)

任何有关这方面的帮助将不胜感激......

Ana*_*aev 5

Part I. Binding in ControlTemplate

如果要在a中使用BindingControlTemplate,则应使用以下构造:

<ControlTemplate TargetType="{x:Type SomeControl}">
    <Rectangle Fill="{TemplateBinding Background}" />
Run Code Online (Sandbox Code Playgroud)

引自MSDN:

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

Notes about using TemplateBinding

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

<FreezableControl Property="{Binding RelativeSource={RelativeSource TemplatedParent},
                                     Path=Background}" />
Run Code Online (Sandbox Code Playgroud)

此外,您始终可以使用替代方案TemplateBinding:

<Rectangle Fill="{Binding RelativeSource={RelativeSource TemplatedParent},
                          Path=Background}" />
Run Code Online (Sandbox Code Playgroud)

作为另一种可能性,您还可以尝试以下方法:

<Rectangle Fill="{Binding Background, 
                          RelativeSource={RelativeSource AncestorType={x:Type SomeControl}}, 
                          Path=Background}" />
Run Code Online (Sandbox Code Playgroud)

Part II. Notes about your version

在您的情况下,这可能会导致名称冲突ControlTemplate,因为您已经使用Binding背景是为Border.因此,删除它的这个Binding for a Border,或者使用另一个属性,例如Tag附加依赖属性来绑定Background颜色.

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>
Run Code Online (Sandbox Code Playgroud)

Output

在此输入图像描述

在这里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
}
Run Code Online (Sandbox Code Playgroud)

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>
Run Code Online (Sandbox Code Playgroud)

Output

在此输入图像描述

Part III. Setting values for dependency properties

创建并注册附加的依赖项属性时,必须声明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);
}
Run Code Online (Sandbox Code Playgroud)

然后与他们合作将如下:

Set

ButtonExt.SetRectangleBackground(MyButton, Brushes.Red);
Run Code Online (Sandbox Code Playgroud)

Get

Brush MyBrush = ButtonExt.GetRectangleBackground(MyButton);
Run Code Online (Sandbox Code Playgroud)

但就我们而言,并非如此简单.当我使用附加的依赖属性问题时,更新值不是.但在我们的例子中,属性在模板中,在我的情况下没有更新Button.我试图设置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}" />
Run Code Online (Sandbox Code Playgroud)

在哪里RectBackgroundRectBorderBrush实现INotifyPropertyChanged接口.

作为这种情况下的替代方法,请不要使用依赖项属性并将其DataTemplate用于控件.DataTemplate适用于MVVM,非常灵活和动态.

例如,使用DataTemplate,你可以看到我的答案:

创建(创建)可重用的动态视图

一个用于UserControl和Window的ViewModel或单独的ViewModel