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

6

我正在使用WPF图表工具包,通过以下命名空间:

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"

我有一个图表控件,在每次进行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>
LineSeries 是通过以下方式定义的:
<ChartingToolkit:LineSeries Title="{Binding DisplayName}" 
                            AnimationSequence="FirstToLast"
                            SnapsToDevicePixels="True" 
                            DataPointStyle="{StaticResource LineDataPointStyle}"
                            ItemsSource="{Binding Data}"
                            IndependentValueBinding="{Binding X}"
                            DependentValueBinding="{Binding Y}"/>

现在,这个功能很好,但图例标记显示的颜色与我为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>

我的问题是,如何将“Rectangle.Fill”在“TestSuiteLegendItemStyle”样式中“绑定”,以使其颜色与上面定义的“LineDataPointStyle”设置的“LineSeries”颜色相同?感谢您的时间。
编辑。如下所示,我尝试了设置一个保存我的图的“Background”颜色的“DependencyProperty”。
<Style x:Key="LineDataPointStyle" 
        TargetType="ChartingToolkit:LineDataPoint">
    ...
    <Setter Property="Background" 
            Value="{Binding RelativeSource={RelativeSource Self}, 
                            Converter={StaticResource ColorBrushConverter}}"/>
    ...
</Style>

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

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();
    }
}

DP(Dependency Property)被定义为

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);
}

我会尝试翻译以下内容:通过这个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>

希望你能提供帮助...

2个回答

6

第一部分. 在ControlTemplate中使用绑定

如果您希望在ControlTemplate中使用绑定,请使用以下结构:

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

MSDN引用:

TemplateBinding是模板场景下优化的Binding形式,类似于使用{Binding RelativeSource={RelativeSource TemplatedParent}}构建的Binding。

关于使用TemplateBinding的注意事项

TemplateBinding不能在模板之外或其VisualTree属性之外工作,因此甚至不能在模板的触发器中使用TemplateBinding。此外,TemplateBinding不能应用于Freezable(主要是人为原因),例如-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将background绑定到Border上。因此,请删除这种Binding并将其替换为Border,或者使用另一个属性(如Tagattached依赖属性)来绑定背景颜色。

使用示例

与其使用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>

输出

enter image description here

在这里,为矩形(Rectangle)设置了两种颜色:默认颜色为矩形(Rectangle)边框(Border)的颜色则在标签中设置。然而,我认为这不是一个好的解决方案,原因如下:

  • 如果需要为边框(Border)矩形(Rectangle)分别设置不同的值,例如背景、边框厚度、边框笔刷等,那么一个标签是不够的。

  • 必须明确属性名称的用途,而一个名为“标签(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>

输出

enter image description here

第三部分.为依赖属性设置值

当您创建并注册您的附加依赖属性时,必须声明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);
}

然后与他们一起工作将按如下方式进行: < p > 然后执行以下操作:< /p > < p > < em > < code >设置

ButtonExt.SetRectangleBackground(MyButton, Brushes.Red);

Get

Brush MyBrush = ButtonExt.GetRectangleBackground(MyButton);

但在我们的情况下,情况并不那么简单。当我使用附加的依赖属性时,更新值的问题并不存在。但在我们的情况下,该属性位于模板中,在我的情况下没有更新Button

我尝试将Mode=TwoWayUpdateSourceTrigger=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}" />

RectBackgroundRectBorderBrush实现了INotifyPropertyChanged接口。

在这种情况下的替代方案是,不使用依赖属性,而是使用DataTemplate来控制。 DataTemplate非常适合MVVM,非常灵活和动态。

例如,使用DataTemplate工作,您可以查看我的答案:

创建可重用的动态视图

用户控件和窗口的一个ViewModel或单独的ViewModels


1
再次感谢您的回复,我从您的答案中学到了很多。我看到您之前提到过使用“Tag”,但在这里,我不清楚您打算如何使用它来整合“Background”。这是一个个人项目,所以我会尝试在今晚下班后实现您的建议... 再次感谢。 - MoonKnight
1
注意,我已经在 https://dev59.com/GHvaa4cB1Zd3GeqPFqBN 上提出了一个更一般性的问题,该问题有一个活跃的赏金。这个问题的答案将是我在上面新问题中所做的和你的回答的结合... 如果您的建议有效,我将很乐意之后为您颁发赏金。 - MoonKnight
1
@Killercam:请查看我的编辑。 我以“Button”控件为基础而非“ChartingToolkit”控件,因为这样更容易演示这些样式的想法,并且我个人没有使用过“ChartingToolkit”。如果您在实现“ChartingToolkit”时遇到问题或有进一步的问题,请提出。 - Anatoliy Nikolaev
@Killercam:我想问一下,你是否帮助了我的示例,或者有任何问题? - Anatoliy Nikolaev
@Killercam:感谢您的反应迅速,并尊重和欣赏他人的工作。如果这确实能帮到您,我将在另一个问题的奖励贴中发布这个答案。 - Anatoliy Nikolaev
显示剩余3条评论

1

在LineSeries控件上设置x:Name

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

然后,您可以通过使用ElementName进行绑定,将TestSuiteLegendItemStyle绑定到其中:
<Rectangle Fill="{Binding Background, ElementName=lineSeries}"/>

谢谢您的回复,我应该知道这个方法!我已经尝试过了,现在它将填充设置为透明。您有任何想法为什么会发生这种情况吗? - MoonKnight
1
确保绑定不会中断。我怀疑两个控件不在同一个可视树中。透明可能是默认颜色。检查输出窗口是否有任何绑定错误。 - Rohit Vats
这个不起作用。由于某种原因,它无法通过“ElementName”绑定到指定的元素... - MoonKnight

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接