Why return bool from SetProperty? It's not used.
This is from boilerplate code coming out of a Xamarin.Forms template.
It's not used in inherited classes either.
public class BaseViewModel : INotifyPropertyChanged
{
private bool isBusy = false;
public bool IsBusy
{
get { return isBusy; }
set { SetProperty(ref isBusy, value); }
}
protected bool SetProperty<T>(ref T backingStore,
T value,
[CallerMemberName] string propertyName = "",
Action onChanged = null)
{
if (EqualityComparer<T>.Default.Equals (backingStore, value))
return false;
backingStore = value;
onChanged?.Invoke();
OnPropertyChanged(propertyName);
return true;
}
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
var changed = PropertyChanged;
if (changed == null)
return;
changed.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
The return value of this SetProperty method indicates if it has effectively changed its underlying backing field.
The IsBusy implementation doesn't use the return value of SetProperty, but in a derived class, which can access this protected SetProperty method, its return value could be used to perform additional specialized tasks.
By the way, I actually didn't expect the BaseViewModel class to have an IsBusy property with a public setter. As it is now, the outside world can determine if a BaseViewModel instance is busy or not. As I see it, that's somewhat strange.
Related
I'm binding an activity indicator to a property called IsLoading to show that the page is busy (e.g. processing API calls). I need to implement this in all my MAUI app pages, so my question, how can I re-use this code? The property is implemented as follows:
public event PropertyChangedEventHandler PropertyChanged;
private bool isLoading;
public bool IsLoading
{
get => isLoading;
set
{
isLoading = value;
OnPropertyChanged();
}
}
private void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
You probably want to put this in a base class that you inherit from. For example.
Create a class/file BaseViewModel.cs, of course the name can be whatever you want. It might look like this:
public class BaseViewModel
{
public event PropertyChangedEventHandler PropertyChanged;
private bool isLoading;
public bool IsLoading
{
get => isLoading;
set
{
isLoading = value;
OnPropertyChanged();
}
}
private void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Now, whenever you create a new view model, you need to inherit from this. Let's say that you create a EditPersonViewModel, that would look like this:
public class EditPersonViewModel : BaseViewModel // This is where you inherit from the BaseViewModel
{
private bool isSaved;
public bool IsSaved
{
get => isSaved;
set
{
isSaved = value;
OnPropertyChanged();
}
}
}
Now you have access to all the things that are also in BaseViewModel. So you can set IsLoading to true or false, or you can implement a new property, IsSaved in my above example, and you can still call OnPropertyChanged to make the UI aware of the value change.
So I have
boolean variableName = false
Is it possible to write a event (observeVariableName) that is "observing" variableName all the time till it changes to true and when it is on true the event does something? Like for example:
public void observeVariableName() //triggers when variableName == true
{
// do actions here
variableName = false
}
It's not possible just with having a boolean variable. You can wrap that value in a class and add an event there, if you want the event to be triggered every time the value changes, you can do it in the setter method of the property.
Try to use implement interface INotifyPropertyChanged on your class that contains your boolean.
For exemple,
public class DemoCustomer : INotifyPropertyChanged
{
private bool _selected;
public bool Selected
{
get
{
return _selected;
}
set
{
_selected = value;
NotifyPropertyChanged("Selected");
}
}
public event PropertyChangedEventHandler PropertyChanged;
// This method is called by the Set accessor of each property.
// The CallerMemberName attribute that is applied to the optional propertyName
// parameter causes the property name of the caller to be substituted as an argument.
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
Then, you listen this event.
var d = new DemoCustomer();
d.PropertyChanged += (s,e) => { if(e.PropertyName = "Selected" && ((DemoCustomer)s).Selected) { //do something}};
You should use property variableName.
public bool variableName {
get {
return variableName;
}
set {
variableName = value;
if (value)
// do stuff;
}
}
Look for instructions.
I'm trying to simplify an INotifyPropertyChanged implementation without using a base class. The best I've come up with is this extension method:
public static bool SetNotify<T>(
this INotifyPropertyChanged sender,
ref T storage,
T value,
PropertyChangedEventHandler handler,
[CallerMemberName] string propertyName = null)
{
if (Equals(storage, value))
{
return false;
}
storage = value;
handler?.Invoke(sender, new PropertyChangedEventArgs(propertyName));
return true;
}
This way I can implement INotifyPropertyChanged in a class, declare a PropertChangedEventHandler event handler, and use it to trigger an event like so:
this.SetNotify(ref _name, value, PropertyChanged);
But I'm curious to know if it's possible to fire an event in a SetNotify() extension method to this, while not passing the event handler. Seeing as the event handler is public, it's accessible from outside the object. So why isn't this allowed?
public static bool SetNotify2<T>(
this INotifyPropertyChanged sender,
ref T storage,
T value,
[CallerMemberName] string propertyName = null)
{
if (Equals(storage, value))
{
return false;
}
PropertyChangedEventHandler handler = sender.PropertyChanged; // <-- Not allowed
storage = value;
handler?.Invoke(sender, new PropertyChangedEventArgs(propertyName));
return true;
}
Does anyone know if BindableBase is still a viable or should we stick with INotifyChanged events? It seems like BindableBase has lost its luster quickly. Thanks for any info you can provide.
INotifyPropertyChanged
The ViewModel should implement the INotifyPropertyChanged interface and should raise it whenever the propertychanges
public class MyViewModel : INotifyPropertyChanged
{
private string _firstName;
public event PropertyChangedEventHandler PropertyChanged;
public string FirstName
{
get { return _firstName; }
set
{
if (_firstName == value)
return;
_firstName = value;
PropertyChanged(this, new PropertyChangedEventArgs("FirstName"));
}
}
}
}
Problem is with ICommand interface as most of the code is duplicated also since it passes string it becomes error prone.
Whereas Bindablebase is an abstract class that implements INotifyPropertyChanged interface and provide SetProperty<T>.You can reduce the set method to just one line also ref parameter allows you to update its value. The BindableBase code below comes from INotifyPropertyChanged, The .NET 4.5 Way - Revisited
public class MyViewModel : BindableBase
{
private string _firstName;
private string _lastName;
public string FirstName
{
get { return _firstName; }
set { SetProperty(ref _firstName, value); }
}
}
//Inside Bindable Base
public abstract class BindableBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] String propertyName = null)
{
if (Equals(storage, value))
{
return false;
}
storage = value;
this.OnPropertyChanged(propertyName);
return true;
}
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler eventHandler = this.PropertyChanged;
if (eventHandler != null)
{
eventHandler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
It is not a choice between these two.
BindableBase implements INotifyPropertyChanged.
So if you use BindableBase you will be using INotifyPropertyChanged.
INotifyPropertyChanged is more or less mandatory when implementing MVVM using DataBinding.
Whether to use BindableBase or an other implementation depends on preference and use of Prism.
To expand on Rohit's answer, if you are using .NET 4.6 you can take advantage of the Null-conditional operator and simplify the OnPropertyChanged method in the following way:
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
INotifyPropertyChanged, The .NET 4.6 Way explains it in more detail.
In XAML, i have a textblock
<TextBlock x:Name="block" Text="{Binding b1}"/>
and in c# i created a property
public int _b1;
public int b1
{
get { return _b1; }
set
{
_b1 = value;
}
}
public MainPage()
{
InitializeComponent();
block.DataContext = this;
}
this worked fine, textblock show the _b1. But when i add a button to chage the _b1 variable
private void bt_click(object sender, RoutedEventArgs e)
{
_b1 = 4;
}
the textblock didn't update ?????
To add to dotNet's answer (which is the correct answer), use a baseclass where you implement INotifyPropertyChanged if you want to avoid redundand code: (this is one example, there are other ways to implement this)
public abstract class BindableBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
{
if (Equals(storage, value)) { return false; }
storage = value;
OnPropertyChanged(propertyName);
return true;
}
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var eventHandler = PropertyChanged;
if (eventHandler != null)
{
eventHandler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
And use it like so:
class MyClass: BindableBase
{
private int _b1;
public int B1
{
get { return _b1; }
set { SetProperty(ref _b1, value); }
}
}
For UI to update automatically upon property value change, your property needs to either be a DependencyProperty or your class needs to implement INotifyPropertyChanged interface.
For creating a DependencyProperty, you could use Visual Studio's propdp snippet (type propdp inside your class and press Tab) and fill in respective values. If you want to go INotifyPropertyChanged path, you'll need to write the following code in the setter of your property (AFTER setting the value of _b1):
if(PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("b1"));