前言
在使用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模式:
建立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
命令即可。
實現的效果如下所示:
實現了新增選擇框的效果,但是會覺得一個一個點太麻煩了,這時候就可以增加全選與取消全選的功能。
在選單中新增這兩項功能:
<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)模式中,使用命令而不是事件的主要原因包括以下幾點:
- 解耦:命令將使用者介面(View)與業務邏輯(ViewModel)分離,使得兩者之間的耦合度降低。這有助於提高程式碼的可維護性和可測試性。
- 可測試性:命令使得ViewModel中的邏輯更容易進行單元測試,因為命令可以獨立於UI進行測試,而事件則需要依賴於特定的UI元素。
- 一致性:命令提供了一種一致的方式來處理使用者操作,無論這些操作是透過按鈕點選、選單選擇還是其他UI元素觸發的。
- 資料繫結:命令可以透過資料繫結直接與UI元素關聯,這使得ViewModel可以控制UI元素的啟用和禁用狀態,而無需直接操作UI元素。
- 複用性:命令可以在不同的UI元素之間複用,而事件通常與特定的UI元素繫結,複用性較差。
透過使用命令,開發者可以更好地遵循MVVM模式的原則,實現更清晰、更模組化的程式碼結構。
以上就是透過這個Demo,我們可以簡單瞭解的一些內容,希望對你有所幫助。