WPF/C#:在DataGrid中顯示選擇框

mingupupup發表於2024-06-20

前言

在使用WPF的過程中可能會經常遇到在DataGrid的最前或者最後新增一列選擇框的需求,今天跟大家分享一下,在自己的專案中是如何實現的。

整體實現效果如下:

如果對此感興趣,可以接下來看具體實現部分。

實踐

假設資料庫中的模型如下:

  public class Person
  {
      public int Id { get; set; }
      public string? Name { get; set; } 
      public string? Home { get; set; }
  }

但是要實現這個需求,需要為模型增加一個bool型別的屬性表示是否選中,但是這個屬性只是為了在介面上顯示,根本就不需要存入資料庫,也就是說資料庫中的模型與MVVM模式中的模型可能會有一些不同,這是很常見的一種情況,因為為了使用MVVM模式,模型也要實現INotifyPropertyChanged,如果資料庫模型與MVVM中的模型是同一個的話,會讓模型結構變得不直觀。

這時候可以建立一個PersonViewModel類,如下所示:

 public partial class PersonViewModel : ObservableObject
 {
     [ObservableProperty]
     private int id;

     [ObservableProperty]
     private string? name;

     [ObservableProperty]
     private string? home;

     [ObservableProperty]
     private bool isSelected;

     public PersonViewModel(Person person)
     {
         this.Id = person.Id;
         this.Name = person.Name;
         this.Home = person.Home;
         this.IsSelected = false;
     }
 }

這裡使用了CommunityToolkit.Mvvm包簡化實現MVVM模式:

image-20240620085658595

建立ViewModel:

public partial class DataGridDemoViewModel : ObservableObject
{
    [ObservableProperty]
    private ObservableCollection<PersonViewModel> people;

    public DataGridDemoViewModel()
    {
        People = new ObservableCollection<PersonViewModel>();
    }

    [RelayCommand]
    private void GetPeople()
    {
        var people = GetPeopleFromDataBase();
        var peopleViewModels = people.Select(n => new PersonViewModel(n));
        People.AddRange(peopleViewModels);
    }
  
    List<Person> GetPeopleFromDataBase()
    {
        var People = new List<Person>()
        {
            new Person { Id = 1, Name = "小一", Home = "北京" },
            new Person { Id = 2, Name = "小二", Home = "上海" },
            new Person { Id = 3, Name = "王五", Home = "廣州" },
            new Person { Id = 4, Name = "小紅", Home = "深圳" },
            new Person { Id = 5, Name = "小綠", Home = "杭州" },
            new Person { Id = 6, Name = "小剛", Home = "南京" },
        };
        return People;
    }
}

首先使用GetPeopleFromDataBase模擬從資料庫中獲取型別為List<Person>的資料:

 List<Person> GetPeopleFromDataBase()
    {
        var People = new List<Person>()
        {
            new Person { Id = 1, Name = "小一", Home = "北京" },
            new Person { Id = 2, Name = "小二", Home = "上海" },
            new Person { Id = 3, Name = "王五", Home = "廣州" },
            new Person { Id = 4, Name = "小紅", Home = "深圳" },
            new Person { Id = 5, Name = "小綠", Home = "杭州" },
            new Person { Id = 6, Name = "小剛", Home = "南京" },
        };
        return People;
    }

為了在頁面上實現增減通知,一般在ViewModel中使用的是ObservableCollection<T>而不是List<T>,因此需要一個轉換:

 var people = GetPeopleFromDataBase();
 var peopleViewModels = people.Select(n => new PersonViewModel(n));

由於ObservableCollection<T>沒有實現AddRange方法,可以使用擴充套件方法寫一下。

擴充套件方法(Extension Methods)是C#中的一種特殊方法,允許向現有型別新增新方法,而無需修改原始型別的程式碼或建立派生型別。擴充套件方法必須在靜態類中定義,並且第一個引數前帶有this關鍵字,指定要擴充套件的型別。這使得可以為內建型別或第三方庫中的型別新增自定義方法,從而提高程式碼的可讀性和重用性。擴充套件方法的使用方式與例項方法相同,可以透過例項直接呼叫。

新增的擴充套件方法如下所示:

  public static class ObservableCollectionExtensions
  {
      public static void AddRange<T>(this ObservableCollection<T> collection, IEnumerable<T> range)
      {
          foreach (var item in range)
          {
              collection.Add(item);
          }
      }
  }

現在來建立View:

