客户预约管理系统( WPF )( 重构美化 )
C# - WPF实战-重构与美化
本章目标
- 构建数据模型(Model)
- 重构项目:MVVM (Model-View-ViewModel)
- 美化UI(Material Design)
什么是数据模型
Model
- Model:一种可以描述复杂事物的方法。
逻辑概念 or 物理概念
构建数据模型
重构:构建数据模型
- 逆向数据库获得数据模型(Model)。
- 使用Entity Framework取代SQL语句。
- 通过数据模型向UI传递和绑定数据。
所需工具
安装nuget程序包
Microsoft.EntityFrameworkCore.SqlServer
Microsoft.EntityFrameworkCore.Tools
在项目中新建Models文件夹
打开程序包管理器控制台
输入命令
Scaffold-DbContext "Data Source=DESKTOP-V6GQHHS\SQLSERVER2012;Initial Catalog=master;Integrated Security=True" Microsoft.EntityFrameworkCore.SqlServer -OutputDir Models -Context AppDbContext
得到逆向数据库得到的数据模型
ORM数据管理(上)
- 不用写SQL了。
修改原有代码
(showCustomers)
(customerList_SelectionChanged)
namespace _05SQL
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private SqlConnection _sqlConnection;
public MainWindow()
{
InitializeComponent();
string connectionString =
"Data Source=DESKTOP-V6GQHHS\\SQLSERVER2012;Initial Catalog=master;Integrated Security=True";
_sqlConnection = new SqlConnection(connectionString);
showCustomers();
}
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 e) {
MessageBox.Show(e.ToString());
}
}
//显示关联型数据:客户预约记录
private void customerList_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
try
{
Customer selectedItem = customerList.SelectedItem as Customer;
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 error)
{
MessageBox.Show(e.ToString());
}
}
}
}
运行测试
ORM数据管理(下)
实例
DeleteAppointment_Click
DeleteCustomer_Click
AddCustomer_Click
AddAppointment_Click
UpdateCustomer_Click
private void DeleteAppointment_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();
db.Appointments.Remove(appointmentToRemove);
db.SaveChanges();
}
}
catch (Exception error)
{
MessageBox.Show(e.ToString());
}
finally {
customerList_SelectionChanged(null, null);
}
}
//删除客户
private void DeleteCustomer_Click(object sender, RoutedEventArgs e)
{
try {
var customerId = customerList.SelectedValue;
using (var db = new AppDbContext())
{
var customerToRemove = db.Customers
.Include(c => c.Appointments)
.Where(c => c.Id == (int)customerId).FirstOrDefault();
db.Customers.Remove(customerToRemove);
db.SaveChanges();
}
}
catch (Exception error)
{
MessageBox.Show(e.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();
}
}
catch (Exception error)
{
MessageBox.Show(e.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();
}
}
catch (Exception error)
{
MessageBox.Show(e.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();
}
}
catch (Exception error)
{
MessageBox.Show(e.ToString());
}
finally
{
showCustomers();
}
}
修改完DeleteCustomer_Click方法后报错,需修改customerList_SelectionChanged中的selectedItem
//---------------------------------------
//显示关联型数据:客户预约记录
private void customerList_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
try
{
Customer selectedItem = customerList.SelectedItem as Customer;
//新增if判断
if (selectedItem == null)
{
appointmentList.ItemsSource = null;
return;
}
//新增if判断
NameTextBox.Text = selectedItem.Name;
}
//---------------------------------------
删除所有与DbConnection相关的代码
MainWindow
- 注释的即与DbConnection的代码
public MainWindow()
{
InitializeComponent();
/* string connectionString =
"Data Source=DESKTOP-V6GQHHS\\SQLSERVER2012;Initial Catalog=master;Integrated Security=True";
_sqlConnection = new SqlConnection(connectionString);
*/
showCustomers();
}
删除各个方法中的_sqlConnection.Close();
美化主页面
<Window x:Class="WPF_CMS.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WPF_CMS"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<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="#7f3089">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<Image Height="90" Margin="5" Source="/Images/logo.jpg"/>
<TextBlock Text="WPF客户管理系统" 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="添加新预约" />
<DatePicker Margin="10" />
<Button Content="预约" />
</StackPanel>
</Grid>
</Window>
组件化布局
新建Controls文件夹
新建用户控件(WPF)
HeaderControl.xaml
<UserControl x:Class="WPF_CMS.Controls.HeaderControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WPF_CMS.Controls"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<!--header-->
<Border Grid.ColumnSpan="3" Background="#7f3089">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<Image Height="90" Margin="5" Source="/Images/logo.jpg"/>
<TextBlock Text="WPF客户管理系统" FontSize="40" VerticalAlignment="Center" Foreground="#ffffff"/>
</StackPanel>
</Border>
</UserControl>
- 在MainWindow.xaml中仅需如下代码所示即可实现调用。
<!--header-->
<controls:HeaderControl Grid.ColumnSpan="3"/>
测试
MVVM架构
什么是MVVM
MVVM的优点
- 兼容MVC架构。
- 方便测试 和 维护。
MVVM的缺点
- 代码量增加。
- 对象调用复杂度增加。
创建视图模型(显示客户列表)
新建ViewModels文件夹
建立MainViewModel类
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using WPF_CMS.Models;
namespace WPF_CMS.ViewModels
{
public class MainViewModel
{
public List<Customer> Customers { get; set;} = new();
public void LoadCustomers()
{
using (var db = new AppDbContext())
{
Customers = db.Customers.Include(c => c.Appointments).ToList();
}
}
}
}
在MainWindow.xaml.cs中声明私有的视图模型
namespace WPF_CMS
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private MainViewModel _viewModel;
public MainWindow()
{
InitializeComponent();
_viewModel = new MainViewModel();
_viewModel.LoadCustomers();
DataContext = _viewModel;
//ShowCustomers();
}
}
}
修改MainWindow.xaml —— 显示客户列表
<!--header-->
<controls:HeaderControl Grid.ColumnSpan="3"/>
<StackPanel Grid.Row="1" Grid.Column="0">
<Button Content="添加客户"/>
<ListView ItemsSource="{Binding Customers,Mode=OneWay}" DisplayMemberPath="Name"/>
</StackPanel>
测试
正常显示用户列表
双向绑定(选择客户)
MainViewModel类
using _05SQL.Models;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace WPF_CMS.ViewModels
{
public class MainViewModel
{
public List<Customer> Customers { get; set; } = new();
//选择客户
private Customer _selectedCustomer;
public Customer SelectedCustomer
{
get => _selectedCustomer; set
{
if (value != _selectedCustomer)
{
_selectedCustomer = value;
}
}
}
public void LoadCustomers()
{
using (var db = new AppDbContext())
{
Customers = db.Customers.Include(c => c.Appointments).ToList();
}
}
}
}
修改MainWindow.xaml —— 添加客户
<!--header-->
<controls:HeaderControl Grid.ColumnSpan="3"/>
<StackPanel Grid.Row="1" Grid.Column="0">
<Button Content="添加客户"/>
<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 Margin="10" Text="{Binding SelectedCustomer.Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
<TextBlock Text="身份证" Margin="10 10 10 0"/>
<TextBox Margin="10" Text="{Binding SelectedCustomer.IdNumber, 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>
ViewModel的嵌套与分解
注释MainViewModel类的 .Include(c => c.Appointments)
public void LoadCustomers()
{
using (var db = new AppDbContext())
{
Customers = db.Customers
/* .Include(c => c.Appointments)*/
.ToList();
}
}
}
分解 —— 嵌套子视图模型(生成类型)
MainViewModel.cs
using _05SQL.Models;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace WPF_CMS.ViewModels
{
public class MainViewModel
{
/* public List<Customer> Customers { get; set; } = new();*/
//分解 —— 嵌套子视图模型(生成类型)
public List<CustomerViewModel> Customers { get; set; } = new();
public List<AppointmentViewModel> Appointments { get; set; } = new();
//选择客户
private CustomerViewModel _selectedCustomer;
public CustomerViewModel SelectedCustomer
{
get => _selectedCustomer; set
{
if (value != _selectedCustomer)
{
_selectedCustomer = value;
}
}
}
public void LoadCustomers()
{
using (var db = new AppDbContext())
{
var customers = db.Customers
/* .Include(c => c.Appointments)*/
.ToList();
foreach (var c in customers)
{
Customers.Add(new CustomerViewModel(c));
}
}
}
}
}
CustomerViewModel
using _05SQL.Models;
namespace WPF_CMS.ViewModels
{
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;
}
}
}
}
}
INotifyPropertyChanged 与 ObservableCollection (新建客户信息)
添加客户(MainWindow.xaml.cs)
//添加客户
private void ClearSelectedCustomer_Click(object sender, RoutedEventArgs e)
{
_viewModel.ClearSelectedCustomer();
}
MainViewModel.cs
ClearSelectedCustomer
public void ClearSelectedCustomer()
{
_selectedCustomer = null;
}
INotifyPropertyChanged
在MainViewModel.cs中继承INotifyPropertyChanged
- 搜索“PropertyChange”即为新增代码。
using _05SQL.Models;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace WPF_CMS.ViewModels
{
public class MainViewModel : INotifyPropertyChanged
{
/* public List<Customer> Customers { get; set; } = new();*/
//分解 —— 嵌套子视图模型(生成类型)
public List<CustomerViewModel> Customers { get; set; } = new();
public List<AppointmentViewModel> Appointments { get; set; } = new();
//选择客户
private CustomerViewModel _selectedCustomer;
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChange(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
nameof(SelectedCustomer)public CustomerViewModel SelectedCustomer
{
get => _selectedCustomer; set
{
if (value != _selectedCustomer)
{
_selectedCustomer = value;
RaisePropertyChange(nameof(Customers));
}
}
}
public void LoadCustomers()
{
using (var db = new AppDbContext())
{
var customers = db.Customers
/* .Include(c => c.Appointments)*/
.ToList();
foreach (var c in customers)
{
Customers.Add(new CustomerViewModel(c));
}
}
}
public void ClearSelectedCustomer()
{
_selectedCustomer = null;
RaisePropertyChange(nameof(Customers));
}
}
}
SaveCustomer
public void SaveCustomer(string name,string idNumber,string address) {
if (SelectedCustomer != null)
{
//更新客户数据
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方法中新增Customers.Clear()方法
LoadCustomers();
}
}
}
MainViewModel.cs
SaveCustomer_Click
private void SaveCustomer_Click(object sender, RoutedEventArgs e)
{
try
{
string name = NameTextBox.Text.Trim();
string idNumber = IdNumberTextBox.Text.Trim();
string address = AddressTextBox.Text.Trim();
_viewModel.SaveCustomer(name,idNumber,address);
}
catch (Exception error)
{
MessageBox.Show(error.ToString());
}
}
给TextBox添加Name
<StackPanel Grid.Row="1" Grid.Column="1">
<TextBlock Text="姓名" Margin="10 10 10 0"/>
<TextBox Name ="NameTextBox" Margin="10" Text="{Binding SelectedCustomer.Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
<TextBlock Text="身份证" Margin="10 10 10 0"/>
<TextBox Name ="IdNumberTextBox" Margin="10" Text="{Binding SelectedCustomer.IdNumber, Mode=TwoWay}" />
<TextBlock Text="地址" Margin="10 10 10 0"/>
<TextBox 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>
ObservableCollection
//ObservableCollection代替List
public ObservableCollection<CustomerViewModel> Customers { get; set; } = new();
显示预约列表
AppointmentViewModel.cs
using System;
using _05SQL.Models;
namespace WPF_CMS.ViewModels
{
public class AppointmentViewModel
{
private Appointment _appointment;
public AppointmentViewModel(Appointment appointment)
{
_appointment = appointment;
}
public int Id { get => _appointment.Id; }
public DateTime Time { get => _appointment.Time;
set
{
if (value != _appointment.Time)
{
_appointment.Time = value;
}
}}
}
}
修改MainViewModel.cs
public List<AppointmentViewModel> Appointments { get; set; } = new();
- 将上面这段代码修改为以下代码:(
ObservableCollection
)
public ObservableCollection<AppointmentViewModel> Appointments { get; set; } = new();
MainViewModel.cs —— 创建新方法 —— LoadAppointments
public void LoadAppointments(int customerId)
{
Appointments.Clear();
using (var db = new AppDbContext())
{
var appointments = db.Appointments.Where(a => a.CustomerId == customerId).ToList();
foreach (var c in appointments)
{
Appointments.Add(new AppointmentViewModel(c));
}
}
}
在SelectedCustomer方法中调用LoadAppointments方法
public CustomerViewModel SelectedCustomer
{
get => _selectedCustomer; set
{
if (value != _selectedCustomer)
{
_selectedCustomer = value;
RaisePropertyChange(nameof(SelectedCustomer));
//在SelectedCustomer方法中调用LoadAppointments方法
LoadAppointments(SelectedCustomer.Id);
}
}
}
MainWindow.xaml
DisplayMemberPath="Time"
<StackPanel Grid.Row="1" Grid.Column="2">
<ListView ItemsSource="{Binding Appointments, Mode=TwoWay}" DisplayMemberPath="Time"/>
<TextBlock Text="添加新预约" />
<DatePicker Margin="10" />
<Button Content="预约" />
</StackPanel>
测试 —— 显示预约列表
添加新预约
MainViewModel.cs —— 创建新方法 —— AddAppointment
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);
}
MainWindow.xaml
Name ="AppointmentDatePicker"
<StackPanel Grid.Row="1" Grid.Column="2">
<ListView ItemsSource="{Binding Appointments, Mode=TwoWay}" DisplayMemberPath="Time"/>
<TextBlock Text="添加新预约" />
<DatePicker Name ="AppointmentDatePicker" Margin="10" />
<Button Content="预约" Click="AddAppointment_Click" />
</StackPanel>
MainWindow.xaml.cs创建点击事件
AddAppointment_Click
private void AddAppointment_Click(object sender, RoutedEventArgs e)
{
try
{
DateTime time = DateTime.Parse(AppointmentDatePicker.Text);
_viewModel.AddAppointment(time);
}
catch (Exception error)
{
MessageBox.Show(error.ToString());
}
}
配置MaterialUI框架
nuget中下载MaterialDesignThemes
项目文档
http://materialdesigninxaml.net/
App.xaml
配置MaterialDesignThemes
<Application x:Class="_05SQL.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:_05SQL"
StartupUri="MainWindow.xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Light.xaml" />
<ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Defaults.xaml" />
<ResourceDictionary Source="pack://application:,,,/MaterialDesignColors;component/Themes/Recommended/Primary/MaterialDesignColor.DeepPurple.xaml" />
<ResourceDictionary Source="pack://application:,,,/MaterialDesignColors;component/Themes/Recommended/Accent/MaterialDesignColor.Lime.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
测试
修改主页UI
主页名称
<Window x:Class="WPF_CMS.MainWindow"
//-----------------------------省略
mc:Ignorable="d"
Title="WPF客户预约管理系统" Height="600" Width="900"
Background="Transparent" AllowsTransparency="True" WindowStyle="None"
WindowStartupLocation="CenterScreen" FontFamily="Cambria">
添加边框
<Border Background="White" CornerRadius="30">
<Grid>
//------------------------------省略
</Grid>
</Border>
修改按钮
Width="190" Margin="10
<StackPanel Grid.Row="1" Grid.Column="0">
<Button Content="添加客户" Click="ClearSelectedCustomer_Click" Width="190" Margin="10"/>
<ListView ItemsSource="{Binding Customers, Mode=OneWay}" DisplayMemberPath="Name" SelectedItem="{Binding SelectedCustomer, Mode=TwoWay}" />
</StackPanel>
<StackPanel Grid.Row="1" Grid.Column="2">
<ListView ItemsSource="{Binding Appointments, Mode=TwoWay}" DisplayMemberPath="Time"/>
<TextBlock Text="添加新预约" />
<DatePicker Name ="AppointmentDatePicker" Margin="10" />
<Button Content="预约" Click="AddAppointment_Click" Width="190" Margin="10"/>
</StackPanel>
配置MaterialDesign:Card
<MaterialDesign:Card Grid.Row="1" Grid.Column="1"
Width="250" Height="440" Margin="10">
<StackPanel >
<TextBlock Text="姓名" Margin="10 10 10 0"/>
<TextBox Name ="NameTextBox" Margin="10" Text="{Binding SelectedCustomer.Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
<TextBlock Text="身份证" Margin="10 10 10 0"/>
<TextBox Name ="IdNumberTextBox" Margin="10" Text="{Binding SelectedCustomer.IdNumber, Mode=TwoWay}" />
<TextBlock Text="地址" Margin="10 10 10 0"/>
<TextBox 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>
</MaterialDesign:Card>
<MaterialDesign:Card Grid.Row="1" Grid.Column="2" Width="310" Margin="35, 30 35, 30">
<StackPanel Grid.Row="1" Grid.Column="2">
<ListView ItemsSource="{Binding Appointments, Mode=TwoWay}" DisplayMemberPath="Time"/>
<TextBlock Text="添加新预约" />
<DatePicker Name ="AppointmentDatePicker" Margin="10" />
<Button Content="预约" Click="AddAppointment_Click" Width="190" Margin="10"/>
</StackPanel>
</MaterialDesign:Card>
引入图片cartoon.png
修改其属性 - 生成操作 为“资源”
MainWindow.xaml中插入图片
<Border Margin="10" CornerRadius="20" Background="#FFFFEEFA">
<Image Source="/Images/cartoon.png" Stretch="Uniform" Height="150" />
</Border>
修改输入框
StaticResource MaterialDesignOutlinedTextBox
MaterialDesign:HintAssist.Hint="xxx"
<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
Name="NameTextBox"
Margin="10"
Style="{StaticResource MaterialDesignOutlinedTextBox}"
MaterialDesign:HintAssist.Hint="姓名"
Text="{Binding SelectedCustomer.Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
<TextBox
Name="IdNumberTextBox"
Margin="10"
Style="{StaticResource MaterialDesignOutlinedTextBox}"
MaterialDesign:HintAssist.Hint="身份证"
Text="{Binding SelectedCustomer.IdNumber, Mode=TwoWay}" />
<TextBox
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" />
<Button Content="保存" Margin="10 10 10 30" VerticalAlignment="Bottom" HorizontalAlignment="Left" Click="SaveCustomer_Click" />
</StackPanel>
</MaterialDesign:Card>
测试
自定义依赖属性 (预约日历)
新建ArrachedProperties文件夹
CalendarAttachedProperties.cs
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
namespace WPF_CMS.ArrachedProperties
{
// 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
}
}
MainViewModel.cs
修改public ObservableCollection<AppointmentViewModel> Appointments { get; set; } = new();
为DateTime
/*public ObservableCollection<AppointmentViewModel> Appointments { get; set; } = new();*/
public ObservableCollection<DateTime> Appointments { get; set; } = new();
在LoadAppointments方法中对数据进行塑形
Appointments.Add(a.Time);
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);
}
}
}
MainWindow.xaml修改预约日历
引入命名空间
xmlns:li="clr-namespace:WPF_CMS.ArrachedProperties" xmlns:viewmodels="clr-namespace:WPF_CMS.ViewModels" d:DataContext="{d:DesignInstance Type=viewmodels:MainViewModel}"
mc:Ignorable="d"
<Calendar>
- 如果报错则重新生成解决方案。
<MaterialDesign:Card Grid.Row="1" Grid.Column="2" Width="310" Margin="35, 30 35, 30">
<StackPanel Grid.Row="1" Grid.Column="2">
<!--<ListView ItemsSource="{Binding Appointments, Mode=TwoWay}" DisplayMemberPath="Time"/>-->
<Calendar Name="AppointmentCalender" Height="320" Width="300" li:CalendarAttachedProperties.RegisterBlackoutDates="{Binding Appointments, Mode=OneWay}" SelectedDate="{Binding SelectedDate, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
</Calendar>
<Button Content="预约" Click="AddAppointment_Click" Width="190" Margin="10"/>
</StackPanel>
</MaterialDesign:Card>
添加日期类型数据(MainViewModel.cs)
private DateTime _selectedDate;
public DateTime SelectedDate { get =>_selectedDate;
set
{
if (_selectedDate != value)
{
_selectedDate= value;
RaisePropertyChange(nameof(SelectedDate));
}
}
}
测试
- 成功预约,显示为灰色。
Comments | NOTHING