数据模板中的WPF绑定不能用于定制类

数据模板中的WPF绑定不能用于定制类

问题描述:

使用显示可用平铺背景列表的ComboBox工作。这只是一个简单的ComboBox,其中一个ItemSource被设置为MapTileBackground对象的集合。数据模板中的WPF绑定不能用于定制类

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

布局只是屏幕像素坐标和六方晶系之间的转换工具。定义几何只需添加6行的六角线。 TileBackground设置器在给定非空MapTileBackground时,会根据背景定义更新描边和填充。我已经成功测试了这个控件(在组合框数据模板之外)。

和说:

<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对象的集合),以及标签名称和说明。

我的问题是形状始终绘制为空(因为在TileBackground为null),并且从不调用setter。名称标签和描述文本块的行为与预期相同(显示正确的文本)。在我的调试尝试期间,我在预览对象上创建了一个id属性,然后调用TileBackground Setter并将其绑定到Id属性(避免当前的对象绑定),同样,TileBackgroundId setter从不被调用。我甚至添加了一个绑定到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()返回基于标识正确的对象。

我还测试了数据模板外部的MapTileBackgroundPreview设置TileBackgroundId的实例。

有什么想法是怎么回事?

+0

可以共享'Hex'?或者在上面的代码中删除它? – Iron

+0

在这种情况下,Hex是一个名称空间别名。 – David

的CLR包装的依赖setter方法是不应该被设定为WPF绑定引擎调用GetValueSetValue方法直接:

Setters not run on Dependency Properties?

Why are .NET property wrappers bypassed at runtime when setting dependency properties in XAML?

的getter和CLR wrapper属性的setter应该分别为分别调用GetValueSetValue方法。

如果您想在依赖属性设置做一些事情,你应该注册一个回调:

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

我不能相信我在搜索答案或研究数据绑定时没有遇到过,因此完全是我的错。感谢快速回答。无论如何,我使用更改事件重新实现了所有三个属性,并将Id绑定到TileBackgroundId时一切正常。当绑定\到TileBackground时,我仍然没有收到任何东西。我可以用基于int的属性居住,我只是想知道为什么它不能以第二种方式工作。谢谢。 – David