.Net-Avalonia学习笔记(七)-待办事项应用(MVVM实战)

这是一个简单的待办事项列表应用,使用了 Model-View-ViewModel (MVVM) 模式,您将会了解以下内容(官网话语;我是没了解到):

  • RaiseAndSetIfChanged
  • ReactiveCommand
  • IObservable<>
  • Observable.Merge() 与 Select()Take() 和 Subscribe() 方法的使用。

官方代码地址:https://github.com/AvaloniaUI/Avalonia.Samples

本文对官方代码进行了修改,修改后可作为一个单窗体的MVVM示例。首先安装NuGet包CommunityToolkit.Mvvm、Newtonsoft.Json

1、Model

namespace AvaloniaUI_MVVM_Simple.Models
{
    /// <summary>
    /// 待办事项应用 Model
    /// </summary>
    public class ToDoItem
    {
        /// <summary>
        /// 是否勾选
        /// </summary>
        public bool IsChecked { get; set; }

        /// <summary>
        /// 事项内容
        /// </summary>
        public string Content { get; set; } = string.Empty;
    }
}

2、View

<Window xmlns="https://github.com/avaloniaui"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
		xmlns:vm="using:AvaloniaUI_MVVM_Simple"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
		xmlns:i="clr-namespace:Avalonia.Xaml.Interactivity;assembly=Avalonia.Xaml.Interactivity"
		xmlns:iac="clr-namespace:Avalonia.Xaml.Interactions.Core;assembly=Avalonia.Xaml.Interactions"
        mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
		x:Class="AvaloniaUI_MVVM_Simple.Views.ToDoItemsWindow"
		x:DataType="vm:ToDoItemsViewModel"
        Title="待办事项应用">
	<!-- Loaded ="{Binding LoadedCommand}" Closed ="{Binding SaveCommand}" Closed="Window_Closed"-->
	<i:Interaction.Behaviors>
		<iac:EventTriggerBehavior EventName="Loaded">
			<iac:InvokeCommandAction Command="{Binding LoadedCommand}"/>
		</iac:EventTriggerBehavior>
		<iac:EventTriggerBehavior EventName="Closing">
			<iac:InvokeCommandAction Command="{Binding ClosingCommand}"/>
		</iac:EventTriggerBehavior>
		<iac:EventTriggerBehavior EventName="Closed">
			<iac:InvokeCommandAction Command="{Binding ClosedCommand}"/>
		</iac:EventTriggerBehavior>
	</i:Interaction.Behaviors>
	<Design.DataContext>
		<vm:ToDoItemsViewModel />
	</Design.DataContext>

	<Grid RowDefinitions="Auto, *, Auto" x:Name="Root">
		<TextBlock Text="待办事项列表" Classes="h1" />
		
        <!-- 待办事项列表 -滑动框 -->
		<ScrollViewer Grid.Row="1">
			<!-- 待办事项列表 -->
			<ItemsControl ItemsSource="{Binding ToDoItems}">
				<ItemsControl.ItemTemplate>
					<DataTemplate DataType="vm:ToDoItemViewModel">
						<Grid ColumnDefinitions="*, Auto">
							<!-- 一个选择框 -->
							<CheckBox IsChecked="{Binding IsChecked}" Content="{Binding Content}" />
							<!-- 一个按钮 -->
							<Button Grid.Column="1" Command="{Binding #Root.((vm:ToDoItemsViewModel)DataContext).RemoveItemCommand}" CommandParameter="{Binding .}">
								<PathIcon Data="{DynamicResource DeleteIconData}" Height="15" Foreground="Red" />
							</Button>
						</Grid>
					</DataTemplate>
				</ItemsControl.ItemTemplate>
			</ItemsControl>
		</ScrollViewer>

		<!-- 用于添加新Item -->
		<TextBox Grid.Row="2" Text="{Binding NewItemContent}" Watermark="添加新项">
			<!-- 再内部添加一个按钮  -->
			<TextBox.InnerRightContent>
				<Button Command="{Binding AddItemCommand}">
					<PathIcon Data="{DynamicResource AcceptIconData}" Foreground="Green" />
				</Button>
			</TextBox.InnerRightContent>
			<!-- 绑定回车键  -->
			<TextBox.KeyBindings>
				<KeyBinding Gesture="Enter" Command="{Binding AddItemCommand}" />
			</TextBox.KeyBindings>
		</TextBox>
		<!-- <Button Content="Click me">
			<i:Interaction.Behaviors>
				<iac:EventTriggerBehavior EventName="Click">
					<iac:InvokeCommandAction Command="{Binding MyCommand}"/>
				</iac:EventTriggerBehavior>
			</i:Interaction.Behaviors>
		</Button> -->
	</Grid>

</Window>
using Avalonia;
using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Markup.Xaml;
using System;
using System.Diagnostics;

namespace AvaloniaUI_MVVM_Simple.Views;

public partial class ToDoItemsWindow : Window
{
    public ToDoItemsWindow()
    {
        InitializeComponent();

        DataContext = new ToDoItemsViewModel();
    }

    //private void Window_Closed(object? sender, System.EventArgs e)
    //{
    //    Debug.WriteLine("Closed");
    //}
}

