简体   繁体   English

删除绑定到 ObservableCollection 的 DataGrid 中的选定行

[英]Delete selected row in DataGrid bound to an ObservableCollection

I'm writing an app in WPF, trying to use the MVVM-design pattern (which is new to me).我正在 WPF 中编写一个应用程序,尝试使用 MVVM 设计模式(这对我来说是新的)。 I have a DataGrid bound to an ObservableCollection .我有一个绑定到ObservableCollectionDataGrid

What I'm trying to achieve:我想要达到的目标:

Delete the currently selected DataGrid-row using a 'Delete'-button.使用“删除”按钮删除当前选定的 DataGrid 行。 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): ViewModel(使用工作代码更新,感谢 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: Model:

public class LicenseHolders {

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

XAML: 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.由于您使用的是 MVVM,因此不应使用按钮事件 Click 处理程序来操作 ViewModel 数据。
To do this, you must create a command-property in the ViewModel and bind it to the buttons.为此,您必须在 ViewModel 中创建一个命令属性并将其绑定到按钮。
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).命令参数绑定到 SelectedItem(如果选择了 Single 模式并且只删除了一个选定的行),或者绑定到 SelectedItems(如果选择了 Extended 模式并且删除了多行)。

I don't know which implementations of INotifyPropertyChanged and ICommand you are using.我不知道您使用的是 INotifyPropertyChanged 和 ICommand 的哪些实现。
Therefore, I am writing an example based on my own implementation of the BaseInpc and RelayCommand classes .因此,我正在编写一个基于我自己实现的 BaseInpc 和 RelayCommand 类的示例

    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 (尝试实施“EldHasp”错误消息的解决方案时

The error is due to using MVVMLight.该错误是由于使用了 MVVMLight。
You initially did not indicate that you are using this package.您最初并未表明您正在使用此 package。
In it, the RelayCommand is not the base for the RelayCommand<T> .其中, RelayCommand不是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:这就是 MVVMLight 需要它的方式:

    private RelayCommand<LicenseHolders> _removeLicenseHoldersCommand;

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

Additional advice on assigning DataContext.有关分配 DataContext 的其他建议。

Assigning it in Code Behind is not handled by XAML Designer at design time. XAML Designer 在设计时不处理在代码隐藏中分配它。 Therefore, you do not see during development how your UI elements will look when data is transferred to them.因此,在开发过程中,您看不到数据传输到 UI 元素时的外观。
This is extremely inconvenient.这是非常不方便的。
To solve this, you need to set the Development Time DataContext.要解决这个问题,您需要设置 Development Time DataContext。
But in simple cases, you just need to set the DataContext in XAML.但在简单的情况下,只需要在 XAML 中设置 DataContext 即可。

Clear Code Behind:清除代码背后:

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

Add to XAML:添加到 XAML:

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

You can use Prism.您可以使用棱镜。 Intall package Prism.Core then Install-Package Microsoft.Xaml.Behaviors.Wpf -Version 1.1.31 packages in your project, in your xaml declare namespace as - xmlns:i="http://schemas.microsoft.com/xaml/behaviors" Intall package Prism.Core then Install-Package Microsoft.Xaml.Behaviors.Wpf -Version 1.1.31 packages in your project, in your xaml declare namespace as - xmlns:i="http://schemas.microsoft.com/xaml/behaviors"

<Button Content="Button">
        <i:Interaction.Triggers>
            <i:EventTrigger EventName="Click">
                <i:InvokeCommandAction Command="{Binding MyCommand }" 
                            CommandParameter="{Binding PassRow/IDHere}" />
            </i:EventTrigger>
        </i:Interaction.Triggers>
    </Button>

In Your ViewModel -在您的视图模型中 -

public class ViewModel
{
    public ICommand MyCommand { get; set; }

    public ViewModel()
    {
        MyCommand = new DelegateCommand<object>(OnRowDeleted);
    }

    public void OnRowDeleted(object o)
    {
        // remove the value from your observablecollection here using LINQ
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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