Event handler for array - arrays

Have we event handler,event listener,action listener or any like these, for arrayin any compile-based language (e.g. Java,c++,c)?
I am searching a way to track the changes of an array. So when value of a cell changed the listener notify me about this change and the cell number that this changed occurred for it.
is there any language or any option for this problem?

Edit
If you do not want to use a custom type you can have a look at this implementation to observe when an array is changed : http://www.codeproject.com/Articles/162966/Observing-Changes-to-an-Underlying-Array
Here is an example from Microsoft in C# that does what you are looking for with an ArrayList
http://msdn.microsoft.com/en-us/library/aa645739(v=vs.71).aspx
// events1.cs
using System;
namespace MyCollections
{
using System.Collections;
// A delegate type for hooking up change notifications.
public delegate void ChangedEventHandler(object sender, EventArgs e);
// A class that works just like ArrayList, but sends event
// notifications whenever the list changes.
public class ListWithChangedEvent: ArrayList
{
// An event that clients can use to be notified whenever the
// elements of the list change.
public event ChangedEventHandler Changed;
// Invoke the Changed event; called whenever list changes
protected virtual void OnChanged(EventArgs e)
{
if (Changed != null)
Changed(this, e);
}
// Override some of the methods that can change the list;
// invoke event after each
public override int Add(object value)
{
int i = base.Add(value);
OnChanged(EventArgs.Empty);
return i;
}
public override void Clear()
{
base.Clear();
OnChanged(EventArgs.Empty);
}
public override object this[int index]
{
set
{
base[index] = value;
OnChanged(EventArgs.Empty);
}
}
}
}
namespace TestEvents
{
using MyCollections;
class EventListener
{
private ListWithChangedEvent List;
public EventListener(ListWithChangedEvent list)
{
List = list;
// Add "ListChanged" to the Changed event on "List".
List.Changed += new ChangedEventHandler(ListChanged);
}
// This will be called whenever the list changes.
private void ListChanged(object sender, EventArgs e)
{
Console.WriteLine("This is called when the event fires.");
}
public void Detach()
{
// Detach the event and delete the list
List.Changed -= new ChangedEventHandler(ListChanged);
List = null;
}
}
class Test
{
// Test the ListWithChangedEvent class.
public static void Main()
{
// Create a new list.
ListWithChangedEvent list = new ListWithChangedEvent();
// Create a class that listens to the list's change event.
EventListener listener = new EventListener(list);
// Add and remove items from the list.
list.Add("item 1");
list.Clear();
listener.Detach();
}
}
}

Related

OnPropertyChanged wont change when used wth observable collection and single property

Loads the dataGrid and populates the Datagrid a row of 1'
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
update();
//this.DataContext = this;
}
CricketEvent events = new CricketEvent();
private void update()
{
events.updateList(new CricketEvent[1] { new CricketEvent(){Runs="1"} });
DG1.ItemsSource = events.RunsList;
}
private void DG1_SelectedCellsChanged(object sender, SelectedCellsChangedEventArgs e)
{
Window1 windowToOpen = new Window1();
var selectedUser = this.DG1.SelectedItem;
windowToOpen.Show();
}
}
Main class that loads the OnPropertyChanged I have a List property and string property that calls the OnPropertyChanged but I want the individual "Runs" property to be updated on its own rather than the whole collection.
class CricketEvent : INotifyPropertyChanged
{
private ObservableCollection<CricketEvent> runsList;
public string runs { get; set; }
public CricketEvent(string numofRuns) {
this.Runs = numofRuns;
}
public CricketEvent() { }
public event PropertyChangedEventHandler PropertyChanged;
public ObservableCollection<CricketEvent> RunsList
{
get { return this.runsList; }
set
{
if (value != this.runsList)
{
this.runsList = value;
OnPropertyChanged("RunsList");
}
}
}
public string Runs
{
get { return runs; }
set
{
runs = value;
// Call OnPropertyChanged whenever the property is updated
OnPropertyChanged("Runs");
}
}
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
public ObservableCollection<CricketEvent> updateList(CricketEvent []events)
{
runsList = new ObservableCollection<CricketEvent>(events.ToList());
return runsList;
}
}
This is the update window that brings up a text box and should change the "1s" In the previous window to whatever is typed into the textbox
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
}
CricketEvent events = new CricketEvent();
MainWindow main = new MainWindow();
private void Button_Click(object sender, RoutedEventArgs e)
{
events.updateList(new CricketEvent[1] { new CricketEvent(txt1.Text.ToString()) });
main.DG1.ItemsSource = events.RunsList;
}
The Button_Click event in Window1 does not use the instance of MainWindow that is show - it creates a new Window instance (that is not shown) and adds the updated list to the DG1.ItemsSource property. To solve that, pass the original instance of Window to the created Window1 in constructor and use that.
However, you should review your update strategy (and code style) because there is potential for improvments:
It is not a good idea to create a new collection if you want to update just one property of one item. Observable collections provide change notification, so you dont have to recreate the collection at all.
Instead of assinging the collection in code behind, use databinding to bind the collection to the ItemsSource. DataBinding results in automatic update of GUI elements if the collection or one item of you collection changed.

