WPF:将UIElements添加到其ItemsPanelTemplate是画布的ListBox中?

时间:2009-05-28 10:05:41

标签: wpf data-binding listbox uielement

我正在使用ListBox来覆盖其ItemsPanelTemplate以使用Canvas而不是StackPanelListBoxItems有一个DataTemplate,它使用转换器来定义画布上每个ListBoxItem的外观和位置。当我将一个项添加到ListBox绑定的集合时,我希望能够将其他UIElement添加到画布中。我可以在ListBoxItem转换器之外完成此操作吗?

我的资源部分是这样的:

    <Style TargetType="ListBox">
        <Setter Property="ItemsPanel">
            <Setter.Value>
                <ItemsPanelTemplate>
                    <Canvas x:Key="cnvAwesome">

                    </Canvas>
                </ItemsPanelTemplate>
            </Setter.Value>
        </Setter>
    </Style>
    <Style TargetType="ListBoxItem">
        <Setter Property="Canvas.Left" Value="{Binding Path=Position.X}" />
        <Setter Property="Canvas.Top" Value="{Binding Path=Position.Y}" />
    </Style>

还有一个ListBox:

    <ListBox x:Name="lstAwesome" ItemsSource="{Binding Source={StaticResource someCollection}}"/>

ListBox绑定的集合:

public ObservableCollection<SomeType> someCollection {get; set; }

所以,如果我有一个方法将一个项添加到ListBox lstAwesome所绑定的集合someCollection中:

private void AddItemToDataboundCollection(SomeType someItem)
{
    someCollection.Add(someItem)
    // I'd like to add a UIElement to the canvas that the ListBox uses to layout items here.
}

那么,我可以通过数据绑定列表框将UIElements添加到用于ItemsPanel的画布吗?以编程方式,当我将项目添加到ListBox绑定的集合时?

我非常感谢任何输入!

2 个答案:

答案 0 :(得分:2)

我认为您在问题中已将所有内容添加到Canvas的{​​{1}}。我只是使用你问题中的代码组建了一个简单的WPF测试应用程序,它只是起作用。每当我将项目添加到绑定到ListBox的{​​{1}}的{​​{1}}时,WPF将为该对象创建ObservableCollection,并根据{的样式{1}},ItemsSource将在正确的位置绘制项目。

您是否无法使上述代码正常工作?如果是这种情况,你得到了什么错误,或者你没想到会出现什么错误?

编辑:再次阅读问题之后,我认为混淆是由于对WPF绑定的工作方式存在误解。将集合绑定到ListBox的{​​{1}}时,您需要做的就是将项添加到该集合中(假设集合实现了ListBoxItemListBoxItem一样)。 WPF订阅该接口提供的事件,并根据对列表所做的更改创建/销毁Canvas

编辑2 :虽然为此使用ListBox会更好,但是当项目添加到集合时会自动添加该行,我不是确定如何获得先前添加的项目的位置,以便该行从正确的位置开始。

可以使用的一个选项是子类ItemsSource,并将其设置为INotifyCollectionChanged的{​​{1}}。我相信它有一个函数,你可以覆盖每当添加一个控件时调用它。在该函数中,您可以获取先前添加的控件的位置(将在ObservableCollection子类中缓存),然后直接将控件添加到ListBoxItems的控件。这确实假设DataTemplate中控件的位置不能改变。如果可以,您可能必须订阅对象的Canvas事件并相应地更新行。

编辑3 :由于您的要求,绑定似乎不是一个选项,最不好的选择是直接添加子项。您可以使用VisualTreeHelper.GetChild()

搜索可视树来完成此操作
ItemsPanelTemplate

像这样使用:

ListBox

此代码以递归方式搜索Canvas的可视树,以控制指定的类型和名称。最好在Canvas上使用Canvas(因为这是PropertyChanged返回的内容),但private T FindChild<T>(FrameworkElement parent, string name) where T : FrameworkElement { FrameworkElement curObject = null; T obj = default(T); int count = VisualTreeHelper.GetChildrenCount(parent); for(int i = 0; i < count; i++) { curObject = VisualTreeHelper.GetChild(parent, i) as FrameworkElement; obj = curObject as T; if(null != obj && obj.Name.Equals(name)) { break; } obj = FindChild<T>(curObject, name); if(null != obj) { break; } } return obj; } 仅在Canvas canvas = FindChild<Canvas>(lstAwesome, "cnvAwesome"); 上定义。< / p>

如果你想获得幻想,你甚至可以将其作为一种扩展方法。

答案 1 :(得分:1)

您可以通过设置ItemTemplate来指定展示商品的方式,例如:

<ListBox.ItemTemplate>
    <DataTemplate>
        <Rectangle Width="{Binding Width}" Height="{Binding Height}" />
    </DataTemplate>
</ListBox.ItemTemplate>

如果您希望根据商品更改ItemTemplate,则可以设置ItemTemplateSelector并确定以编程方式使用ItemTemplate