--\App.xaml
<Application x:Class="WPF_CMS.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WPF_CMS"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
StartupUri="MainWindow.xaml">
<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>
</Application>
--\MainWindow.xaml
<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"
xmlns:controls="clr-namespace:WPF_CMS.Controls"
xmlns:MaterialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:alex="clr-namespace:WPF_CMS.ArrachedProperties"
mc:Ignorable="d"
Title="CMS客户管理系统" Height="600" Width="900" Background="Transparent" AllowsTransparency="True" WindowStyle="None" WindowStartupLocation="CenterScreen" FontFamily="Cambria">
<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"/>
<StackPanel Grid.Row="1" Grid.Column="0">
<Button Content="添加客户" Click="ClearSelectedCustomer_Click" Width="192" 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
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.IdNnumber, 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" />
</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"/>-->
<Calendar Name="AppointmentCalender" Height="320" Width="300" alex: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>
</Grid>
</Border>
</Window>
--\MainWindow.xaml.cs
public partial class MainWindow : Window
{
private MainViewModel _viewModel;
public MainWindow()
{
InitializeComponent();
_viewModel = new MainViewModel();
_viewModel.LoadCustomers();
DataContext = _viewModel;
//ShowCustomers();
}
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 error)
{
MessageBox.Show(error.ToString());
}
}
private void AddAppointment_Click(object sender, RoutedEventArgs e)
{
try
{
_viewModel.AddAppointment();
}
catch (Exception error)
{
MessageBox.Show(error.ToString());
}
}
}
--\ArrachedProperties\CalendarAttachedProperties.cs
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
}
}
--\ViewModels\MainViewModel.cs
public class MainViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
//public List<Customer> Customers { get; set; } = new();
public ObservableCollection<CustomerViewModel> Customers { get; set; } = new();
public ObservableCollection<DateTime> Appointments { get; set; } = new();
private DateTime? _selectedDate;
public DateTime? SelectedDate { get => _selectedDate;
set
{
if(_selectedDate != value)
{
_selectedDate = value;
RaisePropertyChanged(nameof(SelectedDate));
}
} }
private CustomerViewModel _selectedCustomer;
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())
{
// Select * from Customers as c join Appointments as a on c.Id = a. CustomerId
var customers = db.Customers
//.Include(c => c.Appointments)
.ToList();
foreach (var c in customers)
{
Customers.Add(new CustomerViewModel(c));
}
}
}
public void ClearSelectedCustomer()
{
_selectedCustomer = null;
RaisePropertyChanged(nameof(SelectedCustomer));
}
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.IdNnumber = idNumber;
customer.Address = address;
db.SaveChanges();
}
}
else
{
// 添加新客户
using (var db = new AppDbContext())
{
var newCustomer = new Customer()
{
Name = name,
IdNnumber = 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);
}
}

最终效果示例图