【发布时间】:2021-07-10 00:50:38
【问题描述】:
我正在尝试创建一个名为PieceImage 的自定义控件,其中包含几个预定义的Canvas 元素之一。我在名为 PieceImageDictionary.xaml 的ResourceDictionary 中定义了这些画布。我在PieceImage、Color1 和Color2 中设置了一些依赖属性,我想绑定到画布中路径的Fill 和Stroke。我对自定义控件和简单的数据绑定有很好的理解,但是样式让我有点困惑。
所以基本上我有一个带有画布的ResourceDictionary,我将其视为图像,我希望能够有多个PieceImageControl实例,每个实例都可以独立选择其中一个图像(不是全局样式)和我希望能够在PieceImage 控件上使用DependencyProperty 设置颜色。
我尝试将我的画布包装在 ControlTemplates 中,但无论出于何种原因,“Canvas”都不是有效的TargetType。
如果我将TargetType 设置为“Control”,我可以应用此模板,但是当我尝试将TemplateBinding 添加到Paths 时,我想设置(Color1) 的属性不能成立。我有点理解这一点,因为Color1 属性在我的PieceImage 控件中,所以TemplateBinding 到Control 甚至Canvas 都不起作用。
然后我尝试使用 AncestorType 的“PieceImage”RelativeBinding,但这也不起作用。没有错误,只是一个空白Canvas。我尝试在Canvas 上调用UpdateLayout() 和InvalidateVisual() 以防万一,但没有任何变化。
我尝试使用 XamlReader 和 Writer 来创建这些画布,而不是使用模板,这最初在我静态定义颜色时有效,但当我尝试添加 RelativeBinding 时,我收到一个 Xaml Parse 错误,说它无法创建字符串“local:PieceImage”中的类型
我尝试在代码中添加绑定,但也得到了一个空白 Canvas。我承认我对编码绑定了解不多,这是我的实现:
var binding = new Binding(Color1Property.Name);
binding.RelativeSource = new RelativeSource(RelativeSourceMode.TemplatedParent);
BindingOperations.SetBinding(path, Path.FillProperty, binding);
这是我的基本代码,为了清楚起见,我已经清除了所有失败的尝试,否则会一团糟。
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(部分)
<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
<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>
我希望有人可以提供一些提示或指出正确的方向,因为 MSDocs 有针对 StyleSelectors 和 DataTemplateSelectors 的指南,这两者都更多地用于数据项样式,而不是使用样式设置另一个变量控件。
谢谢,
【问题讨论】:
标签: c# wpf xaml data-binding wpf-controls