ListView复选框数据绑定-移除已选项目

3
我试图在点击删除按钮时删除列表视图中的所有选中项。 ListView 我已经看到在许多地方提到了IsChecked="{Binding RelativeSource={RelativeSource AncestorType={x:Type ListBoxItem}}, Path=IsSelected}",但我不知道如何将其与我的已经存在的IsChecked绑定结合起来。
在我的代码中,这会引发异常:lvStores.Items.Remove(item); 我正在导入具有以下结构的CSV文件:
"ID","NAME",CheckedINT
例如:
"1662","BØRSTER",0 <-- 其中1 = 已选中,0 = 未选中。
"1658","BØRSTER",1 我在另一个方法中使用它来循环遍历ListView中的数据。
    // Loop through ListView
    foreach (var item in lvStores.Items.OfType<Store>())
    {
        // If 1 item in Listview is checked then
        if (item.IsChecked == true)
        {
            blnOk = true;
            return;
        }
    }

XAML

<Window x:Class="CloneMacro.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:CloneMacro"
        mc:Ignorable="d"
        Title="Clone Macro" Height="348" Width="620.045" Closed="MainWindow_Closed" Icon="C:\Users\Niclas VMWare\Documents\Visual Studio 2015\Projects\CloneMacro\CloneMacro\images\copy.png">
    <Grid Background="#FFDEDEDE" Margin="-1,0,-8,0" HorizontalAlignment="Left" Width="621">
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <Button x:Name="cmdNew" HorizontalAlignment="Left" Margin="39,36,0,0" VerticalAlignment="Top" Width="39" Height="40" ToolTip="New" Click="cmdNew_Click">
            <Image x:Name="imgNew" HorizontalAlignment="Left" Height="37" VerticalAlignment="Top" Width="36" Source="C:\Users\Niclas VMWare\Documents\Visual Studio 2015\Projects\CloneMacro\CloneMacro\images\Generic_Document.png" RenderTransformOrigin="0.172,1.089"/>
        </Button>
        <Button x:Name="cmdOpen" HorizontalAlignment="Left" Margin="80,36,0,0" VerticalAlignment="Top" Width="39" Height="40" ToolTip="Open" Click="cmdOpen_Click">
            <Image x:Name="imgOpen" HorizontalAlignment="Left" Height="37" VerticalAlignment="Top" Width="36" Source="C:\Users\Niclas VMWare\Documents\Visual Studio 2015\Projects\CloneMacro\CloneMacro\images\folder_Open_32xLG.png"/>
        </Button>
        <Button x:Name="cmdSave" HorizontalAlignment="Left" Margin="121,36,0,0" VerticalAlignment="Top" Width="39" Height="40" ToolTip="Save" Click="cmdSave_Click">
            <Image x:Name="imgSave" HorizontalAlignment="Left" Height="37" VerticalAlignment="Top" Width="36" Source="C:\Users\Niclas VMWare\Documents\Visual Studio 2015\Projects\CloneMacro\CloneMacro\images\save_16xLG.png"/>
        </Button>
        <Button x:Name="cmdDelete" HorizontalAlignment="Left" Margin="235,36,0,0" VerticalAlignment="Top" Width="39" Height="40" Click="cmdDelete_Click">
            <Image x:Name="imgDelete" HorizontalAlignment="Left" Height="33" VerticalAlignment="Top" Width="32" Source="C:\Users\Niclas VMWare\Documents\Visual Studio 2015\Projects\CloneMacro\CloneMacro\images\action_Cancel_16xLG.png" ToolTip="Delete" RenderTransformOrigin="1.334,0.534"/>
        </Button>
        <Button x:Name="cmdNewStore" HorizontalAlignment="Left" Margin="275,36,0,0" VerticalAlignment="Top" Width="39" Height="40" Click="cmdNewStore_Click">
            <Image x:Name="imgNewStore" HorizontalAlignment="Left" Height="37" VerticalAlignment="Top" Width="36" Source="C:\Users\Niclas VMWare\Documents\Visual Studio 2015\Projects\CloneMacro\CloneMacro\images\action_add_16xLG.png" ToolTip="Add New Store" RenderTransformOrigin="0.5,0.5"/>
        </Button>
        <Button x:Name="cmdExecute" HorizontalAlignment="Left" Margin="355,36,0,0" VerticalAlignment="Top" Width="39" Height="40" ToolTip="Execute" Click="cmdExecute_Click">
            <Image x:Name="image" HorizontalAlignment="Left" Height="37" VerticalAlignment="Top" Width="37" Source="C:\Users\Niclas VMWare\Documents\Visual Studio 2015\Projects\CloneMacro\CloneMacro\images\StatusAnnotations_Play_32xLG_color.png"/>
        </Button>
        <Button x:Name="cmdCancel" HorizontalAlignment="Left" Margin="395,36,0,0" VerticalAlignment="Top" Width="39" Height="40" ToolTip="Cancel" Click="cmdCancel_Click">
            <Image x:Name="imgCancel" HorizontalAlignment="Left" Height="37" VerticalAlignment="Top" Width="36" Source="C:\Users\Niclas VMWare\Documents\Visual Studio 2015\Projects\CloneMacro\CloneMacro\images\CancelApproval_32x32.png"/>
        </Button>
        <CheckBox x:Name="chOpenLastUsed" Content="Open Last Used File" Margin="456,48,0,0" VerticalAlignment="Top" Height="21" Width="147" HorizontalAlignment="Left"/>
        <ListView x:Name="lvStores" HorizontalAlignment="Left" Height="161" Margin="39,98,0,0" VerticalAlignment="Top" Width="466">
            <ListView.View>
                <GridView>
                    <GridView.Columns>
                        <GridViewColumn Header="ID" Width="Auto">
                            <GridViewColumn.CellTemplate>
                                <DataTemplate>
                                    <CheckBox Content="{Binding sID}" IsChecked="{Binding IsChecked}"/>
                                </DataTemplate>
                            </GridViewColumn.CellTemplate>
                        </GridViewColumn>
                        <GridViewColumn Header="Name" Width="Auto" DisplayMemberBinding="{Binding Path=sName}"/>
                        <GridViewColumn Header="Import File" Width="Auto" DisplayMemberBinding="{Binding Path=sImportFile}"/>
                    </GridView.Columns>
                </GridView>
            </ListView.View>
        </ListView>
        <Label x:Name="lblProgress" x:FieldModifier="public" Content="Status Text" HorizontalAlignment="Left" Margin="40,264,0,0" VerticalAlignment="Top" Width="465" BorderThickness="1" BorderBrush="#FF7C7C7C"/>
        <Button x:Name="ComDialog" HorizontalAlignment="Left" Margin="533,152,0,0" VerticalAlignment="Top" Width="48" Height="51">
            <Image x:Name="imgComDialog" HorizontalAlignment="Left" Height="44" VerticalAlignment="Top" Width="48" Source="C:\Users\Niclas VMWare\Documents\Visual Studio 2015\Projects\CloneMacro\CloneMacro\images\DialogID_6220_32x.png"/>
        </Button>
    </Grid>
