I would like to cache some UI-specific properties only at the time an object is DataBound, if it gets bound.
Say I have
public interface IAmA<T> ()
{
T Value {get;set;}
}
public class MyString : IAmA<string>, INotifyPropertyChanged {
private string _value = String.Empty;
public virtual string Value
{
get
{
return this._value;
}
set
{
this._value = value;
OnPropertyChanged();
}
}
public virtual event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
var e = new PropertyChangedEventArgs(propertyName);
handler(this, e);
}
}
}
Is it possible to implement an action or event to fire at the time the object is databound?
event Action OnDataBound { ... }
So I can detect, say, when
MyString myStrObj = new MyString("text");
myStrObj.OnDataBound += CacheUIElements();
Do.OtherStuff();
otherObj.DataSource = myStrObj; //causes CacheUIElements() to fire
You can very well do it in the property setter and call the Delegate.
In above case it will be for the OtherObj.DataSource. Because you want the Action to get triggered when you are setting the datasource. So it should be in the setter of whichever property where you want the action to trigger.
class OtherClass
{
DataSource
{
get{return _dataSource}
set
{
_dataSource = value;
OnPropertyChanged("DataSource");
OnDataBound();
}
}
}
If not, assuming OtherClass Implements OnPropertyChanged and triggers for DataSource like this, If its a framework element for sure it will implement NotifyPropertyChanged for DataSource because only then UI will refresh with the changes.
class OtherClass: INotifyPropertyChanged
{
DataSource
{
get{return _dataSource}
set
{
_dataSource = value;
OnPropertyChanged("DataSource");
}
}
}
Then you can subscribe to this event and check against the property name and trigger the OnDataBound(); e.g:
otherClassObj.PropertyChanged += OtherClassPropertyChanged()
private void OtherClassPropertyChanged(Obj sender, PropertyChangedEventargs e)
{
if(e.PropertyName = "DataSource")
{
OnDataBound();
}
}
Related
I'm having a problem with subscribing to PropertyChangedEventHandler event of a property on a bound instance of my class.
Here is the setup:
XAML:
<CheckBox IsChecked="{Binding MyObservableClassInstance.BooleanProperty}"/>
DataContext class property:
public MyObservableClass MyObservableClassInstance
{
get { return _myClassInstance; }
set
{
_myClassInstance= value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("MyObservableClassInstance"));
}
}
the initialisation and subscription to PropertyChanged event (this subscribed method is never reached):
MyObservableClassInstance = new MyObservableClass();
MyObservableClassInstance.PropertyChanged += OnMyObservableClassPropertyChanged; // <--- This method is never hit
my observable class: (the BooleanProperty is working normally with the the XAML checkbox binding)
public class MyObservableClass : INotifyPropertyChanged
{
bool _mybool = false;
public event PropertyChangedEventHandler PropertyChanged;
public bool BooleanProperty
{
get { return _mybool; }
set
{
_mybool = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("BooleanProperty")); // <--- This is reached normally on checking/unchecking the checkbox
}
}
}
So why is my OnMyObservableClassPropertyChanged method never reached upon Invoking the PropertyChanged event?
You have to attach (and detach) the handler method to the PropertyChanged event whenever the MyObservableClass property value changes.
public MyObservableClass MyObservableClassInstance
{
get { return _myClassInstance; }
set
{
if (_myClassInstance != null)
{
_myClassInstance.PropertyChanged -= OnMyObservableClassPropertyChanged;
}
_myClassInstance = value;
if (_myClassInstance != null)
{
_myClassInstance.PropertyChanged += OnMyObservableClassPropertyChanged;
}
PropertyChanged?.Invoke(
this, new PropertyChangedEventArgs(nameof(MyObservableClassInstance)));
}
}
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.
What is the best way to bind a property to a control so that when the property value is changed, the control's bound property changes with it.
So if I have a property FirstName which I want to bind to a textbox's txtFirstName text value. So if I change FirstName to value "Stack" then the property txtFirstName.Text also changes to value "Stack".
I know this may sound a stupid question but I'll appreciate the help.
You must implement INotifyPropertyChanged And add binding to textbox.
I will provide C# code snippet. Hope it helps
class Sample : INotifyPropertyChanged
{
private string firstName;
public string FirstName
{
get { return firstName; }
set
{
firstName = value;
InvokePropertyChanged(new PropertyChangedEventArgs("FirstName"));
}
}
#region Implementation of INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
public void InvokePropertyChanged(PropertyChangedEventArgs e)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, e);
}
#endregion
}
Usage :
Sample sourceObject = new Sample();
textbox.DataBindings.Add("Text",sourceObject,"FirstName");
sourceObject.FirstName = "Stack";
A simplified version of the accepted answer that does NOT require you to type names of properties manually in every property setter like OnPropertyChanged("some-property-name"). Instead you just call OnPropertyChanged() without parameters:
You need .NET 4.5 minimum.
CallerMemberName is in the System.Runtime.CompilerServices namespace
public class Sample : INotifyPropertyChanged
{
private string _propString;
private int _propInt;
//======================================
// Actual implementation
//======================================
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
//======================================
// END: actual implementation
//======================================
public string PropString
{
get { return _propString; }
set
{
// do not trigger change event if values are the same
if (Equals(value, _propString)) return;
_propString = value;
//===================
// Usage in the Source
//===================
OnPropertyChanged();
}
}
public int PropInt
{
get { return _propInt; }
set
{
// do not allow negative numbers, but always trigger a change event
_propInt = value < 0 ? 0 : value;
OnPropertyChanged();
}
}
}
Usage stays the same:
var source = new Sample();
textbox.DataBindings.Add("Text", source, "PropString");
source.PropString = "Some new string";
Hope this helps someone.
When i first assign the bidning to the Label in its constructor, the Label binds correctly and displays correct information based on current ComponentData value of CurrentMarket class. However when ComponentData changes, OnPropertyChanged event fires ok, but the ProperyChanged handler is always NULL. Can someone kindly suggest what am i doing wrong?
I have a label and i set the binding like this:
public StyledLabel(string Property, int i)
{
Binding BindingText = new System.Windows.Data.Binding(Property);
BindingText.Source = Statics.CurrentMarket.ComponentData;
BindingText.Converter = new TextConverter();
this.SetBinding(Label.ContentProperty, BindingText);
}
Current market class looks like this:
public class CurrentMarket : INotifyPropertyChanged
{
string sMarket = "";
ComponentData cComponentData;
public string Market
{
set
{
sMarket = value;
OnPropertyChanged("Market");
ComponentData = SharedBoxAdmin.Components[sMarket];
}
get
{
return sMarket;
}
}
public ComponentData ComponentData
{
get { return cComponentData; }
set
{
cComponentData = value;
OnPropertyChanged("ComponentData");
}
}
public CurrentMarket()
{
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string info)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(info));
}
}
}
Thanks!
Try specifying the property name that you want to bind to as the Path of the Binding (rather than as part of the Source):
Binding BindingText = new System.Windows.Data.Binding(Property);
BindingText.Source = Statics.CurrentMarket;
BindingText.Path = new PropertyPath("ComponentData");
BindingText.Converter = new TextConverter();
this.SetBinding(Label.ContentProperty, BindingText);
I just recently discovered an INotifyPropertyChange interface. I managed to implement this interface in my clss and everything works fine. However I was wondering if it is possible to intercept this event in code and fire a function
Let's say that I have a function
DoStuff()
and I wan't to fire this function everytime property1, property2 or property3 changes.
Of course I could put this function in set block in my class but this is not a good idea(I think).
If you mean to internal method that'll handle this event you can do it by registering to the event in the class constructor. For example:
public class AnswerViewModel : IAnswerViewModel
{
public event PropertyChangedEventHandler PropertyChanged;
private string content;
public AnswerViewModel()
{
PropertyChanged += (sender, args) => DoStuff();
}
public string Content
{
get { return content; }
set
{
content = value;
PropertyChanged(this, new PropertyChangedEventArgs("Content"));
}
}
public void DoStuff()
{
// this method will be called whenever PropertyChanged event raised
}
}
If the intercepting method belongs to other class:
public class PropertiesInterceptor
{
private readonly AnswerViewModel viewModel;
private readonly List<string> propertiesToIntercept =
new List<string> { "property1", "property2", "property3" };
public PropertiesInterceptor(AnswerViewModel viewModel)
{
this.viewModel = viewModel;
viewModel.PropertyChanged += OnPropertyChanged;
}
private void OnPropertyChanged(object sender, PropertyChangedEventArgs args)
{
if (propertiesToIntercept.Contains(args.PropertyName))
{
DoStuff();
}
}
private void DoStuff()
{
// Do something with viewModel
}
}
Intercept the PropertyChanged Event:
http://msdn.microsoft.com/en-us/library/system.componentmodel.inotifypropertychanged.propertychanged.aspx
You could fire the method from a RaisePropertyChanged() method:
public int Property1
{
get { return this.property1; }
set
{
if (this.property1 != value)
{
this.property1 = value;
RaisePropertyChanged("Property1");
}
}
}
private void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
DoStuff(); // Call DoStuff here.
}
Stealing Elisha's answer to answer your question in Merlyn's answer
public class AnswerViewModel : IAnswerViewModel
{
public event PropertyChangedEventHandler PropertyChanged;
private string property1;
private string property2;
private string propertyX;
public AnswerViewModel()
{
PropertyChanged += (sender, args) =>
{
if(args.PropertyName == "Property1" || args.PropertyName == "Property2")
DoStuff();
}
}
public string Property1
{
get { return content; }
set
{
property1 = value;
PropertyChanged(this, new PropertyChangedEventArgs("Property1"));
}
}
public string Property2
{
get { return content; }
set
{
property2 = value;
PropertyChanged(this, new PropertyChangedEventArgs("Property2"));
}
}
public string PropertyX
{
get { return content; }
set
{
propertyX = value;
PropertyChanged(this, new PropertyChangedEventArgs("PropertyX"));
}
}
public void DoStuff()
{
// this method will be called whenever PropertyChanged event raised from Property1 or Property2
}
}
If the class DoStuff is in is a member you can do
private otherClass
public AnswerViewModel()
{
PropertyChanged += (sender, args) =>
{
if(args.PropertyName == "Property1" || args.PropertyName == "Property2")
otherClass.DoStuff();
}
}
Otherwise you can just have otherClass register a event on its own in your main code.
Did you need it to replace the existing NotifyPropertyChanged event handlers, or just get called when NotifyPropertyChanged is called?
If you mean the second, you can simply register an event handler
edit
You can add an event handler that gets called on NotifyPropertyChanged, checks if the property parameter is equal to Property1, Property2, or Property3, and only then forwards it to the actual function you want to call.