如何克服ItemsPanelTemplate中的Grid.Row / Column的错误?

问题描述 投票:0回答:1

创建了一个简单的AP属性,以简化与元素模板的绑定。代替这个:

<ItemsControl ItemsSource="{Binding Mode=OneWay, Source={StaticResource Points.Grid}}" 
              ItemsPanel="{StaticResource Grid.Panel}">
    <ItemsControl.ItemTemplate>
        <DataTemplate DataType="Point">
            <Ellipse Fill="Coral"/>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
    <ItemsControl.ItemContainerStyle>
        <Style>
            <Setter Property="Grid.Row" Value="{Binding Y}"/>
            <Setter Property="Grid.Column" Value="{Binding X}"/>
        </Style>
    </ItemsControl.ItemContainerStyle>
</ItemsControl>

Можноприменятьтакую:

<ItemsControl Grid.Row="1"
        ItemsSource="{Binding Mode=OneWay, Source={StaticResource Points.Grid}}"
        ItemsPanel="{StaticResource Grid.Panel}">
    <ItemsControl.ItemTemplate>
        <DataTemplate DataType="Point">
            <Ellipse Fill="LightBlue"
                             pa:Grid.Row="{Binding Y}"
                             pa:Grid.Column="{Binding X}"/>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl> 

这里是完整的代码所附属性:

public static partial class Grid
{
    public static int GetRow(FrameworkElement element)
    {
        return (int)element.GetValue(RowProperty);
    }

    public static void SetRow(FrameworkElement element, int value)
    {
        element.SetValue(RowProperty, value);
    }

    // Using a DependencyProperty as the backing store for Row.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty RowProperty =
        DependencyProperty.RegisterAttached("Row", typeof(int), typeof(Grid),
        new FrameworkPropertyMetadata
        (
            0,
            FrameworkPropertyMetadataOptions.AffectsParentArrange | FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
            RowChanged
        ));

    private static void RowChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if (!(d is FrameworkElement element))
            throw new ArgumentException("Должен быть  FrameworkElement", nameof(d));

        FrameworkElement parent;

        while ((parent = VisualTreeHelper.GetParent(element) as FrameworkElement) != null && !(parent is System.Windows.Controls.Grid))
            element = parent;

        if (parent is System.Windows.Controls.Grid grid)
            element.SetValue(System.Windows.Controls.Grid.RowProperty, (int)e.NewValue);
    }

    private static void GridLoaded(object sender, RoutedEventArgs e)
        => ((System.Windows.Controls.Grid)sender).InvalidateMeasure();

    public static int GetColumn(FrameworkElement element)
    {
        return (int)element.GetValue(ColumnProperty);
    }

    public static void SetColumn(FrameworkElement element, int value)
    {
        element.SetValue(ColumnProperty, value);
    }

    // Using a DependencyProperty as the backing store for Column.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty ColumnProperty =
        DependencyProperty.RegisterAttached("Column", typeof(int), typeof(Grid),
            new FrameworkPropertyMetadata
            (
                0,
                FrameworkPropertyMetadataOptions.AffectsParentArrange | FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
                ColumnChanged
            ));

    private static void ColumnChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if (!(d is FrameworkElement element))
            throw new ArgumentException("Должен быть  FrameworkElement", nameof(d));

        FrameworkElement parent;

        while ((parent = VisualTreeHelper.GetParent(element) as FrameworkElement) != null && !(parent is System.Windows.Controls.Grid))
            element = parent;

        if (parent is System.Windows.Controls.Grid grid)
            element.SetValue(System.Windows.Controls.Grid.ColumnProperty, (int)e.NewValue);
    }
}

属性没什么复杂的。使用Canvas,同样可以正常工作。对于网格,在将元素附加到集合中或将第一个元素添加到集合时会出现问题-元素在网格中显示而与它们的位置无关。尽管在可视树和属性浏览器中查看时,Grid.Row / Column的附加属性设置正确。并且只要在窗口中有一点变化,元素就都就位。

我认为这是一个坦率的错误。但是如何处理呢?

完整的XAML演示代码:

<Window x:Class="AttachedPropertiesWPF.BindParentWind"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:AttachedPropertiesWPF"
        mc:Ignorable="d"
        Title="BindParentWind" Height="450" Width="800"
        xmlns:pa="clr-namespace:AttachedProperties;assembly=AttachedProperties">
    <Window.Resources>
        <x:Array x:Key="Points.Grid" Type="Point">
            <Point X="1" Y="0"/>
            <Point X="0" Y="2"/>
            <Point X="2" Y="1"/>
        </x:Array>
        <ItemsPanelTemplate x:Key="Grid.Panel">
            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition/>
                    <RowDefinition/>
                    <RowDefinition/>
                </Grid.RowDefinitions>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition/>
                    <ColumnDefinition/>
                    <ColumnDefinition/>
                </Grid.ColumnDefinitions>
            </Grid>
        </ItemsPanelTemplate>
    </Window.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <ItemsControl ItemsSource="{Binding Mode=OneWay, Source={StaticResource Points.Grid}}" 
                      ItemsPanel="{StaticResource Grid.Panel}">
            <ItemsControl.ItemTemplate>
                <DataTemplate DataType="Point">
                    <Ellipse Fill="Coral"/>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
            <ItemsControl.ItemContainerStyle>
                <Style>
                    <Setter Property="Grid.Row" Value="{Binding Y}"/>
                    <Setter Property="Grid.Column" Value="{Binding X}"/>
                </Style>
            </ItemsControl.ItemContainerStyle>
        </ItemsControl>
        <ItemsControl Grid.Row="1"
                      ItemsSource="{Binding Mode=OneWay, Source={StaticResource Points.Grid}}"
                      ItemsPanel="{StaticResource Grid.Panel}">
            <ItemsControl.ItemTemplate>
                <DataTemplate DataType="Point">
                    <Ellipse Fill="LightBlue"
                             pa:Grid.Row="{Binding Y}"
                             pa:Grid.Column="{Binding X}"/>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
    </Grid>
</Window>

输出如下:enter image description here

它应该像这样:enter image description here

wpf grid row attached-properties itemspaneltemplate
1个回答
0
投票

欢迎您使用SO!

老实说,此代码有很多很多问题,但我会坚持您所发布的内容....

@@ Clemens是正确的,对于如何将元素放置在网格上似乎有些困惑。在第一个ItemsControl中,您将通过ItemContainerStyle进行操作,在第二个控件中,则将其直接应用到Ellipse中(尽管使用自定义的Grid helper DP会令人困惑)。您对第一个控件所做的操作不会影响第二个控件,因此,当然,您看到的布局行为在它们之间也将有所不同。

项目模板(例如椭圆)不会直接添加到父面板容器中,而是封装在ContentPresenter中。因此,您的第一个控件是正确执行此操作。还要在第二个ItemsControl中设置ItemContainerStyle,从第二个控件中的Ellipse标记中删除ap:Grid.Row和ap:Grid.Column设置器,并完全摆脱该Grid helper类,就不需要它了。

© www.soinside.com 2019 - 2024. All rights reserved.