iT邦幫忙

2022 iThome 鐵人賽

DAY 17
0
Software Development

30天學習.Net MAUI系列 第 17

17.實作Todo (二)

  • 分享至 

  • xImage
  •  

創建ViewModel

1.首先我們安裝MVVM的toolkit套件CommunityToolkit.Mvvm

https://ithelp.ithome.com.tw/upload/images/20221002/201089318BurozKDRm.png

2.我們創建新的資料夾ViewModels並加入TodoListViewModelAddUpdateTodoViewModel

TodoListViewModel.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Todo.ViewModels
{
    public class TodoListViewModel
    {
    }
}

AddUpdateTodoViewModel.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Todo.ViewModels
{
    public class AddUpdateTodoViewModel
    {
    }
}

回到我們的MauiProgram.cs把他們給加入
MauiProgram.cs


public static class MauiProgram
{
	public static MauiApp CreateMauiApp()
	{
		...

		// Add ViewModel
		builder.Services.AddSingleton<TodoListViewModel>();
		builder.Services.AddSingleton<AddUpdateTodoViewModel>();

		return builder.Build();
	}
}

3.加入ViewModel

打開我們的Todo\Pages\TodoPage.xaml.cs並加入ViewModel

using Todo.ViewModels;

namespace Todo;

public partial class TodoPage : ContentPage
{
	public TodoPage(TodoListViewModel vm)
	{
		InitializeComponent();
		this.BindingContext = vm;
	}

	private void ButtonClicked(object sender, EventArgs e)
	{
		Navigation.PushAsync(new AddTodoPage());
	}
}

Todo\Pages\AddTodoPage.xaml.cs也加入ViewModel

namespace Todo;
using Todo.Models;
using Todo.Services;
using Todo.ViewModels;

public partial class AddTodoPage : ContentPage
{
	public AddTodoPage(AddUpdateTodoViewModel vm)
	{
		InitializeComponent();
		this.BindingContext = vm;
	}

}

修改我們的Todo\Pages\TodoPage.xaml.cs

using AndroidX.Lifecycle;
using Todo.ViewModels;

namespace Todo;

public partial class TodoPage : ContentPage
{
    private TodoListViewModel _vm;
    public TodoPage(TodoListViewModel vm)
	{
		InitializeComponent();
        _vm = vm;   
        this.BindingContext = vm;
    }

	private async void ButtonClicked(object sender, EventArgs e)
	{
        await AppShell.Current.GoToAsync(nameof(AddTodoPage));
    }
}

並在MauiProgram.cs註冊views

using Todo.Services;
using Todo.ViewModels;

namespace Todo;

public static class MauiProgram
{
	public static MauiApp CreateMauiApp()
	{
		var builder = MauiApp.CreateBuilder();
		builder
			.UseMauiApp<App>()
			.ConfigureFonts(fonts =>
			{
				fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
				fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
			});

		// Add TodoItem Service
		builder.Services.AddSingleton<ITodoService, TodoService>();

		// Add Views
		builder.Services.AddSingleton<AddTodoPage>();
        builder.Services.AddSingleton<TodoPage>();


		// Add ViewModel
		builder.Services.AddSingleton<TodoListViewModel>();
		builder.Services.AddSingleton<AddUpdateTodoViewModel>();

		return builder.Build();
	}
}

並且到Todo\AppShell.xaml.cs加入路徑

namespace Todo;

public partial class AppShell : Shell
{
	public AppShell()
	{
		InitializeComponent();
		Routing.RegisterRoute(nameof(AddTodoPage), typeof(AddTodoPage));
	}
}

並刪除TestTabbedPage

並且修改Todo\AppShell.xaml

<?xml version="1.0" encoding="UTF-8" ?>
<Shell
    x:Class="Todo.AppShell"
    xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:local="clr-namespace:Todo"
    Shell.FlyoutBehavior="Disabled">
    <TabBar x:Name="Tabs">
        <Tab Title="Home">
            <ShellContent
                Title="Home"
                ContentTemplate="{DataTemplate local:MainPage}"
                Route="MainPage" 
            />
        </Tab>
        <Tab Title="Todo">
            <ShellContent ContentTemplate="{DataTemplate local:TodoPage}" Route="TodoPage" />
        </Tab>
        <Tab Title="User">
            <ShellContent ContentTemplate="{DataTemplate local:UserPage}" Route="UserPage" />
        </Tab>
    </TabBar>
    
</Shell>

4.實作ViewModel

AddUpdateTodoViewModel.cs

using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Todo.Models;
using Todo.Services;

namespace Todo.ViewModels
{
    [QueryProperty(nameof(TodoItem), "TodoItem")]
    public partial class AddUpdateTodoViewModel : ObservableObject
    {
        [ObservableProperty]
        private TodoItemModel _todoItem = new TodoItemModel();
        private readonly ITodoService _todoService;

        public AddUpdateTodoViewModel(ITodoService todoService)
        {
            _todoService = todoService;
        }

