WPF Update UserControl when item has been added from other user control - wpf

I am working on WPF project using MVVM and I have an issue that I am sure someone came across..
I have a MainWindow in which I am loading UserControl1 In UserControl1, I have a hyperlink which opens the Modal window and in which, I load UserControl2 as modal. Both UserControls has their own view models. Now from second view model, I want to trigger a PropertyChangednotification to a collection in view model of a UserControl1? How can I achieve that?
what I am trying to accomplish is: I have a combobox on UserControl1 that needs to be updated when item is added from UserControl2.
I am using the following class and both viewmodels inherits the follwoing class:
public abstract class PropertyChangedNotification : INotifyPropertyChanged, IDataErrorInfo
{
#region Fields
private readonly Dictionary<string, object> _values = new Dictionary<string, object>();
#endregion
#region Protected
/// <summary>
/// Sets the value of a property.
/// </summary>
/// <typeparam name="T">The type of the property value.</typeparam>
/// <param name="propertySelector">Expression tree contains the property definition.</param>
/// <param name="value">The property value.</param>
protected void SetValue<T>(Expression<Func<T>> propertySelector, T value)
{
string propertyName = GetPropertyName(propertySelector);
SetValue<T>(propertyName, value);
}
/// <summary>
/// Sets the value of a property.
/// </summary>
/// <typeparam name="T">The type of the property value.</typeparam>
/// <param name="propertyName">The name of the property.</param>
/// <param name="value">The property value.</param>
protected void SetValue<T>(string propertyName, T value)
{
if (string.IsNullOrEmpty(propertyName))
{
throw new ArgumentException("Invalid property name", propertyName);
}
_values[propertyName] = value;
NotifyPropertyChanged(propertyName);
}
/// <summary>
/// Gets the value of a property.
/// </summary>
/// <typeparam name="T">The type of the property value.</typeparam>
/// <param name="propertySelector">Expression tree contains the property definition.</param>
/// <returns>The value of the property or default value if not exist.</returns>
protected T GetValue<T>(Expression<Func<T>> propertySelector)
{
string propertyName = GetPropertyName(propertySelector);
return GetValue<T>(propertyName);
}
/// <summary>
/// Gets the value of a property.
/// </summary>
/// <typeparam name="T">The type of the property value.</typeparam>
/// <param name="propertyName">The name of the property.</param>
/// <returns>The value of the property or default value if not exist.</returns>
protected T GetValue<T>(string propertyName)
{
if (string.IsNullOrEmpty(propertyName))
{
throw new ArgumentException("Invalid property name", propertyName);
}
object value;
if (!_values.TryGetValue(propertyName, out value))
{
value = default(T);
_values.Add(propertyName, value);
}
return (T)value;
}
/// <summary>
/// Validates current instance properties using Data Annotations.
/// </summary>
/// <param name="propertyName">This instance property to validate.</param>
/// <returns>Relevant error string on validation failure or <see cref="System.String.Empty"/> on validation success.</returns>
protected virtual string OnValidate(string propertyName)
{
if (string.IsNullOrEmpty(propertyName))
{
throw new ArgumentException("Invalid property name", propertyName);
}
string error = string.Empty;
var value = GetValue(propertyName);
var results = new List<System.ComponentModel.DataAnnotations.ValidationResult>(1);
var result = Validator.TryValidateProperty(
value,
new ValidationContext(this, null, null)
{
MemberName = propertyName
},
results);
if (!result)
{
var validationResult = results.First();
error = validationResult.ErrorMessage;
}
return error;
}
#endregion
#region Change Notification
/// <summary>
/// Raised when a property on this object has a new value.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Raises this object's PropertyChanged event.
/// </summary>
/// <param name="propertyName">The property that has a new value.</param>
protected void NotifyPropertyChanged(string propertyName)
{
this.VerifyPropertyName(propertyName);
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
var e = new PropertyChangedEventArgs(propertyName);
handler(this, e);
}
}
protected void NotifyPropertyChanged<T>(Expression<Func<T>> propertySelector)
{
var propertyChanged = PropertyChanged;
if (propertyChanged != null)
{
string propertyName = GetPropertyName(propertySelector);
propertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion // INotifyPropertyChanged Members
#region Data Validation
string IDataErrorInfo.Error
{
get
{
throw new NotSupportedException("IDataErrorInfo.Error is not supported, use IDataErrorInfo.this[propertyName] instead.");
}
}
string IDataErrorInfo.this[string propertyName]
{
get
{
return OnValidate(propertyName);
}
}
#endregion
#region Privates
private string GetPropertyName(LambdaExpression expression)
{
var memberExpression = expression.Body as MemberExpression;
if (memberExpression == null)
{
throw new InvalidOperationException();
}
return memberExpression.Member.Name;
}
private object GetValue(string propertyName)
{
object value;
if (!_values.TryGetValue(propertyName, out value))
{
var propertyDescriptor = TypeDescriptor.GetProperties(GetType()).Find(propertyName, false);
if (propertyDescriptor == null)
{
throw new ArgumentException("Invalid property name", propertyName);
}
value = propertyDescriptor.GetValue(this);
_values.Add(propertyName, value);
}
var propertyDescriptor1 = TypeDescriptor.GetProperties(GetType()).Find(propertyName, false);
if (propertyDescriptor1 == null)
{
throw new ArgumentException("Invalid property name", propertyName);
}
value = propertyDescriptor1.GetValue(this);
return value;
}
#endregion
#region Debugging
/// <summary>
/// Warns the developer if this object does not have
/// a public property with the specified name. This
/// method does not exist in a Release build.
/// </summary>
[Conditional("DEBUG")]
[DebuggerStepThrough]
public void VerifyPropertyName(string propertyName)
{
// Verify that the property name matches a real,
// public, instance property on this object.
if (TypeDescriptor.GetProperties(this)[propertyName] == null)
{
string msg = "Invalid property name: " + propertyName;
if (this.ThrowOnInvalidPropertyName)
throw new Exception(msg);
else
Debug.Fail(msg);
}
}
/// <summary>
/// Returns whether an exception is thrown, or if a Debug.Fail() is used
/// when an invalid property name is passed to the VerifyPropertyName method.
/// The default value is false, but subclasses used by unit tests might
/// override this property's getter to return true.
/// </summary>
protected virtual bool ThrowOnInvalidPropertyName { get; private set; }
#endregion // Debugging Aides
}
UserControl1 is an investment control and user control 2 is an institution control as seen below in screenshot
When someone clicks on "Add New" it opens the Institution control in a modal window..
investment user control bound to investmentViewModel and institution control bound to institutionViewModel.
When I click on Save changes on modal, it will trigger propertyChangeNotification on institutionViewModel and not the investmentViewModel.

trigger a PropertyChangednotification to a collection in view model of a UserControl1
Really don't overthink it...Put a method on the class for UserControl1 which will trigger the change. On the other control where the process originates, create a dependency property of UserControl's type and assign it in the XAML. Then when UserControl2 needs to do the operation, call usercontrol1 instance method.
have to get a current instance of Investment UserControl while in the InstitutionViewModel. Is there any way, I can get that?
Place a static property of the VM on the app (public partial class App in the code). Then in the constructor of the VM have it look up the app and assign itself to that static. Then it can be gathered by anyone else.
Or create a flag dependency property on UserControl1 & UserControl2 and bind both controls to a shared Boolean using TwoWay binding. When the Boolean changes from UserControl2 changing it, do the update in UserControl1.

Related

Is there a way to test the RaiseChanged use of a property in MVVMLight?

I have lots of properties in my viewmodels that have very little logic in them but they have the RaisePropertyChanged() method in them to refresh the GUI. i.e.
private bool _StatesIsSelected;
public bool StatesIsSelected
{
get { return _StatesIsSelected; }
set
{
_StatesIsSelected = value;
RaisePropertyChanged("StatesIsSelected");
}
}
I am starting to wonder if I should have unit tests confirming that the RaisePropertyChanged() method has been called. If I forgot to put it in the property the GUI wouldn't get refreshed and the application would have a bug...so it should have a unit test. But how do you test that?
So to sum it up....Am I being to militant about having unit tests for this logic? And if I am not being to militant...what is there a good way to test for this?
Are you being militant? That is not really an easy question to answer. We do test the majority of our property changed events, of which there are a lot, and I'm not really sure how much value there is in those tests. By that I mean if we removed them and stopped writing them in the future would we start seeing more bugs, or even any that wouldn't be pretty obvious as soon as you used the client? To be honest the answer is probably no. Conversely they are easy tests to write and certainly don't hurt.
Anyway, yes there is a very nice way to do this (had to make a few minor tweeks, so can't guarantee the code will compile, but should make the concepts clear):
public static class PropertyChangedTestHelperFactory
{
/// <summary>
/// Factory method for creating <see cref="PropertyChangedTestHelper{TTarget}"/> instances.
/// </summary>
/// <param name="target">
/// The target.
/// </param>
/// <typeparam name="TTarget">
/// The target type.
/// </typeparam>
/// <returns>
/// The <see cref="PropertyChangedTestHelper{TTarget}"/>
/// </returns>
public static PropertyChangedTestHelper<TTarget> CreatePropertyChangedHelper<TTarget>(
this TTarget target)
where TTarget : INotifyPropertyChanged
{
return new PropertyChangedTestHelper<TTarget>(target);
}
}
public sealed class PropertyChangedTestHelper<TTarget> : IDisposable
where TTarget : INotifyPropertyChanged
{
/// <summary>
/// This list contains the expected property names that should occur in property change notifications
/// </summary>
private readonly Queue<string> propertyNames = new Queue<string>();
/// <summary>
/// The target of the helper
/// </summary>
private readonly TTarget target;
/// <summary>
/// Initialises a new instance of the <see cref="StrictPropertyChangedTestHelper{TTarget}"/> class.
/// </summary>
/// <param name="target">The target.</param>
public PropertyChangedTestHelper(TTarget target)
{
this.target = target;
this.target.PropertyChanged += this.HandleTargetPropertyChanged;
}
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
this.target.PropertyChanged -= this.HandleTargetPropertyChanged;
if (this.propertyNames.Count != 0)
{
Assert.Fail("Property change notification {0} was not raised", this.propertyNames.Peek());
}
}
/// <summary>
/// Sets an expectation that a refresh change notification will be raised.
/// </summary>
public void ExpectRefresh()
{
this.propertyNames.Enqueue(string.Empty);
}
/// <summary>
/// Sets an expectation that a property change notification will be raised.
/// </summary>
/// <typeparam name="TProperty">The type of the property.</typeparam>
/// <param name="propertyExpression">The property expression.</param>
public void Expect<TProperty>(Expression<Func<TTarget, TProperty>> propertyExpression)
{
this.propertyNames.Enqueue(((MemberExpression)propertyExpression.Body).Member.Name);
}
/// <summary>
/// Handles the target property changed event.
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The <see cref="PropertyChangedEventArgs"/> instance containing the event data.</param>
private void HandleTargetPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (this.propertyNames.Count == 0)
{
Assert.Fail("Unexpected property change notification {0}", e.PropertyName);
}
var expected = this.propertyNames.Dequeue();
var propertyName = (e.PropertyName ?? string.Empty).Trim();
if (propertyName != expected)
{
Assert.Fail("Out of order property change notification, expected '{0}', actual '{1}'", expected, propertyName);
}
}
}
Usage:
[TestMethod]
public void StatesIsSelected_RaisesIsValidChangeNotification()
{
// Arrange
var target = new SomeViewModel();
using (var helper = target.CreatePropertyChangedHelper())
{
helper.Expect(item => item.StatesIsSelected);
// Act
target.StatesIsSelected = true;
// Assert
}
}
When the helper is disposed the expectations are interrogated and the test will fail if they are not all met in the order they were defined.
We also have a Weak version that only requires that the expectations are met, not that they are met exactly (i.e. other property change events could be raised) and that is not order dependent.
FYI - if I were you I'd think about ditching MVVMLight and moving to Caliburn.Micro, its in a different league.
you can test it easily:
void TestMethod()
{
Container container = new Container();
bool isRaised = false;
container.PropertyChanged += (o,e) => {
if(e.PropertyName == "StatesIsSelected")
isRaised = true;
};
container.StatesIsSelected = true;
Assert.True(isRaised);
}
It think it would be useful to write the test for the ViewModel base class but not for all properties which raise a change, thats just too extreme
If you're targeting .NET Framework >= 4.5 you can inherit from ViewModelBase and write helper method:
public class ViewModelBaseExtended : ViewModelBase
{
protected void TryRaisePropertyChanged<T>(ref T oldValue, T newValue,
[CallerMemberName] string propertyName = "")
{
if (oldValue == null || !oldValue.Equals(newValue))
{
oldValue = newValue;
RaisePropertyChanged(propertyName);
}
}
}
and your property code would look like that:
private bool _StatesIsSelected;
public bool StatesIsSelected
{
get { return _StatesIsSelected; }
set
{
TryRaisePropertyChanged(ref _StatesIsSelected, value);
}
}
Now you would only have to assert property value in your unit tests.

