ChargerIIC
ChargerIIC

Reputation: 1690

Unable to get DisplayMemberPath to show bound object data in listbox

To cut a long story short I have a section of my application that contains a series of listboxes bound to an object instance in Xaml. Using an IValueConverter, I was able to retrieve a list of <Part> objects from the master object and display the .ToString() form of the retrieved objects. What I want to do, however, is show the Name property of the object instead. I set the DisplayMemberPath to Name but the result only blanks out the listboxitem. I've posted the relevant portions of the code below:

XAML:

<Window.Resources>
    <local:LocationtoEquipmentCOnverter x:Key="locationEquipmentFiller" />
</Window.Resources>
<Window.DataContext>
    <local:MegaWdiget/>
</Window.DataContext>
<ListBox x:Name="listboxFront" HorizontalAlignment="Left" Margin="180,45,0,0" VerticalAlignment="Top" Width="82" Opacity="0.5" ItemsSource="{Binding ConverterParameter=Front, Converter={StaticResource locationEquipmentFiller}, Mode=OneWay}" DisplayMemberPath="Name"/>

The ValueConverter:

public class LocationtoEquipmentCOnverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        MegaWidget MWidget = (MegaWidget)value;
        Location loc = MWidget.Locations[(string)parameter];
        return loc.Contents;
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

The MegaWidget Object contains the following:

[XmlElement]
public Dictionary<string, Location> Locations { get; set; }

The Location Object contains a List that has the actual objects I need to query for their names:

 public List<Part> Contents;

Solution Found

After continuing along the line of troubleshooting recommended by Mate, I've found that the objects being passed are Part objects and not ListBoxItems. This has resulted in the ListBox being filled with the actual objects instead of ListBoxItems. By changing the ValueConverter to pass a List of ListBoxItems with the content tag set to what I need, the ListBoxes populate properly. I've listed the solution in the question area below:

Upvotes: 0

Views: 985

Answers (3)

Sir Rufo
Sir Rufo

Reputation: 19106

Here an example howto

  • Model

    public class MegaWidget
    {
        public Dictionary<string, Location> Locations { get; set; }
    }
    
    public class Location
    {
        public List<Part> Contents { get; set; }
    }
    
    public class Part
    {
        public int Id { get; set; }
        public string PartName { get; set; }
    }
    
  • Converter

    public class LocationEquipmentConverter : IValueConverter
    {
        public object Convert( object value, Type targetType, object parameter, CultureInfo culture )
        {
            MegaWidget widget = value as MegaWidget;
            string location = (string) parameter;
            return widget?.Locations[ location ]?.Contents;
        }
    
        public object ConvertBack( object value, Type targetType, object parameter, CultureInfo culture )
        {
            throw new NotImplementedException( );
        }
    }
    
  • ViewModel

    public class FooViewModel : ViewModelBase
    {
        public FooViewModel()
        {
            Widget = new MegaWidget
            {
                Locations = new Dictionary<string, Location>
                {
                    [ "Front" ] = new Location
                    {
                        Contents = new List<Part>
                        {
                            new Part { Id = 1, PartName = "Part 01" },
                            new Part { Id = 2, PartName = "Part 02" },
                            new Part { Id = 3, PartName = "Part 03" },
                        }
                    },
                    [ "Back" ] = new Location
                    {
                        Contents = new List<Part>
                        {
                            new Part { Id = 11, PartName = "Part 11" },
                            new Part { Id = 12, PartName = "Part 12" },
                            new Part { Id = 13, PartName = "Part 13" },
                        }
                    },
                }
            };
        }
    
        public MegaWidget Widget { get; }
    
        #region Property FrontPart
        private Part _frontPart;
    
        public Part FrontPart
        {
            get { return _frontPart; }
            set { SetProperty( ref _frontPart, value ); }
        }
        #endregion
    
        #region Property BackPart
        private Part _backPart;
    
        public Part BackPart
        {
            get { return _backPart; }
            set { SetProperty( ref _backPart, value ); }
        }
        #endregion
    }
    
  • View

    <Window x:Class="WPG.WpfApp.FooView"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:ViewModel="clr-namespace:WPG.WpfApp.ViewModel"
            xmlns:local="clr-namespace:WPG.WpfApp"
            mc:Ignorable="d"
            Title="FooView" Height="300" Width="300">
        <Window.Resources>
            <ViewModel:LocationEquipmentConverter x:Key="LocationEquipmentConverter"/>
        </Window.Resources>
        <Window.DataContext>
            <ViewModel:FooViewModel/>
        </Window.DataContext>
        <Grid>
            <StackPanel>
                <ListBox ItemsSource="{Binding Widget, ConverterParameter=Front, Converter={StaticResource LocationEquipmentConverter}, Mode=OneWay}"
                         SelectedItem="{Binding Path=FrontPart}"
                         DisplayMemberPath="PartName"/>
                <ListBox ItemsSource="{Binding Widget, ConverterParameter=Back, Converter={StaticResource LocationEquipmentConverter}, Mode=OneWay}"
                         SelectedItem="{Binding Path=BackPart}"
                         DisplayMemberPath="PartName"/>
            </StackPanel>
        </Grid>
    </Window>
    
  • Screenshot

enter image description here

Upvotes: 0

ChargerIIC
ChargerIIC

Reputation: 1690

After continuing along the line of troubleshooting recommended by Mate, I've found that the objects being passed are Part objects and not ListBoxItems. This has resulted in the ListBox being filled with the actual objects instead of ListBoxItems. By changing the ValueConverter to pass a List of ListBoxItems with the content tag set to what I need, the ListBoxes populate properly.

The new ValueConverter:

public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
    MegaWidget MWidget = (MegaWidget)value;
    Location loc = MWidget.Locations[(string)parameter];
    List<ListBoxItem> displayContent = new List<ListBoxItem>();

    foreach (Part item in loc.Contents)
    {
        ListBoxItem lbi = new ListBoxItem();
        lbi.Content = item.Name;
        displayContent.Add(lbi);
    }

    return displayContent;
}

Upvotes: 2

Mate
Mate

Reputation: 5274

Based on your response "if I do not add DisplayMemberPath, but shows the inheritance (system.MWidget.Part)", I suppose the property Name is coming empty.

To check, please test:

public class LocationtoEquipmentCOnverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter,   System.Globalization.CultureInfo culture)
    {
        MegaWidget MWidget = (MegaWidget)value;
        Location loc = MWidget.Locations[(string)parameter];

        //Refers to Class "Content" used in loc.Contents collection. I do not know what the name that you have used
        foreach (Content item in loc.Contents)
        {
            item.Name += "***";
        }

        return loc.Contents;
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

Upvotes: 1

Related Questions