        [RelayCommand]
        public async void AddUpdateTodo()
        {
            int res = -1;
            if(TodoItem.TodoId > 0) {
                res = await _todoService.UpdateTodo(TodoItem);
            } else {
                res = await _todoService.AddTodo(new Models.TodoItemModel
                {
                    Title = TodoItem.Title,
                    IsDone = false,
                });
            }

            if(res > 0) {
                await Shell.Current.DisplayAlert("Status: Todo Saved", "The Todo Items Saved", "Ok");
            } else {
                await Shell.Current.DisplayAlert("Status: Wrong", "Something is wrong", "Ok");
            }
        }
    }
}

TodoListViewModel.cs

using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Todo.Models;
using Todo.Services;

namespace Todo.ViewModels
{
    public partial class TodoListViewModel : ObservableObject
    {
        public ObservableCollection<TodoItemModel> Todos { get; set; } = new ObservableCollection<TodoItemModel>();
        private readonly ITodoService _todoService;

        public TodoListViewModel(ITodoService todoService)
        {
            _todoService = todoService;
        }


        [RelayCommand]
        public async void GetTodoList()
        {
            Todos.Clear();
            var todoList = await _todoService.GetTodos();
            if (todoList?.Count > 0) {
                foreach (var todo in todoList) {
                    Todos.Add(todo);
                }
            }
        }
    }
}

5.修改Page

首先修改我們的Todo\Pages\TodoPage.xaml.cs,加入OnAppearing當畫面切過去實會執行GetTodo

using AndroidX.Lifecycle;
using Todo.ViewModels;

namespace Todo;

public partial class TodoPage : ContentPage
{
    private TodoListViewModel _vm;
    public TodoPage(TodoListViewModel vm)
	{
		InitializeComponent();
        _vm = vm;   
        this.BindingContext = vm;
    }

	private async void ButtonClicked(object sender, EventArgs e)
	{
        await AppShell.Current.GoToAsync(nameof(AddTodoPage));
    }

    protected override void OnAppearing()
    {
        base.OnAppearing();
        _vm.GetTodoList();
    }
}

Todo\Pages\AddTodoPage.xaml

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="Todo.AddTodoPage"
             Title="AddTodoPage">
    <VerticalStackLayout Padding="10">
        <Label 
            Text="Add Todo"
            VerticalOptions="Center" 
            HorizontalOptions="Center" />
        <Entry Placeholder="Add Todo" Text="{Binding TodoItem.Title}"/>
        <Button Text="Add Todo Item" Command="{Binding AddUpdateTodoCommand}" />
    </VerticalStackLayout>
</ContentPage>

Todo\Pages\TodoPage.xaml

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:models="clr-namespace:Todo.Models"
             x:Class="Todo.TodoPage"
             Title="TodoPage">
    <VerticalStackLayout Margin="10">
        <Label 
            Text="Welcome to .NET MAUI!"
            VerticalOptions="Center" 
            HorizontalOptions="Center" />
        <Button Text="Go to Add Page" Clicked="ButtonClicked"/>
        <CollectionView ItemsSource="{Binding Todos}">
            <CollectionView.ItemTemplate>
                <DataTemplate x:DataType="models:TodoItemModel">
                    <HorizontalStackLayout>
                        <Label Text="{Binding Title}" />
                        <CheckBox IsChecked="{Binding IsDone}"/>
                    </HorizontalStackLayout>
                </DataTemplate>
            </CollectionView.ItemTemplate>
        </CollectionView>
    </VerticalStackLayout>
</ContentPage>

Test

https://ithelp.ithome.com.tw/upload/images/20221002/201089311765NBimE2.png
https://ithelp.ithome.com.tw/upload/images/20221002/20108931MYgWIsbWqi.png
https://ithelp.ithome.com.tw/upload/images/20221002/20108931sc31Mtf1tR.png

app-test.gif

大致上新增和列出todo簡易的完成,明天再來繼續完善我們的todo


上一篇
16.實作Todo (一)
下一篇
18.實作Todo (三)
系列文
30天學習.Net MAUI30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
yhanshuang
iT邦新手 5 級 ‧ 2023-11-10 09:55:34

您好,
我在NuGet安裝這套件communitytoolkit.mvvm後出現以下錯誤,請問有解決辦法嗎?
警告 CS8032 不可從 C:\Users\hans.huang.nuget\packages\communitytoolkit.mvvm\8.2.2\analyzers\dotnet\roslyn4.3\cs\CommunityToolkit.Mvvm.SourceGenerators.dll 建立分析器 CommunityToolkit.Mvvm.SourceGenerators.AsyncVoidReturningRelayCommandMethodAnalyzer 的執行個體: 無法載入檔案或組件 'System.Collections.Immutable, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' 或其相依性的其中之一。 系統找不到指定的檔案。。 TestCommunityToolkit (net6.0-android) D:\MAUI\TestCommunityToolkit\CSC 1 作用中

Felix阿甫 iT邦研究生 5 級 ‧ 2023-11-11 23:51:13 檢舉

不確定問題,可能是communitytoolkit.mvvm的版本與System.Collections.Immutable不符合或是缺少System.Collections.Immutable。可能要看一下你的communitytoolkit.mvvm版本,你目前的版本應該是8.2.2。

阿甫您好,
是8.2.2沒錯,後來改安裝8.0.0版就沒有出現這錯誤了。
剛檢查發現我的確缺少System.Collections.Immutable,這部分我會再做測試,感謝回覆!

我要留言

立即登入留言