I found some solution of INotifyPropertyChanged wrapper but it doesnot do anything. What I am doing wrong? Name updating asynchronous but value in windows do not change. Why?* *
namespace WpfApplication1.ViewModel
{
class CustomerViewModel : INotifyPropertyChanged, IWeakEventListener
{
private readonly Customer _customer;
internal CustomerViewModel(Customer customer)
{
if (customer == null)
{
throw new ArgumentNullException("personModel");
}
_customer = customer;
NotifyPropertyChangedEventManager.AddListener(_customer, this);
Action Start = new Action(UpdateAsync);
IAsyncResult result = Start.BeginInvoke(null, null);
}
private void UpdateAsync()
{
int i = 0;
while (true)
{
System.Threading.Thread.Sleep(1000);
_customer.Name = (++i).ToString();
}
}
public string Name
{
get { return _customer.Name; }
set { _customer.Name = value; }
}
public string JobTitle
{
get { return _customer.Work; }
set { _customer.Work = value; }
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
#region IWeakEventListener Members
public bool ReceiveWeakEvent(Type managerType, object sender, EventArgs e)
{
PropertyChangedEventArgs pcArgs = e as PropertyChangedEventArgs;
if (pcArgs != null)
{
OnPropertyChanged(pcArgs.PropertyName);
return true;
}
return false;
}
#endregion
}
public class NotifyPropertyChangedEventManager : WeakEventManager
{
public static NotifyPropertyChangedEventManager CurrentManager
{
get
{
var manager_type = typeof(NotifyPropertyChangedEventManager);
var manager = WeakEventManager.GetCurrentManager(manager_type) as NotifyPropertyChangedEventManager;
if (manager == null)
{
manager = new NotifyPropertyChangedEventManager();
WeakEventManager.SetCurrentManager(manager_type, manager);
}
return manager;
}
}
public static void AddListener(INotifyPropertyChanged source, IWeakEventListener listener)
{
CurrentManager.ProtectedAddListener(source, listener);
return;
}
public static void RemoveListener(INotifyPropertyChanged source, IWeakEventListener listener)
{
CurrentManager.ProtectedRemoveListener(source, listener);
return;
}
protected override void StartListening(object source)
{
((INotifyPropertyChanged)source).PropertyChanged += Source_PropertyChanged; return;
}
protected override void StopListening(object source)
{
((INotifyPropertyChanged)source).PropertyChanged -= Source_PropertyChanged;
return;
}
void Source_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
{ CurrentManager.DeliverEvent(sender, e); };
}
}
}
Customer
public class Customer:INotifyPropertyChanged
{
public string Name { get; set; }
public string Number { get; set; }
public string Work { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
And Xaml code
<Grid>
<Label Content="{Binding Name}"></Label>
</Grid>
Instead of doing the PropertyChanged yourself, you can also use Fody.PropertyChanged. (https://github.com/Fody/PropertyChanged)
You can install it via Nuget in Visual Studio.
What is does it automaticly adds the PropertyChanged implementation when compiling.
Your code:
using PropertyChanged;
[ImplementPropertyChanged]
public class Person
{
public string GivenNames { get; set; }
public string FamilyName { get; set; }
public string FullName
{
get
{
return string.Format("{0} {1}", GivenNames, FamilyName);
}
}
}
What gets compiled:
public class Person : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
string givenNames;
public string GivenNames
{
get { return givenNames; }
set
{
if (value != givenNames)
{
givenNames = value;
OnPropertyChanged("GivenNames");
OnPropertyChanged("FullName");
}
}
}
string familyName;
public string FamilyName
{
get { return familyName; }
set
{
if (value != familyName)
{
familyName = value;
OnPropertyChanged("FamilyName");
OnPropertyChanged("FullName");
}
}
}
public string FullName
{
get
{
return string.Format("{0} {1}", GivenNames, FamilyName);
}
}
public virtual void OnPropertyChanged(string propertyName)
{
var propertyChanged = PropertyChanged;
if (propertyChanged != null)
{
propertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
There are also other features, please read them at the wiki: https://github.com/Fody/PropertyChanged/wiki
I know this question is old and doesn't exactly answer your question. I thought this would be a better solution for you.
Bindable uses a dictionary as a property store. It's easy enough to add the necessary overloads for a subclass to manage its own backing field using ref parameters.
No magic string
No reflection
Can be improved to suppress the default dictionary lookup
The code:
public class Bindable : INotifyPropertyChanged
{
private Dictionary<string, object> _properties = new Dictionary<string, object>();
/// <summary>
/// Gets the value of a property
/// <typeparam name="T"></typeparam>
/// <param name="name"></param>
/// <returns></returns>
protected T Get<T>([CallerMemberName] string name = null)
{
object value = null;
if (_properties.TryGetValue(name, out value))
return value == null ? default(T) : (T)value;
return default(T);
}
/// <summary>
/// Sets the value of a property
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="value"></param>
/// <param name="name"></param>
protected void Set<T>(T value, [CallerMemberName] string name = null)
{
if (Equals(value, Get<T>(name)))
return;
_properties[name] = value;
OnPropertyChanged(name);
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
used like this
public class Item : Bindable
{
public Guid Id { get { return Get<Guid>(); } set { Set<Guid>(value); } }
}
You have to invoke OnPropertyChanged("Name");
the best place is on the Name poperty setter.
Very convenient way to implement INotifyPropertyChanged is to implement a following method in your class:
protected virtual bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(storage, value))
return false;
storage = value;
this.OnPropertyChanged(propertyName);
return true;
}
Then you use it like this:
private string _foo;
public string Foo
{
get { return this._foo; }
set { SetProperty(ref this._foo, value); }
}
However, because you are making a wrapper to other class, you can't use references to properties in your CustomerViewModel class. This can be worked around, but it will lead to writing a massive amount of code just to properly implement INotifyPropertyChanged.
Instead of wrapping your Customer class into some other, make it a public property instead:
private Customer _customer;
public Customer Customer
{
get { return this._customer; }
private set { SetProperty(ref this._customer, value); }
}
And in your XAML:
<Grid>
<Label Content="{Binding Customer.Name}"></Label>
</Grid>
Hope this helps.
Aside from the great answers above, you can probably achieve this behavior with a DynamicObject wrapper, wrapping the accessed class, and changing its properties on behalf, triggering a property changed event.
I haven't tried it yet (I'd probably go for Fody), but something like this pseudo might work:
public class InpcWrapperViewModel<T> : DynamicObject, INotifyPropertyChanged
{
public T Original { get; }
public InpcWrapperViewModel(T original)
{
Original = original;
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
//set property of Original with reflection etc.
//raise property changed
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
//set with reflection etc.
return true;
}
}
Usage:
dynamic x = new InpcWrapperViewModel<TEntity>(entity);
window.DataContext = x;
x.FirstName = "Hello"; //triggers INPC
If you're using Prism or any other DI etc. you can create a ViewModel factory that wraps the items automatically.
Again, the above is pseudo code never tested or tried.
Related
I created a MTObservableCollection class that derives from ObservableCollection for multithreading. This is how the function looks like:
public class MTObservableCollection<T> : ObservableCollection<T> where T: LogClassa
{
public MTObservableCollection() { }
public override event NotifyCollectionChangedEventHandler CollectionChanged;
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
NotifyCollectionChangedEventHandler CollectionChanged = this.CollectionChanged;
if (CollectionChanged != null)
foreach (NotifyCollectionChangedEventHandler nh in CollectionChanged.GetInvocationList())
{
DispatcherObject dispObj = nh.Target as DispatcherObject;
if (dispObj != null)
{
Dispatcher dispatcher = dispObj.Dispatcher;
if (dispatcher != null && !dispatcher.CheckAccess())
{
dispatcher.BeginInvoke(
(Action)(() => nh.Invoke(this,
new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset))),
DispatcherPriority.DataBind);
continue;
}
}
nh.Invoke(this, e);
}
}
}
And here is my LogClass
public class LogClass : INotifyPropertyChanged
{
private string timestamp;
private string title;
private string message;
private MTObservableCollection<LogClass> childRowLog = new MTObservableCollection<LogClass>();
public string TimeStamp
{
get { return timestamp; }
set
{
if( timestamp != value )
{
timestamp = value;
OnPropertyChanged("TimeStamp");
}
}
}
public string Title
{
get { return title; }
set
{
if( title!= value )
{
title= value;
OnPropertyChanged("Title");
}
}
}
public string Message
{
get { return message; }
set
{
if( message!= value )
{
message= value;
OnPropertyChanged("Message");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
So if I have two MTObservableCollection collections and I add elements to one of them such as:
public static MTObservableCollection<LogClass> parentLogCollection = new MTObservableCollection<LogClass>();
public MTObservableCollection<LogClass> childLogCollection = new MTObservableCollection<LogClass>();
childLogClass.Add( new LogClass { TimeStamp = "time", Title = "title", Message = "message" } );
Now if I want to add childLogCollection to parentLogCollection, I would do this:
parentLogCollection = new MTObservableCollection<LogClass>(childLogCollection);
I would think I need to create an implementation within the MBObservationCollection class which should be
public MTObservableCollection(MTObservableCollection<T> collection)
{
}
I just don't know what to input into it, how would I perform it?
Try changing the parent of your MTObservableCollection like this:
public MTObservableCollection(MTObservableCollection<T> collection) : base(collection)
{
}
When using INotifyPropertyChanged it is possible to do something like this to get the name of the property where the method invoking the event was called.
public void RaisePropertyChanged([CallerMemberName] string prop = "")
{
if (PropertyChanged != null)
{
PropertyChanged(new object(), new PropertyChangedEventArgs(prop));
}
}
Is there some other type of attribute to use to also get a reference to the class that contains that property? I want to be able to call RaisePropertyChanged() from any property from any of my viewmodel classes. All my viewmodel classes derive from a base so I'm thinking I can do something like this.
public void RaisePropertyChanged([CallerMemberName] string prop = "", [CallerClassRef] VmBase base = null)
{
if (PropertyChanged != null)
{
PropertyChanged(base, new PropertyChangedEventArgs(prop));
}
}
The keyword to access the current class reference is called this:
public void RaisePropertyChanged([CallerMemberName] string prop = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(prop));
}
}
This will work no matter how many times you derive this class, this is always the instance this function was called on.
Try using Fody - PropertyChanged add in. It helps you to inject INotifyPropertyChanged implementation to IL code.
Source code :
[ImplementPropertyChanged]
public class Person
{
public string GivenNames { get; set; }
public string FamilyName { get; set; }
public string FullName
{
get
{
return string.Format("{0} {1}", GivenNames, FamilyName);
}
}
}
When compiled
public class Person : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
string givenNames;
public string GivenNames
{
get { return givenNames; }
set
{
if (value != givenNames)
{
givenNames = value;
OnPropertyChanged("GivenNames");
OnPropertyChanged("FullName");
}
}
}
string familyName;
public string FamilyName
{
get { return familyName; }
set
{
if (value != familyName)
{
familyName = value;
OnPropertyChanged("FamilyName");
OnPropertyChanged("FullName");
}
}
}
public string FullName
{
get
{
return string.Format("{0} {1}", GivenNames, FamilyName);
}
}
public virtual void OnPropertyChanged(string propertyName)
{
var propertyChanged = PropertyChanged;
if (propertyChanged != null)
{
propertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Source code copied from : https://github.com/Fody/PropertyChanged
I'm writing a program that calls some erp class/functions (the erp is pretty much obsolete and doesn't have any support available). I'm having trouble define an 'object', and would appreciate some help in this regards. thanks in advance
I have a class typeUDDBTble (definition further below).
My coding is.
typeUDDBTbl UDDB = new typeUDDBTbl();
UDDB.name = "xxxx";
UDDB.Rec = new typeRec[1];
// Edited, incorrect code
// UDDB[0].Items = new typeFld[1];
UDDB.Rec[0].Items = new typeFld[1];
The Items is an object array. I tried using new Object[1], or other type(typeFld), but all get the
Object reference not set to an instance of an object
Any idea how i can solve this problem?
The definition:
public partial class typeUDDBTbl : object, System.ComponentModel.INotifyPropertyChanged
{
private typeRec[] recField;
private string nameField;
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute("Rec", Order = 0)]
public typeRec[] Rec
{
get
{
return this.recField;
}
set
{
this.recField = value;
this.RaisePropertyChanged("Rec");
}
}
/// <remarks/>
[System.Xml.Serialization.XmlAttributeAttribute(DataType = "token")]
public string name
{
get
{
return this.nameField;
}
set
{
this.nameField = value;
this.RaisePropertyChanged("name");
}
}
public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string propertyName)
{
System.ComponentModel.PropertyChangedEventHandler propertyChanged = this.PropertyChanged;
if ((propertyChanged != null))
{
propertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
}
}
}
public partial class typeRec : object, System.ComponentModel.INotifyPropertyChanged
{
private object[] itemsField;
private string dummyField;
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute("Fld", typeof(typeFld), Order = 0)]
[System.Xml.Serialization.XmlElementAttribute("LangFld", typeof(typeLangFld), Order = 0)]
public object[] Items
{
get
{
return this.itemsField;
}
set
{
this.itemsField = value;
this.RaisePropertyChanged("Items");
}
}
/// <remarks/>
[System.Xml.Serialization.XmlAttributeAttribute(DataType = "token")]
public string dummy
{
get
{
return this.dummyField;
}
set
{
this.dummyField = value;
this.RaisePropertyChanged("dummy");
}
}
public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string propertyName)
{
System.ComponentModel.PropertyChangedEventHandler propertyChanged = this.PropertyChanged;
if ((propertyChanged != null))
{
propertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
}
}
}
public partial class typeFld : object, System.ComponentModel.INotifyPropertyChanged
{
private string nameField;
private string valueField;
/// <remarks/>
[System.Xml.Serialization.XmlAttributeAttribute(DataType = "token")]
public string name
{
get
{
return this.nameField;
}
set
{
this.nameField = value;
this.RaisePropertyChanged("name");
}
}
/// <remarks/>
[System.Xml.Serialization.XmlTextAttribute()]
public string Value
{
get
{
return this.valueField;
}
set
{
this.valueField = value;
this.RaisePropertyChanged("Value");
}
}
Your typeUDDBTble does not contain an indexer, so you trying to access its object with square brackets is invalid.
I suppose you meant:
UDDB.Rec[0].Items = ...
Also, you will have to initialize the first cell in Rec unless you want another exception.
UDDB.Rec[0] = new typeRec();
UDDB.Rec[0].Items = new object[1]
This is incorrect:
UDDB[0].Items = new typeFld[1];
UDDB is of type typeUDDBTbl, which does not have an indexer method.
It also does not appear to have a property called Items.
I am working in vs2010.
I have created a DataGrid which is bounded to
ObservableCollection List;
the Class_CMD looks like this :
public class Class_RetrieveCommand
{
public string CMD { get; set; }
public bool C_R_CMD { get; set; }
public bool S_CMD { get; set; }
public bool C_S_CMD { get; set; }
}
i have 4 delegates which i pass to another window, and this window needs to update the list during runtime. During the runtime i can see the string column of the grid updated all the time but the DataGridCheckBoxColumns are never updated.
the DataGrid -
<DataGrid Background="Transparent" x:Name="DataGrid_CMD" Width="450" MaxHeight="450" Height="Auto" ItemsSource="{Binding}" AutoGenerateColumns="True">
one of the delegates which updates the bool is -
public void UpdateC_S_CMD(string Msg)
{
foreach (Class_CMD c in List.ToArray())
{
if (c.CMD.Equals(Msg))
c.C_S_CMD = true;
}
}
I don't understand why the bool columns are not updated....
can anyone help please?
thanks.
Your class Class_RetrieveCommand needs to implement the INotifyPropertyChanged interface. Otherwise the individual rows databound to the instances of the class don't know that the underlying properties have changed. If you change it to something like this, you should see the changes reflected in your grid:
public class Class_RetrieveCommand : INotifyPropertyChanged
{
private bool _cRCmd;
private bool _cSCmd;
private string _cmd;
private bool _sCmd;
public string CMD
{
get { return _cmd; }
set
{
_cmd = value;
InvokePropertyChanged(new PropertyChangedEventArgs("CMD"));
}
}
public bool C_R_CMD
{
get { return _cRCmd; }
set
{
_cRCmd = value;
InvokePropertyChanged(new PropertyChangedEventArgs("C_R_CMD"));
}
}
public bool S_CMD
{
get { return _sCmd; }
set
{
_sCmd = value;
InvokePropertyChanged(new PropertyChangedEventArgs("S_CMD"));
}
}
public bool C_S_CMD
{
get { return _cSCmd; }
set
{
_cSCmd = value;
InvokePropertyChanged(new PropertyChangedEventArgs("C_S_CMD"));
}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
public void InvokePropertyChanged(PropertyChangedEventArgs e)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, e);
}
}
}
You should implement INotifyPropertyChanged in the Class_RetrieveCommand like this:
public class Class_RetrieveCommand : INotifyPropertyChanged
{
private string _CMD;
public string CMD
{
get { return _CMD; }
set { _CMD = value; OnPropertyChanged("CMD"); }
}
... similar for the other properties
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Unfortunately you can't use auto properties anymore then (except you resort to proxygenerators).
In .NET I have a class called Caption. I have another class called Gauge. Within the Gauge class I have a property defined as a Caption.
I am trying to figure out how to do the following:
When a certain property is changed in my Caption class how do I get it to execute a subroutine in the Gauge class? I am thinking I have to declare an event and AddHandlers to fire it off, but I can't think of how to accomplish this.
You'll want to look at implementing the INotifyPropertyChanged interface, which is designed exactly for the purpose - raising an event when a property of a class instance changes.
A good example of usage is given on this MSDN page.
// This class implements a simple customer type
// that implements the IPropertyChange interface.
public class DemoCustomer : INotifyPropertyChanged
{
// These fields hold the values for the public properties.
private Guid idValue = Guid.NewGuid();
private string customerName = String.Empty;
private string companyNameValue = String.Empty;
private string phoneNumberValue = String.Empty;
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
// The constructor is private to enforce the factory pattern.
private DemoCustomer()
{
customerName = "no data";
companyNameValue = "no data";
phoneNumberValue = "no data";
}
// This is the public factory method.
public static DemoCustomer CreateNewCustomer()
{
return new DemoCustomer();
}
// This property represents an ID, suitable
// for use as a primary key in a database.
public Guid ID
{
get
{
return this.idValue;
}
}
public string CompanyName
{
get {return this.companyNameValue;}
set
{
if (value != this.companyNameValue)
{
this.companyNameValue = value;
NotifyPropertyChanged("CompanyName");
}
}
}
public string PhoneNumber
{
get { return this.phoneNumberValue; }
set
{
if (value != this.phoneNumberValue)
{
this.phoneNumberValue = value;
NotifyPropertyChanged("PhoneNumber");
}
}
}
}
public class Caption
{
private int myInt;
public event EventHandler MyIntChanged;
private void OnMyIntChanged()
{
var handler = this.MyIntChanged;
if (handler != null)
{
handler(this, EventArgs.Empty);
}
}
public int MyInt
{
get
{
return this.myInt;
}
set
{
if (this.myInt != value)
{
this.myInt = value;
this.OnMyIntChanged();
}
}
}
}
So now, in your guage class:
public class Guage
{
private Caption caption;
public Caption Caption
{
get
{
return this.caption;
}
set
{
if (this.caption!= value)
{
this.caption= value;
this.caption.MyIntChanged += new EventHandler(caption_MyIntChanged);
}
}
}
private void caption_MyIntChanged(object sender, EventArgs e)
{
//do what you gotta do
}
}