一、Entity Framework Core
特點:【跨平臺】,【建?!浚静樵?、更改、保存】,【并發(fā)】,【事務(wù)】,【緩存】,【數(shù)據(jù)遷移】
EF的組件
二、重構(gòu):構(gòu)建數(shù)據(jù)模型
項目延續(xù)C#學習(九)的 項目代碼,以此基礎(chǔ)進行進一步重構(gòu)
所需的NuGet包如下:
逆向數(shù)據(jù)庫獲得數(shù)據(jù)模型(Model)
首先在根目錄下創(chuàng)建Models文件夾,然后使用Tools->NuGet包管理器->程序包管理器控制臺
輸入指令
Scaffold-DbContext "自己的數(shù)據(jù)庫連接字符串" Microsoft.EntityFrameworkCore.Sqlserver -OutputDir Models -Context AppDbContext
處理完成后,就可以在Models文件夾中看到通過逆向數(shù)據(jù)庫構(gòu)建的數(shù)據(jù)模型啦!
三、OMR數(shù)據(jù)管理
使用Entity Framework 取代SQL語句
使用ORM來自動生成SQL語句,通過數(shù)據(jù)庫的映射框架獲取數(shù)據(jù)模型,通過模型框架的鏈式結(jié)構(gòu)來處理數(shù)據(jù),可以使業(yè)務(wù)實現(xiàn)在代碼中,而不是實現(xiàn)在SQL語句中。因此對于程序員來說,使用對象的鏈式結(jié)構(gòu)更加符合面向?qū)ο蟮木幊汤砟睢?/p>
通過數(shù)據(jù)模型向UI傳遞和綁定數(shù)據(jù)
代碼改進后如下:
MainWindow.xaml.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
ShowCustomers();
}
//訪問數(shù)據(jù)庫
private void ShowCustomers()
{
try
{
using(var db = new AppDbContext())
{
var customers = db.Customers.ToList();
customerList.DisplayMemberPath = "Name";
customerList.SelectedValuePath = "Id";
customerList.ItemsSource = customers;
}
}catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
private void customerList_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
try
{
Customer selectedItem = customerList.SelectedItem as Customer;
if (selectedItem == null)
{
appointmentList.ItemsSource = null;
return;
}
NameTextBox.Text = selectedItem.Name;
IdTextBox.Text = selectedItem.IdNumber;
AddressTextBox.Text = selectedItem.Address;
using(var db = new AppDbContext())
{
var customerId = customerList.SelectedValue;
var appointment = db.Appointments
.Where(a => a.CustomerId == (int)customerId)
.ToList();
appointmentList.DisplayMemberPath = "Time";
appointmentList.SelectedValuePath = "Id";
appointmentList.ItemsSource = appointment;
}
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
private void CancelAppointment_Click(object sender, RoutedEventArgs e)
{
try
{
var appointmentId = appointmentList.SelectedValue;
using (var db = new AppDbContext())
{
var appointmentToRemove = db.Appointments
.Where(a => a.Id == (int)appointmentId)
.FirstOrDefault();//因為Id主鍵是唯一選擇,因此這里過濾后不再是列表,而是獨立的對象,因此使用FirstOrDefault
db.Appointments.Remove(appointmentToRemove);
db.SaveChanges();
}
MessageBox.Show("取消預(yù)約成功!");
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
finally
{
customerList_SelectionChanged(null, null);
}
}
private void DeleteCustomer_Click(object sender, RoutedEventArgs e)
{
try
{
var customerId = customerList.SelectedValue;
using(var db = new AppDbContext())
{
//使用Entity Framework后,不需要進行兩次數(shù)據(jù)庫操作,只需要使用Include方法
var customerToRemove = db.Customers
//.Include(c => c.Appointments)
.Where(c => c.Id == (int)customerId)
.Include(c => c.Appointments)
.FirstOrDefault();
db.Customers.Remove(customerToRemove);
db.SaveChanges();
}
MessageBox.Show("刪除用戶成功!");
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
finally
{
ShowCustomers();
customerList_SelectionChanged(null, null);
}
}
private void AddCustomer_Click(object sender, RoutedEventArgs e)
{
try
{
using (var db = new AppDbContext())
{
var customer = new Customer()
{
Name = NameTextBox.Text,
IdNumber = IdTextBox.Text,
Address = AddressTextBox.Text
};
db.Customers.Add(customer);
db.SaveChanges();
}
MessageBox.Show("添加用戶信息成功!");
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
finally
{
ShowCustomers();
}
}
private void AddAppointment_Click(object sender, RoutedEventArgs e)
{
try
{
using(var db = new AppDbContext())
{
var appointment = new Appointment()
{
Time = DateTime.Parse(AppointmentDatePicker.Text),
CustomerId = (int)customerList.SelectedValue
};
db.Appointments.Add(appointment);
db.SaveChanges();
}
MessageBox.Show("預(yù)約成功!");
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
finally
{
customerList_SelectionChanged(null, null);
}
}
private void UpdateCustomer_Click(object sender, RoutedEventArgs e)
{
try
{
using (var db = new AppDbContext())
{
var customer = db.Customers.Where(c => c.Id == (int)customerList.SelectedValue).FirstOrDefault();
customer.Name = NameTextBox.Text.Trim();
customer.IdNumber = IdTextBox.Text.Trim();
customer.Address = AddressTextBox.Text.Trim();
db.SaveChanges();
}
MessageBox.Show("預(yù)約成功!");
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
finally
{
ShowCustomers();
}
}
}
易報錯點提示
1.聯(lián)級刪除時,會出現(xiàn)appointment表的customerId為空情況,因此需要再customerList_SelectionChanged方法里進行一個判空處理;
2.使用聯(lián)級刪除,使用Entity Framework后,不需要進行兩次數(shù)據(jù)庫操作,只需要使用Include方法,但需要引入using Microsoft.EntityFrameworkCore;
3.注意生成的AppDbContext.cs文件中,OnModelCreating方法中的OnDelete的DeleteBehavior,使用Cascade方法,Automatically deletes dependent entities when the principal is deleted or the
relationship to the principal is severed, but creates a non-cascading foreign key constraint in the database..OnDelete(DeleteBehavior.Cascade)
四、布局重構(gòu)
首先對于原來丑陋的展示頁面進行重新布局,構(gòu)建我們的基礎(chǔ)布局框架
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="240"/>
<ColumnDefinition Width="280"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<!--header-->
<Border Grid.ColumnSpan="3" Background="#9a0070">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<!--隨意添加一個圖片,圖片放置在根目錄文件夾下的Images文件中-->
<Image Height="90" Margin="5" Source="/Images/logo.jpg"/>
<TextBlock Text="WPF客戶管理系統(tǒng)" FontSize="40" VerticalAlignment="Center" Foreground="#ffffff"/>
</StackPanel>
</Border>
<StackPanel Grid.Row="1" Grid.Column="0">
<Button Content="添加客戶"/>
<ListView/>
</StackPanel>
<StackPanel Grid.Row="1" Grid.Column="1">
<TextBlock Text="姓名" Margin="10 10 10 0"/>
<TextBox Margin="10"/>
<TextBlock Text="身份證號" Margin="10 10 10 0"/>
<TextBox Margin="10"/>
<TextBlock Text="地址" Margin="10 10 10 0"/>
<TextBox Margin="10"/>
<Button Content="保存" Margin="10 10 10 30" VerticalAlignment="Bottom" HorizontalAlignment="Left"/>
</StackPanel>
<StackPanel Grid.Row="1" Grid.Column="2">
<ListView/>
<TextBlock Text="添加新預(yù)約"/>
<DatePicker Margin="10"/>
<Button Content="預(yù)約"/>
</StackPanel>
</Grid>
完成布局的組件化控制
首先在根目錄下創(chuàng)建文件夾“Control”,在文件夾中新建項“用戶控件(WPF)”命名HeaderControl.xaml,將MainWindow.xaml中的header代碼轉(zhuǎn)移至此
HeaderControl.xaml
<Border Grid.ColumnSpan="3" Background="#9a0070">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<Image Height="90" Margin="5" Source="/Images/logo.jpg"/>
<TextBlock Text="WPF客戶管理系統(tǒng)" FontSize="40" VerticalAlignment="Center" Foreground="#ffffff"/>
</StackPanel>
</Border>
MainWindow.xaml中對應(yīng)部分刪除,替換為
<!--header-->
<controls:HeaderControl Grid.ColumnSpan="3"/>
五、MVVM架構(gòu)
MVVM指?Model(模型) View(視圖) ViewModel(視圖模型)
直接使用View也可以進行項目的開發(fā),正如上一篇文章所示例的,但是直接使用View訪問數(shù)據(jù)庫,無法完成數(shù)據(jù)的隔離,無法進行復(fù)雜的業(yè)務(wù)開發(fā),甚至無法可持續(xù)的維護系統(tǒng),因此必須進行業(yè)務(wù)與數(shù)據(jù)的隔離,以及業(yè)務(wù)與界面的隔離。因此對于業(yè)務(wù)進行分離后,就得到了視圖模型,視圖模型可以全部或者部分使用模型的字段,模型的字段通過映射的方式向視圖模型提供業(yè)務(wù)的支持,而視圖模型與視圖則雙向綁定,不僅可以讓用戶看到數(shù)據(jù),還可以通過UI交互操作數(shù)據(jù),而視圖模型作為業(yè)務(wù)的載體,也會承擔與數(shù)據(jù)庫的溝通工作,視圖模型會處理一切與UI的交互行為。
MVVM的優(yōu)點
- [ 兼容MVC架構(gòu) ]
- [ 業(yè)務(wù)與UI邏輯徹底分開,方便測試 ]
- [ 方便維護 ]
MVVM的缺點
- [ 代碼量增加 ]
- [ 對象調(diào)用復(fù)雜度增加 ]
MVVM項目代碼重構(gòu)
首先,在根目錄下創(chuàng)建文件夾ViewModels,新建項目MainViewModel,CustomerViewModel,AppointmentViewModel
代碼如下:
MainWindow.xaml
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="240"/>
<ColumnDefinition Width="280"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<!--header-->
<controls:HeaderControl Grid.ColumnSpan="3"/>
<StackPanel Grid.Row="1" Grid.Column="0">
<Button Content="添加客戶" Click="ClearSelectedCustomer_Click"/>
<ListView ItemsSource="{Binding Customers, Mode=OneWay}" DisplayMemberPath="Name" SelectedItem="{Binding SelectedCustomer, Mode=TwoWay}"/>
</StackPanel>
<StackPanel Grid.Row="1" Grid.Column="1">
<TextBlock Text="姓名" Margin="10 10 10 0"/>
<TextBox x:Name="NameTextBox" Margin="10" Text="{Binding SelectedCustomer.Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
<TextBlock Text="身份證號" Margin="10 10 10 0"/>
<TextBox Name="IdTextBox" Margin="10" Text="{Binding SelectedCustomer.IdNumber, Mode=TwoWay}"/>
<TextBlock Text="地址" Margin="10 10 10 0"/>
<TextBox x:Name="AddressTextBox" Margin="10" Text="{Binding SelectedCustomer.Address, Mode=TwoWay}"/>
<Button Content="保存" Margin="10 10 10 30" VerticalAlignment="Bottom" HorizontalAlignment="Left" Click="SaveCustomer_Click"/>
</StackPanel>
<StackPanel Grid.Row="1" Grid.Column="2">
<ListView ItemsSource="{Binding Appointments, Mode=TwoWay}" DisplayMemberPath="Time"/>
<TextBlock Text="添加新預(yù)約"/>
<DatePicker Name="AppointmentDatePicker" Margin="10"/>
<Button Content="預(yù)約" Click="AddAppointment_Click"/>
</StackPanel>
</Grid>
MainWindow.xaml.cs
public partial class MainWindow : Window
{
private MainViewModel _viewModel;
public MainWindow()
{
InitializeComponent();
_viewModel = new MainViewModel();
_viewModel.LoadCustomers();
DataContext = _viewModel;
}
private void ClearSelectedCustomer_Click(object sender, RoutedEventArgs e)
{
_viewModel.ClearSelectedCustomer();
}
private void SaveCustomer_Click(object sender, RoutedEventArgs e)
{
try
{
string name = NameTextBox.Text.Trim();
string idNumber = IdTextBox.Text.Trim();
string address = AddressTextBox.Text.Trim();
_viewModel.SaveCustomer(name, idNumber, address);
}catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
private void AddAppointment_Click(object sender, RoutedEventArgs e)
{
try
{
DateTime time = DateTime.Parse(AppointmentDatePicker.Text);
_viewModel.AddAppointment(time);
}catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
}
AppointmentViewModel.cs
public class AppointmentViewModel
{
private Appointment _appointment;
public AppointmentViewModel(Appointment appointment)
{
_appointment = appointment;
}
//因為Id為只讀屬性,因此不需要set
public int Id { get => _appointment.Id; }
public DateTime Time { get => _appointment.Time; set
{
//有且僅當數(shù)據(jù)發(fā)生變化,才向數(shù)據(jù)庫寫入數(shù)據(jù)
if(value != _appointment.Time)
{
_appointment.Time = value;
}
}
}
}
CustomerViewModel.cs
public class CustomerViewModel
{
private Customer _customer;
public CustomerViewModel(Customer customer)
{
_customer = customer;
}
public int Id { get => _customer.Id; }
public string Name { get => _customer.Name; set
{
if(_customer.Name != value)
{
_customer.Name = value;
}
}
}public string IdNumber { get => _customer.IdNumber; set
{
if(_customer.IdNumber != value)
{
_customer.IdNumber = value;
}
}
}public string Address { get => _customer.Address; set
{
if(_customer.Address != value)
{
_customer.Address = value;
}
}
}
}
MainViewModel.cs
//INotifyPropertyChanged刷新更新的屬性
public class MainViewModel : INotifyPropertyChanged
{
//初始化空列表避免程序運行過程中出現(xiàn)為止的內(nèi)存問題
//使用觀察者模式ObservableCollection實時更新添加后的客戶數(shù)據(jù)
public ObservableCollection<CustomerViewModel> Customers { get; set; } = new();
public ObservableCollection<AppointmentViewModel> Appointments { get; set; } = new();
private CustomerViewModel _selectedCustomer;
public event PropertyChangedEventHandler? PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public CustomerViewModel SelectedCustomer
{
get => _selectedCustomer;
set
{
if(value != _selectedCustomer)
{
_selectedCustomer = value;
RaisePropertyChanged(nameof(SelectedCustomer));
LoadAppointments(SelectedCustomer.Id);
}
}
}
public void LoadCustomers()
{
Customers.Clear();//重置客戶列表
using (var db = new AppDbContext())
{
var customers = db.Customers.ToList();
foreach (var customer in customers)
{
Customers.Add(new CustomerViewModel(customer));
}
}
}
public void ClearSelectedCustomer()
{
_selectedCustomer = null;
RaisePropertyChanged(nameof(SelectedCustomer));
}
public void SaveCustomer(string name, string idNumber, string address)
{
if(SelectedCustomer != null)
{
//更新客戶數(shù)據(jù)
using (var db = new AppDbContext())
{
var customer = db.Customers.Where(c => c.Id == SelectedCustomer.Id).FirstOrDefault();
customer.Name = name;
customer.IdNumber = idNumber;
customer.Address = address;
db.SaveChanges();
}
}
else
{
//添加新客戶
using (var db = new AppDbContext())
{
var newCustomer = new Customer()
{
Name = name,
IdNumber = idNumber,
Address = address
};
db.Customers.Add(newCustomer);
db.SaveChanges();
}
LoadCustomers();
}
}
public void LoadAppointments(int customerId)
{
Appointments.Clear();
using (var db = new AppDbContext())
{
var appointments = db.Appointments.Where(a => a.CustomerId == customerId).ToList();
foreach(var a in appointments)
{
Appointments.Add(new AppointmentViewModel(a));
}
}
}
public void AddAppointment(DateTime selectedDate)
{
if(SelectedCustomer == null) { return; }
using (var db = new AppDbContext())
{
var newAppointment = new Appointment()
{
Time = selectedDate,
CustomerId = SelectedCustomer.Id
};
db.Appointments.Add(newAppointment);
db.SaveChanges();
}
LoadAppointments(SelectedCustomer.Id);
}
}
六、Material UI框架
安裝Material UI框架
接著訪問Material Design Themes的項目URL,可以看到對于此框架的使用講解,將示例中的想使用顏色模式的代碼部分,復(fù)制粘貼到App.xaml文件中,即可應(yīng)用
示例代碼:
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<materialDesign:BundledTheme BaseTheme="Light" PrimaryColor="DeepPurple" SecondaryColor="Lime" />
<ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Defaults.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
在進一步優(yōu)化中,我們想要實現(xiàn)在日歷上顯示預(yù)約,對于已經(jīng)有預(yù)約的日期,不可在預(yù)約這樣的效果。想到可以使用BlackoutDates,然而BlackoutDates不支持數(shù)據(jù)的綁定,也就是無法傳入數(shù)據(jù),因此需要使用其他方法進行。
這里借用作大神的方法進行操作,完美解決我們的需求!
根目錄創(chuàng)建文件夾AttachedProperties,創(chuàng)建文件CalendarAttachedProperties
在大神基礎(chǔ)上對于我們的項目略加調(diào)整------原答案地址
CalendarAttachedProperties.cs
// Adds a collection of command bindings to a date picker's existing BlackoutDates collection, since the collections are immutable and can't be bound to otherwise.
// Usage: <DatePicker hacks:AttachedProperties.RegisterBlackoutDates="{Binding BlackoutDates}" >
public class CalendarAttachedProperties : DependencyObject
{
#region Attributes
private static readonly List<Calendar> _calendars = new List<Calendar>();
private static readonly List<DatePicker> _datePickers = new List<DatePicker>();
#endregion
#region Dependency Properties
public static DependencyProperty RegisterBlackoutDatesProperty = DependencyProperty.RegisterAttached("RegisterBlackoutDates", typeof(ObservableCollection<DateTime>), typeof(CalendarAttachedProperties), new PropertyMetadata(null, OnRegisterCommandBindingChanged));
public static void SetRegisterBlackoutDates(DependencyObject d, ObservableCollection<DateTime> value)
{
d.SetValue(RegisterBlackoutDatesProperty, value);
}
public static ObservableCollection<DateTime> GetRegisterBlackoutDates(DependencyObject d)
{
return (ObservableCollection<DateTime>)d.GetValue(RegisterBlackoutDatesProperty);
}
#endregion
#region Event Handlers
private static void CalendarBindings_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
ObservableCollection<DateTime> blackoutDates = sender as ObservableCollection<DateTime>;
Calendar calendar = _calendars.First(c => c.Tag == blackoutDates);
if (e.Action == NotifyCollectionChangedAction.Reset)
{
calendar.BlackoutDates.Clear();
}
if (e.Action == NotifyCollectionChangedAction.Add)
{
foreach (DateTime date in e.NewItems)
{
calendar.BlackoutDates.Add(new CalendarDateRange(date));
}
}
}
private static void DatePickerBindings_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
ObservableCollection<DateTime> blackoutDates = sender as ObservableCollection<DateTime>;
DatePicker datePicker = _datePickers.First(c => c.Tag == blackoutDates);
if (e.Action == NotifyCollectionChangedAction.Add)
{
foreach (DateTime date in e.NewItems)
{
datePicker.BlackoutDates.Add(new CalendarDateRange(date));
}
}
}
#endregion
#region Private Methods
private static void OnRegisterCommandBindingChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
Calendar calendar = sender as Calendar;
if (calendar != null)
{
ObservableCollection<DateTime> bindings = e.NewValue as ObservableCollection<DateTime>;
if (bindings != null)
{
if (!_calendars.Contains(calendar))
{
calendar.Tag = bindings;
_calendars.Add(calendar);
}
calendar.BlackoutDates.Clear();
foreach (DateTime date in bindings)
{
calendar.BlackoutDates.Add(new CalendarDateRange(date));
}
bindings.CollectionChanged += CalendarBindings_CollectionChanged;
}
}
else
{
DatePicker datePicker = sender as DatePicker;
if (datePicker != null)
{
ObservableCollection<DateTime> bindings = e.NewValue as ObservableCollection<DateTime>;
if (bindings != null)
{
if (!_datePickers.Contains(datePicker))
{
datePicker.Tag = bindings;
_datePickers.Add(datePicker);
}
datePicker.BlackoutDates.Clear();
foreach (DateTime date in bindings)
{
datePicker.BlackoutDates.Add(new CalendarDateRange(date));
}
bindings.CollectionChanged += DatePickerBindings_CollectionChanged;
}
}
}
}
#endregion
}
在我們的MainWindow.xaml里面引入命名空間xmlns:crackpot="clr-namespace:WPF_CMS.AttachedProperties"
關(guān)于整個窗口的設(shè)計 Title="客戶管理系統(tǒng)" Height="600" Width="1000" Background="Transparent" AllowsTransparency="True" WindowStyle="None" WindowStartupLocation="CenterScreen" FontFamily="Cambria">
MainWindow.xaml
<Border Background="White" CornerRadius="30">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="240"/>
<ColumnDefinition Width="280"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<!--header-->
<controls:HeaderControl Grid.ColumnSpan="3" Cursor=""/>
<StackPanel Grid.Row="1" Grid.Column="0">
<Button Content="添加客戶" Click="ClearSelectedCustomer_Click" Width="195" Height="33" Margin="10"/>
<ListView ItemsSource="{Binding Customers, Mode=OneWay}" DisplayMemberPath="Name" SelectedItem="{Binding SelectedCustomer, Mode=TwoWay}"/>
</StackPanel>
<MaterialDesign:Card Grid.Row="1" Grid.Column="1" Width="250" Height="440" Margin="10">
<StackPanel >
<Border Margin="10" CornerRadius="20" Background="#FFFFEEFA">
<Image Source="/Images/cartoon.png" Stretch="Uniform" Height="150"/>
</Border>
<TextBox x:Name="NameTextBox" Margin="10"
Style="{StaticResource MaterialDesignOutlinedTextBox}"
MaterialDesign:HintAssist.Hint="姓名"
Text="{Binding SelectedCustomer.Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
<TextBox Name="IdTextBox" Margin="10"
Style="{StaticResource MaterialDesignOutlinedTextBox}"
MaterialDesign:HintAssist.Hint="身份證號"
Text="{Binding SelectedCustomer.IdNumber, Mode=TwoWay}"/>
<TextBox x:Name="AddressTextBox" Margin="10"
Style="{StaticResource MaterialDesignOutlinedTextBox}"
MaterialDesign:HintAssist.Hint="家庭地址"
Text="{Binding SelectedCustomer.Address, Mode=TwoWay}"/>
<Button Content="保存" Margin="10 10 10 30" VerticalAlignment="Bottom" HorizontalAlignment="Left" Click="SaveCustomer_Click"/>
</StackPanel>
</MaterialDesign:Card>
<MaterialDesign:Card Grid.Row="1" Grid.Column="2" Width="270" Margin="35 30 35 30">
<StackPanel Grid.Row="1" Grid.Column="2">
<!--<ListView ItemsSource="{Binding Appointments, Mode=TwoWay}" DisplayMemberPath="Time"/>-->
<Calendar Name="AppointmentCalendar" Height="320" Width="300"
crackpot:CalendarAttachedProperties.RegisterBlackoutDates="{Binding Appointments, Mode=OneWay}"
SelectedDate="{Binding SelectedDate, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Cursor="Hand">
</Calendar>
<Button Content="預(yù)約" Click="AddAppointment_Click" Width="226" Cursor="Hand"/>
</StackPanel>
</MaterialDesign:Card>
</Grid>
</Border>
由于更改為日歷點擊預(yù)約,因此相關(guān)邏輯代碼也需要更改調(diào)整
MainWindow.xaml.cs
public partial class MainWindow : Window
{
private MainViewModel _viewModel;
public MainWindow()
{
InitializeComponent();
_viewModel = new MainViewModel();
_viewModel.LoadCustomers();
DataContext = _viewModel;
}
private void ClearSelectedCustomer_Click(object sender, RoutedEventArgs e)
{
_viewModel.ClearSelectedCustomer();
}
private void SaveCustomer_Click(object sender, RoutedEventArgs e)
{
try
{
string name = NameTextBox.Text.Trim();
string idNumber = IdTextBox.Text.Trim();
string address = AddressTextBox.Text.Trim();
_viewModel.SaveCustomer(name, idNumber, address);
}catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
private void AddAppointment_Click(object sender, RoutedEventArgs e)
{
try
{
_viewModel.AddAppointment();
}catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
}
MainViewModel.cs文章來源:http://www.zghlxwxcb.cn/news/detail-823775.html
//INotifyPropertyChanged刷新更新的屬性
public class MainViewModel : INotifyPropertyChanged
{
//初始化空列表避免程序運行過程中出現(xiàn)為止的內(nèi)存問題
//public List<Customer> Customers { get; set; } = new();
//使用觀察者模式ObservableCollection實時更新添加后的客戶數(shù)據(jù)
public ObservableCollection<CustomerViewModel> Customers { get; set; } = new();
public ObservableCollection<DateTime> Appointments { get; set; } = new();
//selectedDate可能為空
private DateTime? _selectedDate;
public DateTime? SelectedDate
{
get => _selectedDate;
set
{
if(_selectedDate != value)
{
_selectedDate = value;
RaisePropertyChanged(nameof(SelectedDate));
}
}
}
private CustomerViewModel _selectedCustomer;
public event PropertyChangedEventHandler? PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public CustomerViewModel SelectedCustomer
{
get => _selectedCustomer;
set
{
if(value != _selectedCustomer)
{
_selectedCustomer = value;
RaisePropertyChanged(nameof(SelectedCustomer));
LoadAppointments(SelectedCustomer.Id);
}
}
}
public void LoadCustomers()
{
Customers.Clear();//重置客戶列表
using (var db = new AppDbContext())
{
var customers = db.Customers.ToList();
foreach (var customer in customers)
{
Customers.Add(new CustomerViewModel(customer));
}
}
}
public void ClearSelectedCustomer()
{
_selectedCustomer = null;
RaisePropertyChanged(nameof(SelectedCustomer));
}
public void SaveCustomer(string name, string idNumber, string address)
{
if(SelectedCustomer != null)
{
//更新客戶數(shù)據(jù)
using (var db = new AppDbContext())
{
var customer = db.Customers.Where(c => c.Id == SelectedCustomer.Id).FirstOrDefault();
customer.Name = name;
customer.IdNumber = idNumber;
customer.Address = address;
db.SaveChanges();
}
}
else
{
//添加新客戶
using (var db = new AppDbContext())
{
var newCustomer = new Customer()
{
Name = name,
IdNumber = idNumber,
Address = address
};
db.Customers.Add(newCustomer);
db.SaveChanges();
}
LoadCustomers();
}
}
public void LoadAppointments(int customerId)
{
Appointments.Clear();
using (var db = new AppDbContext())
{
var appointments = db.Appointments.Where(a => a.CustomerId == customerId).ToList();
foreach(var a in appointments)
{
Appointments.Add(a.Time);
}
}
}
public void AddAppointment()
{
if(SelectedCustomer == null) { return; }
using (var db = new AppDbContext())
{
var newAppointment = new Appointment()
{
Time = SelectedDate.Value,
CustomerId = SelectedCustomer.Id
};
db.Appointments.Add(newAppointment);
db.SaveChanges();
}
SelectedDate = null;
LoadAppointments(SelectedCustomer.Id);
}
}
到此,本項目結(jié)束文章來源地址http://www.zghlxwxcb.cn/news/detail-823775.html
到了這里,關(guān)于C#學習(十)——WPF重構(gòu)與美化的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!