删除绑定到 ObservableCollection 的 DataGrid 中的选定行 [英] Delete selected row in DataGrid bound to an ObservableCollection

查看:66
本文介绍了删除绑定到 ObservableCollection 的 DataGrid 中的选定行的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在用 WPF 编写一个应用程序,尝试使用 MVVM 设计模式(这对我来说是新的).我有一个 DataGrid 绑定到一个 ObservableCollection.

我正在努力实现的目标:

使用删除"按钮删除当前选定的 DataGrid 行.我已经尝试了大量的论坛帖子和视频来找到解决方案.该解决方案可能已经多次直视我的脸,但此时我比刚开始时更困惑.

如有任何帮助,我们将不胜感激.

ViewModel(更新了工作代码,感谢 EldHasp):

使用系统;使用 System.Collections.ObjectModel;使用 System.ComponentModel;使用 System.Data;使用 System.Data.SqlClient;使用 System.Windows;使用 GalaSoft.MvvmLight.CommandWpf;使用 Ridel.Hub.Model;命名空间 Ridel.Hub.ViewModel {公共类 LicenseHoldersViewModel :INotifyPropertyChanged {公共 ObservableCollection许可证持有人{得到;}= new ObservableCollection();公共 LicenseHoldersViewModel() {FillDataGridLicenseHolders();}私有无效 FillDataGridLicenseHolders() {尝试 {使用 (SqlConnection sqlCon = new(ConnectionString.connectionString))使用 (SqlCommand sqlCmd = new("select * from tblLicenseHolder", sqlCon))使用 (SqlDataAdapter sqlDaAd = new(sqlCmd))使用 (DataSet ds = new()) {sqlCon.Open();sqlDaAd.Fill(ds, tblLicenseHolder");//如果(licenseHolders == null)//licenseHolders = new ObservableCollection();foreach(ds.Tables[0].Rows 中的 DataRow dr){licenseHolders.Add(新的 LicenseHolders {ID = Convert.ToInt32(dr[0].ToString()),Foretaksnavn = dr[1].ToString(),Foretaksnummer = dr[2].ToString(),地址 = dr[3].ToString(),后编号 = (int)dr[4],Poststed = dr[5].ToString(),BIC = dr[6].ToString(),IBAN = dr[7].ToString(),//个人资料???Kontaktperson = dr[8].ToString(),Epost = dr[9].ToString(),Tlf = dr[10].ToString()});}}} 捕捉(异常前){MessageBox.Show(ex.Message, "Message", MessageBoxButton.OK, MessageBoxImage.Information);}}private RelayCommand_removeLicenseHoldersCommand;public RelayCommandRemoveLicenseHoldersCommand =>_removeLicenseHoldersCommand??(_removeLicenseHoldersCommand = new RelayCommand(RemoveLicenseHolderExecute, RemoveLicenseHolderCanExecute));private bool RemoveLicenseHolderCanExecute(LicenseHolders myLicenseHolder) {//检查集合中的可移除许可持有人返回 licenseHolders.Contains(myLicenseHolder);}私有无效 RemoveLicenseHolderExecute(LicenseHolders myLicenseHolder) {licenseHolders.Remove(myLicenseHolder);}公共事件 PropertyChangedEventHandler PropertyChanged;private void OnPropertyChanged(string propertyName) {if (PropertyChanged != null) {PropertyChanged(this, new PropertyChangedEventArgs(propertyName));}}}}

模型:

public class LicenseHolders {公共字符串示例 { 获取;放;}//更多相同...}

XAML:

代码隐藏:

public partial class Personell : Window {LicenseHoldersViewModel licenseHoldersViewModel;公共人员(){初始化组件();licenseHoldersViewModel = 新 LicenseHoldersViewModel();base.DataContext = licenseHoldersViewModel;}}

解决方案

由于您使用的是 MVVM,因此您不应该使用按钮事件 Click 处理程序来操作 ViewModel 数据.
为此,您必须在 ViewModel 中创建一个命令属性并将其绑定到按钮.
命令参数绑定到 SelectedItem(如果选择了 Single 模式并且只删除了一个选定的行),或者绑定到 SelectedItems(如果选择了扩展模式并删除了多行).

我不知道您使用的是 INotifyPropertyChanged 和 ICommand 的哪些实现.
因此,我正在根据我自己的 BaseInpc 和 RelayCommand 类.

 公共类 LicensesViewModel : BaseInpc{//ObservableCollection 类型的集合属性最好是 «ReadOnly».公共 ObservableCollection许可证持有人 { 得到;}= new ObservableCollection();public void FillDataGrid(){//--------------//--------------//--------------//如果(licenseHolders == null)//licenseHolders = new ObservableCollection();foreach(ds.Tables[0].Rows 中的 DataRow dr){LicensesHolders.Add(新的LicenseHolders{示例 = dr[0].ToString(),//更多相同...});}}}私有 RelayCommand _removeLicenseCommand;public RelayCommand RemoveLicenseCommand =>_removeLicenseCommand??(_removeLicenseCommand = new RelayCommand(RemoveLicenseExecute, RemoveLicenseCanExecute));private bool RemoveLicenseCanExecute(LicenseHolders license){//检查集合中的可移动许可证返回 LicensesHolders.Contains(license);}private void RemoveLicenseExecute(LicenseHolders license){//如有必要,将用于从数据库中删除许可证的代码放在方法的开头.//比如为此调用一些方法,如果删除成功则返回true.var 结果 = RemoveFromBD(license);//然后从集合中删除它.如果(结果)LicensesHolders.Remove(license);}private bool RemoveFromBD(LicenseHolders 许可证){抛出新的 NotImplementedException();}}

多选模式的一个实现选项:

 private RelayCommand _removeLicensesCommand;public RelayCommand RemoveLicensesCommand =>_removeLicensesCommand??(_removeLicensesCommand = new RelayCommand(RemoveLicensesExecute, RemoveLicensesCanExecute));private bool RemoveLicensesCanExecute(IList licenses){//检查集合中的可移动许可证return licenses.OfType().Any(license => LicensesHolders.Contains(license));}private void RemoveLicensesExecute(IList licenses){foreach(licenses.OfType().ToArray() 中的 var license){var 结果 = RemoveFromBD(license);如果(结果)LicensesHolders.Remove(license);}}

<块引用>

更新!(在尝试实施EldHasp"的解决方案时错误信息

错误是由于使用了 MVVMLight.
您最初没有表明您正在使用此软件包.
其中,RelayCommand 不是 RelayCommand 的基础.
因此,属性和变量的类型必须与使用的构造函数的类型相匹配.

这就是 MVVMLight 需要它的方式:

 private RelayCommand_removeLicenseHoldersCommand;public RelayCommandRemoveLicenseHolderCommand =>_removeLicenseHoldersCommand??(_removeLicenseHoldersCommand = new RelayCommand(RemoveLicenseHolderExecute, RemoveLicenseHolderCanExecute);

关于分配 DataContext 的其他建议.

XAML 设计器在设计时不处理在代码隐藏中分配它.因此,您在开发过程中看不到数据传输到 UI 元素时的外观.
这非常不方便.
要解决这个问题,需要设置Development Time DataContext.
但在简单的情况下,您只需要在 XAML 中设置 DataContext.

清除背后的代码:

public 部分类 Personell : Window{公共人员()=>初始化组件();}

添加到 XAML:

<local:LicenseHoldersViewModel/></Window.DataContext>

I'm writing an app in WPF, trying to use the MVVM-design pattern (which is new to me). I have a DataGrid bound to an ObservableCollection.

What I'm trying to achieve:

Delete the currently selected DataGrid-row using a 'Delete'-button. I've tried a plethora of forum-posts and videos to find a solution. The solution has probably stared me right in the face several times, but at this point I'm more confused than I was when I first started.

Any assistance would be appreciated.

ViewModel (updated with working code, thanks to EldHasp):

using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Data;
using System.Data.SqlClient;
using System.Windows;
using GalaSoft.MvvmLight.CommandWpf;
using Ridel.Hub.Model;

namespace Ridel.Hub.ViewModel {

    public class LicenseHoldersViewModel : INotifyPropertyChanged {

        public ObservableCollection<LicenseHolders> licenseHolders { get; }
            = new ObservableCollection<LicenseHolders>();
        

        public LicenseHoldersViewModel() {

            FillDataGridLicenseHolders();
        }

        private void FillDataGridLicenseHolders() {

            try {

                using (SqlConnection sqlCon = new(ConnectionString.connectionString))
                using (SqlCommand sqlCmd = new("select * from tblLicenseHolder", sqlCon))
                using (SqlDataAdapter sqlDaAd = new(sqlCmd))
                using (DataSet ds = new()) {

                    sqlCon.Open();
                    sqlDaAd.Fill(ds, "tblLicenseHolder");

                    //if (licenseHolders == null)
                        //licenseHolders = new ObservableCollection<LicenseHolders>();

                    foreach (DataRow dr in ds.Tables[0].Rows) {

                        licenseHolders.Add(new LicenseHolders {

                            ID = Convert.ToInt32(dr[0].ToString()),
                            Foretaksnavn = dr[1].ToString(),
                            Foretaksnummer = dr[2].ToString(),
                            Adresse = dr[3].ToString(),
                            Postnummer = (int)dr[4],
                            Poststed = dr[5].ToString(),
                            BIC = dr[6].ToString(),
                            IBAN = dr[7].ToString(),
                            //Profilbilde ???
                            Kontaktperson = dr[8].ToString(),
                            Epost = dr[9].ToString(),
                            Tlf = dr[10].ToString()
                        });
                    }
                }

            } catch (Exception ex) {

                MessageBox.Show(ex.Message, "Message", MessageBoxButton.OK, MessageBoxImage.Information);
            }
        }

        private RelayCommand<LicenseHolders> _removeLicenseHoldersCommand;

        public RelayCommand<LicenseHolders> RemoveLicenseHoldersCommand => _removeLicenseHoldersCommand
            ?? (_removeLicenseHoldersCommand = new RelayCommand<LicenseHolders>(RemoveLicenseHolderExecute, RemoveLicenseHolderCanExecute));

        private bool RemoveLicenseHolderCanExecute(LicenseHolders myLicenseHolder) {

            // Checking for the removeable licenseholder in the collection
            return licenseHolders.Contains(myLicenseHolder);
        }

        private void RemoveLicenseHolderExecute(LicenseHolders myLicenseHolder) {

                licenseHolders.Remove(myLicenseHolder);
        }

        public event PropertyChangedEventHandler PropertyChanged;

        private void OnPropertyChanged(string propertyName) {

            if (PropertyChanged != null) {

                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
}

Model:

public class LicenseHolders {

    public string example { get; set; }
    // more of the same...
}

XAML:

<Button Style="{DynamicResource ButtonWithRoundCornersGreen}" 
    FontSize="22" x:Name="btnDelete" Content="Delete" 
    Width="187" Height="47" Background="#48bb88" Foreground="White" 
    Canvas.Left="547" Canvas.Top="668" IsEnabled="False" Click="btnDelete_Click"/>

<DataGrid             
    x:Name="dgLicenseHolder"
    CanUserAddRows="False"
    ItemsSource="{Binding licenseHolders}"
    Height="557" 
    Width="505" 
    ColumnWidth="*"
    Canvas.Top="158" 
    FontSize="20"
    IsReadOnly="True"
    SelectionMode="Single"
    AutoGenerateColumns="False"
    CanUserDeleteRows="False"
    SelectionChanged="dgLicenseHolder_SelectionChanged" Canvas.Left="31" >

    <DataGrid.Columns>
        <DataGridTextColumn Header="Example" Binding="{Binding Path='Example'}" IsReadOnly="True" Visibility="Collapsed"/>
        // more of the same..
    </DataGrid.Columns>            
</DataGrid>

Code-behind:

public partial class Personell : Window {

    LicenseHoldersViewModel licenseHoldersViewModel;

    public Personell() {

        InitializeComponent();

        licenseHoldersViewModel = new LicenseHoldersViewModel();
        base.DataContext = licenseHoldersViewModel;
    }
}

解决方案

Since you are using MVVM, you should not use button event Click handler to manipulate ViewModel data.
To do this, you must create a command-property in the ViewModel and bind it to the buttons.
The command parameter bind either to SelectedItem (if the Single mode is selected and only one selected row is deleted), or to SelectedItems (if the Extended mode is selected and multiple rows are deleted).

I don't know which implementations of INotifyPropertyChanged and ICommand you are using.
Therefore, I am writing an example based on my own implementation of the BaseInpc and RelayCommand classes.

    public class LicensesViewModel : BaseInpc
    {
        // A collection-property of the ObservableCollection type is best done «ReadOnly».
        public ObservableCollection<LicenseHolders> LicensesHolders { get; }
            = new ObservableCollection<LicenseHolders>();
        public void FillDataGrid()
        {

            //---------------
            //---------------
            //---------------

                //if (licenseHolders == null)
                //    licenseHolders = new ObservableCollection<LicenseHolders>();

                foreach (DataRow dr in ds.Tables[0].Rows)
                {

                    LicensesHolders.Add(new LicenseHolders
                    {

                        example = dr[0].ToString(),
                        // more of the same...

                    });
                }
            }
        }


        private RelayCommand _removeLicenseCommand;
        public RelayCommand RemoveLicenseCommand => _removeLicenseCommand
            ?? (_removeLicenseCommand = new RelayCommand<LicenseHolders>(RemoveLicenseExecute, RemoveLicenseCanExecute));

        private bool RemoveLicenseCanExecute(LicenseHolders license)
        {
            // Checking for the Removable License in the Collection
            return LicensesHolders.Contains(license);
        }

        private void RemoveLicenseExecute(LicenseHolders license)
        {
            // If necessary, the code for removing the license from the database is placed at the beginning of the method.
            // For example, calling some method for this, returning true if the deletion is successful.
            var result = RemoveFromBD(license);

            //It is then removed from the collection.
            if (result)
                LicensesHolders.Remove(license);
        }

        private bool RemoveFromBD(LicenseHolders license)
        {
            throw new NotImplementedException();
        }
    }

<Button Style="{DynamicResource ButtonWithRoundCornersGreen}" 
    FontSize="22" x:Name="btnDelete" Content="Delete" 
    Width="187" Height="47" Background="#48bb88" Foreground="White" 
    Canvas.Left="547" Canvas.Top="668" IsEnabled="False"
    Command="{Binding RemoveLicenseCommand}"
    CommandParameter="{Binding SelectedItem, ElementName=dgLicenseHolder}"/>

An implementation option for the multi-selection mode:

        private RelayCommand _removeLicensesCommand;
        public RelayCommand RemoveLicensesCommand => _removeLicensesCommand
            ?? (_removeLicensesCommand = new RelayCommand<IList>(RemoveLicensesExecute, RemoveLicensesCanExecute));

        private bool RemoveLicensesCanExecute(IList licenses)
        {
            // Checking for the Removable License in the Collection
            return licenses.OfType<LicenseHolders>().Any(license => LicensesHolders.Contains(license));
        }

        private void RemoveLicensesExecute(IList licenses)
        {
            foreach (var license in licenses.OfType<LicenseHolders>().ToArray())
            {
                var result = RemoveFromBD(license);

                if (result)
                    LicensesHolders.Remove(license);
            }
        }

<Button Style="{DynamicResource ButtonWithRoundCornersGreen}" 
    FontSize="22" x:Name="btnDelete" Content="Delete" 
    Width="187" Height="47" Background="#48bb88" Foreground="White" 
    Canvas.Left="547" Canvas.Top="668" IsEnabled="False"
    Command="{Binding RemoveLicensesCommand}"
    CommandParameter="{Binding SelectedItems, ElementName=dgLicenseHolder}"/>

Update! (when trying to implement the solution of 'EldHasp' Error message

The error is due to using MVVMLight.
You initially did not indicate that you are using this package.
In it, the RelayCommand is not the base for the RelayCommand<T>.
Therefore, the type of the property and variable must match the type of the used constructor.

This is how you need it for MVVMLight:

    private RelayCommand<LicenseHolders> _removeLicenseHoldersCommand;

    public RelayCommand<LicenseHolders> RemoveLicenseHolderCommand => _removeLicenseHoldersCommand
        ?? (_removeLicenseHoldersCommand = new RelayCommand<LicenseHolders>(RemoveLicenseHolderExecute, RemoveLicenseHolderCanExecute);

Additional advice on assigning DataContext.

Assigning it in Code Behind is not handled by XAML Designer at design time. Therefore, you do not see during development how your UI elements will look when data is transferred to them.
This is extremely inconvenient.
To solve this, you need to set the Development Time DataContext.
But in simple cases, you just need to set the DataContext in XAML.

Clear Code Behind:

public partial class Personell : Window 
{
    public Personell() => InitializeComponent();
}

Add to XAML:

<Window.DataContext>
  <local:LicenseHoldersViewModel/>
</Window.DataContext>

这篇关于删除绑定到 ObservableCollection 的 DataGrid 中的选定行的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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