3、ViewModel

using AvaloniaUI_MVVM_Simple.Models;
using AvaloniaUI_MVVM_Simple.ViewModels;
using CommunityToolkit.Mvvm.ComponentModel;
using System;

namespace AvaloniaUI_MVVM_Simple
{
    /// <summary>
    /// 待办事项应用 ViewModel
    /// </summary>
    public partial class ToDoItemViewModel : ViewModelBase
    {
        #region 变量
        /// <summary>
        /// 是否勾选
        /// </summary>
        [ObservableProperty]
        private bool _isChecked;

        /// <summary>
        /// 事项内容
        /// </summary>
        [ObservableProperty]
        private string _content = string.Empty;
        #endregion 变量

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="item"></param>
        public ToDoItemViewModel()
        {
        }
        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="item"></param>
        public ToDoItemViewModel(ToDoItem item)
        {
            IsChecked = item.IsChecked;
            Content = item.Content;
        }

        /// <summary>
        /// 转ToDoItem
        /// </summary>
        /// <returns>The ToDoItem</returns>
        public ToDoItem GetToDoItem()
            => new ToDoItem()
            {
                IsChecked = this.IsChecked,
                Content = this.Content
            };
    }
}
using Avalonia.Controls;
using AvaloniaUI_MVVM_Simple.Services;
using AvaloniaUI_MVVM_Simple.ViewModels;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using System;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;

namespace AvaloniaUI_MVVM_Simple
{
    /// <summary>
    /// 待办事项列表 ViewModel
    /// </summary>
    public partial class ToDoItemsViewModel : ViewModelBase
    {
        #region 变量
        /// <summary>
        ///待办事项列表
        /// </summary>
        public ObservableCollection<ToDoItemViewModel> ToDoItems { get; } = new ObservableCollection<ToDoItemViewModel>();

        /// <summary>
        /// 新Item的内容
        /// </summary>
        [ObservableProperty]
        [NotifyCanExecuteChangedFor(nameof(AddItemCommand))] // 绑定事件
        private string _newItemContent = string.Empty;
        #endregion 变量

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="item"></param>
        public ToDoItemsViewModel()
        {
            if (Design.IsDesignMode)  // 设计预览环境下
            {
                ToDoItems = new ObservableCollection<ToDoItemViewModel>(
                    new[]
                    {
                        new ToDoItemViewModel() { Content = "Hello" },
                        new ToDoItemViewModel() { Content = "Avalonia", IsChecked = true}
                    });
            }
        }

        #region 操作
        /// <summary>
        /// 加载数据
        /// </summary>
        //private void Window_Loaded(object sender, RoutedEventArgs e)
        [RelayCommand]
        //private void Loaded(RoutedEventArgs e)
        private void Loaded()
        {
            Debug.WriteLine("Loaded");
            if (!Design.IsDesignMode)
            {
                // 数据列表
                var itemsLoaded = ToDoListFileService.LoadFromFile();

                if (itemsLoaded is not null)
                {
                    ToDoItems.Clear();
                    foreach (var item in itemsLoaded)
                    {
                        ToDoItems.Add(new ToDoItemViewModel(item));
                    }
                }
            }
        }

        /// <summary>
        /// 保存数据
        /// </summary>
        [RelayCommand]
        private void Closing()
        {
            Debug.WriteLine("Closing");
            var itemsToSave = ToDoItems.Select(item => item.GetToDoItem());
            ToDoListFileService.SaveToFile(itemsToSave);
        }
        /// <summary>
        /// 保存数据
        /// 因为ToDoItemsViewModel销毁在窗口前所以Closed不可用
        /// </summary>
        [RelayCommand]
        private void Closed()
        {
            Debug.WriteLine("Closed");
            // var itemsToSave = ToDoItems.Select(item => item.GetToDoItem());
            // ToDoListFileService.SaveToFile(itemsToSave);
        }

        /// <summary>
        /// 添加 项
        /// </summary>
        [RelayCommand(CanExecute = nameof(CanAddItem))]
        private void AddItem()
        {
            ToDoItems.Add(new ToDoItemViewModel() { Content = NewItemContent });

            // 清空NewItemContent
            NewItemContent = string.Empty;
        }

        private bool CanAddItem() => !string.IsNullOrEmpty(NewItemContent);

        /// <summary>
        /// 删除 项
        /// </summary>
        /// <param name="item"></param>
        [RelayCommand]
        private void RemoveItem(ToDoItemViewModel item)
        {
            ToDoItems.Remove(item);
        }

        /// <summary>
        /// 删除 项
        /// </summary>
        /// <param name="item"></param>
        [RelayCommand]
        private void My()
        {
            Debug.WriteLine("Test");
        }
        #endregion 操作
    }
}

4、ViewModelBase.cs

using CommunityToolkit.Mvvm.ComponentModel;

namespace AvaloniaUI_MVVM_Simple.ViewModels
{
    public class ViewModelBase : ObservableObject
    {
    }
}

 

posted @ 2024-07-24 14:44  ꧁执笔小白꧂  阅读(113)  评论(0编辑  收藏  举报