Home > Enterprise >  C# Listview binding to dictionary of objects not working, rows and subitems not displaying
C# Listview binding to dictionary of objects not working, rows and subitems not displaying

Time:04-03

I have a development environment with the 4.8 .net framework. I am trying to display a dictionary of this RemoteUnit class in a ListView, but the list view is remaining unpopulated even when I've confirmed the dictionary has items.

Here is my code

RemoteUnit.cs

public class RemoteUnit : INotifyPropertyChanged
{
    public string Id { get { return _uid; } set { _uid = value; NotifyPropertyChanged(); } }
    public string Name { get { return _name; } set { _name = value; NotifyPropertyChanged(); } }
    public bool Alive { get { return _alive; } set { _alive = value; NotifyPropertyChanged(); } }
    public bool Subscribed { get { return _subscribed; } set { _subscribed = value; NotifyPropertyChanged(); } }
}

RemoteUnitTab.xaml

<ListView Grid.Row="0" Name="LstRemoteUnits" Height="Auto" Margin="5,5,5,5" Background="White" ItemsSource="{Binding Path=RemoteUnits.UnitDict}">
    <ListView.View>
        <GridView AllowsColumnReorder="true" ColumnHeaderToolTip="Remote Units On Network">
            <GridView.ColumnHeaderContainerStyle>
                <Style TargetType="{x:Type GridViewColumnHeader}">
                    <Setter Property="Foreground" Value="Black" />
                </Style>
            </GridView.ColumnHeaderContainerStyle>
            <GridViewColumn DisplayMemberBinding="{Binding Path=Value.Name}" Header="Name" Width="Auto"/>
            <GridViewColumn DisplayMemberBinding="{Binding Path=Value.Alive}" Header="Alive" Width="Auto"/>
            <GridViewColumn DisplayMemberBinding="{Binding Path=Value.Subscribed}" Header="Subscribed" Width="Auto"/>
            <GridViewColumn DisplayMemberBinding="{Binding Path=Value.Id}" Header="Uid" Width="Auto"/>
        </GridView>
    </ListView.View>
</ListView>

RemoteUnitTab.xaml.cs

public partial class UnitsOnNetworkControl : UserControl
    {
        private UnitsData RemoteUnits;

        public UnitsOnNetworkControl()
        {
            InitializeComponent();

            RemoteUnits = GlobalUnitReference.GetReference();

            // .. other initialization stuff

            LstRemoteUnits.ItemsSource = RemoteUnits.UnitDict;
            LstRemoteUnits.DisplayMemberPath = "Value";
            LstRemoteUnits.SelectedValuePath = "Key";
        }
}

Like I said, the listview is remaining empty.

I found some stackoverflow answers which suggested setting the view to details, which I did like this:

LstRemoteUnits.View = System.Windows.Forms.View.Details;

but I got this error

Cannot implicitly convert type 'System.Windows.Forms.View' to 'System.Windows.Controls.ViewBase'

I am on a tight schedule and need this to work (project demonstration is due next week and I need to be working on more important things), so I am considering scrapping the dictionary binding for a temporary solution such as inserting RemoteUnit items in manually. Problem is, when I directly add the RemoteUnit class (after disabling ItemsSource) in with this:

LstRemoteUnits.Items.Add(remote_unit);

I get empty rows with no text. I tried manually setting sub items, but apparently "subitems" don't exist with my current environment because this code:

ListViewItem item = new ListViewItem();
item.SubItems.Add("foo");

returns the error

'ListViewItem' does not contain a definition for 'SubItems' and no accessible extension method 'SubItems' accepting a first argument of type 'ListViewItem' could be found (are you missing a using directive or an assembly reference?)

Like I said, I am on a tight schedule and need this field to work because its part of the primary interface. Can anyone help me with this?

Thanks.

[Edit - replaced reference to the project's name]

[Edit 2] Figured out the ListViewItem was coming from System.Windows.Controls and not System.Windows.Forms. Using the item from System.Windows.Forms fixes that part of the question.

CodePudding user response:

You have defined the ItemsSource="{Binding Path=RemoteUnits.UnitDict}" in your RemoteUnitTab.xaml.cs

To display binding source you have to define the DataContext of your control. the DataContext must contain a public property RemoteUnits or add the remote units as DataContext and change ItemsSource.

Try to add LstHo2Units.DataContext = Ho2Units and ItemsSource="{Binding Path=UnitDict}"

CodePudding user response:

I think the reason that you don't see anything in your view is because the UI isn't getting notified properly.

Try changing

public partial class UnitsOnNetworkControl : UserControl
{
    private UnitsData RemoteUnits;

    public UnitsOnNetworkControl()
    {
        InitializeComponent();
        
        RemoteUnits = GlobalUnitReference.GetReference();

        // .. other initialization stuff

        LstRemoteUnits.ItemsSource = RemoteUnits.UnitDict;
        LstRemoteUnits.DisplayMemberPath = "Value";
        LstRemoteUnits.SelectedValuePath = "Key";
    }
  }

to

public partial class UnitsOnNetworkControl : UserControl, INotifyPropertyChanged
{
    private Dictionary<int, RemoteUnit> _remoteUnits = new();
    public Dictionary<int, RemoteUnit> RemoteUnits
    {
        get { return _remoteUnits; }
        set
        {
            _remoteUnits = value;
            NotifyPropertyChanged(nameof(RemoteUnits));
        }
    }

    public UnitsOnNetworkControl()
    {
        InitializeComponent();
        DatContext = this;
        RemoteUnits = GlobalUnitReference.GetReference();

        // .. other initialization stuff
    } 
    public event PropertyChangedEventHandler? PropertyChanged;

    private void NotifyPropertyChanged(string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new 
        PropertyChangedEventArgs(propertyName));
    }
 }

And then in your xaml Changed your Itemssource Property on your ListView to

ItemsSource="{Binding RemoteUnits}"

You can remove the Path in these cases or keep them. Shouldn't make a difference either way.

So what I did is I implemented the INotifyPropertyChanged Interface in the Code Behind, set Code Behind as the DataContext and made that little change in the xaml Code. With this I managed to get it to work with the code you have provived.

I have also changed the type of your RemoteUnits property from UnitsData to RemoteUnit because I don't see UnitsData anywhere else in your code so I assumed this to be a copy paste error or something.

  • Related