</Window>

CS

    public MainWindow()
    {
        // Leave this to show the UI
        InitializeComponent();

        lblProgress.Content = "";


        lvStores.ItemsSource = ReadCSV(@"C:\Users\Niclas VMWare\Downloads\TESTFILE.pcf");

    }

    // Fill ListView
    public class Store
    {
        public string sName { get; set; }
        public string sImportFile { get; set; }
        public string sID { get; set; }
        public bool IsChecked { get; set; }

        public Store(string id, string strName, string isChecked, string strImportFile)
        {
            sName = strName.Replace("\"","");
            sImportFile = Path.GetFileName(strImportFile);
            sID = id.Replace("\"","");

            // Convert isChecked to boolean
            // Do convertion outside the constructor to avoid any exception within
            int iBool = Convert.ToInt32(isChecked);

            switch (iBool)
            {
                case 0: IsChecked = false; break;
                case 1: IsChecked = true; break;
                default: throw new InvalidOperationException("Third value in PCF file must be 0 or 1!");
            }
        }
    }
    public IEnumerable<Store> ReadCSV(string fileName)
    {
        // Make sure the file extension is pcf
        string[] lines = File.ReadAllLines(Path.ChangeExtension(fileName, ".pcf").Replace("\"", ""), Encoding.GetEncoding(65001));

        // lines.Select allows to project each line as a Store
        // This will give an IEnumerable<Store> back.
        return lines.Select(line =>
        {
            string[] data = line.Split(',');
            // Return the store data
            // Return id, Name, isChecked (chkbox checked?), filename
            return new Store(data[0], data[1], data[2], fileName);
        });
    }
    // End Fill ListView

    private void cmdDelete_Click(object sender, RoutedEventArgs e)
    {
        // if selected
        foreach (var item in lvStores.Items.OfType<Store>())
        {
            if (item.IsChecked == true)
            {
                lvStores.Items.Remove(item);
            }
        }
    }

