【问题标题】:WPF binding in data template not working for custom class数据模板中的 WPF 绑定不适用于自定义类
【发布时间】:2017-10-17 02:31:09
【问题描述】:

处理显示可用平铺背景列表的组合框。这只是一个简单的 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 个十六进制线段。 TileBackground 设置器在给定非空 MapTileBackground 时,会根据背景定义更新 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 是空的)并且永远不会调用 setter。 Name Label 和 Description TextBlock 的行为都符合预期(显示正确的文本)。在我的调试尝试期间,我在预览对象上创建了一个 id 属性,该属性又调用 TileBackground Setter 并将其绑定到 Id 属性(避免当前对象绑定),同样,TileBackgroundId 设置器永远不会被调用。我什至添加了一个绑定到 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 的实例。

有什么想法吗?

【问题讨论】:

  • 你能分享Hex吗?还是在上面的代码中去掉?
  • 在此上下文中,Hex 是命名空间别名。

标签: c# wpf xaml data-binding combobox


【解决方案1】:

不应该设置依赖属性的 CLR 包装器的设置器,因为 WPF 绑定引擎直接调用 GetValueSetValue 方法:

Setters not run on Dependency Properties?

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

CLR 包装器属性的getter 和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);
}

【讨论】:

  • 我不敢相信我在搜索答案或研究数据绑定时没有遇到这种情况,所以完全是我的错。感谢快速回答。无论如何,我使用更改事件重新实现了所有三个属性,并且在将 Id 绑定到 TileBackgroundId 时一切正常。将 \ 绑定到 TileBackground 时,我仍然一无所获。我可以使用基于 int 的属性,我只是想知道为什么它不能以第二种方式工作。谢谢。
猜你喜欢
  • 1970-01-01
  • 2013-10-10
  • 1970-01-01
  • 1970-01-01
  • 2012-10-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-25
相关资源
最近更新 更多