How to capture a property changed even of one view model in another view model

i have view model class A and it has the property "a".
and i have class called B , and it ha sproperty "b" where i store list of all A[] as list.
if any change in the property a , i would like to change the property in the parent class A.
that is
class B
{
property b1;
List A[]
}
Class A
{
property a1;
}
I am using MVVM and notify property chnaged event is there in both the places. what i dont know how to wire up both .
In my BaseCollection class which extends ObservableCollection<T>, I have a created a CollectionItemPropertyChanged delegate that I can attach a handler to, so I can be notified when property values have changed in any item in the collection:
public delegate void ItemPropertyChanged(T item, string propertyName);
Then I add a getter and setter of the type of the delegate:
public virtual ItemPropertyChanged CurrentItemPropertyChanged { get; set; }
To achieve this, I have to attach a handler to each item's PropertyChanged event:
public BaseCollection(IEnumerable<T> collection)
{
foreach (T item in collection) Add(item);
}
public new void Add(T item)
{
item.PropertyChanged += Item_PropertyChanged;
base.Add(item);
}
I also need to remove the handlers when objects are removed:
public new bool Remove(T item)
{
if (item == null) return false;
item.PropertyChanged -= Item_PropertyChanged;
return base.Remove(item);
}
Then there is the Item_PropertyChanged handler:
private void Item_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (CollectionItemPropertyChanged != null) CollectionItemPropertyChanged(
currentItem, e.PropertyName);
}
This can then be used in your class B like so:
public List<A> Items
{
get { return items; }
set
{
items = value;
items.CollectionItemPropertyChanged += YourHandler;
NotifyPropertyChanged(Items);
}
}
Then your handler:
private void YourHandler(A item, string propertyName)
{
// propertyName is the name of the changed property from the A object named item
}
To answer your question more directly, you can attach a handler from one view model to this delegate in another... for example:
Items.CollectionItemPropertyChanged += ViewModel2.YourHandler;
Or from the other view model:
ViewModel1.Items.CollectionItemPropertyChanged += YourHandler;
Create View Model A as Singleton and update the a property using that instance.

How to Notify That View Should Get New Value of Calculated Field

