简体   繁体   English

应用 Styles 与绑定

[英]Applying Styles With Binding

I am trying to create a custom control called PieceImage that contains one of several predefined Canvas elements.我正在尝试创建一个名为PieceImage的自定义控件,其中包含几个预定义的Canvas元素之一。 I have defined these Canvases in a ResourceDictionary called PieceImageDictionary.xaml.我在名为 PieceImageDictionary.xaml 的ResourceDictionary中定义了这些画布。 I have set up some dependency properties in PieceImage , Color1 and Color2 , that I'd like to bind to the Fill and Stroke of the paths in the Canvases.我在PieceImageColor1Color2中设置了一些依赖属性,我想将它们绑定到画布中路径的FillStroke I have a pretty good understanding of custom controls and simple databinding but styles are a little confusing to me.我对自定义控件和简单的数据绑定有很好的理解,但是 styles 让我有点困惑。

So basically I have a ResourceDictionary with Canvases that I am treating as images and I want be able to have multiple PieceImage Control instances that can each choose one of these images independently (not a global style) and I want to be able to set the colors using a DependencyProperty on the PieceImage Controls.所以基本上我有一个带有 Canvases 的ResourceDictionary ,我将其视为图像,我希望能够拥有多个PieceImage Control实例,每个实例可以独立选择其中一个图像(不是全局样式),我希望能够设置 colors在PieceImage控件上使用DependencyProperty

I have tried wrapping my Canvases in ControlTemplates but for whatever reason " Canvas " is not a valid TargetType .我尝试将 Canvases 包装在ControlTemplates中,但无论出于何种原因,“ Canvas ”都不是有效的TargetType

I got it to apply this template if I set the TargetType to " Control " however when I tried adding TemplateBinding to the Paths , the Property I wanted to set (Color1) couldn't be found.如果我将TargetType设置为“ Control ”,我可以应用此模板,但是当我尝试将TemplateBinding添加到Paths时,找不到我想要设置的属性(Color1) I kind of understand this because the Color1 property is in my PieceImage control, so TemplateBinding to a Control or even a Canvas won't work.我有点理解这一点,因为Color1属性在我的PieceImage控件中,因此TemplateBindingControl甚至Canvas将不起作用。

I then tried RelativeBinding with an AncestorType of " PieceImage " but this doesn't work either.然后我尝试了使用AncestorType为“ PieceImage ”的RelativeBinding ,但这也不起作用。 No error, just a blank Canvas .没有错误,只是一个空白Canvas I've tried calling UpdateLayout() and InvalidateVisual() on the Canvas just in case but no change.我试过在Canvas上调用UpdateLayout()InvalidateVisual()以防万一,但没有改变。

I tried using a XamlReader and Writer to create these canvases instead of using templates, which initially worked when I statically defined the colors but when I tried to add the RelativeBinding I got a Xaml Parse Error saying it couldnt create the type from the string " local:PieceImage "我尝试使用XamlReader和 Writer 来创建这些画布,而不是使用模板,这最初在我静态定义 colors 时起作用,但是当我尝试添加RelativeBinding时,我得到了 Xaml 解析错误,说它无法从字符串“ local:PieceImage ”创建类型local:PieceImage "

I tried adding a binding in the code but also got a blank Canvas .我尝试在代码中添加绑定,但也得到了一个空白Canvas I will admit I don't know much about coding bindings, this was my implementation:我承认我对编码绑定不太了解,这是我的实现:

var binding = new Binding(Color1Property.Name);
binding.RelativeSource = new RelativeSource(RelativeSourceMode.TemplatedParent);
BindingOperations.SetBinding(path, Path.FillProperty, binding);

Here is my base code, I've cleaned out all the failed attempts for clarity because it would otherwise be a mess.这是我的基本代码,为了清楚起见,我已经清除了所有失败的尝试,否则会一团糟。

PieceImage.cs PieceImage.cs

