I am having difficulty finding a way to use a DataTemplate
in my ListBox
and still be able to use a DisplayMemberPath
which is dynamically assigned (please note this is a requirement). I have read quite a few posts on the subject and all the answers amount to "don't use a DataTemplate
" or "specify the DisplayMemberPath
statically in the DataTemplate
", neither of which is a workable solution for me.
My goal is to build a custom UserControl
called MyListBox
which will be used in many different contexts, and it contains a ListBox
and some other supporting UI items. So my ListBox
has its DisplayMemberPath
bound to a DependencyProperty
of the same name defined in the MyListBox
code-behind, so that when MyListBox
is used in other windows, the DisplayMemberPath
can be assigned based on the needs of that particular window.
This all works fine, but now if I want to use a DataTemplate
as well in this ListBox
(so that I can customize list items by adding a CheckBox
and possibly other items), the DisplayMemberPath
assigned in ListBox
no longer works, and I cannot find a way to assign a dynamic PropertyPath
within the DataTemplate
.
MyListBox.xaml
<UserControl x:Class="MyListBox" x:Name="slb">
<Grid>
<ListBox x:Name="listBox"
DisplayMemberPath="{Binding ElementName=slb, Path=DisplayMemberPath}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox/>
<TextBlock x:Name="lbTextItem"
Text="{This will overwrite DisplayMemberPath above}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</UserControl>
To be used as follows:
MainWindow.xaml
<Window>
<Grid>
<local:MyListBox ItemsSource="{Binding SomeDataSet}"
DisplayMemberPath="InterestingProperty"/>
</Grid>
</Window>
Is there some way around this issue? Is there some way I am missing to dynamically use my DisplayMemberPath
dependency property within the DataTemplate
of MyListBox
? If there was a way for me to access the TextBlock
used in the DataTemplate
from the code-behind, I could change its PropertyPath
in the handler for the DisplayMemberPathPropertyChanged
, but I cannot find a reasonably simple way to do that.
Not sure if this is the most simple approach, but you could use a derived ListBox that sets the Content
property of its ListBoxItems via a Binding that is created in code.
Be aware that you can not use the DisplayMemberPath
property of the derived ListBox, because the framework throws an exceptipon when both DisplayMemberPath
and ItemTemplate
are set. So you have to declare another property like shown below.
public class CustomListBox : ListBox
{
public static readonly DependencyProperty BindingPathProperty =
DependencyProperty.Register(
nameof(BindingPath), typeof(string), typeof(CustomListBox));
public string BindingPath
{
get => (string)GetValue(BindingPathProperty);
set => SetValue(BindingPathProperty, value);
}
protected override void PrepareContainerForItemOverride(
DependencyObject element, object item)
{
base.PrepareContainerForItemOverride(element, item);
((ListBoxItem)element).SetBinding(
ListBoxItem.ContentProperty,
new Binding(BindingPath) { Source = item });
}
}
In your MyListBox, you would use it like this:
<UserControl x:Class="MyListBox" x:Name="slb">
<Grid>
<local:CustomListBox x:Name="listBox"
BindingPath="{Binding DisplayMemberPath, ElementName=slb}">
<local:CustomListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox/>
<TextBlock Text="{Binding}"/>
</StackPanel>
</DataTemplate>
</local:CustomListBox.ItemTemplate>
</local:CustomListBox>
</Grid>
</UserControl>