Reputation: 57
On another post (Prism BindableBase.SetProperty()), @brian-lagunas said he prefers to expose the Model as a property and bind the View to the model properties. He gave the following example code:
public class MyViewModel : BindableBase
{
private Person _myPerson;
public Person Person
{
get { return _myPerson; }
set { SetProperty(ref _myPerson, value); }
}
}
However, I'm unsure how I'm to bind to the properties. Does this notify of property changes?
Update: Is this how I would implement INPC on the model? If so, what have I gained that I would not have had by putting the properties in the view model which already supports INPC?
public class Person : INotifyPropertyChanged
{
private string _FirstName;
private string _LastName;
public string FirstName { get { return _FirstName; } set => SetProperty(ref _FirstName, value); }
public string LastName { get { return _LastName; } set => SetProperty(ref _LastName, value); }
private void SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
{
if (!Equals(storage,value))
{
storage = value;
OnPropertyChanged(propertyName);
}
}
private void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
Upvotes: 2
Views: 2642
Reputation: 5799
Given the following model:
public class Person
{
public string FirstName { get; set; }
}
Binding to the FirstName
property, any update to FirstName would not have a notification of property changes. The only way to accomplish this is if your model implements INotifyPropertyChanged
.
That said, binding directly to a model can be an excellent strategy. As a common example if you have a ListView
, you can usually bind to an ObservableCollection<SomeModel>
safely. If you're working with a relatively small dataset and can afford the overhead of reloading your data source then your pretty safe all the way around.
Now as far as how to bind to a property, given:
public class ViewAViewModel : BindableBase
{
private Person _myPerson;
public Person MyPerson
{
get { return _myPerson; }
set { SetProperty( ref _myPerson, value ); }
}
}
Your XAML Markup would look something like:
<Label Text="{Binding MyPerson.FirstName}" />
UPDATE:
As described in your updated question, yes you could implement INotifyPropertyChanged
like that. Remember it's already done for you with BindableBase
so given your example you could simply inherit from BindableBase
... Another good option is if you use MvvmHelpers from James Montemagno you could use his ObservableObject
on your Models and the BaseViewModel
for your ViewModels which gives you properties like Title, SubTitle, Icon, IsBusy, IsNotBusy.
The benefit of doing this of course is that you now have an ability to bind directly to a Model. A View rarely knows just about a Model consider the following:
Person Model
public class Person : BindableBase
{
private string _firstName;
public string FirstName
{
get { return _firstName; }
set { SetProperty( ref _firstName, value ); }
}
private string _lastName;
public string LastName
{
get { return _lastName; }
set { SetProperty( ref _lastName, value ); }
}
private DateTime _dob;
public DateTime DOB
{
get { return _dob; }
set { SetProperty( ref _dob, value ); }
}
}
User Profile ViewModel
public class UserProfileViewModel : BindableBase, INavigationAware
{
INavigationService _navigationService { get; }
IPageDialogService _pageDialogService { get; }
public UserProfileViewModel( INavigationService navigationService, IPageDialogService pageDialogService )
{
_navigationService = navigationService;
_pageDialogService = pageDialogService;
DoFooCommand = new DelegateCommand( () => _pageDialogService.DisplayAlertAsync( "Alert", "Foo", "Ok" ) );
}
private bool shouldSave = false;
private string _title;
public string Title
{
get { return _title; }
set { SetProperty( ref _title, value ); }
}
private Person _user;
public Person User
{
get { return _user; }
set { SetProperty( ref _user, value ); }
}
public DelegateCommand DoFooCommand { get; }
public void OnNavigatingTo( NavigationParameters parameters )
{
Title = AppResources.UserProfilePageTitle;
User = parameters.GetValue<Person>( "currentUser" );
User.PropertyChanged += ( sender, e ) => shouldSave = true;
}
public void OnNavigatedFrom( NavigationParameters parameters )
{
if( shouldSave )
{
// Do your persistence here.
}
}
public void OnNavigatedTo( NavigationParameters parameters )
{
User.DOB = new DateTime( 2017, 1, 1 );
}
}
User Profile View
<ContentPage Title="{Binding Title}">
<StackLayout>
<Label Text="First Name" />
<Entry Text="{Binding User.FirstName}" />
<Label Text="Last Name" />
<Entry Text="{Binding User.LastName}" />
<Label Text="{Binding User.DOB}" />
<Button Text="Do Foo" Command="{Binding DoFooCommand}" />
</StackLayout>
</ContentPage>
Given this code there should be a couple of things worth noting:
1) Our ViewModel consists of various things that have nothing what so ever to do with our Model like Commands, or properties like the Title. It is also likely consuming various services like the INavigationService
or IPageDialogService
2) Second we may want to restrict which properties a user can edit and the ViewModel can edit.
3) If our model implements INotifyPropertyChanged
we can attach an event handler to let us know that our model changed after we set it so we can persist those changes.
4) It makes the XAML more readable about our intent. We aren't binding to some magic property called FirstName. We are really binding to our Person model's FirstName property.
Upvotes: 5
Reputation: 26075
I'm unsure how I'm to bind to the properties.
To bind to properties of model you have to data bind to property which exposes model and its property, i.e. if you have exposed your model with property 'Person' and want to bind to Name property of person, you'll need no bind to 'Person.Name'.
Does this notify of property changes?
https://msdn.microsoft.com/en-us/library/dn736263(v=pandp.50).aspx
SetProperty method will take care of notifying property change.
Upvotes: 0