Search code examples
wpfuser-controlsdatacontext

How to set a proper context for a ListBox ItemTemplate property in a custom user control


Here is my ListBox Custom control: UCListBox. The MainLabel is the property I have issues with. It is used as a "label" for a ListBox item:

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

CodeBehind:

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

Here I try to use my custom control in a window:

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

I get a binding error:

Property "NameShort1 is not found for WindowEditCaseVM.

The context of the MainLabel property is my View Model like the rest of the properties and not the context of a ListBox item. How do I fix the context of my custom control's property to display listbox items correctly?


Solution

  • The thing that helped me to solve my problem with help of @Clemens was to make the DataTemplate into a resource and define a DataType property for it.

    First, define a namespace, containing my Participant class in the head of my UserControl:

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

    Then define the resource

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

    Full code:

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