附加物业无法正常工作

时间:2019-06-29 07:46:50

标签: .net wpf

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

现在,我想知道某个项目是否由于不适合面板的边界而被隐藏。为此,我创建了一个附加属性,在隐藏项目的情况下将其设置为True。然后将视图模型中的属性绑定到此附加属性。但无论物品是否已隐藏,我都无法获得信息。

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

我希望你们中的一个能弄清楚为什么我没有得到我想要的信息。


这是我的StackPanel。在第二个项目中,类型为String的附加属性“测试”被设置为“已填充”,否则默认值为“空”。

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

最后是我的MainWindow,您可能必须调整“本地”名称空间:

<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>

所有项目的标题都显示为“空”,因此我附加属性的默认值有效。但是我希望第二个项目都显示标题“ Filled”(因为在我的面板的ArrangeOverride中设置了附加属性的值)。

Snoop告诉我,我附加的属性的值确实在第二个项目中都是“已填充”,因此绑定似乎有问题。

我不知道哪里出了问题。

1 个答案:

答案 0 :(得分:1)

ItemTemplate中的Button不是StackPanelEx的直接子项,而该子项用作ItemsPanel,因此从不在Button上设置附加属性。

ItemsControl创建一个ContentPresenter作为 item容器元素,该元素的ContentTemplate属性设置为ItemTemplate

将绑定移动到ItemContainerStyle

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