I have use the following code snippet for Creating ObservableCollection binded to the DataGrid.
public class Test:INotifyPropertyChanged
{
private string _name;
public string Name
{
get { return _name; }
set { _name = value;OnpropertyChanged("Name"); }
}
private string _city;
public string City
{
get { return _city; }
set
{
_city = value;OnpropertyChanged("City");}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
public void OnpropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
var e = new PropertyChangedEventArgs(propertyName);
handler(this, e);
}
}
#endregion
}
class Data:INotifyPropertyChanged
{
private int customerID;
public int CustomerID
{
get { return customerID; }
set { customerID = value; OnpropertyChanged("CustomerID"); }
}
private bool isSelected;
public bool IsSelected
{
get { return isSelected; }
set { isSelected = value; OnpropertyChanged("IsSelected"); }
}
private ObservableCollection<Test> _collection;
public ObservableCollection<Test> Collection
{
get { return _collection; }
set { _collection = value;OnpropertyChanged("Collection" +
""); }
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnpropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
var e = new PropertyChangedEventArgs(propertyName);
handler(this, e);
}
}
}
class ViewModel:NotificationObject
{
public ViewModel()
{
this.GDCSource = Getsource();
}
private ObservableCollection<Data> _gdcsource;
public ObservableCollection<Data> GDCSource
{
get { return _gdcsource; }
set { _gdcsource = value; RaisePropertyChanged("GDCSource");}
}
private ObservableCollection<Data> Getsource()
{
ObservableCollection<Data> items = new ObservableCollection<Data>();
if (items != null)
{
items.Add(new Data()
{
IsSelected = true,
CustomerID = 1,
});
items.Add(new Data()
{
IsSelected = true,
CustomerID = 2,
});
}
return items;
}
}
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
ViewModel vmModel = new ViewModel();
this.datagrid.ItemsSource = vmModel.GDCSource;
vmModel.GDCSource.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(GDCSource_CollectionChanged);
}
void GDCSource_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
//Listen the collection changed event for underlying source
}
// add the object to the Collection property
private void Test_OnClick(object sender, RoutedEventArgs e)
{
(this.DataContext as ViewModel).GDCSource[0].Collection.Add(new Test() { Name = "Name1", City = "City1" });
(this.DataContext as ViewModel).GDCSource[0].Collection.Add(new Test() { Name = "Name1", City = "City1" });
(this.DataContext as ViewModel).GDCSource[0].Collection.Add(new Test() { Name = "Name1", City = "City1" });
}
}
It is possible to listen while adding Collection property in any event.
Thanks in advance
Regards,
Rajasekar
If you mean you want to register for event that is raised when item is added/deleted in observable collection you should look at CollectionChanged event
ObservableCollection<T>.CollectionChanged Event
Occurs when an item is added, removed, changed, moved, or the entire
list is refreshed.
You can extend your own version of ObservableCollection if you want and override the add method,
There you can fire any delegates or whatever you may want to register, the UI will update automatically using ObservableCollection with items added/removed you don't need to do anything for that,
Related
I have been trying to open a property grid on the selection of a tree node and then bind the values of the property grid to the selected tree node in C#. This way if I create a new tree node it has its own values in the property grid.
Can anyone give an example of how that can be done or probably suggest me on the code below,
public partial class WPFpropertygriddemo : Window
{
public WPFpropertygriddemo()
{
InitializeComponent();
_propertyGrid.Visibility = Visibility.Hidden;
}
private void myTreeView_Selected(object sender, RoutedEventArgs e)
{
TreeViewItem tvi = e.OriginalSource as TreeViewItem;
Item itemsub = (Item)tvi.Header;
if (itemsub != null)
{
_propertyGrid.Visibility = Visibility.Visible;
// System.Windows.MessageBox.Show(itemsub.DisplayValue);
_propertyGrid.SelectedObject = itemsub;
}
}
}
public class Item : MyModelINotifyProperty
{
public string DisplayValue { get; set; }
private bool _isSelected = false;
public bool IsSelected
{
get
{
return _isSelected;
}
set
{
_isSelected = value;
OnPropertyChanged("IsSelected");
}
}
private string _sample;
public string Sample
{
get
{
return _sample;
}
set
{
_sample = value;
OnPropertyChanged("Sample");
}
}
}
public class MyViewModel : MyModelINotifyProperty
{
public ObservableCollection<Item> Items
{
get
{
return new ObservableCollection<Item>()
{
new Item() {DisplayValue = "Item1", IsSelected = false, Sample = "Sample: I am Item1"},
new Item() {DisplayValue = "Item2", IsSelected = true, Sample = "Sample: I am Item2"},
new Item() {DisplayValue = "Item3", IsSelected = false, Sample = "Sample: I am Item3"}
};
}
}
}
public abstract class MyModelINotifyProperty : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
What I don't understand is where is TreeViewItem defined and what is the code line
"TreeViewItem tvi = e.OriginalSource as TreeViewItem;
doing? I have an error at the TreeViewItem and Original source. Am I supposed to add a reference to use the e.original?
Thanks
Sree.
I was wondering if anyone knows how to solve the following problem... I have a base class that needs to update it's modifiedDateTime property when a derived class property is changed.
BaseObject.cs
public class BaseObject
{
private DateTime? _modifiedDateTime;
public DateTime? modifiedDateTime
{
get { return _modifiedDateTime ; }
set { _modifiedDateTime = value; }
}
public BaseObject
{
_modifiedDateTime = DateTime.Now.ToUniversalTime();
}
public void Update(object sender, PropertyChangedEventArgs e)
{
_modifiedDateTime = DateTime.Now.ToUniversalTime();
}
}
ExampleClass1.cs
public class ExampleClass1: BaseObject, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private int _data;
public int data
{
get { return _data; }
set
{
_data = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("data"));
}
}
public ExampleClass1()
{
PropertyChanged += base.Update;
}
}
The previous example is working as expected.
However if the derived class contains an object of another class. For example:
ExampleClass2.cs
public class ExampleClass2: BaseObject, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private int _data;
private ExampleClass1 _objClass1;
public int data
{
get { return _data; }
set
{
_data = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("data"));
}
}
public ExampleClass1 objClass1
{
get { return _objClass1; }
set
{
_objClass1 = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("objClass1"));
}
}
public ExampleClass2()
{
PropertyChanged += base.Update;
}
}
When I change the data property of the objClass1, the modifiedDateTime property of the ExampleClass2 inherited by the base class BaseObject is not updated.
How can I solve this problem?
When you set the value of objClass1 subscribe to its property changed event as well
public ExampleClass1 objClass1 {
get { return _objClass1; }
set {
//in case one already existed. unsubscribe from event
if(_objClass1 != null) _objClass1.PropertyChanged -= base.Update
_objClass1 = value;
//subscribe to event
if(_objClass1 != null) _objClass1.PropertyChanged += base.Update
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("objClass1"));
}
}
I set the data context of my View to the ViewModel like this
PersonVM pvm = null;
public MainPage()
{
this.InitializeComponent();
pvm = new PersonVM();
this.DataContext = pvm;
}
then on a Button click I want to add more items to my collection
private void Btn_PointerPressed(object sender, Windows.UI.Xaml.Input.TappedRoutedEventArgs e)
{
pvm.DataSource.Add(new PersonVMWrapper(new PersonModel() { Name = "asdasd", Age = 23 }));
}
Here is my ViewModel where I obviously do something wrong, but cannot figure it out..
namespace App3vv.ViewModel
{
public class PersonVMWrapper : INotifyPropertyChanged
{
PersonModel _pm = null;
public PersonVMWrapper(PersonModel pm)
{
_pm = pm;
}
public string Name
{
get
{
return "mr." + _pm.Name;
}
set { RaisePropertyChanged("Name"); }
}
public string Age
{
get
{
return _pm.Age.ToString() + " years";
}
set { RaisePropertyChanged("Age"); }
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
public class PersonVM : INotifyPropertyChanged
{
private ObservableCollection<PersonVMWrapper> personDataSource;
public PersonVM()
{
this.DataSource.Add(new PersonVMWrapper(new PersonModel() { Name = "John", Age = 32 }));
this.DataSource.Add(new PersonVMWrapper(new PersonModel() { Name = "Kate", Age = 27 }));
this.DataSource.Add(new PersonVMWrapper(new PersonModel() { Name = "Sam", Age = 30 }));
DataSource.CollectionChanged += DataSource_CollectionChanged;
}
void DataSource_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
this.RaisePropertyChanged("DataSource");
}
public ObservableCollection<PersonVMWrapper> DataSource
{
get
{
if (this.personDataSource == null)
{
this.personDataSource = new ObservableCollection<PersonVMWrapper>();
}
return this.personDataSource;
}
set
{
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
EDIT: I implemented CollectionChange and still but my View still does not get the new item added..
In my Model and ViewModel for XAML View (List), I have a String property.
The data are retrieved in LoadData from a WebService.
I don't use MVVMlight.
Model:
public class LocationsModel : INotifyPropertyChanged
{
private string _name;
public string Name
{
get
{
return _name;
}
set
{
if (value != _name)
{
_name = value;
NotifyPropertyChanged("Name");
}
}
}
private string _id;
public string ID
{
get
{
return _id;
}
set
{
if (value != _id)
{
_id = value;
NotifyPropertyChanged("ID");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (null != handler)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
ViewModel:
public class MainViewSurroundingModel : INotifyPropertyChanged
{
public MainViewSurroundingModel()
{
this.Items = new ObservableCollection<LocationsModel>();
}
public ObservableCollection<LocationsModel> Items { get; set; }
private string _name = "";
private string _id = "";
public string Name
{
get
{
return _name;
}
set
{
if (value != _name)
{
_name = value;
NotifyPropertyChanged("Name");
}
}
}
public string ID
{
get
{
return _id;
}
set
{
if (value != _id)
{
_id = value;
NotifyPropertyChanged("ID");
}
}
}
public bool IsDataLoaded
{
get;
private set;
}
public async Task<bool> LoadData()
{
// WEB-API CALL IS HERE...
this.IsDataLoaded = true;
return true;
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (null != handler)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
I now want to update the Name property periodically by code (not re-retrieving data from WebService) every 5 minutes.
How and where can I implement this?
Sorry for vague answer, don't have much time so if anyone else cares to write more about what I said, they are welcome to it.
How to
One of the possibilities is using DispatcherTimer (more about it for example here) and using it's Tick event.(there is a tutorial in the link provided)
Now about where to create your timer.
It depends on what you want to change and when you want to start it.
If I'd like to change ALL of the data in the list some predefined way, i can do it like this:
public class MainViewSurroundingModel : INotifyPropertyChanged
{
//...omitted the parts you have done, only writing the things I'd change...
MainViewSurroundingModel()
{
..if there was something leave it here..
changeTimer = new DispatcherTimer...
changeTimer.Tick += Tick;
changeTimer.Interval = ....et cetera, setting the timer based on tutorial
}
public ObservableCollection<LocationsModel> items;
public ObservableCollection<LocationsModel> Items
{
get //edited so that I can write my own setter
{
return items;
}
set
{
if(value != items)
{
items = value;
//NotifyPropertyChanged("Items"); //can be used here, not necessary
changeTimer.Start();
}
}
}
private DispatcherTimer changeTimer;//based on the tutorial
//based on the tutorial provided, create dispatcher timer and
//tick definition somewhere around here
private Tick(...)
{
//code that iterates through the list and updates it goes here!
}
}
What is the best way to create a class with an event that fires when one of its Properties is changed? Specifically, how do you convey to any subscribers which Property was changed?
Ex:
public class ValueChangedPublisher
{
private int _prop1;
private string _prop2;
public static event ValueChangedHandler(/*some parameters?*/);
public int Prop1
{
get { return _prop1; }
set
{
if (_prop1 != value)
{
_prop1 = value;
ValueChangedHandler(/*parameters?*/);
}
}
}
public string Prop2
{
get { return _prop2; }
set
{
if (_prop2 != value)
{
_prop2 = value;
ValueChangedHandler(/*parameters?*/);
}
}
}
}
public class ValueChangedSubscriber
{
private int _prop1;
private string _prop2;
public ValueChangedSubscriber()
{
ValueChangedPublisher.ValueChanged += ValueChanged;
}
private void ValueChanged(/*parameters?*/)
{
/*how does the subscriber know which property was changed?*/
}
}
My goal is to make this as extensible as possible (e.g. I don't want a bunch of huge if/else if/switch statements lumbering around). Does anybody know of a technique to achieve what I'm looking for?
EDIT:
What I'm really looking for is how to utilize the INotifyPropertyChanged pattern on the subscriber side. I don't want to do this:
private void ValueChanged(string propertyName)
{
switch(propertyName)
{
case "Prop1":
_prop1 = _valueChangedPublisher.Prop1;
break;
case "Prop2":
_prop2 = _valueChangedPublisher.Prop2;
break;
// the more properties that are added to the publisher, the more cases I
// have to handle here :/ I don't want to have to do it this way
}
}
.NET provides the INotifyPropertyChanged interface. Inherit and implement it:
public class ValueChangedPublisher : INotifyPropertyChanged
{
private int _prop1;
private string _prop2;
public event PropertyChangedEventHandler ValueChangedHandler;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public int Prop1
{
get { return _prop1; }
set
{
if (_prop1 != value)
{
_prop1 = value;
NotifyPropertyChanged();
}
}
}
public string Prop2
{
get { return _prop2; }
set
{
if (_prop2 != value)
{
_prop2 = value;
NotifyPropertyChanged();
}
}
}
}
Here is what I did to solve my problem. It's a bit large, so maybe not performant, but works for me.
Edit
See this question for performance details: C# using properties with value types with Delegate.CreateDelegate
Base Class for publishing property change stuff:
public abstract class PropertyChangePublisherBase : INotifyPropertyChanged
{
private Dictionary<string, PropertyInfo> _properties;
private bool _cacheProperties;
public bool CacheProperties
{
get { return _cacheProperties; }
set
{
_cacheProperties = value;
if (_cacheProperties && _properties == null)
_properties = new Dictionary<string, PropertyInfo>();
}
}
protected PropertyChangePublisherBase(bool cacheProperties)
{
CacheProperties = cacheProperties;
}
public bool ContainsBinding(PropertyChangedEventHandler handler)
{
if (PropertyChanged == null)
return false;
return PropertyChanged.GetInvocationList().Contains(handler);
}
public object GetPropertValue(string propertyName)
{
if (String.IsNullOrEmpty(propertyName) || String.IsNullOrWhiteSpace(propertyName))
throw new ArgumentException("Argument must be the name of a property of the current instance.", "propertyName");
return ProcessGetPropertyValue(propertyName);
}
protected virtual object ProcessGetPropertyValue(string propertyName)
{
if (_cacheProperties)
{
if (_properties.ContainsKey(propertyName))
{
return _properties[propertyName].GetValue(this, null);
}
else
{
var property = GetType().GetProperty(propertyName);
_properties.Add(propertyName, property);
return property.GetValue(this, null);
}
}
else
{
var property = GetType().GetProperty(propertyName);
return property.GetValue(this, null);
}
}
#region INotifyPropertyChanged Implementation
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
Base Class for receiving property change stuff:
public abstract class PropertyChangeSubscriberBase
{
protected readonly string _propertyName;
protected virtual object Value { get; set; }
protected PropertyChangeSubscriberBase(string propertyName, PropertyChangePublisherBase bindingPublisher)
{
_propertyName = propertyName;
AddBinding(propertyName, this, bindingPublisher);
}
~PropertyChangeSubscriberBase()
{
RemoveBinding(_propertyName);
}
public void Unbind()
{
RemoveBinding(_propertyName);
}
#region Static Fields
private static List<string> _bindingNames = new List<string>();
private static List<PropertyChangeSubscriberBase> _subscribers = new List<PropertyChangeSubscriberBase>();
private static List<PropertyChangePublisherBase> _publishers = new List<PropertyChangePublisherBase>();
#endregion
#region Static Methods
private static void PropertyChanged(object sender, PropertyChangedEventArgs args)
{
string propertyName = args.PropertyName;
if (_bindingNames.Contains(propertyName))
{
int i = _bindingNames.IndexOf(propertyName);
var publisher = _publishers[i];
var subscriber = _subscribers[i];
subscriber.Value = publisher.GetPropertValue(propertyName);
}
}
public static void AddBinding(string propertyName, PropertyChangeSubscriberBase subscriber, PropertyChangePublisherBase publisher)
{
if (!_bindingNames.Contains(propertyName))
{
_bindingNames.Add(propertyName);
_publishers.Add(publisher);
_subscribers.Add(subscriber);
if (!publisher.ContainsBinding(PropertyChanged))
publisher.PropertyChanged += PropertyChanged;
}
}
public static void RemoveBinding(string propertyName)
{
if (_bindingNames.Contains(propertyName))
{
int i = _bindingNames.IndexOf(propertyName);
var publisher = _publishers[i];
_bindingNames.RemoveAt(i);
_publishers.RemoveAt(i);
_subscribers.RemoveAt(i);
if (!_publishers.Contains(publisher))
publisher.PropertyChanged -= PropertyChanged;
}
}
#endregion
}
Actual class to use for subscribing to property change stuff:
public sealed class PropertyChangeSubscriber<T> : PropertyChangeSubscriberBase
{
private PropertyChangePublisherBase _publisher;
public new T Value
{
get
{
if (base.Value == null)
return default(T);
if (base.Value.GetType() != typeof(T))
throw new InvalidOperationException(String.Format("Property {0} on object of type {1} does not match the type Generic type specified {2}.", _propertyName, _publisher.GetType(), typeof(T)));
return (T)base.Value;
}
set { base.Value = value; }
}
public PropertyChangeSubscriber(string propertyName, PropertyChangePublisherBase bindingPublisher)
: base(propertyName, bindingPublisher)
{
_publisher = bindingPublisher;
}
}
Here is an example of the Class with properties that you wish to be notified about:
public class ExamplePublisher: PropertyChangedPublisherBase
{
private string _id;
private bool _testBool;
public string Id
{
get { return _id; }
set
{
if (value == _id) return;
_id = value;
RaisePropertyChanged("Id");
}
}
public bool TestBool
{
get { return _testBool; }
set
{
if (value.Equals(_testBool)) return;
_testBool = value;
RaisePropertyChanged("TestBool");
}
}
}
Here is an example of the Class that will be notified when the properties in the class above change:
public class ExampleReceiver
{
public PropertyChangeSubscriber<string> Id { get; set; }
public PropertyChangeSubscriber<bool> TestBool { get; set; }
public MyExampleClass(PropertyChangePublisherBase publisher)
{
Id = new PropertyChangeSubscriber<string>("Id", publisher);
TestBool = new PropertyChangeSubscriber<bool>("TestBool", publisher);
}
}