1

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

我的资源部分是这样的:

    <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 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 绑定到的集合中时?

我将不胜感激任何输入!

4

2 回答 2

2

我认为您在问题中已准备好将项目添加CanvasListBox. 我只是使用您问题中的代码组合了一个简单的 WPF 测试应用程序,它就可以正常工作。每当我将项目添加到ObservableCollection绑定到 的ItemsSourceListBox,WPF 都会为该对象创建一个ListBoxItem,并且基于 的样式ListBoxItem,该项目将由Canvas.

您是否无法使您的代码正常工作?如果是这种情况,您得到了什么错误,或者什么没有按您的预期工作?

编辑:再次阅读问题后,我认为混淆是由于对 WPF 绑定如何工作的误解。当您将集合绑定到ListBox'sItemsSource时,您需要做的就是将项目添加到该集合(假设集合实现了INotifyCollectionChangedObservableCollection。WPF 订阅该接口提供的事件,并ListBoxItems根据对列表所做的更改创建/销毁。

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

一种可行的选择是子类Canvas化,并将其设置ItemsPanelTemplateListBox. 我相信它有一个你可以重写的函数,只要添加一个控件就会调用它。在该函数中,您可以获得先前添加的控件的位置(将缓存在您的Canvas子类中),然后将线控件添加到Canvas直接的控件中。这确实假设控件中的位置Canvas不能改变。如果可以,您可能必须订阅对象的PropertyChanged事件并相应地更新行。

编辑 3:由于您的要求,绑定似乎不是一个选项,因此最不坏的选择是直接添加孩子。您可以通过使用VisualTreeHelper.GetChild()搜索可视化树来做到这一点:

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");

此代码递归地在可视树中搜索parent指定类型和名称的控件。最好DependencyObjectFrameworkElement此处使用(因为这是由 . 返回的内容VisualTreeHelper.GetChild()),但Name仅在FrameworkElement.

如果你想变得花哨,你甚至可以把它作为一个扩展方法。

于 2009-05-28T16:48:03.297 回答
1

您可以通过设置 来指定项目的显示方式ItemTemplate,例如:

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

如果您希望ItemTemplate根据您的项目进行更改,那么您可以设置ItemTemplateSelector并决定ItemTemplate以编程方式使用哪个。

于 2009-05-28T13:23:34.910 回答