-1

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

  • 1
    there is not enough code here to demonstrate what you are doing. Please post the relevant portions of your VM. – Jason Nov 16 '22 at 13:10
  • edit thank to you – Matan Fadida Nov 16 '22 at 15:21
  • you posted a meaningless snippet of your code behind, not the relevant portions of your VM – Jason Nov 16 '22 at 15:24
  • this is all my code what do want more? – Matan Fadida Nov 16 '22 at 16:08
  • where is `MainViewModel`? – Jason Nov 16 '22 at 16:10
  • here up when I write here is my function that needs to get the Object... I remove only the public partial class MainViewModel. – Matan Fadida Nov 16 '22 at 17:33
  • 1
    so your VM does not have any properties? Where is the `Order` stored in your VM? How can you pass an `Order` to your Command if your VM does not have an `Order` in it? – Jason Nov 16 '22 at 18:01
  • 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. if I create a new object of order in the VM and do this on all my pages so why I need to pass it. and even if I do this I still can't update the values, only if I do something like this I create a value of what the object include like string dogName1 and after this, I do new Order(){ dogName = dogName1}, I don't want to do this like this, I want to update for the beginning the object in my XAML code. – Matan Fadida Nov 16 '22 at 19:00

2 Answers2

0

Do not think of binding, as creating and passing object to your command.

You ask why you need it in your VM? The VM is you BindingContext for the View.

The memory of this VM is used to store the objects, that you will be binding to your View.

Those objects are not just instances of some Model. They are Observable. There is a code behind your setters and getters that handles notifications and update between your Interface and the Memory that stores those objects behind it.

So, to make the logic-chain now: You need to have object, with specific code in your view model, that ViewModel to be set as BindingContext of your View, and to link this observable property in your XAML of your Page, to make all this magic to work.

What the people in the comments section suggested to you, is to make such object in your ViewModel and use it for binding. When the command is triggered, you do not pass it as parameter to the command, because it is stored already in the memory, and simply use it there. (Pass it in as query parameter).

To do this, I recommend: CommunityToolkit.MVVM. It will save you a lot of work. (And the need to acquire detailed knowledge on binding)

H.A.H.
  • 2,104
  • 1
  • 8
  • 21
0

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();
      }
}
Jessie Zhang -MSFT
  • 9,830
  • 1
  • 7
  • 19
  • so if I used it in `CommunityToolkit.MVVM`. Do I need to do this like this? my `MainViewModel` implement interface `ObservableObject` and do this `[ObservableProperty] Order order;` Instead `public Order order { get; set; }` and in the Order Class need to implement interface `ObservableObject` `[ObservableProperty] DateTime dateStart; [ObservableProperty] DateTime dateEnd; [ObservableProperty] string dogName;` like this? and thank you! – Matan Fadida Nov 17 '22 at 06:52
  • The implementation principle is the same, and CommunityToolkit.MVVM simplifies and hides some implementation code. I recommend that you can use above method first, which will help you understand the implementation details. – Jessie Zhang -MSFT Nov 17 '22 at 09:02
  • Hi @MatanFadida,I have achieved this function with nuget `CommunityToolkit.MVVM` and updated my answer. You can check the updated part at the bottom of my answer. – Jessie Zhang -MSFT Nov 17 '22 at 09:33
  • thank you very much! and I have another question why do we need the constructor? – Matan Fadida Nov 17 '22 at 16:50
  • The constructor function initializes the class object. The constructor of a class must have the same name as the class name and access to initialize the data members. Furthermore, a constructor is mainly used to initialize the private fields of the class while creating an instance for the class. – Jessie Zhang -MSFT Nov 18 '22 at 01:19