3
IsChecked绑定无法理解的原因是因为您没有遵循MVVM模式。在WPF解决方案中,您确实应该学习并实现它。一旦您这样做,就可以监听IsChecked视图模型属性上的PropertyChanged事件并将其从ObservableCollection中移除。 - Maverik
抛出了哪个异常? - Yevgeniy
发生了类型为“System.InvalidOperationException”的异常,但在用户代码中未处理。附加信息:在使用ItemsSource时,操作无效。请改用ItemsControl.ItemsSource访问和修改元素。 - Niclas
@Yevgeniy 我应该在哪里删除这些项目?在ReadCSV方法中吗?你有示例代码吗?谢谢。 - Niclas
1个回答

2

我建议:

  1. 使用ObservableCollection
  2. 在你的Store类(或类似的类)上实现INotifyPropertyChanged(其他人正确地建议了解MVVM模式)。这前两个步骤是必要的,以确保您的UI在运行时更新对象。
  3. 反向迭代循环,并根据需要逐个删除每个项目。

MainWindow.xaml:

<Window x:Class="CloneMacro.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:CloneMacro"
        mc:Ignorable="d"
        Title="Clone Macro" Height="348" Width="620.045">
    <Grid Background="#FFDEDEDE" Margin="-1,0,-8,0" HorizontalAlignment="Left" Width="621">
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <Button x:Name="cmdNew" HorizontalAlignment="Left" Margin="39,36,0,0" VerticalAlignment="Top" Width="39" Height="40" ToolTip="New" >
            New
        </Button>
        <Button x:Name="cmdOpen" HorizontalAlignment="Left" Margin="80,36,0,0" VerticalAlignment="Top" Width="39" Height="40" ToolTip="Open" Click="cmdOpen_Click">
            Open
        </Button>
        <Button x:Name="cmdSave" HorizontalAlignment="Left" Margin="121,36,0,0" VerticalAlignment="Top" Width="39" Height="40" ToolTip="Save">
            Save
        </Button>
        <Button x:Name="cmdDelete" HorizontalAlignment="Left" Margin="235,36,0,0" VerticalAlignment="Top" Width="39" Height="40" Click="cmdDelete_Click">
            Delete
        </Button>
        <Button x:Name="cmdNewStore" HorizontalAlignment="Left" Margin="275,36,0,0" VerticalAlignment="Top" Width="39" Height="40">
            New Store
        </Button>
        <Button x:Name="cmdExecute" HorizontalAlignment="Left" Margin="355,36,0,0" VerticalAlignment="Top" Width="39" Height="40" ToolTip="Execute">
            Execute
        </Button>
        <Button x:Name="cmdCancel" HorizontalAlignment="Left" Margin="395,36,0,0" VerticalAlignment="Top" Width="39" Height="40" ToolTip="Cancel">
            Cancel
        </Button>
        <CheckBox x:Name="chOpenLastUsed" Content="Open Last Used File" Margin="456,48,0,0" VerticalAlignment="Top" Height="21" Width="147" HorizontalAlignment="Left"/>
        <ListView x:Name="lvStores" HorizontalAlignment="Left" Height="161" Margin="39,98,0,0" VerticalAlignment="Top" Width="466">
            <ListView.View>
                <GridView>
                    <GridView.Columns>
                        <GridViewColumn Header="ID" Width="Auto">
                            <GridViewColumn.CellTemplate>
                                <DataTemplate>
                                    <CheckBox Content="{Binding sID}" IsChecked="{Binding IsChecked}"/>
                                </DataTemplate>
                            </GridViewColumn.CellTemplate>
                        </GridViewColumn>
                        <GridViewColumn Header="Name" Width="Auto" DisplayMemberBinding="{Binding Path=sName}"/>
                        <GridViewColumn Header="Import File" Width="Auto" DisplayMemberBinding="{Binding Path=sImportFile}"/>
                    </GridView.Columns>
                </GridView>
            </ListView.View>
        </ListView>
        <Label x:Name="lblProgress" x:FieldModifier="public" Content="Status Text" HorizontalAlignment="Left" Margin="40,264,0,0" VerticalAlignment="Top" Width="465" BorderThickness="1" BorderBrush="#FF7C7C7C"/>
        <Button x:Name="ComDialog" HorizontalAlignment="Left" Margin="533,152,0,0" VerticalAlignment="Top" Width="48" Height="51">
            Dialog
        </Button>
    </Grid>
