.net 附加属性无法按预期工作

.net 附加属性无法按预期工作,.net,wpf,.net,Wpf,我想创建一个只显示符合控件边界的项的控件。为此,我创建了自己的面板,该面板继承自StackPanel并覆盖ArrangeOverride过程。在那里,我让默认的StackPanel进行排列,然后我检测到不符合面板边界的项目并将其隐藏。这很有魅力 现在我想知道一个项目是否因为不适合我的面板边界而被隐藏。为此,我创建了一个附加属性,在隐藏该项时将其设置为True。我将视图模型中的一个属性绑定到此附加属性。但我从未得到信息,不管物品是否被隐藏 为了简单起见,我创建了一个小示例项目,它显示了我的问题,但

我想创建一个只显示符合控件边界的项的控件。为此,我创建了自己的面板,该面板继承自StackPanel并覆盖ArrangeOverride过程。在那里,我让默认的StackPanel进行排列,然后我检测到不符合面板边界的项目并将其隐藏。这很有魅力

现在我想知道一个项目是否因为不适合我的面板边界而被隐藏。为此,我创建了一个附加属性,在隐藏该项时将其设置为True。我将视图模型中的一个属性绑定到此附加属性。但我从未得到信息,不管物品是否被隐藏

为了简单起见,我创建了一个小示例项目,它显示了我的问题,但没有所有的开销

我希望你们中的一个能弄明白为什么我没有得到我所渴望的信息

这是我的面板。在每一个第二项中,字符串类型的附加属性测试都设置为Filled,否则它的默认值为空

Public Class StackPanelEx
    Inherits StackPanel

    Public Shared TestProperty As DependencyProperty = DependencyProperty.RegisterAttached("Test", GetType(String), GetType(StackPanelEx), New FrameworkPropertyMetadata("Empty"))

    Public Shared Function GetTest(ByVal d As DependencyObject) As String
        Return d.GetValue(StackPanelEx.TestProperty)
    End Function

    Public Shared Sub SetTest(ByVal d As DependencyObject, ByVal value As String)
        d.SetValue(StackPanelEx.TestProperty, value)
    End Sub

    Protected Overrides Function ArrangeOverride(finalSize As Size) As Size
        Dim result As Size

        Dim index As Integer

        result = MyBase.ArrangeOverride(finalSize)

        For Each child As UIElement In MyBase.InternalChildren
            index += 1

            If (index Mod 2) = 0 Then
                StackPanelEx.SetTest(child, "Filled")
            End If
        Next child

        Return result
    End Function

End Class
这是我的ItemViewModel,简单明了:

Imports System.ComponentModel

Public Class ItemViewModel
    Implements INotifyPropertyChanged

    Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged

    Protected Sub OnPropertyChanged(propertyName As String)
        RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
    End Sub

    Private _caption As String

    Public Property Caption As String
        Get
            Return _caption
        End Get
        Set(value As String)
            _caption = value
            Me.OnPropertyChanged(NameOf(Me.Caption))
        End Set
    End Property

End Class
这是我的MainViewModel,包含一组ItemViewModels:

Imports System.Collections.ObjectModel
Imports System.ComponentModel

Public Class MainViewModel
    Implements INotifyPropertyChanged

    Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged

    Protected Sub OnPropertyChanged(propertyName As String)
        RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
    End Sub

    Private ReadOnly _items As ObservableCollection(Of ItemViewModel)

    Public Sub New()
        _items = New ObservableCollection(Of ItemViewModel)

        _items.Add(New ItemViewModel With {.Caption = "Item 1"})
        _items.Add(New ItemViewModel With {.Caption = "Item 2"})
        _items.Add(New ItemViewModel With {.Caption = "Item 3"})
        _items.Add(New ItemViewModel With {.Caption = "Item 4"})
        _items.Add(New ItemViewModel With {.Caption = "Item 5"})
    End Sub

    Public ReadOnly Property Items As ObservableCollection(Of ItemViewModel)
        Get
            Return _items
        End Get
    End Property

End Class
最后是我的主窗口,您可能需要调整本地名称空间:

<Window x:Class="MainWindow"
        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:WpfApp4"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Window.DataContext>
        <local:MainViewModel />
    </Window.DataContext>

    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>

        <ItemsControl Grid.Column="0"
                      ItemsSource="{Binding Items}"
                      MinWidth="50">
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding Caption}" />
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>

        <ItemsControl Grid.Column="1"
                      ItemsSource="{Binding Items}">
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <local:StackPanelEx Orientation="Horizontal" />
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <Button Content="{Binding Caption}" Margin="2" local:StackPanelEx.Test="{Binding Caption, Mode=OneWayToSource}" />
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
    </Grid>
</Window>
所有项目的标题都显示为空,因此“我的附加属性”的默认值有效。但由于在面板的ArrangeOverride中设置了附加属性的值,因此我希望每一秒都显示填充的标题

Snoop告诉我,我的附加属性的值确实在每一个条目中都被填充,所以我的绑定中似乎有问题


我不知道我哪里出错了

ItemTemplate中的按钮不是用作ItemsPanel的StackPanelEx的直接子级,因此按钮上从未设置附加属性

ItemsControl创建ContentPresenter作为项容器元素,该元素将其ContentTemplate属性设置为ItemTemplate

将绑定移动到ItemContainerStyle:

<ItemsControl ...>
    ...
    <ItemsControl.ItemContainerStyle>
        <Style TargetType="ContentPresenter">
            <Setter Property="local:StackPanelEx.Test"
                    Value="{Binding Caption, Mode=OneWayToSource}"/>
        </Style>
    </ItemsControl.ItemContainerStyle>
</ItemsControl>