I am working on a WP7 app that displays some times on one page. I have a code behind that has an ObservableCollection of objects. Each object has a calculated property that uses DateTime.Now to determine the time that's displayed on the page. I can't figure out how to "notify" that the property has changed since the property doesn't change, the current time is changing (just once per second). Any ideas? Here's the jist of what I've got:
//my business object
public class Widget
{
private string _name;
public string Name
{
get { return _name; }
set { _name = value; }
}
private DateTime? _start;
public DateTime? Start
{
get { return _start; }
set { _start = value; }
}
public TimeSpan? TimeSinceStart
{
get { return Start.HasValue ? DateTime.Now - Start.Value : default(TimeSpan); }
}
}
//my viewmodel
public class WidgetDisplayerViewModel : BaseViewModel
{
public WidgetDisplayerViewModel()
{
TimeUpdateTimer.Tick += new EventHandler(TimeUpdateTimer_Tick);
TimeUpdateTimer.Interval = new TimeSpan(0, 0, 1);
TimeUpdateTimer.Start();
}
public WidgetDisplayerViewModel(string selectedCategory) : this()
{
Category = MockDataService.GetCategory(selectedCategory);
Category.Widgets = MockDataService.GetWidgets(selectedCategory).ToObservableCollection();
}
public DispatcherTimer TimeUpdateTimer = new DispatcherTimer();
private DateTime _currentTime;
public DateTime CurrentTime
{
get { return _currentTime; }
set {
_currentTime = value;
NotifyPropertyChanged("CurrentTime");
}
}
public Category Category { get; set; }
void TimeUpdateTimer_Tick(object sender, EventArgs e)
{
CurrentTime = DateTime.Now;
}
}
And then the view is very simple and just needs to display the CurrentTime and then for each Widget in the collection it needs to show the TimeSinceStart. The CurrentTime is getting updated each second by the timer and that gets propogated to the view. That one is easy because the timer is setting it and so I have a chance to call NotifyPropertyChanged("CurrentTime"), but how would I "notify" that all of the TimeSinceStart getters should be called to update the calculated value for each Widget since I'm not setting them?
Thanks!
You'll have to manually refresh the property one way or another. I see you already have a timer ticking every second. So I can suggest you two solutions:
1/ Define a "UpdateTime" method in the Widget object. In this method, call NotifyPropertyChanged("TimeSinceStart"). When the timer is ticking, enumerate the list of widgets, and call the UpdateTime method on each.
2/ Create a global object implementing the INotifyPropertyChanged interface, and holding the value of CurrentTime. Make each of your Widget objects subscribe to the PropertyChanged event of this global class to know when the time is updated. Then, when the event is triggered, call NotifyPropertyChanged("TimeSinceStart").
This can be a tricky one to work out and it can get very messy very fast.
I would suggest you stick with your current approach of having only one timer which is initialised in the main viewmodel. You then have to ask yourself the question - does the age (TimeSinceStart) of the Widget belong on the Widget, or is it purely for display/informational purposes? Is it a core piece of information that each Widget must keep during its lifespan?
This looks to me like it is for display purposes only. So my suggestion is this: once you have called GetWidgets, you could enumerate through each Widget and wrap it in a thin viewmodel of its own. The constructor for that viewmodel takes two parameters - the timer from the main viewmodel, and the Widget. You then subscribe to the timer's Tick event, and from that you notify that the TimeSinceStart property has changed.
public class WidgetWrapper : INotifyPropertyChanged
{
public WidgetWrapper(DispatcherTimer timer, Widget widget)
{
_widget = widget;
timer.Tick += TimerTick;
}
private void TimerTick(object sender, EventArgs e)
{
OnPropertyChanged("TimeSinceStart");
}
public Widget Widget { get { return _widget; } }
public TimeSpan? TimeSinceStart
{
get { return _widget.Start.HasValue ? DateTime.Now - _widget.Start.Value : default(TimeSpan); }
}
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
private readonly Widget _widget;
}
public class WidgetDisplayerViewModel : BaseViewModel
{
public WidgetDisplayerViewModel(string selectedCategory) : this()
{
Category = MockDataService.GetCategory(selectedCategory);
var wrappedWidgets = new ObservableCollection<WidgetWrapper>();
MockDataService.GetWidgets(selectedCategory).ForEach(widget => wrappedWidgets.Add(new WidgetWrapper(TimeUpdateTimer, widget)));
Category.Widgets = wrappedWidgets;
}
}
Wrapping a DTO (entity, Data Transfer Object) with its own viewmodel is a quite common approach when adding functionality to an entity. If you use this appoach you will have to slightly modify any UI bindings that were targetting properties on the Widget, as those UI elements will now be dealing with a WidgetWrapper (or you can just surface the required properties in the WidgetWrapper itself, then no bindings have to change).
Invoke the NotifyPropertyChanged method for the specified property.
public DateTime CurrentTime
{
get { return _currentTime; }
set {
_currentTime = value;
NotifyPropertyChanged("CurrentTime");
NotifyPropertyChanged("TimeSinceStart");
}
}
Subscribe all widgets to CurrentTime PropertyChanged event in Widget constructor
private Widget()
{
App.ViewModel.PropertyChanged += (s, e) =>
{
if (e.PropertyName.Equals("CurrentTime")
{
NotifyPropertyChanged("TimeSinceStart");
}
};
}

Initializing sorted collections from XAML

I have a SortedObservableCollection class (initially based on this). It does exactly what it promises - it is a generic collection, which implements INotifyCollectionChanged and maintains its elements in sorted order (according to a provided IComparer). The order is only checked upon insertion though - when an item is being inserted, it is inserted into a correct place into the collection.
However I encountered a major issue while trying to initialize the collection from XAML with syntax like this (the Items property is of SortedObservableCollection<MyItem> type, Priority is the sorting key):
<my:SomeElement.Items>
<my:MyItem Priority="0">
<my:MyItem Priority="2">
<my:MyItem Priority="1">
</my:SomeElement.Items>
This should result in collection with items in order 2, 1, 0, but it results in order 1, 2, 0.
It took me quite some time to discover the cause: Collection items are first constructed, then added to the collection and only then are their properties' values assigned.
I couldn't find this behavior documented anywhere and I agree it doesn't really matter usually. But in my case, the Priority property is always of value 0, so the sorting doesn't take occur at all (in fact, the items are inserted in reverse order than they are in XAML). And the, after the sorting has taken place, the Priority is initialized.
Did you encounter this behavior yourself? Why is the XAML implemented like this? How can I work around this issue?
The only solution I can think of is to let the items implement INotifyPropertyChanged and then subscribe to it in the Add method (and then update the order when necessary), but I guess this would bring more trouble than it is worth (performance, memory leaks...).
Thanks for any help!
If you are aiming at a collection which is properly sorted at all times, then you'll need to go for the listening approach. You could make you items support a weak event mechanism to prevent them from holding a strong reference to the collection.
Another approach would be to defer sorting until the collection is "fully constructed". You could for example have a flag isSorted in your collection implementation. Have this flag set to false whenever the collection is modified (for simplicity) and check it before the collection is "read".
Something like this:
public void Add(T item)
{
_innerList.Add(item);
_isSorted = false;
}
and:
public int IndexOf(T item)
{
EnsureSorted();
return _innerList.IndexOf(item);
}
where EnsureSorted could look something like this:
private void EnsureSorted()
{
if (!_isSorted)
{
_innerList.Sort(_comparer);
_isSorted = true;
// TODO: Raise the CollectionChanged event here, specifying
// NotifyCollectionChangedAction.Reset
}
}
This should make your collection appear sorted while still allowing it to be unsorted while populating the list.
Perhaps this would be a feasible workaround?
Update:
I created a simple observable collection with this kind of deferred sorting. I think you might find it helpful, at least it should clearify what I mean.
The idea is to call the EnsureSorted method just before "reading" the collection and to clear the isSorted flag whenever collection is modified.
public class SortedObservableCollection<T> : IList<T>, IList, INotifyCollectionChanged, INotifyPropertyChanged
{
private readonly List<T> _innerList;
private IComparer<T> _comparer;
private bool _isSorted;
public event NotifyCollectionChangedEventHandler CollectionChanged;
public event PropertyChangedEventHandler PropertyChanged;
public SortedObservableCollection()
: this(null)
{
}
public SortedObservableCollection(IComparer<T> comparer)
{
_innerList = new List<T>();
_comparer = comparer ?? Comparer<T>.Default;
}
// Call this before "reading" collection
private void EnsureSorted()
{
if (!_isSorted)
{
_innerList.Sort(_comparer);
_isSorted = true;
}
}
// Call this after modifying the collection
private void NotifyChanged()
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("Count"));
}
if (CollectionChanged != null)
{
CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
_isSorted = false;
}
#region List implementation
public int IndexOf(T item)
{
EnsureSorted();
return _innerList.IndexOf(item);
}
public void Insert(int index, T item)
{
EnsureSorted();
_innerList.Insert(index, item);
NotifyChanged();
}
public void RemoveAt(int index)
{
EnsureSorted();
_innerList.RemoveAt(index);
NotifyChanged();
}
public T this[int index]
{
get
{
EnsureSorted();
return _innerList[index];
}
set
{
EnsureSorted();
_innerList[index] = value;
NotifyChanged();
}
}
public void Add(T item)
{
_innerList.Add(item);
NotifyChanged();
}
public void Clear()
{
_innerList.Clear();
NotifyChanged();
}
public bool Contains(T item)
{
return _innerList.Contains(item);
}
public void CopyTo(T[] array, int arrayIndex)
{
EnsureSorted();
_innerList.CopyTo(array, arrayIndex);
}
public int Count
{
get { return _innerList.Count; }
}
public bool IsReadOnly
{
get { return false; }
}
public bool Remove(T item)
{
if (!_innerList.Remove(item))
{
return false;
}
NotifyChanged();
return true;
}
public IEnumerator<T> GetEnumerator()
{
EnsureSorted();
return _innerList.GetEnumerator();
}
#endregion
// Non-generic implementation omitted for brevity...
}