public class PieceImage : Control
{
    private Canvas _canvas;
    static PieceImage()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(PieceImage), new FrameworkPropertyMetadata(typeof(PieceImage)));
    }
    public static readonly DependencyProperty Color1Property = DependencyProperty.Register(nameof(Color1), typeof(Brush), typeof(PieceImage), new PropertyMetadata(Brushes.BlanchedAlmond));
    public static readonly DependencyProperty Color2Property = DependencyProperty.Register(nameof(Color2), typeof(Brush), typeof(PieceImage), new PropertyMetadata(Brushes.DarkGray));
    public static readonly DependencyProperty PieceTypeProperty = DependencyProperty.Register(nameof(PieceType), typeof(Enums.PieceType), typeof(PieceImage), new PropertyMetadata(Enums.PieceType.Pawn));
    public static readonly DependencyProperty SwapColorsProperty = DependencyProperty.Register(nameof(SwapColors), typeof(bool), typeof(PieceImage), new PropertyMetadata(false));

    public Brush Color1
    {
        get { return (Brush)GetValue(Color1Property); }
        set { SetValue(Color1Property, value); }
    }
    public Brush Color2
    {
        get { return (Brush)GetValue(Color2Property); }
        set { SetValue(Color2Property, value); }
    }
    public Enums.PieceType PieceType
    {
        get { return (Enums.PieceType)GetValue(PieceTypeProperty); }
        set { SetValue(PieceTypeProperty, value); }
    }
    public bool SwapColors
    {
        get { return (bool)GetValue(SwapColorsProperty); }
        set { SetValue(SwapColorsProperty, value); }
    }
    public override void OnApplyTemplate()
    {
        _canvas = Template.FindName("PART_Canvas", this) as Canvas;
        //Here is where most of my logic would go when I was loading xaml or coding bindings
        base.OnApplyTemplate();
    }
}

Generic.xaml (partial) Generic.xaml(部分)

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:Wagner.Chess.UI.Controls">
    <ResourceDictionary.MergedDictionaries>
        <ResourceDictionary Source="\pieceimagedictionary.xaml" />
    </ResourceDictionary.MergedDictionaries>
    <Style TargetType="{x:Type local:PieceImage}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="local:PieceImage">
                    <Canvas x:Name="PART_Canvas"/>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

