user7643143
user7643143

Reputation: 31

Set DisplayMemberPath for ListBoxItem's Content

I'm trying to figure out how to bind a List of ListBoxItems and have the correct DisplayMemberPath be shown on my control. I need to use a list of ListBoxItems because my control is accessing properties such as ActualHeight/Width, which I lose if I bind the ItemsSource to a List of State

public class State
{
    public string LongName { get; set; }
    public string ShortName { get; set; }
}

I have a AllItems, a List of State. I am populating my ListBox's ItemsSource as follows:

BoxItems = new ObservableCollection<MyListBoxItem>(AllItems.Select(i => new MyListBoxItem() { Content = i }).ToList());

I want my DisplayMemberPath to be LongName. I've tried many different ways of modifying the .xaml to properly display the LongName (New York, New Jersey, Florida, etc), but I always end up with namespace.classname in my listbox. I've attempted setting the ListBox's DisplayMemberPath and ItemsSource in the .xaml and in the .xaml.cs.

I've tried many different things such as setting the content control and adding a data template in the .xaml.

Upvotes: 2

Views: 2342

Answers (2)

mm8
mm8

Reputation: 169270

The DisplayMemberPath refers to a property of the type T in the IEnumerable<T> ItemsSource of the ListBox. So if you set the ItemsSource to an ObservableCollection<MyListBoxItem>, it is the MyListBoxItem class that must have a LongName property for you to be able to set the DisplayMemberPath property to "LongName".

I need to use a list of ListBoxItems because my control is accessing properties such as ActualHeight/Width, which I lose if I bind the ItemsSource to a List of State.

Any item that you add to the Items/ItemsSource property that is not a ListBoxItem is implicitly wrapped in a ListBoxItem container. You can set any properties of this one using the ItemContainerStyle property of the ListBox. This means that you could add Height and Width properties to the State class and bind to these provided that you set the ItemsSource to an IEnumerable<State>:

<ListBox x:Name="lb" ItemsSource="{Binding AllItems}" DisplayMemberPath="LongName" Loaded="ListBox_Loaded">
    <ListBox.ItemContainerStyle>
        <Style TargetType="ListBoxItem">
            <Setter Property="Height" Value="{Binding Height}" />
        </Style>
    </ListBox.ItemContainerStyle>
</ListBox>

You could also get a reference such a container once it has been loaded, using the ItemContainerGenerator:

private void ListBox_Loaded(object sender, RoutedEventArgs e)
{
    foreach (var item in lb.Items)
    {
        var container = lb.ItemContainerGenerator.ContainerFromItem(item) as ListBoxItem;
        if (container != null)
        {
            //...
        }
    }
}

Setting the ItemsSource to an IEnumerable<ListBoxItem> is not a good idea.

Upvotes: 0

Clemens
Clemens

Reputation: 128070

Creating an ObservableCollection<MyListBoxItem> doesn't seem to make sense.

Better bind directly to AllItems:

<ListBox ItemsSource="{Binding AllItems}"
         DisplayMemberPath="LongName"/>

Instead of setting DisplayMemberPath, you may set the ItemTemplate property:

<ListBox ItemsSource="{Binding AllItems}">
    <ListBox.ItemTemplate>
         <DataTemplate>
             <TextBlock Text="{Binding LongName}"/>
         </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

Upvotes: 2

Related Questions