11-10 【重構】創建視圖模型,顯示客戶列表
正式進入 MVVM 架構的代碼實戰。在之前的課程中, Model 和 View 這部分的代碼重構實際上已經完成了。 Model 就是在 Models 文件夾中看到的兩個文件, Customer 和 Appointment。 而 View 則是所有與 UI相關的 xaml 頁面。接下來,我們將會把主要精力集中在 ViewModel 視圖模型的實現上,并通過視圖模型來綁定 UI 界面與數據模型。
右擊“WPF_CMS”項目,新建一文件夾 ViewModels 。根據 MVVM 的設計原則,理論上,每一個頁面都會對應一個 ViewModel。 而我們只有這個 MainWindow.xaml 頁面,所以我們給這個 MainWindow 創建視圖模型,新建文件 MainViewModel.cs。
左側導航欄的客戶列表,對應 Customer 客戶模型,封裝為 List<Customer>類型并初始化為空列表。
接下來我們需要從數據庫中獲得數據,創建方法, LoadCustomers,為了獲取到更多的數據,我們可以使用一個 Include 語句,把預約列表也一起裝進來。
--\ViewModels\MainViewModel.cs
public List<Customer> Customers { get; set; } = new();
public void LoadCustomers()
{
? ? using (var db = new AppDbContext())
? ? {
? ? ? ? Customers = db.Customers.Include(c => c.Appointments).ToList();
? ? }
}
現在我們就可以在主界面中使用這個視圖模型了。
首先聲明一個私有的視圖模型 _viewModel,類型為 MainViewModel.。
第二步,在 MainWindow 的構造方法中,初始化這個視圖模型。
第三步,向這個視圖模型中加載數據。調用 LoadCustomers 這個方法,現在客戶數據就從數據庫中加載到客戶列表了。
最后一步也是最重要的,我們需要把視圖模型綁定在頁面的數據上下文中,使用 DataContext, 讓它等于 _viewModel。
--\MainWindow.xaml.cs
public partial class MainWindow : Window
{
? ? private MainViewModel _viewModel;
? ? public MainWindow()
? ? {
? ? ? ? InitializeComponent();
? ? ? ? _viewModel = new MainViewModel();
? ? ? ? _viewModel.LoadCustomers();
? ? ? ? DataContext = _viewModel;
? ? }
? ? ……
}
MainWindow.xaml 頁面中,再進行相應的綁定
--\MainWindow.xaml
<ListView ItemsSource="{Binding Customers, Mode=OneWay}" DisplayMemberPath="Name" />
11-11 【實戰】雙向綁定,選擇客戶
接下來,我們還需要選擇客戶,并且在客戶的詳情頁面中顯示,甚至更改當前的客戶信息。對于選擇客戶的過程我們同樣可以通過視圖模型來完成,但這次我們不但需要顯示當前的客戶選擇,還需要通過 UI 的變化來操縱選擇數據。所以對于客戶選擇來說,視圖模型的綁定是雙向的。
--\ViewModels\MainViewModel.cs
添加一個新的數據,用來對應當前的選擇客戶
private Customer _selectedCustomer;
public Customer SelectedCustomer
{
? ? get => _selectedCustomer;?
? ? set
? ? {
? ? ? ? if (value != _selectedCustomer)
? ? ? ? {
? ? ? ? ? ? _selectedCustomer = value;
? ? ? ? }
? ? }
}
OK,視圖模型我們處理完畢,其實就是配置了選擇客戶的數據讀取和操作的方法。
--\MainWindow.xaml
接下來回到 xaml 文件,給客戶列表 ListView 綁定一個 SelectedItem 屬性。
<ListView ItemsSource="{Binding Customers, Mode=OneWay}" DisplayMemberPath="Name" SelectedItem="{Binding SelectedCustomer, Mode=TwoWay}" />
接下來我們還需要更新客戶的詳情,以及客戶的預約列表的 UI。
這里我們不僅需要顯示客戶的數據,還需要處理客戶數據的更新。所以綁定同樣也是雙向的,Mode=TwoWay。
我們希望在更改客戶名稱的時候,客戶名稱可以同步反映在客戶列表中,使用 UpdateSourceTrigger=PropertyChanged。
<StackPanel Grid.Row="1" Grid.Column="1">
? ? <TextBlock Text="姓名" Margin="10 10 10 0"/>
? ? <TextBox Margin="10" Text="{Binding SelectedCustomer.Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
? ? <TextBlock Text="身份證" Margin="10 10 10 0"/>
? ? <TextBox Margin="10" Text="{Binding SelectedCustomer.IdNnumber, Mode=TwoWay}" />
? ? <TextBlock Text="地址" Margin="10 10 10 0"/>
? ? <TextBox Margin="10" Text="{Binding SelectedCustomer.Address, Mode=TwoWay}" />
? ? <Button Content="保存" Margin="10 10 10 30" VerticalAlignment="Bottom" HorizontalAlignment="Left" />
</StackPanel>
<StackPanel Grid.Row="1" Grid.Column="2">
? ? <ListView ItemsSource="{Binding SelectedCustomer.Appointments, Mode=TwoWay}" />
? ? <TextBlock Text="添加新預約" />
? ? <DatePicker Margin="10" />
? ? <Button Content="預約" />
</StackPanel>
?