casillas
casillas

Reputation: 16813

Sending data from Main to Detail and Updating Object in MainViewMode

I have the following recyclerview where it contains list of TestViewModel objects. In this object I have age, gender and name properties. I am trying to achieve when user click on a list item, it takes user to detail view where user could able to update and click on the save button, then it updates the selected item properties.

Issue:

The following piece of code in MainViewModel where I receive the message from DetailViewModel works when user enter values in the detail and updating each property,

private void OnMessageReceived(TestMessage obj)
{
    _selectedItem.Age = obj.messageTest.Age;
    _selectedItem.Name = obj.messageTest.Name;
    _selectedItem.Gender = obj.messageTest.Gender;
}

but the following piece of code does not work where I am trying to update the object by itself directly.

private void OnMessageReceived(TestMessage obj)
{
    _selectedItem= obj.messageTest;
    RaisePropertyChanged(() => SelectedItem);
}

Code Implementation is as follows:

<MvxRecyclerView
    android:id="@+id/TestRecyclerView"
    android:scrollbars="vertical"
    android:layout_width="match_parent"
    android:layout_height="0dp"
    local:MvxBind="ItemsSource TestsViews; ; ItemClick ItemSelected" /> 

MainViewModel

public MainViewModel SelectedItem
{
    get { return _selectedItem; }
    set
    {
        _selectedItem = value;
        ShowViewModel<DetailViewModel>(
        new DetailViewModel.Parameter
        {
            Age = _selectedItem.Age,
            Name = _selectedItem.Name,
            Gender = _selectedItem.Gender,
        });
        RaisePropertyChanged(() => SelectedItem);
    }
}

public virtual ICommand ItemSelected
{
   get
    {
      return new MvxCommand<TestViewModel>(item =>
      {
          SelectedItem = item;
      });
     }
}

private void OnMessageReceived(TestMessage obj)
{
    _selectedItem.Age= obj.messageTest.Age;
    _selectedItem.Name= obj.messageTest.Name;
    _selectedItem.Gender= obj.messageTest.Gender;
 }

TestMessage

public class TestMessage : MvxMessage
{
    public MainModel messageTest { get; set; }

    public TestMessage(object sender, MainModel editTest) : base(sender)
    {
        messageTest = editTest;
    }
}

DetailViewModel

public TestViewModel EditTest
{
    get { return _editTest; }
    set
    {
        _editTest = value;
        RaisePropertyChanged(() => EditTest);
    }
}

public DetailViewModel(IMvxMessenger messenger)
{
    _messenger = messenger;
}

public void Save()
{
    UpdateValues();
}

public void UpdateValues()
{
    var message = new TestMessage(this, _editTest);
    _messenger.Publish(message, typeof(TestMessage));
}

public void Init(Parameter param)
{
    _editTest = new TestViewModel();
    _editTest.Age = param.Age;
    _editTest.Name = param.Name;
    _editTest.Gender = param.Gender;

public class Parameter
{
    public double Age { get; set; }
    public int Gender { get; set; }
    public string Name { get; set; }
}

DetailView xml

<EditText
    style="@style/InputNumbersEditText"
    android:layout_weight="1"
    android:layout_width="0dp"
    android:layout_height="44dp"
    android:gravity="center_vertical|right"
    android:hint="00.000"
    local:MvxBind="Text EditTest.Age, Converter=Nullable;" />

TestViewModel

public class TestViewModel : BaseViewModel
{
    public string? Name { get; set; }
    public double? Age { get; set; }
    public int? Gender { get; set; }
}

NullableValueConverter

public class NullableValueConverter : MvxValueConverter
{
    public override object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        if (string.IsNullOrEmpty(value?.ToString()))
        {
            return parameter;
        }
        return value;
    }

    public override object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        if (value == null || string.IsNullOrEmpty(value.ToString()))
        {
            return null;
        }

        return value;
    }
}

Upvotes: 0

Views: 56

Answers (2)

Sven-Michael St&#252;be
Sven-Michael St&#252;be

Reputation: 14760

private void OnMessageReceived(TestMessage obj)
{
    _selectedItem= obj.messageTest;
    RaisePropertyChanged(() => SelectedItem);
}

This can't work, because your are just changing the reference of _selectedItem to point to another object. But this object is not included in the list that is used to show in the recycler view. You are not updating a object, just a reference! You should definitely have a look at the basics of C# to understand this kind of data structure. E.g. Reference vs. Value Type

Your code is a bit faulty.

  • You SelectedItem has the type MainViewModel
  • Your click command gets a item of type TestViewModel

    public virtual ICommand ItemSelected
    {
        get
        {
            return new MvxCommand<TestViewModel>(item =>
            {
                SelectedItem = item;
            });
        }
    } 
    

Normally this should work:

private void OnMessageReceived(TestMessage obj)
{
    _selectedItem.Age= obj.messageTest.Age;
    _selectedItem.Name= obj.messageTest.Name;
    _selectedItem.Gender= obj.messageTest.Gender;
}

but with a TestViewModel like

public class TestViewModel : BaseViewModel
{
    private string? name;
    public string? Name { get{ return name; } set { SetProperty(ref name, value); } }
    // same for Age and Gender
}

SetProperty sets the value and calls the OnPropertyChanged event.

Upvotes: 3

Plac3Hold3r
Plac3Hold3r

Reputation: 5192

Updated Answer

Assigning the _selectedItem a new TestViewModel breaks the reference link it has to the TestViewModel held in the TestsViews data source list. While assigning the individual properties maintains the reference to the orginal TestViewModel in the TestsViews list.

Orginal Answer

As you are updating the backing field, _selectedItem, so when you receive the message event the RaisePropertyChanged event defined in the set of your SelectedItem property will never run. You will have to manually trigger the RaisePropertyChanged in order to trigger the binding update.

Try changing your current method:

private void OnMessageReceived(TestMessage obj)
{
    _selectedItem= obj.messageTest;
}

To raise the property changed event:

private void OnMessageReceived(TestMessage obj)
{
    _selectedItem = obj.messageTest;
    RaisePropertyChanged(() => SelectedItem);
}

Upvotes: 0

Related Questions