WPF【11_11】WPF实战-重构与美化(配置Material UI框架)-示例

发布于:2025-05-31 ⋅ 阅读:(21) ⋅ 点赞:(0)

--\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);
    }
}

最终效果示例图