</Window>

MainWindow.xaml.cs:

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace CloneMacro
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        ObservableCollection<Store> stores;

        internal const string TESTFILE = @"C:\Temp\TESTFILE.pcf";
        public MainWindow()
        {
            // Leave this to show the UI
            InitializeComponent();

            lblProgress.Content = "";

            OpenFile( TESTFILE );
        }

        internal void OpenFile( string fileName )
        {
            stores = ReadCSV( TESTFILE );
            lvStores.ItemsSource = stores;
        }

        // Fill ListView
        public class Store : INotifyPropertyChanged
        {
            public event PropertyChangedEventHandler PropertyChanged;
            protected void OnPropertyChanged( string name )
            {
                PropertyChangedEventHandler handler = PropertyChanged;
                if ( handler != null )
                {
                    handler(this, new PropertyChangedEventArgs(name));
                }
            }

            public string sName { get; set; }
            public string sImportFile { get; set; }
            public string sID { get; set; }

            private bool isChecked;
            public bool IsChecked
            {
                get { return isChecked; }
                set
                {
                    if ( isChecked != value )
                    {
                        isChecked = value;
                        OnPropertyChanged("IsChecked");
                    }
                }
            }

            public Store( string id, string strName, string isChecked, string strImportFile )
            {
                sName = strName.Replace("\"", "");
                sImportFile = System.IO.Path.GetFileName(strImportFile);
                sID = id.Replace("\"", "");

                // Convert isChecked to boolean
                // Do convertion outside the constructor to avoid any exception within
                int iBool = Convert.ToInt32(isChecked);

                switch ( iBool )
                {
                    case 0: IsChecked = false; break;
                    case 1: IsChecked = true; break;
                    default: throw new InvalidOperationException("Third value in PCF file must be 0 or 1!");
                }
            }
        }

        public ObservableCollection<Store> ReadCSV( string fileName )
        {
            // Make sure the file extension is pcf
            string [] lines = File.ReadAllLines(
                System.IO.Path.ChangeExtension(fileName, ".pcf").Replace("\"", ""), Encoding.GetEncoding(65001));

            // lines.Select allows to project each line as a Store
            // This will give an IEnumerable<Store> back.
            var enumerable = lines.Select(line =>
            {
                string [] data = line.Split(',');
                // Return the store data
                // Return id, Name, isChecked (chkbox checked?), filename
                return new Store(data [0], data [1], data [2], fileName);
            });

            stores = new ObservableCollection<Store>();
            foreach ( var item in enumerable )
            {
                stores.Add( item );
            }
            return stores;
        }
        // End Fill ListView

        private void cmdDelete_Click( object sender, RoutedEventArgs e )
        {
            for ( int i = stores.Count - 1; i >= 0; i-- )
            {
                if ( stores[i].IsChecked )
                {
                    stores.RemoveAt( i );
                }
            }
        }

        private void cmdOpen_Click( object sender, RoutedEventArgs e )
        {
            OpenFile( TESTFILE );
        }

    }
}

TESTFILE.pcf:

"1662","BØRSTER",0
"1658","BØRSTER",1
"1669","A Møøse once bit my sister.",1

谢谢,它完美地工作了。你没有在XAML中改变任何东西吗?你介意在你的代码中加入一些注释吗?我会开始阅读MVVM的相关资料 :-) - Niclas
@Niclas 这是满足您需求的正确方式。由于我的回答不适用,我会将其删除。 - Tanveer Badar
@Niclas,我并没有真正改变你发布的代码中的XAML,只是删除了一些本地路径下的图像文件。至于代码中的注释...那需要额外付费。如果你有任何具体问题,随时问我就好了。 - Vince I

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接