ICustomTypeDescriptor wth XamDataGrid

I have extended the XamDataGrid to support DynamicColumn generation using a DependencyProperty called ColumnSource. Thus the gird now will generate columns dynamically based on a dependency property called "ColumnSource". this idea was inspired from the DevExpress WPF Grid I had used before.
Having said that, I need to mention that I am using Field (not UnBoundField) inside the extended control to generate the columns and binding them to the ViewModel objects. It has worked fine for requirement I had till now.
Now I have a situation where I have a ViewModel that needs to have dynamic properties. Obviously I have ICustomTypeDescriptor in mind.I just am curious is it possible to view data in the XamDataGrid with the following limitations:
.Net 4.0
ICustomTypeDescriptor
Use of field and not UnboundField class for column generations.
Data shown should be two way bindable,
that is change in cell data should change appropriate ViewModel
property.
I am pasting the Extended control's code here. It is very long so I will try to curtail the code responsible for other functionalities.
public class AdvancedXamDataGrid : XamDataGrid
{
#region Static Constructor
static AdvancedXamDataGrid()
{
//Dependency properties overrides if any to be done here.
DataSourceProperty.OverrideMetadata(typeof(AdvancedXamDataGrid), new FrameworkPropertyMetadata(null, DataSourcePropetyChanged));
}
#endregion
#region Dependency Properties
/// <summary>
/// Dependency proeprty for Columns List shown in the Grid Header
/// </summary>
public static readonly DependencyProperty ColumnsSourceProperty = DependencyProperty.Register("ColumnsSource", typeof(IEnumerable),
typeof(AdvancedXamDataGrid), new FrameworkPropertyMetadata(null, OnColumnsSourceChanged));
/// <summary>
/// Gets or sets the <see cref="ColumnsSource"/>.
/// This is a Dependency Property.
/// </summary>
public IEnumerable ColumnsSource
{
get { return GetValue(ColumnsSourceProperty) as IEnumerable; }
set { SetValue(ColumnsSourceProperty, value); }
}
#endregion
#region Dependency Property Property Changed Handlers (static).
/// <summary>
/// The handler is fired when the <see cref="ColumnsSource"/> is changed.
/// </summary>
/// <param name="sender">The dependency object that raises the event.</param>
/// <param name="e">The event argument</param>
private static void OnColumnsSourceChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
var control = sender as AdvancedXamDataGrid;
if (null != control)
{
if (null != control._fieldAdornerSettings)
control.DetachAdorner();
control._fieldAdornerSettings = new FieldAdornerSettings();
control._fieldAdornerList = new List<FieldAdorner>();
var oldValue = e.OldValue as IEnumerable;
var newValue = e.NewValue as IEnumerable;
if (BindingOperations.IsDataBound(sender, ColumnsSourceProperty))
control.ColumnsSourceChanged(oldValue, newValue);
}
}
/// <summary>
/// This handler is fired when the data source property changes.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private static void DataSourcePropetyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
var control = sender as AdvancedXamDataGrid;
if (null != control)
{
var dataSource = e.NewValue as IEnumerable;
control.DataSource = dataSource;
}
}
#endregion
#region Instance Properties and Event Handlers
/// <summary>
/// Handles when the <see cref="ColumnsSource"/> is changed.
/// </summary>
/// <param name="oldValue"></param>
/// <param name="newValue"></param>
private void ColumnsSourceChanged(IEnumerable oldValue, IEnumerable newValue)
{
if (null != oldValue)
//I could never figure out why I need this check. But this check is requred for consistent laytout for first time load.Funny I know!
FieldLayouts.Clear(); //Clear the existing columns.
var oldColSource = oldValue as INotifyCollectionChanged;
if (null != oldColSource)
{
oldColSource.CollectionChanged -= oldColSource_CollectionChanged;
//Remove the columns first.
foreach (IGridColumn column in oldValue)
{
RemoveField(column);
}
}
var newColSource = newValue as INotifyCollectionChanged;
if (null != newColSource)
{
newColSource.CollectionChanged += oldColSource_CollectionChanged;
}
if (null != newValue)
{
var fieldLayout = new FieldLayout {IsDefault = true, Key = Convert.ToString(Guid.NewGuid())};
FieldLayouts.Add(fieldLayout);
foreach (IGridColumn col in newValue)
{
AddField(col);
}
DefaultFieldLayout = fieldLayout;
}
}
/// <summary>
/// Fires when the ColumnsSource Collection changes.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void oldColSource_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
//Remove old Items.
foreach (IGridColumn col in e.OldItems)
{
RemoveField(col);
}
//Add new items.
foreach (IGridColumn col in e.NewItems)
{
AddField(col);
}
}
/// <summary>
/// Adds a Field to the wrapped grids FiledCollection.
/// </summary>
/// <param name="column"></param>
private void AddField(IGridColumn column)
{
if (FieldLayouts.Count > 0)
{
var fieldLayout = FieldLayouts[0];
var field = new Field {Name = column.Name, Label = column.DisplayName.ToUpper(), ToolTip = column.ToolTip};
switch (column.ColumnType)
{
// case GridColumnType.Text:
// field.DataType = typeof(string);
// break;
case GridColumnType.Boolean:
var style = new Style(typeof (XamCheckEditor));
style.Setters.Add(new Setter(XamCheckEditor.IsCheckedProperty,
new Binding()
{
Path = new PropertyPath(string.Concat("DataItem.", column.Name)),
UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged,
Mode = BindingMode.TwoWay
}));
field.Settings.EditorType = typeof (XamCheckEditor);
field.Settings.EditorStyle = style;
break;
}
if (column.ColumnType == GridColumnType.Combo)
{
var style = new Style(typeof (XamComboEditor));
style.Setters.Add(new Setter(XamComboEditor.ItemsSourceProperty,
new Binding() {Path = new PropertyPath(column.ItemsSource)}));
style.Setters.Add(new Setter(XamComboEditor.SelectedItemProperty,
new Binding(column.SelectedItemPropertyName) {Mode = BindingMode.TwoWay}));
style.Setters.Add(new Setter(XamComboEditor.DisplayMemberPathProperty, column.DisplayMemberPath));
style.Setters.Add(new Setter(XamComboEditor.ValuePathProperty, column.ValueMemberPath));
field.Settings.EditorType = typeof (XamComboEditor);
field.Settings.EditorStyle = style;
}
if (column.IsReadOnly)
field.Settings.AllowEdit = false;
if (!column.IsVisible)
field.Visibility = Visibility.Collapsed;
fieldLayout.Fields.Add(field);
if (!string.IsNullOrEmpty(column.TemplateKey))
_fieldAdornerList.Add(new FieldAdorner()
{
Name = column.Name,
BindToParentSource = column.BindToParent,
TemplateKey = column.TemplateKey
});
//Register to the property changed notofication.
var propertyNotifier = column as INotifyPropertyChanged;
propertyNotifier.PropertyChanged += propertyNotifier_PropertyChanged;
}
}
/// <summary>
/// Removes a field
/// </summary>
/// <param name="column"></param>
private void RemoveField(IGridColumn column)
{
if (FieldLayouts.Count > 0)
{
var fieldLayout = FieldLayouts[0];
var field = fieldLayout.Fields.FirstOrDefault(f => f.Name.Equals(column.Name));
if (null != field)
fieldLayout.Fields.Remove(field);
var propertyNotifier = column as INotifyPropertyChanged;
propertyNotifier.PropertyChanged -= propertyNotifier_PropertyChanged;
}
}
/// <summary>
/// Event handler for handling property notification.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void propertyNotifier_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
var column = sender as IGridColumn;
if (null != column)
{
var fieldLayout = FieldLayouts[0];
var field = fieldLayout.Fields.FirstOrDefault(f => f.Name.Equals(column.Name));
if (e.PropertyName.Equals("IsVisible"))
{
if (field != null)
field.Visibility = column.IsVisible ? Visibility.Visible : Visibility.Collapsed;
}
if (e.PropertyName.Equals("IsReadOnly"))
{
if (field != null)
field.Settings.AllowEdit = !column.IsReadOnly;
}
}
}
#endregion
}
Here is the IGridColumn contract:
/// <summary>
/// A contract that need to be implemented by an item that needs to participate in ColumnSource binding.
/// </summary>
public interface IGridColumn : INotifyPropertyChanged
{
/// <summary>
/// Gets or sets the PropertyName to which the Column would bind.
/// </summary>
string Name { get; set; }
/// <summary>
/// Gets or sets the Display Text that will be visible in the column header.
/// </summary>
string DisplayName { get; set; }
/// <summary>
/// Gets the type of the property that gets bound to this column.
/// </summary>
GridColumnType ColumnType { get; }
/// <summary>
/// Gets or sets if the column is read-only.
/// </summary>
bool IsReadOnly { get; set; }
/// <summary>
/// Gets or sets if the column is visible.
/// </summary>
bool IsVisible { get; set; }
#region For Combo Columns
/// <summary>
/// Gets or sets the Items source of the combo editor.
/// </summary>
string ItemsSource { get; set; }
/// <summary>
/// Gets or sets the SelectedItem propertyName.
/// </summary>
string SelectedItemPropertyName { get; set; }
/// <summary>
/// Gets or sets the name of the property that be the display item of the combo.
/// </summary>
string DisplayMemberPath { get; set; }
/// <summary>
/// Gets or sets the name of the property that be the value item of the combo.
/// </summary>
string ValueMemberPath { get; set; }
/// <summary>
/// Gets or sets the tool tip on the column.
/// </summary>
string ToolTip { get; set; }
/// <summary>
/// Gets or sets the Template Key for the adorner.
/// </summary>
string TemplateKey { get; set; }
/// <summary>
/// Gets or sets if the smart tag, would be bound to the view model of the grid.
/// <remarks>
/// Note: By default it would be bound to an item of the grid.
/// </remarks>
/// </summary>
bool BindToParent { get; set; }
/// <summary>
/// Gets or sets the caption for the smart tag.
/// </summary>
string SmartTagCaption { get; set; }
#endregion
}
/// <summary>
/// An enumeration offering various types of Grid Column types.
/// </summary>
public enum GridColumnType
{
Text=0,
Boolean,
Integer,
Double,
Decimal,
Combo
} ;
I had the plan to populating the ColumnSource for the Grid and bind them to ICustomTypeDescriptor instances of ViewModels whose Dynamic property names would match with the IGridColumn Names.

