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.
Related
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.
I have two different objects instantiated in my UWP program. Their class definitions look like this:
namespace StackOverflow
{
public class FirstClass : INotifiyPropertyChanged
{
private bool isEnabled = false;
public bool IsEnabled
{
get => isEnabled;
set
{
isEnabled = value;
NotifyPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] string propertyName = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public class SecondClass : INotifiyPropertyChanged
{
private bool isEnabled = false;
public bool IsEnabled
{
get => isEnabled;
set
{
isEnabled = value;
NotifyPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] string propertyName = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
The binding on the XAML side currently looks like this:
<Button Content="Press me if you can" IsEnabled="{x:Bind FirstClass.IsEnabled, Mode=OneWay}"/>
With this code, Button.IsEnabled is only dependent on FirstClass.IsEnabled, what I'd like to achieve is for the Button.IsEnabled property to be dependent on FirstClass.IsEnabled AND SecondClass.IsEnabled. How can I obtain such a result?
I wanted to implement MultiBinding, but it seems to be a WPF-only feature
We can't bind two properties directly in xaml.It seems to only be able to operate in code-behind.When you set the IsEnabled of FirstClass or SecondClass,then judge the IsEnabled of Button manually.
I try to binding textblock usercontrol with property of my class, but it only works at initial stage, I have implement IPropertyChnaged in my class.
In my class, _Feedbackpos (field of property) would change in background, I don't know how to solve this problem.
my class
public class TestControl : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string propertyname)
{
if(PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyname));
}
}
private double _Feedbackpos;
public double Feedbackpos
{
get
{
return _Feedbackpos;
}
set
{
_Feedbackpos = value;
NotifyPropertyChanged("Feedbackpos");
}
}
//it's a callback function, it would excute when detect feedback position of controller change
private void ReadFeedbackpos()
{
_Feedbackpos = Controller.Read();
}
}
application windows
TestControl TestDll = new TestControl();
Binding BindingTxtBlk = new Binding(){Source= TestDll, Path = new Property("Feedbackpos")};
FeedbackPosTxtBlk.Setbinding(Textblock.TextProperty,BindingTxtBlk);
Change the function ReadFeedbackpos() to
private void ReadFeedbackpos()
{
Feedbackpos = Controller.Read();
}
Otherwise NotifyPropertyChanged("Feedbackpos"); will never get called.
Here's what I have now:
class MyClass
{
public string status;
private void DoSomething()
{
// do something and make change to this.status;
}
}
class MyClass2
{
public List<MyClass> MyClassLst;
private void DetectChangeInList()
{
// if the status property of an item in this.MyClassLst changed, remove this item from list
}
}
I have a List<MyClass>, and each of the MyClass will do some work and change the property status. I want to detect if any of the MyClass has its status changed and remove this item from MyClassLst.
I read about something on event but not very clearly about how to make it work.
If you need to be notified about changes to individual properties of each MyClass instance, it's not something that can magically happen.
Your MyClass will have to be responsible for firing an event whenever something changes (usually the PropertyChanged event, i.e. the INotifyPropertyChanged interface), and the other class will have to attach a handler to each item in the list to get notified.
C#6 has a couple of syntactic improvements which simplify this a bit, but you still have lots of work to do for each property:
public class Model : INotifyPropertyChanged
{
// this is the event which gets fired
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
// you need to raise the event in each property's setter
private _someValue;
public string SomeValue
{
get { return _someValue; }
set { if (value != _someValue) { _someValue = value; OnPropertyChanged(); } }
}
private _anotherVal;
public string AnotherValue
{
get { return _anotherVal; }
set { if (value != _anotherVal) { _anotherVal = value; OnPropertyChanged(); } }
}
}
In your case, it would be:
public class MyClass : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
// Never use public fields!
// Status should be a property which gets and sets
// the actual private backing field (_status).
private _status;
public string Status
{
get { return _status; }
set { if (value != _status) { _status = value; OnPropertyChanged(); } }
}
}
You will also most likely want to change List<MyClass> into your own implementation of ICollection<T> which will attach and detach these handlers as you add or remove items. It is usually done by deriving from Collection<T> and overriding relevant methods. If you are not comfortable with that, a slightly simpler approach might be to make the list private and expose Add/Remove and similar methods where you will attach/detach to the PropertyChanged event.
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"));