Search code examples
c#mauimaui-blazormaui-community-toolkit

how I can update an object in Maui and send it to another page


my problem is when I send an object of Order to my function, it comes Null without updating my values, how can I edit them?

I try to update my Object. I expecting get in my function Object of Order with the value and not null

here is my function that needs to get the Object.

        [RelayCommand]
        async Task Continue(Order order)
        {
            //Order order = new Order()
            //{
            //    DateStart = dateStart,
            //    DateEnd = dateEnd,
            //    DogName = dogName
            //};
            await Shell.Current.GoToAsync(nameof(ShowOrderPage), true, new Dictionary<string, object>
            {
                {"Order", order}
            });
        }

using Pension.ViewsModels;

namespace Pension.View;

public partial class MainPage : ContentPage
{
   public MainPage(MainViewModel vm)
   {
      InitializeComponent();
      BindingContext = vm;//here the error
   }

}

my 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:model="clr-namespace:Pension.Models"
             xmlns:viewmodel="clr-namespace:Pension.ViewsModels"
             x:DataType="viewmodel:MainViewModel"
             x:Class="Pension.Views.MainPage"
             BackgroundColor="#F2F2F2">

    <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" x:DataType="model:Order">
                <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 DateStart}"/>
                </Frame>
                <Frame CornerRadius="0" Padding="10,0">
                    <DatePicker x:Name="DateEnd" MinimumDate="01/01/2022"
                MaximumDate="12/31/2025"
                Date="{Binding DateEnd}" />
                </Frame>
                <Frame CornerRadius="0" Padding="10,0">
                    <Entry x:Name="dogName" Placeholder="שם הכלב/ה" Text="{Binding DogName}"/>
                </Frame>
                <Button Text="המשך" BackgroundColor="#EEBF3E"
                        TextColor="Black" CornerRadius="0"
                        Command="{Binding Source={RelativeSource AncestorType={x:Type viewmodel:MainViewModel}},Path=ContinueCommand}"
                        CommandParameter="{Binding Source={RelativeSource AncestorType={x:Type model:Order}}}">
                   
                    <Button.Shadow>
                        <Shadow Brush="#ccccd0"
                            Offset="3,6"
                            Opacity="1"/>
                    </Button.Shadow>
                </Button>
            </VerticalStackLayout>
        </Grid>
    </VerticalStackLayout>

</ContentPage>

into the Entry, I try to insert my values and it's not working


Solution

  • 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();
          }
    }