How to implement INotifyCollectionChanged in MVVM for Reset, Add,Remove,Move,Replace of NotifyCollectionChangedAction

My ViewModelBase class is:
public abstract class ViewModelBase:INotifyPropertyChanged, IDisposable, INotifyCollectionChanged
{
#region Constructor
protected ViewModelBase()
{
}
#endregion // Constructor
#region DisplayName
/// <summary>
/// Returns the user-friendly name of this object.
/// Child classes can set this property to a new value,
/// or override it to determine the value on-demand.
/// </summary>
public virtual string DisplayName { get; protected set; }
#endregion // DisplayName
#region Debugging Aides
/// <summary>
/// Warns the developer if this object does not have
/// a public property with the specified name. This
/// method does not exist in a Release build.
/// </summary>
[Conditional("DEBUG")]
[DebuggerStepThrough]
public void VerifyPropertyName(string propertyName)
{
// Verify that the property name matches a real,
// public, instance property on this object.
if (TypeDescriptor.GetProperties(this)[propertyName] == null)
{
string msg = "Invalid property name: " + propertyName;
if (this.ThrowOnInvalidPropertyName)
throw new Exception(msg);
else
Debug.Fail(msg);
}
}
/// <summary>
/// Returns whether an exception is thrown, or if a Debug.Fail() is used
/// when an invalid property name is passed to the VerifyPropertyName method.
/// The default value is false, but subclasses used by unit tests might
/// override this property's getter to return true.
/// </summary>
protected virtual bool ThrowOnInvalidPropertyName { get; private set; }
#endregion // Debugging Aides
#region INotifyPropertyChanged Members
/// <summary>
/// raised when property of this object has some new value
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
var e = new PropertyChangedEventArgs(propertyName);
handler(this, e);
}
}
#endregion
#region IDisposable Members
public void Dispose()
{
this.OnDispose();
}
/// <summary>
/// child classes can override this method to perform cleanup logic,like removing eventhandlers and disposing objects
/// Anindya
/// </summary>
protected virtual void OnDispose()
{
//no implementation has been done here
//intentionhally I have done so
//so that this method will be only used for the overriding of this method
//by default nothing I have kept in this method
}
#endregion
#region INotifyCollectionChanged Members
/// <summary>
/// Occurs when an item is added, removed, changed, moved, or the entire list is refreshed.
/// </summary>
public event NotifyCollectionChangedEventHandler CollectionChanged;
protected virtual void OnCollectionChanged(CollectionChangeEventArgs ccevent)
{
//NotifyCollectionChangedEventHandler handler = this.CollectionChanged;
//if (handler != null)
//{
// var e = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction
//}
}
#endregion
}
and my WorkSpaceViewModel is inheriting from ViewModelBase as followes:
public abstract class WorkspaceViewModel:ViewModelBase
{
#region Fields
RelayCommand _closeCommand;
#endregion // Fields
#region Constructor
protected WorkspaceViewModel()
{
}
#endregion // Constructor
#region CloseCommand
/// <summary>
/// Returns the command that, when invoked, attempts
/// to remove this workspace from the user interface.
/// </summary>
public ICommand CloseCommand
{
get
{
if (_closeCommand == null)
_closeCommand = new RelayCommand(param => this.OnRequestClose());
return _closeCommand;
}
}
private void CanDoSomeImportantMethod()
{
}
#endregion // CloseCommand
#region RequestClose [event]
/// <summary>
/// Raised when this workspace should be removed from the UI.
/// </summary>
public event EventHandler RequestClose;
void OnRequestClose()
{
EventHandler handler = this.RequestClose;
if (handler != null)
handler(this, EventArgs.Empty);
}
#endregion // RequestClose [event]
}
My ViewModel is inheriting from WorkSpaceViewModel as followes:
public class MainWindowViewModel:WorkspaceViewModel,INotifyCollectionChanged
{
//
RelayCommand _loadCommand;
MatchBLL matchBLL = new MatchBLL();
EfesBetServiceReference.EfesBetClient proxy = new EfesBetClient();
public MainWindowViewModel()
{
_matchObsCollection = new ObservableCollection<EfesBet.DataContract.GetMatchDetailsDC>();
Load();
_matchObsCollection.CollectionChanged += new NotifyCollectionChangedEventHandler(_matchObsCollection_CollectionChanged);
}
/// <summary>
/// This will get called when the collection is changed(for reference see http://stackoverflow.com/questions/1427471/observablecollection-not-noticing-when-item-in-it-changes-even-with-inotifyprop)
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void _matchObsCollection_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
}
protected override void OnPropertyChanged(string propertyName)
{
base.OnPropertyChanged(propertyName);
}
public ICommand LoadCommand
{
get
{
if (_loadCommand == null)
{
_loadCommand = new RelayCommand(
param => this.Load(),
param => this.CanLoad
);
}
return _loadCommand;
}
}
List<EfesBet.DataContract.GetMatchDetailsDC> matchList;
ObservableCollection<EfesBet.DataContract.GetMatchDetailsDC> _matchObsCollection;
public ObservableCollection<EfesBet.DataContract.GetMatchDetailsDC> MatchObsCollection
{
get { return _matchObsCollection; }
set
{
_matchObsCollection = value;
OnPropertyChanged("MatchObsCollection");
}
}
public void Load()
{
matchList = new List<GetMatchDetailsDC>();
matchList = proxy.GetMatch().ToList();
foreach (EfesBet.DataContract.GetMatchDetailsDC match in matchList)
{
_matchObsCollection.Add(match);
}
//ajebaje code
PopulateSahibiKonuk();
}
bool CanLoad
{
get { return true; }
}
#region INotifyCollectionChanged Members
public event NotifyCollectionChangedEventHandler CollectionChanged;
#endregion
Now I am having a DataGrid in UI and I want OnCollectionChanged of my ObservableCollection. How NotifyCollectionChangedAction like add, move, remove, replace, reset my viewmodel should fire. But I do not know how to implement or what I have to do in my base classes or in my viewmodel. Please provide me some useful code or urls or suggestion regarding this.
Thanking you in advance.
Typically, a view model would not implement the INotifyCollectionChanged interface... that is for collection classes to implement. I have a massive WPF project and I haven't needed to implement that interface once.
I generally use custom collection classes that extend ObservableCollection<T> and these collections already implement the INotifyCollectionChanged interface. Therefore, when I need to display collections, I just add properties for these collections in my view model classes. If I then need to monitor changes in the collection, I would add a handler for the CollectionChanged event.
This is not something that can be built into the base view model unless you are certain that every view model will have a collection of a particular type. Even then, what would you do when you need more than one collection in a view model? You'd have to add a collection property and extra handlers, so why don't you just do that whenever you need to?