<StackPanel>
    <StackPanel>
        <TabControl Margin="0,8,0,0">
            <TabItem>
                <TabItem.Header>
                    <StackPanel Orientation="Horizontal">
                        <ui:SymbolIcon Margin="0,0,6,0" Symbol="Home24" />
                        <TextBlock Text="DataGrid新增選擇框Demo" />
                    </StackPanel>
                </TabItem.Header>
                <StackPanel>
                    <StackPanel Orientation="Horizontal">
                        <Menu FontSize="14">
                            <ui:MenuItem Header="操作選單" Icon="{ui:SymbolIcon ToolBox28}">
                                <ui:MenuItem Header="獲取People集合" 
                                      Icon="{ui:SymbolIcon Search16}"
                                      Command="{Binding GetPeopleCommand}"/>                              
                            </ui:MenuItem>
                            <Separator />
                        </Menu>
                    </StackPanel>
                    <StackPanel>
                        <ui:DataGrid Height="400" ItemsSource="{Binding People}">
                        </ui:DataGrid>
                    </StackPanel>
                </StackPanel>
            </TabItem>
        </TabControl>
    </StackPanel>
</StackPanel>
 <ui:DataGrid Height="400" ItemsSource="{Binding People}">
                        </ui:DataGrid>

直接將ItemsSource繫結至People集合就行了。

<ui:MenuItem Header="獲取People集合" 
             Icon="{ui:SymbolIcon Search16}"
             Command="{Binding GetPeopleCommand}"/>    

繫結GetPeopleCommand命令即可。

實現的效果如下所示:

image-20240620095433218

實現了新增選擇框的效果,但是會覺得一個一個點太麻煩了,這時候就可以增加全選與取消全選的功能。

在選單中新增這兩項功能:

 <Menu FontSize="14">
     <ui:MenuItem Header="操作選單" Icon="{ui:SymbolIcon ToolBox28}">
         <ui:MenuItem Header="獲取People集合" 
               Icon="{ui:SymbolIcon Search16}"
               Command="{Binding GetPeopleCommand}"/>
         <ui:MenuItem Header="全部選中" 
               Icon="{ui:SymbolIcon SelectAllOn24}"
               Command="{Binding SelectAllPeopleCommand}"/>
         <ui:MenuItem Header="取消全部選中" 
               Icon="{ui:SymbolIcon SelectAllOff24}"
               Command="{Binding UnselectAllPeopleCommand}"/>
     </ui:MenuItem>
     <Separator />
 </Menu>

在ViewModel中新增這兩個命令:

 [RelayCommand]
 private void SelectAllPeople()
 {
     foreach (var person in People)
     {
         person.IsSelected = true;
     }
 }

 [RelayCommand]
 private void UnselectAllPeople()
 {
     foreach (var person in People)
     {
         person.IsSelected = false;
     }
 }

現在在看看看效果:

回顧

透過這個Demo,總體上學習了MVVM模式的簡單使用。MVVM中的Model不一定就是資料庫的Model可以自己根據頁面顯示的需要增減屬性。為了簡化MVVM模式的實現,可以藉助一些MVVM庫,本文使用的是CommunityToolkit.Mvvm,使用了ObservableObject基類、[ObservableProperty]特性與[RelayCommand]特性。當發現型別缺少我們需要的方法時,可以使用擴充套件方法寫一個。為了減少和頁面耦合,MVVM模式中使用命令而不是事件。以下是大模型的回答,可以簡單參考一下:

在WPF的MVVM(Model-View-ViewModel)模式中,使用命令而不是事件的主要原因包括以下幾點:

  1. 解耦:命令將使用者介面(View)與業務邏輯(ViewModel)分離,使得兩者之間的耦合度降低。這有助於提高程式碼的可維護性和可測試性。
  2. 可測試性:命令使得ViewModel中的邏輯更容易進行單元測試,因為命令可以獨立於UI進行測試,而事件則需要依賴於特定的UI元素。
  3. 一致性:命令提供了一種一致的方式來處理使用者操作,無論這些操作是透過按鈕點選、選單選擇還是其他UI元素觸發的。
  4. 資料繫結:命令可以透過資料繫結直接與UI元素關聯,這使得ViewModel可以控制UI元素的啟用和禁用狀態,而無需直接操作UI元素。
  5. 複用性:命令可以在不同的UI元素之間複用,而事件通常與特定的UI元素繫結,複用性較差。

透過使用命令,開發者可以更好地遵循MVVM模式的原則,實現更清晰、更模組化的程式碼結構。

以上就是透過這個Demo,我們可以簡單瞭解的一些內容,希望對你有所幫助。

相關文章