I am developing an application which uses a hierarchical object structure and displays a few key object properties from those objects on the main GUI within a DataGridView. Those values must update when the underlying data changes. I have considered a few options:
Bind the individual DataGridView cells to the relevant object properties. I understand that this is not possible, and DGV binding is all or nothing.
Dynamically position Textboxes over the grid cells and bind those, but this seems messy.
Create an intermediate list/array/collection which references only the relevant object properties, and then use that list as a data source for the DataGridView.
Respond to the PropertyChanged events. The complication is that I have got multiple classes. The top-level object exists within the UI scope, and has a child object which in turn may have multiple child objects of its own, and so on. The UI can access properties of all objects, but not their events.
I have been looking at passing the PropertyChanged event from whichever level it occurs up the chain so that it can be handled within the UI. So within a particular class I want to respond to OnPropertyChanged within that class, and within any children, and make any events raised available to the parent class. Thus events would flow up the tree to the top.
I understand how to do the two steps individually, I think, with reference to the following:
Handling OnPropertyChanged
Pass click event of child control to the parent control
However, although I presume the two can be combined, I am not quite sure how to do this. In the UI I have got this:
project.PropertyChanged += new PropertyChangedEventHandler(ProjectPropertyChanged);
private void ProjectPropertyChanged(object sender, PropertyChangedEventArgs e) {
MessageBox.Show("In main UI. Project property changed!");
}
And then one level down I have got something like this:
public class Project : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public Project() {
childObject.PropertyChanged += new PropertyChangedEventHandler(ProjectPropertyChanged);
}
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
ProjectPropertyChanged(sender, e); // this doesn't work due to different parameters
}
private void PropertyChanged(object sender, PropertyChangedEventArgs e)
{
// Event available to parent class
}
}
The idea being that each class would pass its own OnPropertyChanged() events to its PropertyChanged() method, and respond to its children's OnPropertyChanged() events, and expose all to the parent class.
If doing this, ideally I would like to retain knowledge of which property changed in order to respond accordingly.
The most immediate issue is lack of compatibility between ProjectPropertyChanged and OnPropertyChanged due to different parameters. More fundamentally, though, I am not sure whether this method is workable or optimal.
How best to do this?
To answer my own question:
I tried unsuccessfully to do this with an intermediate binding source, using a DataTable (as per this question.). The problem was that I couldn't create references to the data objects. The DataTable seemed to contain values.
So I ended up using a method I was more sure about, but is less elegant, which is option 2 in my question above. I position Labels where I need bound data values, and bind to those labels. This works.
With some simplification, and pretending that our objects are animals, my solution was this:
Label[,] dashboardLabels = new Label[3,14];
private void Form1_Shown(object sender, EventArgs e)
{
CreateLabels(); // create and position labels (no binding yet)
}
public void CreateLabels(int cols = 3, int rows = 14)
{
for (int col = 0; col < cols; col++)
{
for (int row = 0; row < rows; row++)
{
// Create label...
Label l = new Label();
l.Text = "N/A";
l.ForeColor = Color.Red
dashboardLabels[col, row] = l;
this.Controls.Add(l);
// Position label over DGV cell...
Point dgvCell = dataGridView1.GetCellDisplayRectangle(col + 2, rowNumbersLabel[row], false).Location;
Point dgvGrid = dataGridView1.Location;
l.Left = dgvGrid.X + dgvCell.X;
l.Top = dgvGrid.Y + dgvCell.Y;
}
}
}
private void UpdateLabels(List<Dog> dogs)
{
for (int i = 0; i < dogs; i++)
{
if (!dashboardLabels[i, 0].Visible) dashboardLabels[i, 0].Visible = true;
if (dogs[i].IsSetUp) BindLabel(dashboardLabels[i, 0], dogs[i],"Name");
}
}
private void BindLabel(Label l, Dog dog, string observation)
{
Binding b = new Binding("Text", dog, observation);
l.DataBindings.Add(b);
l.ForeColor = Color.Green;
}
}
Then when the objects are created, I call UpdateLabels(). If not initialised, the label will show 'N/A' in red at this point. If initialised, the label will become green and will be bound to the object's name so it will update automatically from that point on.
I did much searching and the information I was finding suggested that a DataGridView does not support complex data binding i.e. it is pretty much one class to one DGV, or not at all. I couldn't find an alternative grid-like control which would do it either.
Related
So I have 4 TextBoxes and I have already set the maximum value of my ProgressBar to 4.
ProgressAttr.Maximum = 4;
What I would like to do is increase my ProgressBar Value by 1 every time I fill out a TextBox.
My code right now looks like this:
if (!string.IsNullOrEmpty(Name_txtBox.Text))
{
ProgressAttr.Value += 1;
}
if (!string.IsNullOrEmpty(Serial_TxtBox.Text))
{
ProgressAttr.Value += 1;
}
if (!string.IsNullOrEmpty(Cap_TxtBox.Text))
{
ProgressAttr.Value += 1;
}
if (!string.IsNullOrEmpty(IDprk_TxtBox.Text))
{
ProgressAttr.Value += 1;
}
This doesn't increase the value of my ProgressBar.
I've also tried this:
if (textbox.Text.Length > 0)
{
ProgressAttr.Value += 1;
}
None of this works for me and Ive been trying to find a solution for hours. I would really appreciate your help and am looking forward to seeing solutions that you guys suggest!
I'm proposing you a method that makes use of DataBindings to synchronize the content of your TextBoxes with the Value property of a ProgressBar.
A class object can notify changes related to its Properties values implementing the INotifyPropertyChanged interface. Its public PropertyChanged event is raised to notify bound Controls that Properties of the data Provider have changed.
All bound Properties are then updated to the new values.
This allows you to have all the logic in a single place and changes to the User Interface (your Form, here) do not affect the data binding in any way.
You can add or remove Controls from the UI. The binding procedure doesn't change or needs to keep track of what has changed in the UI.
For example, bind your ProgressBar.Value property to the ProgressBarController.Value property. You initialize the ProgressBarController with the instances of the TextBox (or RichTextBox) Controls that you want to include, add a Binding to link the properties and that's all. All the rest happens automatically.
ProgressBarController pbarController = null;
// Form Constuctor
public SomeForm()
{
InitializeComponent();
// [...]
// These TextBoxes could be child of a Container (e.g., a Panel), so you could
// also get all the child Controls of this Container to build the array
var textBoxes = new[]{ Name_txtBox, Serial_TxtBox, Cap_TxtBox, IDprk_TxtBox}
ProgressAttr.Maximum = textBoxes.Length;
pbarController = new ProgressBarController(textBoxes);
ProgressAttr.DataBindings.Add("Value", pbarController, "Value", false,
DataSourceUpdateMode.OnPropertyChanged);
}
protected override void OnFormClosed(FormClosedEventArgs e)
{
pbarController.Dispose();
base.OnFormClosed(e);
}
Here, two TextBoxes already contain some text when the Form is loaded, so the ProgressBar shows a progress. If you remove all text in the Designer, of course the progress shown is initially 0:
The ProgressBarController class is initialized with the array of Controls passed in its Contructor.
► It then build a Dictionary<TextBoxBase, int> to keep track of the progress value associated to a Control: 0 if its Text is empty, otherwise 1.
TextBoxBase so you can also use RichTextBox Controls.
► The TextChanged event of these Controls is subscribes to using a single handler. The sender object will be the Control that raised the event.
► If/when the associated value has changed (the Control Text state determines a change), the PropertyChanged event is raised and the DataBinding notifies the ProgressBar to update its Value property.
► When the Parent Form is closed, call the Dispose() method of this class to remove the subscription to the TextChanged events.
using System.Runtime.CompilerServices;
private class ProgressBarController : INotifyPropertyChanged, IDisposable
{
public event PropertyChangedEventHandler PropertyChanged;
private Dictionary<TextBoxBase, int> states;
private int m_Value = 0;
public ProgressBarController(params TextBoxBase[] tboxes) {
states = new Dictionary<TextBoxBase, int>();
for (int i = 0; i < tboxes.Length; i++) {
states.Add(tboxes[i], tboxes[i].Text.Length > 0 ? 1 : 0);
tboxes[i].TextChanged += TextChanged;
}
m_Value = states.Values.Sum();
}
public int Value {
get => m_Value;
private set {
if (value != m_Value) {
m_Value = value;
OnPropertyChanged();
}
}
}
protected void OnPropertyChanged([CallerMemberName] string propertyName = "") =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
protected void TextChanged(object sender, EventArgs e)
{
var tbx = sender as TextBoxBase;
int state = tbx.Text.Length > 0 ? 1 : 0;
if (states[tbx] != state) {
states[tbx] = state;
Value = states.Values.Sum();
}
}
public void Dispose() {
foreach (var tb in states.Keys) {
tb.TextChanged -= this.TextChanged;
}
}
}
Hmm, not sure if I chose the right approach.
I have a grid of components. In the first column there are DatePickers.
In the second column there are combo-boxes. In the last column there are text-boxes. The grid has 15 rows.
I named them by their column and row number as you would number cells in a grid.
So dp1_1 for DatePicker are position (1,1), dp2_1 for position (2,1).
cb1_1 for ComboBox are position (1,1), cb2_1 for ComboBox position (2,1).
I keep my date pickers data, combo-boxes data, text-boxes data in an ordinary list for easy access/reference, like so:
public int numOfRows = 15;
private List<DateTime> _MyDateTimeList = new List<DateTime>();
public List<DateTime> MyDateTimeList
{
get { return _MyDateTimeList; }
set {
DateTime pomDatumObjava;
_MyDateTimeList = value;
for (int i = 0; i < numOfRows; i += 1)
{
pomDatumObjava = new DateTime();
// code for accessing/enabling/disabling the appropriate date picker, which doesn't work since I don't know how to send the window reference where my date pickers reside
// pomDatumObjava = Utils.enableDisableDatePicker(null, Constants.DP_LABEL + stringIndex, true, 1).SelectedDate.Value;
_MyDateTimeList.Add(pomDatumObjava);
}
OnPropertyChanged("MyDateTimeList");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string name)
{
Console.WriteLine("OnPropertyChanged -> " + name);
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
Console.WriteLine("handler != null -> " + name);
handler(this, new PropertyChangedEventArgs(name));
}
}
public static DatePicker enableDisableDatePicker(System.Windows.Window myWindow, string name, bool enableDisable, double op)
{
DatePicker dp = (DatePicker)myWindow.FindName(name);
if (!enableDisable)
{
dp.SelectedDate = null;
}
dp.IsEnabled = enableDisable;
dp.Opacity = op;
return dp;
}
How do I access my components in the window and reference them appropriately so that each time I change a value in certain DatePicker, I detect the change in the list?
You can find the Utils function in comments line. Where it says null, there should be the window objects where my components are placed.
Or, is this the right approach?
I will have lot of components(15x3 = 45 x code for OnPropertyChanged), so the MVVM file will be quite large to set OnPropertyChanged() for all of them.
As ASh says, you need an ObservableCollection of objects, one for each row. ObservableCollections automatically update their bound controls when you add or remove objects, and pass on events when the objects change. These objects would presumably have three properties (for the datepicker, combobox and text box) that have OnPropertyChanged().
Then bind the ObservableCollection to the ItemSource of your grid, and the three controls to the three properties of an item.
For MVVM, you shouldn't ever need to reference a control in the View. Instead the view should reflect the state of the ViewModel. If you want to disable a datepicker, it's Enabled property should be bound to some thing that raises OnPropertyChanged().
If you post your view, we can suggest how to do this.
I've found similar answers to my question before, but not quite to what I'm trying to do...
In Visual Basic (last I used it, in 06/07) there was an "Index" property you could assign to multiple controls with the same name. I used this primarily to loop through controls, i.e.:
For i = 1 to 500
picSeat(i).Print "Hello"
Next i
Is there a way to do this in C#? I know there is a .IndexOf(), but would that really help for what I'm doing? I want to have multiple controls with the same name, just different index.
This is a Windows Form Application, and I'm using Visual Studio 2012. I am talking about controls, not arrays/lists; this was possible in VB and I was wondering if it was possible at all in C#. So I want to have, say, 30 seats in a theatre. I want to have each seat represented by a picturebox named "picSeat". VB would let me name several objects the exact same, and would assign a value to a control property "Index". That way, I could use the above loop to print "Hello" in every picture box with only 3 lines of code.
No, this feature does not exist in C#, and was never implemented in the transition from classic VB to VB.Net.
What I normally do instead is put each of the controls in question in a common parent container. The Form itself can work, but if you need to distinguish these from others of the same type a GroupBox or Panel control will work, too. Then, you access the controls like this:
foreach (var picBox in parentControl.Controls.OfType<PictureBox>())
{
// do something with each picturebox
}
If you want to use a specific control, just write by name:
pictureBox6.SomeProperty = someValue;
If you need to change a specific control determined at run-time, normally this is in response to a user event:
void PictureBox_Click(object sender, EventArgs e)
{
var picBox = sender As PictureBox;
if (picBox == null) return;
//picBox is now whichever box was clicked
// (assuming you set all your pictureboxes to use this handler)
}
If you really really want the Control Arrays feature, you can do it by adding code to create the array to your form's Load event:
PictureBox[] pictureBoxes = Me.Controls.OfType<PictureBox>().ToArray();
Are we talking WinForms here? I'm not sure, but I don't think you can have multiple controls in winforms with same name. But I vaguely recall doing something similar and the solution was to name them Button_1, Button_2 etc. Then you can iterate through all controls and get your own index.
Beware though that if you want to instanciate a separate control for each seat in a theatre, you might run into some serious performance issues :) I've done something similar to that as well and ended up drawing the whole thing on a canvas and using mouse coordinates to handle the events correctly.
You may want to check out the Uid property of controls.
(http://msdn.microsoft.com/en-us/library/system.windows.uielement.uid(v=vs.110).aspx)
You can access Control through Uid property with the following
private static UIElement FindUid(this DependencyObject parent, string uid)
{
var count = VisualTreeHelper.GetChildrenCount(parent);
if (count == 0) return null;
for (int i = 0; i < count; i++)
{
var el = VisualTreeHelper.GetChild(parent, i) as UIElement;
if (el == null) continue;
if (el.Uid == uid) return el;
el = el.FindUid(uid);
if (el != null) return el;
}
return null;
}
And simply use
var control = FindUid("someUid");
I copied code from this post
If you create an indexed dictionary of your user control, it will behave pretty much the same as in VB6, though you'll not see it on the VS C# GUI. You'll have to get around the placement issues manually. Still - and most importantly -, you'll be able to refer to any instance by the index.
The following example is for 3 pieces for clarity, but of course you could automate every step of the process with appropriate loops.
public partial class Form1 : Form
{
...
Dictionary<int, UserControl1> NameOfUserControlInstance = new Dictionary<int, UserControl1>()
{
{ 1, new UserControl1 {}},
{ 2, new UserControl1 {}},
{ 3, new UserControl1 {}}
};
private void Form1_Load(object sender, EventArgs e)
{
NameOfUserControlInstance[1].Location = new System.Drawing.Point(0, 0);
NameOfUserControlInstance[2].Location = new System.Drawing.Point(200, 0);
NameOfUserControlInstance[3].Location = new System.Drawing.Point(400, 0);
Controls.Add(NameOfUserControlInstance[1]);
Controls.Add(NameOfUserControlInstance[2]);
Controls.Add(NameOfUserControlInstance[3]);
}
...
}
I like using Tags to apply any type of meta data about the controls
for (int i = 0; i< 10; ++i)
{
Button button = new Button();
button.Tag = i;
}
Sorry for the poor quality of the title. I couldn't think of a better way to phrase this.
For a project I'm currently working on with a few friends, I got myself in the situation where I have created a dynamic form (with reflection) which I now want to validate.
Example (ignore the black box, it contains old form elements which are now irrelevant and i didn't want to confuse you guys):
As you may have guessed already, it is an application for creating a mysql database.
Which is where I get to my problem(s). I want to disable checkboxes if others are checked.
For example: If I check "PrimaryKey" I want to disable the checkbox "Null".
Changing from unsigned to signed changes the numericupdown minimum and maximum etc.
But with reflection and all, I find it difficult to know exactly which checkbox to disable.
I was hoping you guys would have some suggestions.
I have been thinking about this for a while and a few thoughts have come to mind. Maybe these are better solutions than the current one.
Thought 1: I create UserControls for every datatype. Pro's: no problems with reflection and easy identifying of every control in the UserControl for validation. Con's: Copy-Pasting, Lots of UserControls, with a lot of the same controls.
Thought 2: Doing something with the description tags for every property of the classes. Creating rules in the description that allow me to link the checkboxes together. Here I'll only have to copy the rules to every class property and then it should be ok.
I had been thinking of other solutions but I failed to remember them.
I hope you guys can give me a few good pointers/suggestions.
[Edit]
Maybe my code can explain a bit more.
My code:
PropertyInfo[] properties = DataTypes.DataTypes.GetTypeFromString(modelElement.DataType.ToString()).GetType().GetProperties();
foreach (PropertyInfo prop in properties)
{
if (prop.Name != "Label" && prop.Name != "Project" && prop.Name != "Panel")
{
var value = prop.GetValue(modelElement.DataType, null);
if (value != null)
{
tableLayoutPanel1.Controls.Add(new Label { Text = prop.Name, Anchor = AnchorStyles.Left, AutoSize = true });
switch (value.GetType().ToString())
{
case "System.Int32":
NumericUpDown numericUpDown = new NumericUpDown();
numericUpDown.Text = value.ToString();
numericUpDown.Dock = DockStyle.None;
tableLayoutPanel1.Controls.Add(numericUpDown);
break;
case "System.Boolean":
CheckBox checkBox = new CheckBox();
checkBox.Dock = DockStyle.None;
// checkbox will become huge if not for these changes
checkBox.AutoSize = false;
checkBox.Size = new Size(16, 16);
if (value.Equals(true))
{
checkBox.CheckState = CheckState.Checked;
}
tableLayoutPanel1.Controls.Add(checkBox);
break;
default:
MessageBox.Show(#"The following type has not been implemented yet: " + value.GetType());
break;
}
}
}
}
Here is a mockup from my comments:
// The ViewModel is responsible for handling the actual visual layout of the form.
public class ViewModel {
// Fire this when your ViewModel changes
public event EventHandler WindowUpdated;
public Boolean IsIsNullCheckBoxVisible { get; private set; }
// This method would contain the actual logic for handling window changes.
public void CalculateFormLayout() {
Boolean someLogic = true;
// If the logic is true, set the isNullCheckbox to true
if (someLogic) {
IsIsNullCheckBoxVisible = true;
}
// Inform the UI to update
UpdateVisual();
}
// This fires the 'WindowUpdated' event.
public void UpdateVisual() {
if (WindowUpdated != null) {
WindowUpdated(this, new EventArgs());
}
}
}
public class TheUI : Form {
// Attach to the viewModel;
ViewModel myViewModel = new ViewModel();
CheckBox isNullCheckBox = new CheckBox();
public TheUI() {
this.myViewModel.WindowUpdated += myViewModel_WindowUpdated;
}
void myViewModel_WindowUpdated(object sender, EventArgs e) {
// Update the view here.
// Notie that all we do in the UI is to update the visual based on the
// results from the ViewModel;
this.isNullCheckBox.Visible = myViewModel.IsIsNullCheckBoxVisible;
}
}
The basic idea here is that you ensure that the UI does as little as possible. It's role should just be to update. Update what? That's for the ViewModel class to decide. We perform all of the updating logic in the ViewModel class, and then when the updating computations are done, we call the UpdateVisual() event, which tells the UI that it needs to represent itself. When the WindowUpdated Event occurs, the UI just responds by displaying the configuration set up by the ViewModel.
This may seem like a lot of work to set up initially, but once in place it will save you tons and tons of time down the road. Let me know if you have any questions.
Try relating the event of one checkbox to disable the other; something like this:
private void primaryKeyBox_AfterCheck(object sender, EventArgs e)
{
nullBox.Enabled = false;
}
This is a very simple example and would have to be changed a bit, but for what I think you're asking it should work. You would also have to add to an event for the boxes being unchecked. You would also need logic to only get data from certain checkboxes based on the ones that are and are not checked.
For all the other things, such as changing the numbers based on the dropdown, change them based on events as well.
For WinForms I would use data binding.
Create an object and implement INotifyPropertyChanged and work with that object.
Then, If you have an object instance aObj:
To bind the last name property to a textbox on the form do this:
Private WithEvents txtLastNameBinding As Binding
txtLastNameBinding = New Binding("Text", aObj, "LastName", True, DataSourceUpdateMode.OnValidation, "")
txtLastName.DataBindings.Add(txtLastNameBinding)
Take a look here for more info.
INotifyPropertyChanged
What is the use of ObservableCollection in .net?
ObservableCollection is a collection that allows code outside the collection be aware of when changes to the collection (add, move, remove) occur. It is used heavily in WPF and Silverlight but its use is not limited to there. Code can add event handlers to see when the collection has changed and then react through the event handler to do some additional processing. This may be changing a UI or performing some other operation.
The code below doesn't really do anything but demonstrates how you'd attach a handler in a class and then use the event args to react in some way to the changes. WPF already has many operations like refreshing the UI built in so you get them for free when using ObservableCollections
class Handler
{
private ObservableCollection<string> collection;
public Handler()
{
collection = new ObservableCollection<string>();
collection.CollectionChanged += HandleChange;
}
private void HandleChange(object sender, NotifyCollectionChangedEventArgs e)
{
foreach (var x in e.NewItems)
{
// do something
}
foreach (var y in e.OldItems)
{
//do something
}
if (e.Action == NotifyCollectionChangedAction.Move)
{
//do something
}
}
}
An ObservableCollection works essentially like a regular collection except that it implements
the interfaces:
INotifyCollectionChanged,
INotifyPropertyChanged
As such it is very useful when you want to know when the collection has changed. An event is triggered that will tell the user what entries have been added/removed or moved.
More importantly they are very useful when using databinding on a form.
From Pro C# 5.0 and the .NET 4.5 Framework
The ObservableCollection<T> class is very useful in that it has the ability to inform external objects
when its contents have changed in some way (as you might guess, working with
ReadOnlyObservableCollection<T> is very similar, but read-only in nature).
In many ways, working with
the ObservableCollection<T> is identical to working with List<T>, given that both of these classes
implement the same core interfaces. What makes the ObservableCollection<T> class unique is that this
class supports an event named CollectionChanged. This event will fire whenever a new item is inserted, a current item is removed (or relocated), or if the entire collection is modified.
Like any event, CollectionChanged is defined in terms of a delegate, which in this case is
NotifyCollectionChangedEventHandler. This delegate can call any method that takes an object as the first parameter, and a NotifyCollectionChangedEventArgs as the second. Consider the following Main()
method, which populates an observable collection containing Person objects and wires up the
CollectionChanged event:
class Program
{
static void Main(string[] args)
{
// Make a collection to observe and add a few Person objects.
ObservableCollection<Person> people = new ObservableCollection<Person>()
{
new Person{ FirstName = "Peter", LastName = "Murphy", Age = 52 },
new Person{ FirstName = "Kevin", LastName = "Key", Age = 48 },
};
// Wire up the CollectionChanged event.
people.CollectionChanged += people_CollectionChanged;
// Now add a new item.
people.Add(new Person("Fred", "Smith", 32));
// Remove an item.
people.RemoveAt(0);
Console.ReadLine();
}
static void people_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
// What was the action that caused the event?
Console.WriteLine("Action for this event: {0}", e.Action);
// They removed something.
if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Remove)
{
Console.WriteLine("Here are the OLD items:");
foreach (Person p in e.OldItems)
{
Console.WriteLine(p.ToString());
}
Console.WriteLine();
}
// They added something.
if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add)
{
// Now show the NEW items that were inserted.
Console.WriteLine("Here are the NEW items:");
foreach (Person p in e.NewItems)
{
Console.WriteLine(p.ToString());
}
}
}
}
The incoming NotifyCollectionChangedEventArgs parameter defines two important properties,
OldItems and NewItems, which will give you a list of items that were currently in the collection before the event fired, and the new items that were involved in the change. However, you will want to examine these lists only under the correct circumstances. Recall that the CollectionChanged event can fire when
items are added, removed, relocated, or reset. To discover which of these actions triggered the event,
you can use the Action property of NotifyCollectionChangedEventArgs. The Action property can be
tested against any of the following members of the NotifyCollectionChangedAction enumeration:
public enum NotifyCollectionChangedAction
{
Add = 0,
Remove = 1,
Replace = 2,
Move = 3,
Reset = 4,
}
Explanation without Code
For those wanting an answer without any code behind it (boom-tish) with a story (to help you remember):
Normal Collections - No Notifications
Every now and then I go to NYC and my wife asks me to buy stuff. So I take a shopping list with me. The list has a lot of things on there like:
Louis Vuitton handbag ($5000)
Clive Christian’s Imperial Majesty Perfume ($215,000 )
Gucci Sunglasses ($2000)
hahaha well I"m not buying that stuff. So I cross them off and remove them from the list and I add instead:
12 dozen Titleist golf balls.
12 lb bowling ball.
So I usually come home without the goods and she's never pisssssssed off the thing is that she doesn't know about what i take off the list and what I add onto it; she gets no notifications.
The ObservableCollection - notifications when changes made
Now, whenever I remove something from the list: she get's a notification.
The observable collection works just the same way. If you add or remove something to or from it: someone is notified.
And when they are notified, then bunker down or run for cover! Of course, the consequences are customisable via an event handler.
Silly story, but hopefully you'll remember the concept now.
One of the biggest uses is that you can bind UI components to one, and they'll respond appropriately if the collection's contents change. For example, if you bind a ListView's ItemsSource to one, the ListView's contents will automatically update if you modify the collection.
EDIT:
Here's some sample code from MSDN:
http://msdn.microsoft.com/en-us/library/ms748365.aspx
In C#, hooking the ListBox to the collection could be as easy as
listBox.ItemsSource = NameListData;
though if you haven't hooked the list up as a static resource and defined NameItemTemplate you may want to override PersonName's ToString(). For example:
public override ToString()
{
return string.Format("{0} {1}", this.FirstName, this.LastName);
}
it is a collection which is used to notify mostly UI to change in the collection , it supports automatic notification.
Mainly used in WPF ,
Where say suppose you have UI with a list box and add button and when you click on he button an object of type suppose person will be added to the obseravablecollection and you bind this collection to the ItemSource of Listbox , so as soon as you added a new item in the collection , Listbox will update itself and add one more item in it.
class FooObservableCollection : ObservableCollection<Foo>
{
protected override void InsertItem(int index, Foo item)
{
base.Add(index, Foo);
if (this.CollectionChanged != null)
this.CollectionChanged(this, new NotifyCollectionChangedEventArgs (NotifyCollectionChangedAction.Add, item, index);
}
}
var collection = new FooObservableCollection();
collection.CollectionChanged += CollectionChanged;
collection.Add(new Foo());
void CollectionChanged (object sender, NotifyCollectionChangedEventArgs e)
{
Foo newItem = e.NewItems.OfType<Foo>().First();
}
ObservableCollection Caveat
Mentioned above (Said Roohullah Allem)
What makes the ObservableCollection class unique is that this
class supports an event named CollectionChanged.
Keep this in mind...If you adding a large number of items to an ObservableCollection the UI will also update that many times. This can really gum up or freeze your UI.
A work around would be to create a new list, add all the items then set your property to the new list. This hits the UI once. Again...this is for adding a large number of items.