Silverlight DataBinding cross thread issue

I have an Image control with it's source bound to a property on an object(string url to an image). After making a service call, i update the data object with a new URL. The exception is thrown after it leaves my code, after invoking the PropertyChanged event.
The data structure and the service logic are all done in a core dll that has no knowledge of the UI. How do I sync up with the UI thread when i cant access a Dispatcher?
PS: Accessing Application.Current.RootVisual in order to get at a Dispatcher is not a solution because the root visual is on a different thread(causing the exact exception i need to prevent).
PPS: This only is a problem with the image control, binding to any other ui element, the cross thread issue is handled for you.
System.Windows.Deployment.Current.Dispatcher.BeginInvoke(() => {...});
Also look here.
Have you tried implementing INotifyPropertyChanged?
The property getter for RootVisual on the Application class has a thread check which causes that exception. I got around this by storing the root visual's dispatcher in my own property in my App.xaml.cs:
public static Dispatcher RootVisualDispatcher { get; set; }
private void Application_Startup(object sender, StartupEventArgs e)
{
this.RootVisual = new Page();
RootVisualDispatcher = RootVisual.Dispatcher;
}
If you then call BeginInvoke on App.RootVisualDispatcher rather than Application.Current.RootVisual.Dispatcher you shouldn't get this exception.
I ran into a similar issue to this, but this was in windows forms:
I have a class that has it's own thread, updating statistics about another process, there is a control in my UI that is databound to this object. I was running into cross-thread call issues, here is how I resolved it:
Form m_MainWindow; //Reference to the main window of my application
protected virtual void OnPropertyChanged(string propertyName)
{
if(PropertyChanged != null)
if(m_MainWindow.InvokeRequired)
m_MainWindow.Invoke(
PropertyChanged, this, new PropertyChangedEventArgs(propertyName);
else
PropertyChanged(this, new PropertyChangedEventArgs(propertyName);
}
This seems to work great, if anyone has suggestions, please let me know.
When ever we want to update UI related items that action should happen in the UI thread else you will get an invalid cross thread access exception
Deployment.Current.Dispatcher.BeginInvoke( () =>
{
UpdateUI(); // DO the actions in the function Update UI
});
public void UpdateUI()
{
//to do :Update UI elements here
}
The INotifyPropertyChanged interface is used to notify clients, typically binding clients, that a property value has changed.
For example, consider a Person object with a property called FirstName. To provide generic property-change notification, the Person type implements the INotifyPropertyChanged interface and raises a PropertyChanged event when FirstName is changed.
For change notification to occur in a binding between a bound client and a data source, your bound type should either:
Implement the INotifyPropertyChanged interface (preferred).
Provide a change event for each property of the bound type.
Do not do both.
Example:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Runtime.CompilerServices;
using System.Windows.Forms;
// Change the namespace to the project name.
namespace TestNotifyPropertyChangedCS
{
// This form demonstrates using a BindingSource to bind
// a list to a DataGridView control. The list does not
// raise change notifications. However the DemoCustomer type
// in the list does.
public partial class Form1 : Form
{
// This button causes the value of a list element to be changed.
private Button changeItemBtn = new Button();
// This DataGridView control displays the contents of the list.
private DataGridView customersDataGridView = new DataGridView();
// This BindingSource binds the list to the DataGridView control.
private BindingSource customersBindingSource = new BindingSource();
public Form1()
{
InitializeComponent();
// Set up the "Change Item" button.
this.changeItemBtn.Text = "Change Item";
this.changeItemBtn.Dock = DockStyle.Bottom;
this.changeItemBtn.Click +=
new EventHandler(changeItemBtn_Click);
this.Controls.Add(this.changeItemBtn);
// Set up the DataGridView.
customersDataGridView.Dock = DockStyle.Top;
this.Controls.Add(customersDataGridView);
this.Size = new Size(400, 200);
}
private void Form1_Load(object sender, EventArgs e)
{
// Create and populate the list of DemoCustomer objects
// which will supply data to the DataGridView.
BindingList<DemoCustomer> customerList = new BindingList<DemoCustomer>();
customerList.Add(DemoCustomer.CreateNewCustomer());
customerList.Add(DemoCustomer.CreateNewCustomer());
customerList.Add(DemoCustomer.CreateNewCustomer());
// Bind the list to the BindingSource.
this.customersBindingSource.DataSource = customerList;
// Attach the BindingSource to the DataGridView.
this.customersDataGridView.DataSource =
this.customersBindingSource;
}
// Change the value of the CompanyName property for the first
// item in the list when the "Change Item" button is clicked.
void changeItemBtn_Click(object sender, EventArgs e)
{
// Get a reference to the list from the BindingSource.
BindingList<DemoCustomer> customerList =
this.customersBindingSource.DataSource as BindingList<DemoCustomer>;
// Change the value of the CompanyName property for the
// first item in the list.
customerList[0].CustomerName = "Tailspin Toys";
customerList[0].PhoneNumber = "(708)555-0150";
}
}
// This is a simple customer class 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 customerNameValue = String.Empty;
private string phoneNumberValue = String.Empty;
public event PropertyChangedEventHandler PropertyChanged;
// This method is called by the Set accessor of each property.
// The CallerMemberName attribute that is applied to the optional propertyName
// parameter causes the property name of the caller to be substituted as an argument.
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
// The constructor is private to enforce the factory pattern.
private DemoCustomer()
{
customerNameValue = "Customer";
phoneNumberValue = "(312)555-0100";
}
// 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 CustomerName
{
get
{
return this.customerNameValue;
}
set
{
if (value != this.customerNameValue)
{
this.customerNameValue = value;
NotifyPropertyChanged();
}
}
}
public string PhoneNumber
{
get
{
return this.phoneNumberValue;
}
set
{
if (value != this.phoneNumberValue)
{
this.phoneNumberValue = value;
NotifyPropertyChanged();
}
}
}
}
}

Resources