使用显示可用图块背景列表的ComboBox。这只是一个简单的ComboBox,其ItemSource设置为MapTileBackground对象的集合。
MapTileBackground类完全使用属性定义:
public partial class MapTileBackground
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public byte[] Content { get; set; }
public Nullable<int> Color { get; set; }
public int StrokeColor { get; set; }
public byte StrokeThickness { get; set; }
}
在一个单独的库中定义,我宁愿不改变它。
我已经定义了一个简单的形状扩展来绘制背景::
public class MapTileBackgroundPreview : Shape
{
public static readonly DependencyProperty SizeProperty = DependencyProperty.Register("Size", typeof(Point), typeof(MapTileBackgroundPreview));
public static readonly DependencyProperty TileBackgroundProperty = DependencyProperty.Register("TileBackground", typeof(MapTileBackground), typeof(MapTileBackgroundPreview));
public MapTileBackgroundPreview()
{
layout = new Hex.Layout(Hex.Orientation.Flat, new Hex.Point(8, 8), new Hex.Point(4, 4));
Size = new Point(8, 8);
TileBackground = null;
}
private Hex.Layout layout;
protected override Geometry DefiningGeometry
{
get
{
var points = layout.HexCorners(0, 0).ToArray();
var path = new PathFigure();
path.StartPoint = points[5].ToWin();
for (var i = 0; i < 6; i++)
path.Segments.Add(new LineSegment(points[i].ToWin(), true));
var geo = new PathGeometry();
geo.Figures.Add(path);
return geo;
}
}
public Point Size
{
get
{
return (Point)GetValue(SizeProperty);
}
set
{
SetValue(SizeProperty, value);
layout.Size = value.ToHex();
layout.Origin = new Hex.Point(layout.Size.X / 2, layout.Size.Y / 2);
}
}
public MapTileBackground TileBackground
{
get
{
return (MapTileBackground)GetValue(TileBackgroundProperty);
}
set
{
SetValue(TileBackgroundProperty, value);
if (value == null)
{
Fill = Brushes.Transparent;
Stroke = Brushes.Black;
StrokeThickness = 1;
}
else
{
Stroke = value.Stroke();
StrokeThickness = value.StrokeThickness();
Fill = value.Fill(layout.Orientation);
}
}
}
}
布局只是屏幕像素坐标和六边形系统之间的转换实用程序。 DefiningGeometry只需添加十六进制的6个线段。当给定非空MapTileBackground时,TileBackground setter会在背景定义时更新Stroke和Fill。我已经成功测试了这个控件(在组合框数据模板之外)。
说到:
<DataTemplate x:Key="TileListItemRenderer">
<Grid Width="225">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="24"/>
<ColumnDefinition Width="75"/>
<ColumnDefinition />
</Grid.ColumnDefinitions>
<local:MapTileBackgroundPreview Grid.Row="0" Grid.Column="0" Size="12,12" VerticalAlignment="Center" HorizontalAlignment="Center" TileBackground="{Binding /}"/>
<Label Grid.Row="0" Grid.Column="1" Content="{Binding Name}" HorizontalAlignment="Left" VerticalAlignment="Center" FontWeight="Bold"/>
<TextBlock Grid.Row="0" Grid.Column="2" Text="{Binding Description}" HorizontalAlignment="Stretch" VerticalAlignment="Top" TextWrapping="Wrap" />
</Grid>
</DataTemplate>
所以我只创建一个形状和两个标签,将形状绑定到当前的MapTileBackground对象(组合框ItemSource是MapTileBackground对象的集合),标签绑定到Name和Description。
我的问题是形状总是被绘制为空(如在TileBackground中为null)并且永远不会调用setter。名称标签和描述TextBlock都按预期运行(显示正确的文本)。在我的调试尝试期间,我在预览对象上创建了一个id属性,该属性又调用TileBackground Setter并将其绑定到Id属性(避免当前对象绑定),同样,从不调用TileBackgroundId setter。我甚至添加了一个绑定到Id的新标签,看看它是否正常工作,并按预期显示id。以下是那些再次无效的变化。打开下拉列表时,永远不会设置TileBackgroundId或TileBackground属性。
<DataTemplate x:Key="TileListItemRenderer">
<Grid Width="225">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="24"/>
<ColumnDefinition Width="75"/>
<ColumnDefinition />
</Grid.ColumnDefinitions>
<local:MapTileBackgroundPreview Grid.Row="0" Grid.Column="0" Size="12,12" VerticalAlignment="Center" HorizontalAlignment="Center" TileBackgroundId="{Binding Id}"/>
<Label Grid.Row="0" Grid.Column="0" Content="{Binding Id}" HorizontalAlignment="Left" VerticalAlignment="Center" FontWeight="Bold"/>
<Label Grid.Row="0" Grid.Column="1" Content="{Binding Name}" HorizontalAlignment="Left" VerticalAlignment="Center" FontWeight="Bold"/>
<TextBlock Grid.Row="0" Grid.Column="2" Text="{Binding Description}" HorizontalAlignment="Stretch" VerticalAlignment="Top" TextWrapping="Wrap" />
</Grid>
</DataTemplate>
public static readonly DependencyProperty TileBackgroundIdProperty = DependencyProperty.Register("TileBackgroundId", typeof(int), typeof(MapTileBackgroundPreview));
public int TileBackgroundId
{
get
{
return (int)GetValue(TileBackgroundIdProperty);
}
set
{
SetValue(TileBackgroundIdProperty, value);
TileBackground = TMapTileBackgroundTool.Get(value);
}
}
TMapTileBackgroundTool.Get()根据Id返回正确的对象。
我还在数据模板外测试了MapTileBackgroundPreview设置TileBackgroundId的实例。
有关正在发生的事情的任何想法?
答案 0 :(得分:1)
不应该设置依赖项属性的CLR包装器的setter,因为WPF绑定引擎直接调用GetValue
和SetValue
方法:
Setters not run on Dependency Properties?
Why are .NET property wrappers bypassed at runtime when setting dependency properties in XAML?
CLR包装器属性的getter和setter应仅分别调用GetValue
和SetValue
方法。
如果要在设置依赖项属性时执行某些操作,则应注册回调:
public static readonly DependencyProperty TileBackgroundIdProperty = DependencyProperty.Register("TileBackgroundId", typeof(int), typeof(MapTileBackgroundPreview),
new PropertyMetadata(0, new PropertyChangedCallback(TileBackgroundIdChanged)));
public int TileBackgroundId
{
get
{
return (int)GetValue(TileBackgroundIdProperty);
}
set
{
SetValue(TileBackgroundIdProperty, value);
}
}
private static void TileBackgroundIdChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
MapTileBackgroundPreview ctrl = (MapTileBackgroundPreview)d;
ctrl.TileBackground = TMapTileBackgroundTool.Get((int)e.NewValue);
}