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)
{
}
I have a JSON class file which contains three classes, all of which follow this structure:
public class ManifestJSON : INotifyPropertyChanged
{
[JsonProperty("dataType")]
private string dataType;
public string DataType
{
get
{
return dataType;
}
set
{
if(dataType != value)
{
dataType = value;
RaisePropertyChanged("DataType");
}
}
}
[JsonProperty("ttl")]
private int time_to_live;
public int Time_To_Live
{
get
{
return time_to_live;
}
set
{
if (time_to_live != value)
{
time_to_live = value;
RaisePropertyChanged("Time_To_Live");
}
}
}
[JsonProperty("serial")]
private long serial;
public long Serial
{
get
{
return serial;
}
set
{
if (serial != value)
{
serial = value;
RaisePropertyChanged("Serial");
}
}
}
[JsonProperty("modifiedIso8601")]
private string modifiedIso8601;
public string ModifiedIso8601
{
get
{
return modifiedIso8601;
}
set
{
if (modifiedIso8601 != value)
{
modifiedIso8601 = value;
RaisePropertyChanged("ModifiedIso8601");
}
}
}
[JsonProperty("modifiedTimestamp")]
private long modifiedTimestamp;
public long ModifiedTimestamp
{
get
{
return modifiedTimestamp;
}
set
{
if (modifiedTimestamp != value)
{
modifiedTimestamp = value;
RaisePropertyChanged("ModifiedTimestamp");
}
}
}
[JsonProperty("timezone")]
private string timezone;
public string Timezone
{
get
{
return timezone;
}
set
{
if (timezone != value)
{
timezone = value;
RaisePropertyChanged("Timezone");
}
}
}
[JsonProperty("exports")]
private ObservableCollection<ManifestItem> manifest_Items;
public ObservableCollection<ManifestItem> Manifest_Items
{
get
{
return manifest_Items;
}
set
{
if (manifest_Items != value)
{
manifest_Items = value;
RaisePropertyChanged("Manifest_Items");
}
}
}
//Event handling
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string property)
{
Console.WriteLine("Updated");
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
}
}
In another class, I've created a global instance of type ManifestJSON
public ManifestJSON manifestData;
which is filled by deserializing a JSON string into this object using the DeserializeObject method from the Newtonsoft.json library like so:
manifestData = JsonConvert.DeserializeObject<ManifestJSON>(JSONString).
This fills the ManifestJSON class successfully, but none of my property methods or events are triggering. What am I doing wrong here?
If you want to update your existing data-bound ManifestJSON object, you should not replace this one with a new object but de-serialize the JSON string into new object and then set the properties of the existing manifestData object:
var newData = JsonConvert.DeserializeObject<ManifestJSON>(JSONString);
manifestData.DataType = newData.DataType;
manifestData.Time_To_Live = newData.Time_To_Live;
manifestData.Serial = newData.Serial;
//...
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.
I'm having trouble with a simple image source binding.
I have a class that store the path to the image file (and other stuff) which look like this:
public class Ekta {
...
public string PATHMED { get; set; }
public string FICMED { get; set; }
public string FULLPATH { get { return PATHMED + FICMED; } }
...
}
I have the following property in my window:
public Ekta mainImg { get; set; }
And in the xaml, the binding is done like this:
<Image Source="{Binding Path=mainImg.FULLPATH}"/>
This work well when I set mainImg's value the first time (Before InitializeComponent() is called), but when I update it (mainImg = e; where e is an instance of Ekta) the UI doesn't change.
Am I missing something ? Is it the right way to bind an image source to a custom item ?
I suggest to make a base class named Notifier and use it for any class which needs INotifyPropertyChanged implementation
public class Notifier : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged([CallerMemberName] string propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Then
public class Ekta : Notifier
{
private string _PATHMED;
public string PATHMED
{
get { return _PATHMED; }
set
{
_PATHMED = value;
RaisePropertyChanged();
RaisePropertyChanged("FULLPATH");
}
}
private string _FICMED;
public string FICMED
{
get { return _FICMED; }
set
{
_FICMED = value;
RaisePropertyChanged();
RaisePropertyChanged("FULLPATH");
}
}
public string FULLPATH
{
get { return PATHMED + FICMED; }
}
}
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).