如何使用C#WPF将所选项目从一个listviewitem移动到另一个listviewitem [英] How do I move selected items from one listviewitem to another using C# WPF

查看:246
本文介绍了如何使用C#WPF将所选项目从一个listviewitem移动到另一个listviewitem的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

下面的代码会将所选项目从左侧列表移动到右侧列表视图但是在尝试从Leftside中删除该项目时会出错(Memberlist.Items.Remove(item)。



错误是当ItemsSource正在使用时操作无效。使用ItemsControl.ItemsSource访问和修改元素。



如果我设置了MemberList .ItemSource = null它只删除所有项目。任何建议都表示赞赏。



此外,我想允许用户点击一个按钮,所有项目都将移动。任何建议如何使这项工作表示赞赏。



我尝试过:



My code below will move Selected Items from the Left to the Right Listview but I get an error when trying to remove that item from the Leftside (Memberlist.Items.Remove(item).

The error is "Operations is not valid while ItemsSource is in use. Access and modify elements with ItemsControl.ItemsSource.

If I set MemberList.ItemSource = null it just removes all items. Any advice is appreciated.

Also, I would like to allow users to click a button and all items will be move. any recommendations how to make that work is appreciated.

What I have tried:

private void Button_Click(object sender, RoutedEventArgs e)
       {

           foreach (var item in new ArrayList(MembersList.SelectedItems))
           {

               MembersSelected.Items.Add(item);
               MembersList.Items.Remove(item);
           }
       }

推荐答案

如果你有一个数据集集合,那么修改集合,ListView将更新。这仅适用于实现 INotifyCollectionChanged 的集合。数据绑定系统使用 INotifyCollectionChanged 来传达订户(UI)和主题(数据)之间的变化 - 这称为Observer模式 [ ^ ]。



例如, ObservableCollection<> 类实现 INotifyCollectionChanged ,因此ListViews将自动更改(通过数据绑定),但 List<> 不使用 INotifyCollectionChanged 并且ListViews不会看到更改。



替代方案,因为两个ListView使用相同的数据,绑定每个ListView单独的 CollectionViewSource 。如果数据项尚不存在,则向数据项添加分组 - 这是为了识别该项所属的列表。现在将每个 CollectionViewSource 的过滤器设置为gouping属性,ListViews将显示其各自的组。现在,要将数据项从一个ListView移动到另一个ListView,只需更改数据项上的分组属性,ListViews将自动更新,该项似乎从一个ListView移动到另一个。最后,要使其正常工作,您的数据项需要实现 INotifyPropertyChanged ,并且数据项集合需要实现 INotifyCollectionChanged



**更新:我决定整理两个工作版本的快速示例:

If you have databound a collection, then modify the collection and the ListView will update. This is only true for collection that implement INotifyCollectionChanged. INotifyCollectionChanged is used by the data binding system to communicate changes between the subscriber (UI) and the subject (data) - this is called the Observer pattern[^].

For example, the ObservableCollection<> class implements INotifyCollectionChanged, so the ListViews will change automatically (through data binding) however the List<> does not use INotifyCollectionChanged and the ListViews won't see the change.

The alternative, as the two ListViews are working with the same data, bind each ListView to a separate CollectionViewSource. Add a grouping to the data items if one does not already exist - this is to identify which list the item belongs to. Now set the filter of each CollectionViewSource to the gouping property and the ListViews will show their respective groups. Now, to move data items from one ListView to the other, simply change the grouping property on the data item and the ListViews will automatically update and the item appears to move from one ListView to the other. Lastly, for this to work, your data item needs to implement INotifyPropertyChanged and the collection of data items needs to implement INotifyCollectionChanged.

** Update: I decided to put together a quick example of two working versions:
  1. 将对象从一个集合移动到另一个集合
  2. 使用CollectionViewSource过滤并更改所选项目的属性



首先我们需要一个model:


First we need a model:

public class PersonModel : INotifyPropertyChanged
{
    private string name;
    public string Name
    {
        get { return name; }
        set
        {
            name= value;
            RaisePropertyChanged();
        }
    }

    private string group = "A";
    public string Group
    {
        get { return group; }
        set {
                group = value;
                RaisePropertyChanged();
            }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    private void RaisePropertyChanged([CallerMemberName] string propertyName = "")
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}



现在我们可以设置集合, CollectionViewSource s和测试数据:


Now we can set up the collections, CollectionViewSources and the test data:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        DataContext = this;

        Models1A = new ObservableCollection<PersonModel>(Models.Where(x => GroupA(x)));
        Models1B = new ObservableCollection<PersonModel>(Models.Where(x => GroupB(x)));

        const string filterProperty = nameof(PersonModel.Group);

        CsvA.Source = Models;
        SetFiltering(CsvA, GroupA, filterProperty);

        CsvB.Source = Models;
        SetFiltering(CsvB, GroupB, filterProperty);
    }

    private void SetFiltering(CollectionViewSource Csv, Predicate<PersonModel> filter, string FilterProperty)
    {
        Csv.IsLiveFilteringRequested = true;
        Csv.LiveFilteringProperties.Add(FilterProperty);
        Csv.View.Filter = x => filter((PersonModel)x);
    }

    public ObservableCollection<PersonModel> Models1A { get; }
    public ObservableCollection<PersonModel> Models1B { get; }

    private Predicate<PersonModel> GroupA { get; } = new Predicate<PersonModel>(x => x.Group == "A");
    private Predicate<PersonModel> GroupB { get; } = new Predicate<PersonModel>(x => x.Group == "B");

    public ObservableCollection<PersonModel> Models { get; }
        = new ObservableCollection<PersonModel>
        {
            new PersonModel { Name = "Person 1", Group = "B" },
            new PersonModel { Name = "Person 2" },
            new PersonModel { Name = "Person 3" },
            new PersonModel { Name = "Person 4" }
        };

    public CollectionViewSource CsvA { get; } = new CollectionViewSource();
    public CollectionViewSource CsvB { get; } = new CollectionViewSource();

    private void OnManualMove(object sender, RoutedEventArgs e)
    {
        var model = (PersonModel)((Button)sender).DataContext;
        if (Models1A.Contains(model))
        {
            Models1A.Remove(model);
            Models1B.Add(model);
        }
        else
        {
            Models1B.Remove(model);
            Models1A.Add(model);
        }
    }

    private void OnFilteredMove(object sender, RoutedEventArgs e)
    {
        var model = (PersonModel)((Button)sender).DataContext;
        model.Group = model.Group == "A" ? "B" : "A";
    }
}



最后,窗口(UI):


Lastly, the Window (UI):

<Window

    x:Class="SwitchLists.MainWindow"

    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    Title="Code Project Q&A  |  Move item between Lists"

    Height="350" Width="525" WindowStartupLocation="CenterScreen">

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition/>
            <RowDefinition Height="Auto"/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>

        <Grid.Resources>

            <Style TargetType="{x:Type ListBox}">
                <Setter Property="Margin" Value="10"/>
            </Style>

            <DataTemplate x:Key="ManualItemTempate">
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition />
                        <ColumnDefinition Width="Auto"/>
                    </Grid.ColumnDefinitions>
                    <TextBlock Text="{Binding Name}"/>
                    <Button Content="MOVE"

                            Padding="2" Margin="2" Grid.Column="1"

                            Click="OnManualMove"/>
                </Grid>
            </DataTemplate>

            <DataTemplate x:Key="FilterItemTempate">
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition />
                        <ColumnDefinition Width="Auto"/>
                    </Grid.ColumnDefinitions>
                    <TextBlock Text="{Binding Name}"/>
                    <Button Content="MOVE"

                            Padding="2" Margin="2" Grid.Column="1"

                            Click="OnFilteredMove"/>
                </Grid>
            </DataTemplate>

            <Style TargetType="{x:Type ListBoxItem}">
                <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
            </Style>
        </Grid.Resources>

        <TextBlock Text="Between Lists"/>

        <ListBox Grid.Row="1"

                 ItemsSource="{Binding Models1A}"

                 ItemTemplate="{StaticResource ManualItemTempate}">

        </ListBox>
        <ListBox Grid.Row="1" Grid.Column="1"

                 ItemsSource="{Binding Models1B}"

                 ItemTemplate="{StaticResource ManualItemTempate}">

        </ListBox>

        <TextBlock Text="CollectionViewSource" Grid.Row="2"/>
        
        <ListBox Grid.Row="3"

                 ItemsSource="{Binding CsvA.View}"

                 ItemTemplate="{StaticResource FilterItemTempate}">

        </ListBox>
        <ListBox Grid.Row="3" Grid.Column="1"

                 ItemsSource="{Binding CsvB.View}"

                 ItemTemplate="{StaticResource FilterItemTempate}">

        </ListBox>

    </Grid>

</Window>



Now, when you click the \"Move\" button, the item will switch Lists:

  • The top two lists are moving from one collection to the other
  • The bottom two are Filtered views to the same collection with only the item Group property changing
  • public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            DataContext = this;
     
            Models1A = new ObservableCollection<PersonModel>(Models.Where(x => GroupA(x)));
            Models1B = new ObservableCollection<PersonModel>(Models.Where(x => GroupB(x)));
        }
     
        public ObservableCollection<PersonModel> Models1A { get; }
        public ObservableCollection<PersonModel> Models1B { get; }
     
        private Predicate<PersonModel> GroupA { get; } = new Predicate<PersonModel>(x => x.Group == "A");
        private Predicate<PersonModel> GroupB { get; } = new Predicate<PersonModel>(x => x.Group == "B");
     
        public ObservableCollection<PersonModel> Models { get; }
            = new ObservableCollection<PersonModel>
            {
                new PersonModel { Name = "Person 1", Group = "B" },
                new PersonModel { Name = "Person 2" },
                new PersonModel { Name = "Person 3" },
                new PersonModel { Name = "Person 4" }
            };
     
        private void OnManualMove(object sender, RoutedEventArgs e)
        {
            var model = (PersonModel)((Button)sender).DataContext;
            if (Models1A.Contains(model))
            {
                Models1A.Remove(model);
                Models1B.Add(model);
            }
            else
            {
                Models1B.Remove(model);
                Models1A.Add(model);
            }
        }
    }



    The Xaml used is as follows:


    The Xaml used is as follows:

    <Grid.Resources>
        <DataTemplate x:Key="ManualItemTempate">
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition />
                    <ColumnDefinition Width="Auto"/>
                </Grid.ColumnDefinitions>
                <TextBlock Text="{Binding Name}"/>
                <Button Content="MOVE"
    
                        Padding="2" Margin="2" Grid.Column="1"
    
                        Click="OnManualMove"/>
            </Grid>
        </DataTemplate>
    </Grid.Resources>
    
    <ListBox Grid.Row="1"
    
         ItemsSource="{Binding Models1A}"
    
         ItemTemplate="{StaticResource ManualItemTempate}"/>
    
    <ListBox Grid.Row="1" Grid.Column="1"
    
         ItemsSource="{Binding Models1B}"
    
         ItemTemplate="{StaticResource ManualItemTempate}"/>



    2. Changing (OnFilteredMove) an item (PersonModel) grouping (PersonModel.Group) and the CollectionViewSource Filters automatically changes the collections from one collection (CsvA) to the other collection (CsvB).



    The C# code:


    2. Changing (OnFilteredMove) an item (PersonModel) grouping (PersonModel.Group) and the CollectionViewSource Filters automatically changes the collections from one collection (CsvA) to the other collection (CsvB).

    The C# code:

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            DataContext = this;
     
            const string filterProperty = nameof(PersonModel.Group);
     
            CsvA.Source = Models;
            SetFiltering(CsvA, GroupA, filterProperty);
     
            CsvB.Source = Models;
            SetFiltering(CsvB, GroupB, filterProperty);
        }
     
        private void SetFiltering(CollectionViewSource Csv, Predicate<PersonModel> filter, string FilterProperty)
        {
            Csv.IsLiveFilteringRequested = true;
            Csv.LiveFilteringProperties.Add(FilterProperty);
            Csv.View.Filter = x => filter((PersonModel)x);
        }
     
        private Predicate<PersonModel> GroupA { get; } = new Predicate<PersonModel>(x => x.Group == "A");
        private Predicate<PersonModel> GroupB { get; } = new Predicate<PersonModel>(x => x.Group == "B");
     
        public ObservableCollection<PersonModel> Models { get; }
            = new ObservableCollection<PersonModel>
            {
                new PersonModel { Name = "Person 1", Group = "B" },
                new PersonModel { Name = "Person 2" },
                new PersonModel { Name = "Person 3" },
                new PersonModel { Name = "Person 4" }
            };
     
        public CollectionViewSource CsvA { get; } = new CollectionViewSource();
        public CollectionViewSource CsvB { get; } = new CollectionViewSource();
     
        private void OnFilteredMove(object sender, RoutedEventArgs e)
        {
            var model = (PersonModel)((Button)sender).DataContext;
            model.Group = model.Group == "A" ? "B" : "A";
        }
    }



    The Xaml used is as follows:


    The Xaml used is as follows:

    <Grid.Resources>
        <DataTemplate x:Key="FilterItemTempate">
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition />
                    <ColumnDefinition Width="Auto"/>
                </Grid.ColumnDefinitions>
                <TextBlock Text="{Binding Name}"/>
                <Button Content="MOVE"
    
                        Padding="2" Margin="2" Grid.Column="1"
    
                        Click="OnFilteredMove"/>
            </Grid>
        </DataTemplate>
    </Grid.Resources>
    
    <ListBox Grid.Row="3"
    
             ItemsSource="{Binding CsvA.View}"
    
             ItemTemplate="{StaticResource FilterItemTempate}"/>
    
    </ListBox>
    <ListBox Grid.Row="3" Grid.Column="1"
    
             ItemsSource="{Binding CsvB.View}"
    
             ItemTemplate="{StaticResource FilterItemTempate}"/>



    The Predicate Filter is just a static lambda method that is used to select data and needs to be called to be used. You don’t need to use it for your project. You also don’t need to have a single collection for your items however the data is common to both lists and acts as a central repository for your data that can be used elsewhere in your app - this could be replaced by your storage like a database.



    I hope that this clarifies.


    The Predicate Filter is just a static lambda method that is used to select data and needs to be called to be used. You don't need to use it for your project. You also don't need to have a single collection for your items however the data is common to both lists and acts as a central repository for your data that can be used elsewhere in your app - this could be replaced by your storage like a database.

    I hope that this clarifies.


    这篇关于如何使用C#WPF将所选项目从一个listviewitem移动到另一个listviewitem的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