在自定义面板实现期间,ItemsControl 中附加 属性 的值

Value of Attached Property in ItemsControl, during custom panel implementation

我实现了自定义面板 Shelf,它根据附加 属性 Shelf.Exact:

排列子项
class Shelf : Panel
{
    public static readonly DependencyProperty ExactProperty = DependencyProperty.RegisterAttached("Exact", typeof(int), typeof(Shelf),
        new FrameworkPropertyMetadata(100, new PropertyChangedCallback(OnExactChanged)));
    private static void OnExactChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        Console.WriteLine(d); // This Callback is added only for debug purposes
    }
    public static int GetExact(UIElement element)
    {
        return (int)element.GetValue(ExactProperty);
    }
    public static void SetExact(UIElement element, int value)
    {
        element.SetValue(ExactProperty, value);
    }
    protected override Size MeasureOverride(Size availableSize)
    {
        Size panelSize = new Size(availableSize.Width, 0);
        foreach (UIElement child in InternalChildren)
        {
            child.Measure(availableSize);
            panelSize.Height += child.DesiredSize.Height;
        }
        return panelSize;
    }
    protected override Size ArrangeOverride(Size finalSize)
    {
        foreach (UIElement child in InternalChildren)
        {
            int exact = Shelf.GetExact(child); // Here I always get 100 which is default value for ExactProperty
            child.Arrange(new Rect(new Point(0, exact * 1), child.DesiredSize));
        }
        return finalSize;
    }
}
<ItemsControl ItemsSource="{Binding Path=Packs}">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <local:Shelf />
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <TextBlock local:Shelf.Exact="{Binding Path=Number}" Text="{Binding Path=Name}"/>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

就像我在上面的代码注释中提到的,我总是得到 Shelf.DependencyProperty 的默认值。我不明白这是为什么?


第一个想法是绑定搞砸了。所以我添加 PropertyChangedCallback 并检查更改的值是否被触发 → 回调被调用并且 d.NewValue 正常但仍然在 ArrangeOverride() 中获得默认值。接下来我完全摆脱绑定,所以我硬编码 local:Shelf.Exact="123" → 我仍然得到默认值。

而且当我直接使用 Shelf 时它完美地工作:

<local:Shelf Grid.Column="1">
    <TextBlock local:Shelf.Exact="223">Test</TextBlock>
    <TextBlock local:Shelf.Exact="332">Test</TextBlock>
    <TextBlock local:Shelf.Exact="443">Test</TextBlock>
</local:Shelf>

DataTemplate中的TextBlock不是ItemsPanel测量排列的元素。

您必须在 ItemContainerStyle 中的项目容器(即 ContentPresenter)上设置附加 属性:

<ItemsControl ItemsSource="{Binding Packs}">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <local:Shelf/>
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
    <ItemsControl.ItemContainerStyle>
        <Style TargetType="ContentPresenter">
            <Setter Property="local:Shelf.Exact" Value="{Binding Number}"/>
        </Style>
    </ItemsControl.ItemContainerStyle>
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding Name}"/>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>