Samples on Live Updating Data Grid WPF

Could you please provide me some samples where DataGrid in WPF updates live.
I am trying to write an app, which will be updating a LIST regularly and that i want to show on a DataGrid using WPF.
Following is the code snippet.
MainWindow.XAMl
Model _model = new Model();
private void Window_Loaded(object sender, RoutedEventArgs e)
{
this.DataContext = _model;
}
DataGrid Xaml
<DataGrid
Height="214"
HorizontalAlignment="Left"
Margin="12,135,0,0"
Name="resultDataGrid"
VerticalAlignment="Top"
Width="720"
ItemsSource="{Binding Path=Results, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
/>
Code where I am updating the Results.
public class Model : INotifyPropertyChanged
{
ObservableCollection<Result> _results = new ObservableCollection<Result>();
public void X()
{
foreach (var file in Files)
{
_results.Add(new Result() { File = file, Status = "passsed" });
}
}
public ObservableCollection<Result> Results
{
get { return _results; }
set { _results = value; OnPropertyChanged("Results"); }
}
}
When I am adding to _results collection Live update is not happening.
Use databinding (by binding DataGrid.ItemsSource to your collection of items) and remember to fire INotifyPropertyChanged.PropertyChanged when an item is updated. Or if it the collection of items and not individual items that change fire INotifyCollectionChanged.CollectionChanged. Obviously you need to databind to classes that implement these interfaces for this to work.
Try using an Observable Collection instead of a normal list. This collection implements INotifyCollectionChanged already.
Note that this will work for adding or removing items in the list, but if you change the item's properties themselves and want to update the ObservableCollection, you need to have an ObservableCollection of ViewModels, which are implementing INotifyPropertyChanged on each property.
EDIT
This may be a silly question but where are you actually calling that x method? I copied your code pretty much exactly, created my own Result class, and implemented INotifyPropertyChanged and created an implementation of the RelayCommand pattern, bound that to the command of the button and it all worked. When I click the button, the datagrid changes.
All I can think of is that you haven't actually implemented the INotifyPropertyChanged, or you aren't running the x method.
here is the code I did:
public class Model : INotifyPropertyChanged
{
ObservableCollection<Result> _results = new ObservableCollection<Result>();
private List<string> Files;
public void X()
{
foreach (var file in Files)
{
_results.Add(new Result() { File = file, Status = "passsed" });
}
_results.Add(new Result() { File = DateTime.Now.ToString(), Status = "passed" });
}
public ObservableCollection<Result> Results
{
get { return _results; }
set { _results = value; OnPropertyChanged("Results"); }
}
public ICommand XCmd { get; protected set; }
private void InitializeCommands()
{
this.XCmd = new RelayCommand((param) => { this.X(); },
(param) => { return true; });
}
public Model()
{
Files = new List<string>();
Files.Add("ONE");
Files.Add("TWO");
Files.Add("THREE");
Files.Add("FOUR");
_results.Add(new Result() { File = "ZERO", Status = "Pending" });
_results.Add(new Result() { File = DateTime.Now.ToString(), Status = "Pending" });
InitializeCommands();
}
#region INotifyPropertyChanged Members
/// <summary>
/// Raised when a property on this object has a new value.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Raises this object's PropertyChanged event.
/// </summary>
/// <param name="propertyName">The property that has a new value.</param>
protected virtual void OnPropertyChanged(string propertyName)
{
this.VerifyPropertyName(propertyName);
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
var e = new PropertyChangedEventArgs(propertyName);
handler(this, e);
}
}
#endregion // INotifyPropertyChanged Members
#region Debugging Aides
/// <summary>
/// Warns the developer if this object does not have
/// a public property with the specified name. This
/// method does not exist in a Release build.
/// </summary>
[Conditional("DEBUG")]
[DebuggerStepThrough]
public void VerifyPropertyName(string propertyName)
{
// Verify that the property name matches a real,
// public, instance property on this object.
if (TypeDescriptor.GetProperties(this)[propertyName] == null)
{
string msg = "Invalid property name: " + propertyName;
if (this.ThrowOnInvalidPropertyName)
throw new Exception(msg);
else
Debug.Fail(msg);
}
}
/// <summary>
/// Returns whether an exception is thrown, or if a Debug.Fail() is used
/// when an invalid property name is passed to the VerifyPropertyName method.
/// The default value is false, but subclasses used by unit tests might
/// override this property's getter to return true.
/// </summary>
protected virtual bool ThrowOnInvalidPropertyName { get; private set; }
#endregion // Debugging Aides
Note that the INotifyPropertyChanged Members region implements the PropertyChanged event, and the Debugging aids region just checks that the property specified in the OnPropertyChanged handler actually exists.
Here is the xaml:
<Grid>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<DataGrid
HorizontalAlignment="Stretch"
Name="resultDataGrid"
VerticalAlignment="Stretch"
ItemsSource="{Binding Path=Results, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
/>
<Button Grid.Row="2" Command="{Binding XCmd}" Margin="5,5,5,5">click</Button>
</Grid>
Its not pretty I know but you can style it as you please
And here is the relaycommand implementation that I linked you earlier:
public class RelayCommand : ICommand
{
#region Private Accessor Fields
/// <summary>
/// A boolean function that contains the code to enable/disable the command and the associated UI elements.
/// </summary>
private readonly Func<object, bool> _canExecute = null;
/// <summary>
/// A generic delegate that will contain the code to execute.
/// </summary>
private readonly Action<object> _executeAction = null;
#endregion //Private Accessor Fields
#region Constructor
/// <summary>
/// Initializes a new instance of the RelayCommannd class
/// </summary>
/// <param name="executeAction">The execute action.</param>
/// <param name="canExecute">The can execute.</param>
public RelayCommand(Action<object> executeAction, Func<object, bool> canExecute)
{
this._executeAction = executeAction;
this._canExecute = canExecute;
}
#endregion
//Modified on 15 August 2011. CanExecuteChanged
#region Implementation of ICommand
/// <summary>
/// Occurs when changes occur that affect whether or not the command should execute.
/// </summary>
public event EventHandler CanExecuteChanged
{
//RequerySuggested occurs when the CommandManager detects conditions that might
//change the ability of a command to execute.
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
/// <summary>
/// Defines the method that determines whether the command can execute in its current state.
/// </summary>
/// <param name="parameter">Data used by the command. If the command does not require data to be passed,
/// this object can be null.</param>
/// <returns>true if this command can be executed; otherwise, false.</returns>
public bool CanExecute(object parameter)
{
if (this._canExecute == null)
{
return true;
}
return this._canExecute(parameter);
}
/// <summary>
/// Defines the method to be called when the command is invoked.
/// </summary>
/// <param name="parameter">Data used by the command. If the command does not require data to be passed,
/// this object can be set to null</param>
public void Execute(object parameter)
{
if (this._executeAction != null)
{
this._executeAction(parameter);
}
}
#endregion
If this doesn't work you will have to show me more of your code, because I really don't know why it's not working.

How to attach FrameworkContentElement to MVVM Light EventToCommand

I'm trying to convert an event to a command on a devexpress wpf grid context menu item which is derived from FrameworkContentElement instead of FrameworkElement. This causes a runtime error :
{"Cannot attach type \"EventToCommand\" to type \"BarButtonItem\". Instances of type \"EventToCommand\" can only be attached to objects of type \"FrameworkElement\"."}
Is there any workaround?
<dxg:TableView.RowCellMenuCustomizations>
<dxb:BarButtonItem Name="deleteRowItem" Content="Delete" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="ItemClick">
<cmd:EventToCommand Command="{Binding FooChangeCommand}"
PassEventArgsToCommand="True" />
</i:EventTrigger>
</i:Interaction.Triggers>
</dxb:BarButtonItem>
<!--ItemClick="deleteRowItem_ItemClick"/>-->
</dxg:TableView.RowCellMenuCustomizations>
Unfortunately devexpress have run into problems changing the base class to FrameworkElement having intended to make that change...
The FrameworkConentElement is a class that is only available in WPF and not in Silverlight. As MVVM Light is intended to provide a common functionality for all WPF dialects (WPF 3.5, WPF 4, Silverlight 3, Silverlight 4, Sivlverlight 5, WP 7, WP 7.1) it cannot include an implementation that only works in one of the frameworks.
For a discussion about the differences between FrameworkElement and FrameworkContentElement see here.
However, you can just easily implement your own EventToCommand class supporting ContentElement (from which FrameworkContentElement inherits). The class was copied from BL0015 of the MVVM Light source code and modified:
using System;
using System.Windows;
using System.Windows.Input;
using System.Windows.Interactivity;
namespace GalaSoft.MvvmLight.Command
{
/// <summary>
/// This <see cref="System.Windows.Interactivity.TriggerAction" /> can be
/// used to bind any event on any FrameworkElement to an <see cref="ICommand" />.
/// Typically, this element is used in XAML to connect the attached element
/// to a command located in a ViewModel. This trigger can only be attached
/// to a FrameworkElement or a class deriving from FrameworkElement.
/// <para>To access the EventArgs of the fired event, use a RelayCommand<EventArgs>
/// and leave the CommandParameter and CommandParameterValue empty!</para>
/// </summary>
////[ClassInfo(typeof(EventToCommand),
//// VersionString = "3.0.0.0",
//// DateString = "201003041420",
//// Description = "A Trigger used to bind any event to an ICommand.",
//// UrlContacts = "http://stackoverflow.com/q/6955785/266919",
//// Email = "")]
public partial class EventToCommandWpf : TriggerAction<DependencyObject>
{
/// <summary>
/// Gets or sets a value indicating whether the EventArgs passed to the
/// event handler will be forwarded to the ICommand's Execute method
/// when the event is fired (if the bound ICommand accepts an argument
/// of type EventArgs).
/// <para>For example, use a RelayCommand<MouseEventArgs> to get
/// the arguments of a MouseMove event.</para>
/// </summary>
public bool PassEventArgsToCommand
{
get;
set;
}
/// <summary>
/// Provides a simple way to invoke this trigger programatically
/// without any EventArgs.
/// </summary>
public void Invoke()
{
Invoke(null);
}
/// <summary>
/// Executes the trigger.
/// <para>To access the EventArgs of the fired event, use a RelayCommand<EventArgs>
/// and leave the CommandParameter and CommandParameterValue empty!</para>
/// </summary>
/// <param name="parameter">The EventArgs of the fired event.</param>
protected override void Invoke(object parameter)
{
if (AssociatedElementIsDisabled())
{
return;
}
var command = GetCommand();
var commandParameter = CommandParameterValue;
if (commandParameter == null
&& PassEventArgsToCommand)
{
commandParameter = parameter;
}
if (command != null
&& command.CanExecute(commandParameter))
{
command.Execute(commandParameter);
}
}
private static void OnCommandChanged(
EventToCommandWpf element,
DependencyPropertyChangedEventArgs e)
{
if (element == null)
{
return;
}
if (e.OldValue != null)
{
((ICommand)e.OldValue).CanExecuteChanged -= element.OnCommandCanExecuteChanged;
}
var command = (ICommand)e.NewValue;
if (command != null)
{
command.CanExecuteChanged += element.OnCommandCanExecuteChanged;
}
element.EnableDisableElement();
}
private bool AssociatedElementIsDisabled()
{
var element = GetAssociatedObject();
return AssociatedObject == null
|| (element != null
&& !element.IsEnabled);
}
private void EnableDisableElement()
{
var element = GetAssociatedObject();
if (element == null)
{
return;
}
var command = this.GetCommand();
if (this.MustToggleIsEnabledValue
&& command != null)
{
SetIsEnabled(element, command.CanExecute(this.CommandParameterValue));
}
}
private void OnCommandCanExecuteChanged(object sender, EventArgs e)
{
EnableDisableElement();
}
/// <summary>
/// Identifies the <see cref="CommandParameter" /> dependency property
/// </summary>
public static readonly DependencyProperty CommandParameterProperty = DependencyProperty.Register(
"CommandParameter",
typeof(object),
typeof(EventToCommandWpf),
new PropertyMetadata(
null,
(s, e) => {
var sender = s as EventToCommandWpf;
if (sender == null)
{
return;
}
if (sender.AssociatedObject == null)
{
return;
}
sender.EnableDisableElement();
}));
/// <summary>
/// Identifies the <see cref="Command" /> dependency property
/// </summary>
public static readonly DependencyProperty CommandProperty = DependencyProperty.Register(
"Command",
typeof(ICommand),
typeof(EventToCommandWpf),
new PropertyMetadata(
null,
(s, e) => OnCommandChanged(s as EventToCommandWpf, e)));
/// <summary>
/// Identifies the <see cref="MustToggleIsEnabled" /> dependency property
/// </summary>
public static readonly DependencyProperty MustToggleIsEnabledProperty = DependencyProperty.Register(
"MustToggleIsEnabled",
typeof(bool),
typeof(EventToCommandWpf),
new PropertyMetadata(
false,
(s, e) => {
var sender = s as EventToCommandWpf;
if (sender == null)
{
return;
}
if (sender.AssociatedObject == null)
{
return;
}
sender.EnableDisableElement();
}));
private object _commandParameterValue;
private bool? _mustToggleValue;
/// <summary>
/// Gets or sets the ICommand that this trigger is bound to. This
/// is a DependencyProperty.
/// </summary>
public ICommand Command
{
get
{
return (ICommand)GetValue(CommandProperty);
}
set
{
SetValue(CommandProperty, value);
}
}
/// <summary>
/// Gets or sets an object that will be passed to the <see cref="Command" />
/// attached to this trigger. This is a DependencyProperty.
/// </summary>
public object CommandParameter
{
get
{
return this.GetValue(CommandParameterProperty);
}
set
{
SetValue(CommandParameterProperty, value);
}
}
/// <summary>
/// Gets or sets an object that will be passed to the <see cref="Command" />
/// attached to this trigger. This property is here for compatibility
/// with the Silverlight version. This is NOT a DependencyProperty.
/// For databinding, use the <see cref="CommandParameter" /> property.
/// </summary>
public object CommandParameterValue
{
get
{
return this._commandParameterValue ?? this.CommandParameter;
}
set
{
_commandParameterValue = value;
EnableDisableElement();
}
}
/// <summary>
/// Gets or sets a value indicating whether the attached element must be
/// disabled when the <see cref="Command" /> property's CanExecuteChanged
/// event fires. If this property is true, and the command's CanExecute
/// method returns false, the element will be disabled. If this property
/// is false, the element will not be disabled when the command's
/// CanExecute method changes. This is a DependencyProperty.
/// </summary>
public bool MustToggleIsEnabled
{
get
{
return (bool)this.GetValue(MustToggleIsEnabledProperty);
}
set
{
SetValue(MustToggleIsEnabledProperty, value);
}
}
/// <summary>
/// Gets or sets a value indicating whether the attached element must be
/// disabled when the <see cref="Command" /> property's CanExecuteChanged
/// event fires. If this property is true, and the command's CanExecute
/// method returns false, the element will be disabled. This property is here for
/// compatibility with the Silverlight version. This is NOT a DependencyProperty.
/// For databinding, use the <see cref="MustToggleIsEnabled" /> property.
/// </summary>
public bool MustToggleIsEnabledValue
{
get
{
return this._mustToggleValue == null
? this.MustToggleIsEnabled
: this._mustToggleValue.Value;
}
set
{
_mustToggleValue = value;
EnableDisableElement();
}
}
/// <summary>
/// Called when this trigger is attached to a DependencyObject.
/// </summary>
protected override void OnAttached()
{
base.OnAttached();
EnableDisableElement();
}
/// <summary>
/// This method is here for compatibility
/// with the Silverlight version.
/// </summary>
/// <returns>The object to which this trigger
/// is attached casted as a FrameworkElement.</returns>
private IInputElement GetAssociatedObject()
{
return AssociatedObject as IInputElement;
}
private void SetIsEnabled(IInputElement element, bool value)
{
if (element is UIElement)
{
((UIElement)element).IsEnabled = value;
}
else if (element is ContentElement)
{
((ContentElement)element).IsEnabled = value;
}
else
{
throw new InvalidOperationException("Cannot set IsEnabled. Element is neither ContentElemen, nor UIElement.");
}
}
/// <summary>
/// This method is here for compatibility
/// with the Silverlight version.
/// </summary>
/// <returns>The command that must be executed when
/// this trigger is invoked.</returns>
private ICommand GetCommand()
{
return Command;
}
}
}
To inlcude it into your code you have to define a xml namespace pointing to the correct dll and then use it just like the normal EventToCommand class.
NOTE: This class does not work in Silverlight!
For those trying to solve this specific issue using dev express, this will do the trick!
<dxg:TableView.RowCellMenuCustomizations>
<dxb:BarButtonItem Name="deleteRowItem" Content="Delete" Command="{Binding View.DataContext.DeleteSelectionCommand}" />
</dxg:TableView.RowCellMenuCustomizations>
I was following their example which had an event on the button, little realising there was also a command I could use. Then the challenge was working out the binding as the menu item is not on the main visual tree. However the above solves that.
I found this to work with DEV express
<dxb:BarButtonItem Content="123" Name="item1">
<dxmvvm:Interaction.Triggers>
<dxmvvm:EventToCommand EventName="ItemClick" Command="{Binding SomeCommand}" CommandParameter="{Binding ElementName=item1, Path=Content}"/>
</dxmvvm:Interaction.Triggers>
</dxb:BarButtonItem>

Resources