why I need him in VM? I have a Model of Order, and I want to send the
object to function after I updated the values of this order in the
XMAL code.
You should not fully understand the meaning of BindingContext and MVVM.
From document Data Binding Basics,we know that:
Data bindings connect properties of two objects, called the source
and
the target
. In code, two steps are required: The BindingContext
property of the target object must be set to the source object, and
the SetBinding
method (often used in conjunction with the Binding
class) must be called on the target object to bind a property of that
object to a property of the source object.
The target property must be a bindable property, which means that the
target object must derive from BindableObject
. The online
Xamarin.Forms documentation indicates which properties are bindable
properties. A property of Label such as Text
is associated with the
bindable property TextProperty
Simply put, when you set the BindingContext = vm;
for your page(MainPage
), you usually
need to bind the properties(including simple types(e.g.string
, int
) or Object
) and command from the vm(Yours is MainViewModel
) instead of binding from other classes.
Based on your code, your MainViewModel
should code as follows:
MainViewModel.cs
public class MainViewModel
{
public Order order { get; set; }
public MainViewModel()
{
order = new Order { DateStart = "2022-11-16", DateEnd = "2022-11-17", DogName = "puppy" };
}
public ICommand ContinueCommand => new Command<Order>(continueItem);
private void continueItem(Order obj)
{
System.Diagnostics.Debug.WriteLine(" the passted Order's name is: " + obj.DogName);
}
}
Order.cs
public class Order: INotifyPropertyChanged
{
public string DateStart { get; set; }
public string DateEnd { get; set; }
//public string DogName { get; set; }
string _dogName;
public string DogName
{
get => _dogName;
set => SetProperty(ref _dogName, value);
}
bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
{
if (Object.Equals(storage, value))
return false;
storage = value;
OnPropertyChanged(propertyName);
return true;
}
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
TestPage.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="MauiCollectionApp.TestPage"
xmlns:model="clr-namespace:MauiCollectionApp.model"
Title="TestPage">
<VerticalStackLayout Margin="0,20,0,0" Spacing="10" FlowDirection="RightToLeft">
<Label Text="הזמנת פנסיון לכלב" FontSize="40" HorizontalOptions="Center" Margin="5"/>
<Label Text="לפני שנתחיל נשמח לדעת מה התאריך בו תרצו להתארח אצלנו" HorizontalOptions="Center" FontSize="20" FontFamily="op-light"/>
<Grid Padding="20" Background="#FFFFFF" Margin="54">
<Grid.Shadow>
<Shadow Brush="#000"
Offset="5,0"
Opacity="0.26"/>
</Grid.Shadow>
<VerticalStackLayout Spacing="10" >
<Label Text="בחירת תאריך" FontSize="30" HorizontalOptions="Center" Margin="0,0,0,15"/>
<Frame x:Name="DateStart" CornerRadius="0" Padding="10,0">
<DatePicker MinimumDate="01/01/2022"
MaximumDate="12/31/2025"
Date="{Binding order.DateStart}"/>
</Frame>
<Frame CornerRadius="0" Padding="10,0">
<DatePicker x:Name="DateEnd" MinimumDate="01/01/2022"
MaximumDate="12/31/2025"
Date="{Binding order.DateEnd}" />
</Frame>
<Frame CornerRadius="0" Padding="10,0">
<Entry x:Name="dogName" Placeholder="שם הכלב/ה" Text="{Binding order.DogName}"/>
</Frame>
<Button Text="המשך" BackgroundColor="#EEBF3E"
TextColor="Black" CornerRadius="0"
Command="{Binding ContinueCommand}"
CommandParameter="{Binding order}">
<Button.Shadow>
<Shadow Brush="#ccccd0"
Offset="3,6"
Opacity="1"/>
</Button.Shadow>
</Button>
</VerticalStackLayout>
</Grid>
</VerticalStackLayout>
</ContentPage>
TestPage.xaml.cs
public partial class TestPage : ContentPage
{
public TestPage()
{
InitializeComponent();
this.BindingContext = new MainViewModel();
}
}
Note:
1.I defined a variable order
and added ICommand ContinueCommand
in ViewModel MainViewModel
.
then, we can bind as follows:
<DatePicker MinimumDate="01/01/2022"
MaximumDate="12/31/2025"
Date="{Binding order.DateStart}"/>
And add command for button like this:
<Button Text="המשך" BackgroundColor="#EEBF3E"
TextColor="Black" CornerRadius="0"
Command="{Binding ContinueCommand}"
CommandParameter="{Binding order}">
<Button.Shadow>
<Shadow Brush="#ccccd0"
Offset="3,6"
Opacity="1"/>
</Button.Shadow>
</Button>
2.I defined class Order
and implemented interface INotifyPropertyChanged
for it. Then if we change the value of DogName
, the value of variable order
in MainViewModel
will be updated.
public class Order: INotifyPropertyChanged
{
public string DateStart { get; set; }
public string DateEnd { get; set; }
//public string DogName { get; set; }
string _dogName;
public string DogName
{
get => _dogName;
set => SetProperty(ref _dogName, value);
}
bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
{
if (Object.Equals(storage, value))
return false;
storage = value;
OnPropertyChanged(propertyName);
return true;
}
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
Update
I achieved this function with nuget CommunityToolkit.MVVM
, and it works on my side.
You can refer to the following code:
MyMainViewModel.cs
public partial class MyMainViewModel
{
public MyOrder order { get; set; }
public MyMainViewModel()
{
order = new MyOrder { DateStart = "2022-11-16", DateEnd = "2022-11-17", DogName = "puppy" };
}
[RelayCommand]
async Task Continue(MyOrder order)
{
System.Diagnostics.Debug.WriteLine(" the passted Order's name is: " + order.DogName);
}
}
MyOrder.cs
public partial class MyOrder: ObservableObject
{
[ObservableProperty]
private string? dateStart;
[ObservableProperty]
private string? dateEnd;
[ObservableProperty]
private string? dogName;
[RelayCommand]
async Task Continue(Order order)
{
System.Diagnostics.Debug.WriteLine(" the passted Order's name is: " + order.DogName);
}
}
TestPage.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="MauiCollectionApp.TestPage"
xmlns:model="clr-namespace:MauiCollectionApp.model"
Title="TestPage">
<VerticalStackLayout Margin="0,20,0,0" Spacing="10" FlowDirection="RightToLeft">
<Label Text="הזמנת פנסיון לכלב" FontSize="40" HorizontalOptions="Center" Margin="5"/>
<Label Text="לפני שנתחיל נשמח לדעת מה התאריך בו תרצו להתארח אצלנו" HorizontalOptions="Center" FontSize="20" FontFamily="op-light"/>
<Grid Padding="20" Background="#FFFFFF" Margin="54">
<Grid.Shadow>
<Shadow Brush="#000"
Offset="5,0"
Opacity="0.26"/>
</Grid.Shadow>
<VerticalStackLayout Spacing="10" >
<Label Text="בחירת תאריך" FontSize="30" HorizontalOptions="Center" Margin="0,0,0,15"/>
<Frame x:Name="DateStart" CornerRadius="0" Padding="10,0">
<DatePicker MinimumDate="01/01/2022"
MaximumDate="12/31/2025"
Date="{Binding order.DateStart}"/>
</Frame>
<Frame CornerRadius="0" Padding="10,0">
<DatePicker x:Name="DateEnd" MinimumDate="01/01/2022"
MaximumDate="12/31/2025"
Date="{Binding order.DateEnd}" />
</Frame>
<Frame CornerRadius="0" Padding="10,0">
<Entry x:Name="dogName" Placeholder="שם הכלב/ה" Text="{Binding order.DogName}"/>
</Frame>
<Button Text="המשך" BackgroundColor="#EEBF3E"
TextColor="Black" CornerRadius="0"
Command="{Binding ContinueCommand}"
CommandParameter="{Binding order}">
<Button.Shadow>
<Shadow Brush="#ccccd0"
Offset="3,6"
Opacity="1"/>
</Button.Shadow>
</Button>
</VerticalStackLayout>
</Grid>
</VerticalStackLayout>
</ContentPage>
TestPage.xaml.cs
public partial class TestPage : ContentPage
{
public TestPage()
{
InitializeComponent();
//this.BindingContext = new MainViewModel();
this.BindingContext = new MyMainViewModel();
}
}