简体   繁体   中英

How to move multiple selected items in a ListBox back to another ListBox?

I've succeeded to move multiple items in LeftListBox to RightListBox. Now, I'd like to move them back from RightListBox to LeftListBox. However, it gives me "System.NullReferenceException". Here is my code:

MainWindow.xaml.cs

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

namespace ListBoxMoveAll
{
    public partial class MainWindow : Window
    {
        ObservableCollection<TestModel> testItem = new ObservableCollection<TestModel>();

        public MainWindow()
        {
            InitializeComponent();

            testItem.Add(new TestModel("Test_1"));
            testItem.Add(new TestModel("Test_2"));
            testItem.Add(new TestModel("Test_3"));
            LeftListBox.ItemsSource = testItem;

            CollectionViewSource.GetDefaultView(LeftListBox.ItemsSource).Filter =
                (tm) => !RightListBox.Items.Cast<TestModel>().Any(x => x.Equals(tm as TestModel));
            CollectionViewSource.GetDefaultView(RightListBox.ItemsSource);
        }

        private void Add_Button_Click(object sender, RoutedEventArgs e)
        {
            foreach (var item in LeftListBox.SelectedItems)
            {
                RightListBox.ItemsSource = null;
                RightListBox.Items.Add(item);
            }
            CollectionViewSource.GetDefaultView(LeftListBox.ItemsSource).Refresh();
        }

        private void Remove_Button_Click(object sender, RoutedEventArgs e)
        {
            foreach (var item in RightListBox.SelectedItems)
            {
                LeftListBox.ItemsSource = null;
                LeftListBox.Items.Add(item);
            }
            CollectionViewSource.GetDefaultView(RightListBox.ItemsSource).Refresh();
        }
    }
}

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="800">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="80" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <ListBox x:Name="LeftListBox" Grid.Column="0" Grid.Row="1" Grid.RowSpan="3" 
                 SelectionMode="Extended" DisplayMemberPath="TestItem">
        </ListBox>
        <ListBox x:Name="RightListBox" Grid.Column="2" Grid.Row="1" Grid.RowSpan="3" DisplayMemberPath="TestItem" />
        <StackPanel Grid.Column="1" Grid.Row="1" VerticalAlignment="Center">
            <Button Content="Add" x:Name="Add_Button" Click="Add_Button_Click"/>
        </StackPanel>
        <StackPanel Grid.Column="1" Grid.Row="3" VerticalAlignment="Center">
            <Button Content="Remove" x:Name="Remove_Button" Click="Remove_Button_Click"/>
        </StackPanel>
    </Grid>
</Window>

Model/TestModel.cs

namespace ListBoxMoveAll.Model
{
    public class TestModel
    {
        public TestModel(string _testItem) 
        { TestItem = _testItem; }

        public string TestItem { get; set; }
    }
}

I added RightListBox.ItemsSource = null; . I think my usage of CollectionViewSource.GetDefaultView is something wrong, but I can't figure it out. Please help me. Thank you in advance.

The problem is you call CollectionViewSource.GetDefaultView on null. RightListBox.ItemsSource is null because you set it to null in Add_Button_Click handler.

The solution is to only refer to ItemsSource for LeftListBox (because you set it in the constructor) and to refer to Items for RightListBox.

Then you will end up with code like below:

public partial class MainWindow : Window
{
    ObservableCollection<TestModel> testItem = new ObservableCollection<TestModel>();
    public MainWindow()
    {
        InitializeComponent();

        testItem.Add(new TestModel("Test_1"));
        testItem.Add(new TestModel("Test_2"));
        testItem.Add(new TestModel("Test_3"));
        LeftListBox.ItemsSource = testItem;
        CollectionViewSource.GetDefaultView(LeftListBox.ItemsSource).Filter = (tm) => !RightListBox.Items.Cast<TestModel>().Any(x => x.Equals(tm as TestModel));
    }

    private void Add_Button_Click(object sender, RoutedEventArgs e)
    {
        foreach (TestModel item in LeftListBox.SelectedItems)
            RightListBox.Items.Add(item);

        CollectionViewSource.GetDefaultView(LeftListBox.ItemsSource).Refresh();
    }

    private void Remove_Button_Click(object sender, RoutedEventArgs e)
    {
        foreach (TestModel item in RightListBox.SelectedItems.OfType<TestModel>().ToList())
            RightListBox.Items.Remove(item);

        CollectionViewSource.GetDefaultView(LeftListBox.ItemsSource).Refresh();
    }
}

However you should keep in mind that the ItemsSource of the LeftListBox always contains all the values, it is just visually filtered based on the Items in the right list.

If you wanted the ItemsSource of the LeftListBox and the RightListBox to change in relation to each other you should consider changing your xaml as below:

<ListBox
    x:Name="LeftListBox"
    Grid.Row="1"
    Grid.RowSpan="3"
    Grid.Column="0"
    DisplayMemberPath="TestItem"
    ItemsSource="{Binding LeftListBoxItems}"
    SelectionMode="Extended" />
<ListBox
    x:Name="RightListBox"
    Grid.Row="1"
    Grid.RowSpan="3"
    Grid.Column="2"
    DisplayMemberPath="TestItem"
    ItemsSource="{Binding RightListBoxItems}" />

and your code behind as below:

public partial class MainWindow : Window
{
    public ObservableCollection<TestModel> LeftListBoxItems { get; } = new ObservableCollection<TestModel>();
    public ObservableCollection<TestModel> RightListBoxItems { get; } = new ObservableCollection<TestModel>();

    public MainWindow()
    {
        InitializeComponent();
        DataContext = this;
        LeftListBoxItems.Add(new TestModel("Test_1"));
        LeftListBoxItems.Add(new TestModel("Test_2"));
        LeftListBoxItems.Add(new TestModel("Test_3"));
    }

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

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM