(Example).NET MAUI for Beginners
- Build .NET MAUI UI with XAML [4 of 8] | .NET MAUI for Beginners
- .NET MAUI Data Binding with MVVM & XAML [5 of 8] | .NET MAUI for Beginners
- Navigating Between Pages in .NET MAUI [6 of 8] | .NET MAUI for Beginners
- IDE: Visual Studio 2022
- Language: C#
- Applied Project Template: .MAUI APP
- NuGet
- CommunityToolkit.Mvvm
- Third Party Libraries: x
- DataBase: x
- install Nuget CommunityToolkit.Mvvm
- create ViewModel Folder and MainViewModel.cs class
- MainViewModel.cs: inherit from ObservableObject
public partial class MainViewModel : ObservableObject
- add ObservableProperty, RelayCommand
public MainViewModel() { Items = new ObservableCollection<string>(); } [ObservableProperty] ObservableCollection<string> items; [ObservableProperty] string? text; [RelayCommand] void Add() { if (string.IsNullOrWhiteSpace(Text)) return; Items.Add(Text); // add our item Text = string.Empty; } [RelayCommand] void Delete(string s) { if (Items.Contains(s)) { Items.Remove(s); } }
- add ObservableProperty, RelayCommand
- MainPage.xaml: add ViewModel and View Combine to ContentPage and Add Data Binding
- ViewModel and View Combine to ContentPage: add xmlns:viewmodel and x:DataType
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="MauiApp3.MainPage" xmlns:viewmodel="clr-namespace:MauiApp3.ViewModel" x:DataType="viewmodel:MainViewModel">
- setting Data Binding for controls
<Entry Grid.Row="1" Placeholder="Enter task" Text="{Binding Text}"/> <Button Grid.Row="1" Grid.Column="1" Text="Add" Command="{Binding AddCommand}"/> <CollectionView Grid.Row="2" Grid.ColumnSpan="2" ItemsSource="{Binding Items}" SelectionMode="None"> <CollectionView.ItemTemplate> <DataTemplate x:DataType="{x:Type x:String}"> <SwipeView> <SwipeView.RightItems> <SwipeItems> <SwipeItem Text="Delete" BackgroundColor="Red" Command="{Binding Source={RelativeSource AncestorType={x:Type viewmodel:MainViewModel}}, Path=DeleteCommand}" CommandParameter="{Binding .}"/> <!-- 기본적으로 상대 바인딩을 의미하는 상위 바인딩을 제공해야 한다.--> </SwipeItems> </SwipeView.RightItems>
- setting x:DataType for DataTemplate
<DataTemplate x:DataType="{x:Type x:String}">
- ViewModel and View Combine to ContentPage: add xmlns:viewmodel and x:DataType
- MainPage.xaml.cs: set the BindingContext for ViewModel in MainPage
public MainPage(MainViewModel vm) { InitializeComponent(); BindingContext = vm; }
- MauiProgram.cs: Add dependency Service(register MainPage and MainViewModel in MAUI program)
builder.Services.AddSingleton<MainPage>(); builder.Services.AddSingleton<MainViewModel>();
-
Add .Net Maui ContentPage(xaml): Add DetailPage
-
Add DetailViewModel class and inherit from ObservableObject
public partial class DetailViewModel : ObservableObject
-
DetailPage.xaml.cs: set the BindingContext for ViewModel in DetailPage(data binding)
public DetailPage(DetailViewModel vm) { InitializeComponent(); BindingContext = vm; }
-
MauiProgram.cs: register DetailPage and DetailViewModel in MAUI program(Dependency injection)
builder.Services.AddTransient<DetailPage>(); builder.Services.AddTransient<DetailViewModel>();
-
AppShell.xaml.cs: register DetailPage with the routing system of .MAUIShell
Routing.RegisterRoute(nameof(DetailPage), typeof(DetailPage));
-
MainPage.xaml: add Navigation when click on an item in the CollectionView (navigate when tapping and not selection)
we don't want to use the built-in selection mode. Because we're just doing navigation, we're not actually selecting anything, I'm more like tapping on an item.
- add SelectionMode="None": we don't want to use the built-in selection mode
<CollectionView Grid.Row="2" Grid.ColumnSpan="2" ItemsSource="{Binding Items}" SelectionMode="None">
- add a TapGestureRecognizer on the Frame( Frame is housing in the label)
<Grid Padding="0, 5"> <Frame> <Frame.GestureRecognizers> <TapGestureRecognizer Command="{Binding Source={RelativeSource AncestorType={x:Type viewmodel:MainViewModel}}, Path=TapCommand}" CommandParameter="{Binding .}"/> </Frame.GestureRecognizers> <Label Text="{Binding .}" FontSize="24"/> </Frame> </Grid>
- setting Data Binding in TapGestureRecognizer
<TapGestureRecognizer Command="{Binding Source={RelativeSource AncestorType={x:Type viewmodel:MainViewModel}}, Path=TapCommand}" CommandParameter="{Binding .}"/>
- add SelectionMode="None": we don't want to use the built-in selection mode
-
MainViewModel.cs: add async Tap Pass Task and use the shell navigation(Shell.Current.GoToAsync)
[RelayCommand] async Task Tap(string s) { await Shell.Current.GoToAsync(nameof(DetailPage)); }
-
there's two ways(query property, data binding) to pass DetailPage information
- use query property way: await Shell.Current.GoToAsync($"{nameof(DetailPage)}?Text={s}");
- MainViewModel.cs
[RelayCommand] async Task Tap(string s) { // do not send informations // await Shell.Current.GoToAsync(nameof(DetailPage)); // [there's two ways to pass DetailPage information] // 1. query property: await Shell.Current.GoToAsync($"{nameof(DetailPage)}?Text={s}"); // 2. URI // to send simple data type like strings and integers across await Shell.Current.GoToAsync($"{nameof(DetailPage)}?Text={s}"); // [QueryProperty("Text", "Text")] // await Shell.Current.GoToAsync($"{nameof(DetailPage)}?id={s}"); // [QueryProperty("Text", "id")] // to send a complex data type, like a person or a car or some other data object // await Shell.Current.GoToAsync($"{nameof(DetailPage)}?Text={s}", // new Dictionary<string, object> // { // {nameof(DetailPage), new object()}, // }); }
- How are we going to get that string into our detail view model?
- DetailViewModel.cs: add [QueryProperty(name, queryId)]
namespace MauiApp3.ViewModel; [QueryProperty("Text", "id")] // await Shell.Current.GoToAsync($"{nameof(DetailPage)}?id={s}");
- DetailViewModel.cs: add name property of QueryProperty(name, queryId)
public partial class DetailViewModel : ObservableObject { [ObservableProperty] string text; // "Text" property of [QueryProperty("Text", "id")] }
- DetailViewModel.cs: add [QueryProperty(name, queryId)]
- use data binding way: async Task to go back
- DetailViewModel.cs: add async Task
[RelayCommand] async Task GoBack() { await Shell.Current.GoToAsync(".."); }
- DetailPage.xaml: add ViewModel and View Combine to ContentPage and Add Data Binding
- ViewModel and View Combine to ContentPage: add xmlns:viewmodel and x:DataType
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="MauiApp3.DetailPage" xmlns:viewmodel="clr-namespace:MauiApp3.ViewModel" x:DataType="viewmodel:DetailViewModel" Title="DetailPage">
- add Go Back button and setting Data Binding for controls
<Label Text="{Binding Text}" FontSize="25" VerticalOptions="Center" HorizontalOptions="Center" /> <Button Text="Go Back" Command="{Binding GoBackCommand}"/>
- ViewModel and View Combine to ContentPage: add xmlns:viewmodel and x:DataType
- DetailViewModel.cs: add async Task
- use query property way: await Shell.Current.GoToAsync($"{nameof(DetailPage)}?Text={s}");