如何在自定义用户控件中为 ListBox ItemTemplate 属性设置适当的上下文

Posted

技术标签:

【中文标题】如何在自定义用户控件中为 ListBox ItemTemplate 属性设置适当的上下文【英文标题】:How to set a proper context for a ListBox ItemTemplate property in a custom user control 【发布时间】:2021-11-02 17:40:24 【问题描述】:

这是我的 ListBox 自定义控件:UCListBoxMainLabel 是我遇到的问题。它用作 ListBox 项的“标签”:

<ListBox ItemsSource="Binding ItemsSource, RelativeSource=RelativeSource AncestorType=local:UCListBox"
         SelectedItem="Binding SelectedItem, RelativeSource=RelativeSource AncestorType=local:UCListBox"
         >
    <ListBox.ItemTemplate>
        <DataTemplate>
            <StackPanel>
                <TextBlock Text="Binding MainLabel, RelativeSource=RelativeSource AncestorType=local:UCListBox"/>
            </StackPanel>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

代码隐藏:

public partial class UCListBox : UserControl

    public UCListBox()
    
        InitializeComponent();
    

    public object ItemsSource
    
        get  return (object)GetValue(ItemsSourceProperty); 
        set  SetValue(ItemsSourceProperty, value); 
    

    // Using a DependencyProperty as the backing store for ItemsSource.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty ItemsSourceProperty =
        DependencyProperty.Register("ItemsSource", typeof(object), typeof(UCListBox), new PropertyMetadata(null));

    public object SelectedItem
    
        get  return (object)GetValue(SelectedItemProperty); 
        set  SetValue(SelectedItemProperty, value); 
    

    // Using a DependencyProperty as the backing store for SelectedItem.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty SelectedItemProperty =
        DependencyProperty.Register("SelectedItem", typeof(object), typeof(UCListBox), new PropertyMetadata(null));


    public string MainLabel
    
        get  return (string)GetValue(MainLabelProperty); 
        set  SetValue(MainLabelProperty, value); 
    

    // Using a DependencyProperty as the backing store for MainLabel.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty MainLabelProperty =
        DependencyProperty.Register("MainLabel", typeof(string), typeof(UCListBox), new PropertyMetadata(string.Empty));
    

这里我尝试在一个窗口中使用我的自定义控件:

<local:UCListBox
    ItemsSource="Binding Participants"
    SelectedItem="Binding SelectedParticipant"
    MainLabel ="Binding NameShort1"
/>

我收到一个绑定错误:

Property "NameShort1 is not found for WindowEditCaseVM.

MainLabel 属性的上下文与其他属性一样是我的视图模型,而不是 ListBox 项的上下文。如何修复自定义控件属性的上下文以正确显示列表框项?

【问题讨论】:

您是否尝试选择项目类的属性(即 Participants 集合的元素类型)作为 Binding 的来源?看看 ItemsControl 的 DisplayMemberPath 是如何工作的。除此之外,如果您从 ListBox 而不是 UserControl 派生控件,则可以节省大量代码。 @Clemens 感谢您的回复,但据我了解,DisplayMemberPath 仅提供具有单个显示属性的简单列表框项目,如果可能的话,我希望将来为我的列表框配置更复杂的数据模板。 这就是为什么我说你应该看看它的工作方式,而不是使用它。目前还不清楚你到底想要达到什么目的。我再次问,MainLabel 是否应该选择项目类的属性?除了 MainLabel 选择的内容之外,DataTemplate 将如何显示其他内容?您现在拥有的只是 DisplayMemberPath 的重新发明。 @Clemens 我想制作一个具有 2-3 个属性的数据模板,例如 MainLabelSecondaryLabelTertiaryLabel,所以当我使用我的 UserControl 时,我只需绑定几个项目类属性给他们并获得结构化的格式。 是什么阻止您使用带有适当 ItemTemplate 的常规 ListBox?您可以简单地为不同的项目类型声明不同的 DataTemplate 资源,即使它们会被自动选择。 【参考方案1】:

在@Clemens 的帮助下帮助我解决问题的方法是将 DataTemplate 变成资源并为其定义 DataType 属性。

首先,定义一个命名空间,在我的 UserControl 的头部包含我的 Participant 类:

         xmlns:model="clr-namespace:DocConstructor.BusinessModel"

然后定义资源

<UserControl.Resources>
    <DataTemplate DataType="x:Type model:Participant">
        <StackPanel>
            <TextBlock Text="Binding NameShort1"/>
            <TextBlock Text="Binding CaseStatusType.Description"/>
        </StackPanel>
    </DataTemplate>
</UserControl.Resources>

完整代码:

<UserControl x:Class="DocConstructor.UserControls.UCListBox"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:DocConstructor.UserControls"
             mc:Ignorable="d" 
             xmlns:model="clr-namespace:DocConstructor.BusinessModel"
>
    <UserControl.Resources>
        <DataTemplate DataType="x:Type model:Participant">
            <StackPanel>
                <TextBlock Text="Binding NameShort1"/>
                <TextBlock Text="Binding CaseStatusType.Description"/>
            </StackPanel>
        </DataTemplate>
    </UserControl.Resources>
    <ListBox ItemsSource="Binding ItemsSource, RelativeSource=RelativeSource AncestorType=local:UCListBox"
             SelectedItem="Binding SelectedItem, RelativeSource=RelativeSource AncestorType=local:UCListBox"            
             >
    </ListBox>
</UserControl>

【讨论】:

请注意,这个 UserControl 是完全多余的。将 DataTemplate 资源移动到 App.xaml 中的 Application.Resources 或 Window.Resources 并写入 &lt;ListBox ItemsSource="Binding Participants" SelectedItem="Binding SelectedParticipant"/&gt;

以上是关于如何在自定义用户控件中为 ListBox ItemTemplate 属性设置适当的上下文的主要内容,如果未能解决你的问题,请参考以下文章

如何在自定义嵌套用户控件中引用另一个 XAML 元素?

如何给用户控件传递参数

[WP8] ListBox的Item宽度自动填满

vb里面listbox控件如何改变某一行的背景颜色

如何在自定义 wpf 控件上绑定数据网格列的可见性?

如何抑制用户对 ListBox 控件的操作