-1

My code has two ListBoxes (LeftListBox and RightListBox) and one TextBox. Those ListBoxes transfer the items between the them. The TextBox shows a number, which corresponds to the SelectedItem in RightListBox (e.g., when T1 is selected, it shows 10, when T2 is selected, it shows 20, and so on).

I've just made it!! ... but those ListBoxes show only the 1st Dictionary item out of three ... How can I show all of them?

enter image description here

Here is my code:

MainWindow.xaml

<Window x:Class="ListBoxMoveAll.MainWindow"
        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:local="clr-namespace:ListBoxMoveAll"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="600">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="2*" />
            <ColumnDefinition Width="80" />
            <ColumnDefinition Width="2*" />
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="2*" />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <ListBox x:Name="LeftListBox" Grid.Row="0" Grid.RowSpan="3" Grid.Column="0"
            ItemsSource="{Binding LeftListBoxItems}" DisplayMemberPath="Key" SelectedValuePath="Value"
            SelectionMode="Extended" Margin="0,10"/>
        <StackPanel Grid.Column="1" Grid.Row="0" VerticalAlignment="Center">
            <Button Content="Add" x:Name="Add_Button" Click="Add_Button_Click"/>
        </StackPanel>
        <StackPanel Grid.Column="1" Grid.Row="2" VerticalAlignment="Center">
            <Button Content="Remove" x:Name="Remove_Button" Click="Remove_Button_Click"/>
        </StackPanel>
        <ListBox x:Name="RightListBox" Grid.Row="0" Grid.RowSpan="3" Grid.Column="2"
            ItemsSource="{Binding RightListBoxItems}" DisplayMemberPath="Key" SelectedValuePath="Value"
            SelectionMode="Extended" Margin="0,10"/>

        <StackPanel Grid.Column="4" Grid.Row="1" Grid.RowSpan="1" Margin="0,10">
            <TextBox Text="{Binding SelectedValue.Step, ElementName=RightListBox}"/>
        </StackPanel>
    </Grid>
</Window>

MainWindow.xaml.cs

using ListBoxMoveAll.Model;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows;

namespace ListBoxMoveAll
{
    public partial class MainWindow : Window
    {
        public ObservableCollection<Dictionary<string, Graph>> LeftListBoxItems { get; } 
            = new ObservableCollection<Dictionary<string, Graph>>();
        public ObservableCollection<Dictionary<string, Graph>> RightListBoxItems { get; } 
            = new ObservableCollection<Dictionary<string, Graph>>();

        public MainWindow()
        {
            InitializeComponent();
            DataContext = this;

            Dictionary<string, Graph> Dic = new Dictionary<string, Graph>();
            Dic.Add("T1", new Graph(10));
            Dic.Add("T2", new Graph(20));
            Dic.Add("T3", new Graph(30));

            LeftListBoxItems.Add(Dic);

            // Yes! There are three items in Dic!
            foreach (var item in Dic)
            {
                MessageBox.Show(item.ToString());
            }
        }

        private void Add_Button_Click(object sender, RoutedEventArgs e)
        {
            //foreach (TestModel item in LeftListBox.SelectedItems.OfType<TestModel>().ToList())
            foreach (Dictionary<string, Graph> item 
                in LeftListBox.SelectedItems.OfType<Dictionary<string, Graph>>().ToList())
            {
                LeftListBoxItems.Remove(item);
                RightListBoxItems.Add(item);
            }
        }

        private void Remove_Button_Click(object sender, RoutedEventArgs e)
        {
            foreach (Dictionary<string, Graph> item 
                in RightListBox.SelectedItems.OfType<Dictionary<string, Graph>>().ToList())
            {
                RightListBoxItems.Remove(item);
                LeftListBoxItems.Add(item);
            }
        }
    }
}

Model/Graph.cs

namespace ListBoxMoveAll.Model
{
    public class Graph
    {
        public Graph(int step) { Step = step; }

        public int Step { get; set; }
    }
}

Since the foreach statement shows that there are three items in Dic, I think my XAML has an issue, but I cannot figure that out. You guys might know what the issue is. Sorry if this is a very basic question ... Thank you in advance.

New Discovery: I've added another foreach statement for LeftListBoxItems:

foreach (var item in LeftListBoxItems)
            {
                MessageBox.Show(item.ToString());
            }

... The output is only once and the content is:

System.Collections.Generic.Dictionary`2[System.String,ListBoxMoveAll.Model.Graph]

What does this mean?

IanHacker
  • 541
  • 9
  • 27

2 Answers2

1

Since you're using data binding the "proper" way to handle this is to wrap both the text and item in a view model:

public class GraphViewModel
{
    public string Text { get; }
    public Graph Graph { get; }

    public GraphViewModel(string text, Graph graph) => (Text, Graph) = (text, graph);
}

No need for the dictionary, your collections can use this type instead:

    public ObservableCollection<GraphViewModel> LeftListBoxItems { get; } = new ObservableCollection<GraphViewModel>();
    public ObservableCollection<GraphViewModel> RightListBoxItems { get; } = new ObservableCollection<GraphViewModel>();

Similarly, there's no point in adding items to a dictionary only to add them to the collection immediately afterwards, just add them to the collection directly:

LeftListBoxItems.Add(new GraphViewModel("T1", new Graph(10)));
LeftListBoxItems.Add(new GraphViewModel("T2", new Graph(20)));
LeftListBoxItems.Add(new GraphViewModel("T3", new Graph(30)));

A minor change to each of your button handlers:

foreach (var item in LeftListBox.SelectedItems.Cast<GraphViewModel>().ToList())
... and ...
foreach (var item in RightListBox.SelectedItems.Cast<GraphViewModel>().ToList())

Finally, bind your list box DisplayMemberPath properties to Text and get rid of the SelectedValuePath:

<ListBox x:Name="LeftListBox" Grid.Row="0" Grid.RowSpan="3" Grid.Column="0"
    ItemsSource="{Binding LeftListBoxItems}" DisplayMemberPath="Text"
    SelectionMode="Extended" Margin="0,10"/>

<ListBox x:Name="RightListBox" Grid.Row="0" Grid.RowSpan="3" Grid.Column="2"
    ItemsSource="{Binding RightListBoxItems}" DisplayMemberPath="Text"
    SelectionMode="Extended" Margin="0,10"/>

This is similar to Genos Loser's 2nd example, but a view model is more typesafe than a KeyValuePair and it's also easier to add INPC to later if you need to.

Mark Feldman
  • 15,731
  • 3
  • 31
  • 58
0

Obviously the LeftListBoxItems only has one item when initializing the MainWindow's constructor, so only shows one item is normal.

So the problem is you should put 3 items in the LeftListBoxItems, not just only once( LeftListBoxItems.Add(Dic); ).

If you want to resolve the problem, perhaps you can change the

ObservableCollection<Dictionary<string, Graph>>

to

ObservableCollection<KeyValuePair<string, Graph>>

, so that you can show 3 items on the app.

Updated

If you want to keep Dictionary type as your collection property, you can refer this link WPF - How can I implement an ObservableCollection<K,T> with a key (like a Dictionary)? to change ObservableCollection type.

Sparking
  • 43
  • 1
  • 6
  • Thank you, but how far should I change from `Dictionary` to `KeyValuePair`? All of them? Then, I got a `CS1061: 'KeyValuePair' does not contain a definition for 'Add'` error at `Dic.Add("T1", new Graph(10));`. Do you know how to fix this? – IanHacker Nov 17 '19 at 10:33