PieceImageDictionary.xaml PieceImageDictionary.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:local="clr-namespace:Wagner.Chess.UI.Controls">
    <Canvas Height="64" Width="64"  x:Key="BishopImage">
        <Path Fill="{Binding RelativeSource={RelativeSource AncestorType=local:PieceImage,AncestorLevel=2,Mode=FindAncestor},Path=Color1}"
              Stroke="{Binding RelativeSource={RelativeSource AncestorType=local:PieceImage,AncestorLevel=2,Mode=FindAncestor},Path=Color2}"
              StrokeThickness="1.5" StrokeMiterLimit="1" StrokeLineJoin="Round">
                <Path.Data>
                    <GeometryGroup>
                        <PathGeometry Figures="M 9,36 C 12.39,35.03 19.11,36.43 22.5,34 C 25.89,36.43 32.61,35.03 36,36 C 36,36 37.65,36.54 39,38
                                      C 38.32,38.97 37.35,38.99 36,38.5 C 32.61,37.53 25.89,38.96 22.5,37.5 C 19.11,38.96 12.39,37.53 9,38.5 C 7.65,38.99
                                      6.68,38.97 6,38 C 7.35,36.54 9,36 9,36 z"/>
                        <PathGeometry Figures="M 15,32 C 17.5,34.5 27.5,34.5 30,32 C 30.5,30.5 30,30 30,30 C 30,27.5 27.5,26 27.5,26 C 33,24.5 33.5,14.5
                                      22.5,10.5 C 11.5,14.5 12,24.5 17.5,26 C 17.5,26 15,27.5 15,30 C 15,30 14.5,30.5 15,32 z"/>
                        <PathGeometry Figures="M 25 8 A 2.5 2.5 0 1 1  20,8 A 2.5 2.5 0 1 1  25 8 z"/>
                    </GeometryGroup>
                </Path.Data>
            </Path>
        <Path Stroke="{Binding RelativeSource={RelativeSource AncestorType=local:PieceImage,AncestorLevel=2,Mode=FindAncestor},Path=Color2}"
               StrokeThickness="1.5" StrokeMiterLimit="1" StrokeEndLineCap="Round" StrokeStartLineCap="Round" StrokeLineJoin="Miter">
                <Path.Data>
                    <GeometryGroup>
                        <PathGeometry Figures="M 17.5,26 L 27.5,26 M 15,30 L 30,30 M 22.5,15.5 L 22.5,20.5 M 20,18 L 25,18" />
                    </GeometryGroup>
                </Path.Data>
            </Path>
            <Canvas.RenderTransform>
                <ScaleTransform ScaleX="1.42222222222" ScaleY="1.42222222222"/>
            </Canvas.RenderTransform>
        </Canvas>
    <Canvas Height="64" Width="64" x:Key="KnightImage">
        <Path Fill="{Binding RelativeSource={RelativeSource AncestorType=local:PieceImage,AncestorLevel=2,Mode=FindAncestor},Path=Color1}"
              Stroke="{Binding RelativeSource={RelativeSource AncestorType=local:PieceImage,AncestorLevel=2,Mode=FindAncestor},Path=Color2}"
              StrokeThickness="1.5" StrokeMiterLimit="1"  StrokeLineJoin="Round" StrokeStartLineCap="Round" StrokeEndLineCap="Round">
            <Path.Data>
                <GeometryGroup>
                    <PathGeometry Figures="M 22,10 C 32.5,11 38.5,18 38,39 L 15,39 C 15,30 25,32.5 23,18"/>
                    <PathGeometry Figures="M 24,18 C 24.38,20.91 18.45,25.37 16,27 C 13,29 13.18,31.34 11,31 C 9.958,30.06 12.41,27.96 11,28 C 10,28 11.19,29.23 10,
                                  30 C 9,30 5.997,31 6,26 C 6,24 12,14 12,14 C 12,14 13.89,12.1 14,10.5 C 13.27,9.506 13.5,8.5 13.5,7.5 C 14.5,6.5 16.5,10 16.5,10 L 
                                  18.5,10 C 18.5,10 19.28,8.008 21,7 C 22,7 22,10 22,10"/>
                </GeometryGroup>
            </Path.Data>
        </Path>
        <Path Fill="{Binding RelativeSource={RelativeSource AncestorType=local:PieceImage,AncestorLevel=2,Mode=FindAncestor},Path=Color2}"
              Stroke="{Binding RelativeSource={RelativeSource AncestorType=local:PieceImage,AncestorLevel=2,Mode=FindAncestor},Path=Color2}"
              StrokeThickness="0.5" StrokeMiterLimit="1" StrokeLineJoin="Round" StrokeStartLineCap="Round" StrokeEndLineCap="Round">
            <Path.Data>
                <GeometryGroup>
                    <PathGeometry Figures="M 9.5 25.5 A 0.5 0.5 0 1 1 8.5,25.5 A 0.5 0.5 0 1 1 9.5 25.5 z"/>
                    <PathGeometry Figures="M 15 15.5 A 0.5 1.5 0 1 1  14,15.5 A 0.5 1.5 0 1 1  15 15.5 z">
                        <PathGeometry.Transform>
                            <MatrixTransform Matrix="0.866,0.5,-0.5,0.866,9.693,-5.173"/>
                        </PathGeometry.Transform>
                    </PathGeometry>
                </GeometryGroup>
            </Path.Data>
        </Path>
        <Canvas.RenderTransform>
            <ScaleTransform ScaleX="1.42222222222" ScaleY="1.42222222222"/>
        </Canvas.RenderTransform>
    </Canvas>
</ResourceDictionary>

I was hoping someone could provide some tips or point me in the right direction because MSDocs have guides for StyleSelectors and DataTemplateSelectors both of which are more for data item styling rather than setting another variable control using a style.我希望有人可以提供一些提示或指出正确的方向,因为 MSDocs 有StyleSelectorsDataTemplateSelectors的指南,这两者都更多地用于数据项样式,而不是使用样式设置另一个变量控件。 Thanks,谢谢,

If I understand your issue correctly, you want to show a piece Canvas based on the PieceType in a your PieceImage control.如果我正确理解您的问题,您想根据PieceType控件中的PieceImage显示一块Canvas Then, you do not have to nest a Canvas inside another Canvas .然后,您不必将Canvas嵌套在另一个Canvas中。 You can replace the Canvas in your PieceImage template with a ContentPresenter ( PART_ContentPresenter ).您可以将Canvas模板中的PieceImage替换为ContentPresenter ( PART_ContentPresenter )。

<Style TargetType="{x:Type local:PieceImage}">
   <Setter Property="Template">
      <Setter.Value>
         <ControlTemplate TargetType="local:PieceImage">
            <ContentPresenter x:Name="PART_ContentPresenter"/>
         </ControlTemplate>
      </Setter.Value>
   </Setter>
</Style>

