【问题标题】:WPF: adding UIElements to a ListBox whose ItemsPanelTemplate is a canvas?WPF:将 UIElements 添加到 ItemsPanelTemplate 是画布的 ListBox?
【发布时间】:2010-10-29 12:56:45
【问题描述】:

我正在开发一个 ListBox,它会覆盖其 ItemsPanelTemplate 以使用 Canvas 而不是 StackPanelListBoxItems 有一个 DataTemplate,它使用转换器来定义每个 ListBoxItem 在画布上的外观和位置。当我将一个项目添加到ListBox 绑定到的集合中时,我希望能够将其他UIElements 添加到画布中。我可以在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 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 绑定到的集合中时?

非常感谢任何意见!

【问题讨论】:

    标签: wpf data-binding listbox uielement


    【解决方案1】:

    我认为您的问题已准备好将项目添加到您的ListBoxCanvas。我只是使用您问题中的代码组合了一个简单的 WPF 测试应用程序,它就可以正常工作。每当我向绑定到ListBoxItemsSourceObservableCollection 添加项目时,WPF 将为该对象创建一个ListBoxItem,并根据ListBoxItem 的样式,将绘制该项目在Canvas 的正确位置。

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

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

    编辑 2:虽然最好为此使用 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 的可视化树以查找指定类型和名称的控件。最好在此处使用DependencyObject 而不是FrameworkElement(因为这是VisualTreeHelper.GetChild() 返回的内容),但是Name 仅在FrameworkElement 上定义。

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

    【讨论】:

    • 嗨,安迪,感谢您查看此内容。而且,是的,项目对我来说也正确显示。我要问的是我是否可以向画布添加额外的 UIElements,除了 到 ListItems。我似乎无法获得对 cnvAwesome 的引用(参见 xaml)
    • 是否可以修改 ItemsPanelTemplate 以将项目添加到 Canvas 那里?否则,如果您想为每个 ListBoxItem 添加一个额外的控件,我建议您遵循 idursun 的方法并修改 DataTemplate,以便在您将项目添加到集合时自动添加额外的控件。
    • 我想效仿 idursun 的例子,但这些 UIElement 需要在运行时按程序添加。具体来说,当一个项目被添加到 ListBox 绑定到的集合中时。我只是无法处理 ListBox 使用的 ItemsPanel,也无法向其中添加 UIElements。我真的很感谢你的帮助,如果我能让我的问题更清楚,请告诉我。
    • 当您将一个项目添加到集合中时,您究竟想添加什么到画布中?如果您提供一些细节,我也许可以帮助您使用 ItemTemplate 来完成,而不必自己手动将控件添加到 Canvas。
    • 我正在绑定一组自定义对象,这些对象具有 Point 类型的属性“Position”。转换器使用此属性设置每个 ListBoxItem 的 Canvas.Top 和 Canvas.Left,并且 ItemTemplate 描述每个项目的外观。当一个项目被添加到集合中时,我想在画布上添加一条线,将 ListBoxItem 连接到另一个。
    【解决方案2】:

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

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

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

    【讨论】:

    • 我不认为这个答案解决了我的问题。如果 ListBox 为其 ItemsPanel 使用画布,我如何以编程方式将其他 UIElements 添加到该面板?
    • 那么请举一个具体的例子说明你真正想要达到的目标。
    • 为了清楚起见,我编辑了我的原始帖子。如果有任何不清楚的地方,请告诉我。谢谢 idursun!
    • @barriovolvo,我认为你不能在单个列表框中做你想做的事,也许覆盖列表框会有所帮助,或者你可以在 WPF 中检查装饰器。
    猜你喜欢
    • 2010-11-12
    • 1970-01-01
    • 1970-01-01
    • 2016-09-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多