In your PieceImage control, you can then search for the corresponding PieceType resource ( Canvas ) using the FindResource method and assign it to the ContentPresenter .在您的PieceImage控件中,您可以使用FindResource方法搜索相应的PieceType资源 ( Canvas ) 并将其分配给ContentPresenter Here I assume that we can map the PieceType using the pattern <Enum Constant Name>Image .这里我假设我们可以使用<Enum Constant Name>Image模式 map PieceType If it is more complex, you could use a dictionary or a custom converter.如果它更复杂,您可以使用字典或自定义转换器。

public class PieceImage : Control
{
   private ContentPresenter _contentPresenter;

   // ...your constructor, dependency property definitions, etc..

   public override void OnApplyTemplate()
   {
      base.OnApplyTemplate();

      _contentPresenter = Template.FindName("PART_ContentPresenter", this) as ContentPresenter;

      if (_contentPresenter != null)
         _contentPresenter.Content = FindPieceImage();
   }

   private Canvas FindPieceImage()
   {
      var pieceTypeName = PieceType.ToString();
      var pieceTypeCanvasKey = $"{pieceTypeName}Image";
      return FindResource(pieceTypeCanvasKey) as Canvas;
   }
}

Since the original Canvas in the control template is gone, remove all the AncestorLevel=2 attributes in your piece images, otherwise the binding source will not be found.由于控制模板中原来的Canvas没有了,把你的作品图片中的AncestorLevel=2属性全部去掉,否则找不到绑定源。 Furthermore, add x:Shared="False" to your Canvas es.此外,将x:Shared="False"添加到您的Canvas es。 This is important, because your piece images are now potentially used multiple times (I guess your are building a chess board), but without setting x:Shared to false the same instances will be reused.这很重要,因为您的棋子图像现在可能会被多次使用(我猜您正在构建棋盘),但是如果不将x:Shared设置为false相同的实例将被重用。

When set to false , modifies WPF resource-retrieval behavior so that requests for the attributed resource create a new instance for each request instead of sharing the same instance for all requests.当设置为false时,修改 WPF 资源检索行为,以便对属性资源的请求为每个请求创建一个新实例,而不是为所有请求共享同一个实例。

This is problematic, because each control can only have a single parent element.这是有问题的,因为每个控件只能有一个父元素。 This means that when you would assign the same Canvas instance multiple times, only the last element will get it as child.这意味着当您多次分配相同的Canvas实例时,只有最后一个元素会将其作为子元素。 You can imagine a chess board where only the last assinged pieces are shown and the rest of the board is empty.您可以想象一个棋盘,其中只显示最后分配的棋子,棋盘的 rest 是空的。 Here is an excerpt of the modifications to your code.这是对您的代码的修改的摘录。

<Canvas Height="64" Width="64"  x:Key="BishopImage"  x:Shared="False">
    <Path Fill="{Binding RelativeSource={RelativeSource AncestorType=local:PieceImage,Mode=FindAncestor},Path=Color1}"
          Stroke="{Binding RelativeSource={RelativeSource AncestorType=local:PieceImage,Mode=FindAncestor},Path=Color2}"
          StrokeThickness="1.5" StrokeMiterLimit="1" StrokeLineJoin="Round">
            <Path.Data>
                <GeometryGroup>
                    <PathGeometry Figures="M 9,36 C 12.39,35.03 19.11,36.43 22.5,34 C 25.89,36.43 32.61,35.03 36,36 C 36,36 37.65,36.54 39,38
                                  C 38.32,38.97 37.35,38.99 36,38.5 C 32.61,37.53 25.89,38.96 22.5,37.5 C 19.11,38.96 12.39,37.53 9,38.5 C 7.65,38.99
                                  6.68,38.97 6,38 C 7.35,36.54 9,36 9,36 z"/>
                    <PathGeometry Figures="M 15,32 C 17.5,34.5 27.5,34.5 30,32 C 30.5,30.5 30,30 30,30 C 30,27.5 27.5,26 27.5,26 C 33,24.5 33.5,14.5
                                  22.5,10.5 C 11.5,14.5 12,24.5 17.5,26 C 17.5,26 15,27.5 15,30 C 15,30 14.5,30.5 15,32 z"/>
                    <PathGeometry Figures="M 25 8 A 2.5 2.5 0 1 1  20,8 A 2.5 2.5 0 1 1  25 8 z"/>
                </GeometryGroup>
            </Path.Data>
        </Path>
        <!-- ...other markup code. -->

The bindings will work now and resolve to PieceImage without any additional code or markup.绑定现在可以工作并解析为PieceImage ,无需任何额外的代码或标记。

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

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