I am trying to unit test a model decorated with NotifyPropertyChanged and DispatchMethod.
BaseModel
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Forms;
public abstract class BaseModel : INotifyPropertyChanged, IDisposable
{
#region Fields
private readonly HashSet<string> ignorablePropertyNameses = new HashSet<string>();
private bool isDirty;
#endregion
#region Constructors and Destructors
protected BaseModel() { this._propertyChanged += this.OnAnyPropertyChanged; }
#endregion
#region Public Events
public event PropertyChangedEventHandler PropertyChanged
{
add { this._propertyChanged += value; }
remove { this._propertyChanged -= value; }
}
#endregion
#region Events
private event PropertyChangedEventHandler _propertyChanged;
#endregion
#region Public Properties
public HashSet<string> IgnorablePropertyNames
{
get { return this.ignorablePropertyNameses; }
}
public bool IsDirty
{
get { return this.isDirty; }
}
#endregion
#region Public Methods and Operators
public void Dispose() { this._propertyChanged -= this.OnAnyPropertyChanged; }
public void RaisePropertyChanged(string propertyName)
{
if (null != this._propertyChanged)
{
this._propertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public virtual void Save()
{
this.isDirty = false;
MessageBox.Show("Changes have been saved");
}
public void SetClean() { this.isDirty = false; }
#endregion
#region Methods
protected void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = this._propertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
private void OnAnyPropertyChanged(object sender, PropertyChangedEventArgs propertyChangedEventArgs)
{
if (!this.ignorablePropertyNameses.Contains(propertyChangedEventArgs.PropertyName))
{
this.isDirty = true;
}
}
#endregion
}
DogModel
using System;
using System.Threading;
using AGP.WinForms.MVC.PassiveView;
using PostSharp.Toolkit.Domain;
using PostSharp.Toolkit.Threading;
[NotifyPropertyChanged]
public class DogModel : BaseModel
{
#region Fields
private Timer timer;
#endregion
#region Constructors and Destructors
public DogModel()
{
this.IgnorablePropertyNames.Add("CurrentDateAndTime");
this.timer = new Timer(state => this.BroadcastTime(), null, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1));
}
#endregion
#region Public Properties
public string Breed { get; set; }
public DateTime CurrentDateAndTime { get; set; }
public string Name { get; set; }
#endregion
#region Methods
[DispatchedMethod]
private void BroadcastTime() { this.CurrentDateAndTime = DateTime.Now; }
#endregion
}
using this test:
using NUnit.Framework;
[TestFixture]
public class DogModelTests
{
[Test]
public void DirtyFlag()
{
var model = new DogModel();
Assert.IsFalse(model.IsDirty);
}
}
but am getting the following error upon test execution:
System.InvalidOperationException : Instances of classes marked with DispatcherObjectAspect can only be crated on threads with synchronization contexts (typically WPF or Windows.Forms UI threads), or must implement IDispatcherObject manually.
How could I provide the required synchronization context?
Apparently all I needed to do is set it up ...
[SetUp]
public void SetUp()
{
SynchronizationContext.SetSynchronizationContext(new SynchronizationContext());
}
Case closed.
Related
I am working on WPF (MVVM) ..
using this tutorial :
https://www.tutorialspoint.com/mvvm/mvvm_validations.htm
when trying to implement "AddEditCustomerViewModel"
I received two errors for this class:
namespace MVVMHierarchiesDemo.ViewModel
{
class AddEditCustomerViewModel : BindableBase
{
public AddEditCustomerViewModel()
{
CancelCommand = new MyICommand(OnCancel);
SaveCommand = new MyICommand(OnSave, CanSave);
}
private bool _EditMode;
public bool EditMode
{
get { return _EditMode; }
set { SetProperty(ref _EditMode, value); }
}
private SimpleEditableCustomer _Customer;
public SimpleEditableCustomer Customer
{
get { return _Customer; }
set { SetProperty(ref _Customer, value); }
}
private Customer _editingCustomer = null;
public void SetCustomer(Customer cust)
{
_editingCustomer = cust;
if (Customer != null) Customer.ErrorsChanged -= RaiseCanExecuteChanged;
Customer = new SimpleEditableCustomer();
Customer.ErrorsChanged += RaiseCanExecuteChanged;
CopyCustomer(cust, Customer);
}
private void RaiseCanExecuteChanged(object sender, EventArgs e)
{
SaveCommand.RaiseCanExecuteChanged();
}
public MyICommand CancelCommand { get; private set; }
public MyICommand SaveCommand { get; private set; }
public event Action Done = delegate { };
private void OnCancel()
{
Done();
}
private async void OnSave()
{
Done();
}
private bool CanSave()
{
return !Customer.HasErrors;
}
}
}
First one;on this lines :
CancelCommand = new MyICommand(OnCancel);
SaveCommand = new MyICommand(OnSave, CanSave);
an error appear on MyICommand constructor as in :
Error CS0305 Using the generic type 'MyICommand' requires 1 type
arguments
Second one , on this line :
CopyCustomer(cust, Customer);
an error appear for CopyCustomer function as in :
Error CS0103 The name 'CopyCustomer' does not exist in the current
context
the implementation of MyICommand as in :
public class MyICommand<T> : ICommand
{
Action<T> _TargetExecuteMethod;
Func<T, bool> _TargetCanExecuteMethod;
public MyICommand(Action<T> executeMethod)
{
_TargetExecuteMethod = executeMethod;
}
public MyICommand(Action<T> executeMethod, Func<T, bool> canExecuteMethod)
{
_TargetExecuteMethod = executeMethod;
_TargetCanExecuteMethod = canExecuteMethod;
}
public void RaiseCanExecuteChanged()
{
CanExecuteChanged(this, EventArgs.Empty);
}
#region ICommand Members
bool ICommand.CanExecute(object parameter)
{
if (_TargetCanExecuteMethod != null)
{
T tparm = (T)parameter;
return _TargetCanExecuteMethod(tparm);
}
if (_TargetExecuteMethod != null)
{
return true;
}
return false;
}
// Beware - should use weak references if command instance lifetime is
//longer than lifetime of UI objects that get hooked up to command
// Prism commands solve this in their implementation
public event EventHandler CanExecuteChanged = delegate { };
void ICommand.Execute(object parameter)
{
if (_TargetExecuteMethod != null)
{
_TargetExecuteMethod((T)parameter);
}
}
#endregion
}
}
to see the whole Project folder ; please see this link :
https://drive.google.com/drive/folders/1jFDU6vDMW_TO0J88NT53oLVWVYbAYyTv?usp=sharing
I have fiddled around with MVVM lately in C# and i got to the point where i thought i understood how bindings work but then this happened...
using System;
using System.Collections.Generic;
using System.Text;
namespace API
{
public class ApiViewModel : BaseViewModel
{
public bool CustomerIsChecked { get; set; }
public bool StorageIsChecked { get; set; }
public bool ArticlesIsChecked { get; set; }
public bool Transfer()
{
if(CustomerIsChecked == true)
{
return true;
}
return false;
}
public override string ToString()
{
return Transfer().ToString();
}
}
}
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using PropertyChanged;
namespace API
{
[AddINotifyPropertyChangedInterface]
public class BaseViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged = (sender, e) => { };
}
}
This works if i just like send in a string or anything to the binding directly but when i try to send in the value of transfer it does not work it gives me an empty button why is this? My question is why this doesnt work quz basicly when you are using a string without any parameters or anything and just do a getter or setter it works but to send in a string that has this doesnt? Why is this?
You should remove the () behind the Transfer method:
public class ApiViewModel : BaseViewModel
{
public bool CustomerIsChecked { get; set; }
public bool StorageIsChecked { get; set; }
public bool ArticlesIsChecked { get; set; }
public bool Transfer // <- remove ()
{
get // it should have a getter.
{
if(CustomerIsChecked == true)
{
return true;
}
return false;
}
}
public override string ToString()
{
return Transfer().ToString();
}
}
The code of Transfer could be simplified. It should return true when CustomerIsChecked is true, otherwise false.
So:
public bool Transfer
{
get => CustomerIsChecked;
}
My old answer (I didn't understood the question)
You should implement the INotifyPropertyChanged and raise he event. Too bad you need to have a full property (add a field)
Since you can only invoke an event from the class it self, you need to implement a method to raise the event in the base class.
For example:
public class BaseViewModel : INotifyPropertyChanged
{
protected void RaisePropertyChanged(string propertyName) =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
public event PropertyChangedEventHandler PropertyChanged;
}
public class ApiViewModel : BaseViewModel
{
private bool _customerIsChecked;
public bool CustomerIsChecked
{
get { return _customerIsChecked; }
set
{
_customerIsChecked = value;
RaisePropertyChanged(nameof(CustomerIsChecked));
}
}
}
It's also possible to create a helper method which takes care of the property changed.
I like this style which allows the new expression-bodied member,
public class BaseViewModel : INotifyPropertyChanged
{
public bool SetField<T>(ref T field, T value, [CallerMemberName] string memberName = "")
{
if (field != null)
{
if (field.Equals(value))
return false;
}
else if (value != null)
return false;
field = value;
RaisePropertyChanged(memberName);
return true;
}
protected void RaisePropertyChanged(string propertyName) =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
public event PropertyChangedEventHandler PropertyChanged;
}
public class ApiViewModel : BaseViewModel
{
private bool _customerIsChecked;
public bool CustomerIsChecked
{
get => _customerIsChecked;
set => SetField(ref _customerIsChecked, value);
}
}
i have this Base class:
public abstract class WiresharkFile
{
protected string _fileName;
protected int _packets;
protected int _packetsSent;
protected string _duration;
public int Packets
{
get { return _packets; }
set { _packets = value; }
}
public int PacketsSent
{
get { return _packetsSent; }
set { _packetsSent = value; }
}
}
And this sub class:
public class Libpcap : WiresharkFile, IDisposable, IEnumerable<WiresharkFilePacket>
{
....
}
Create my object:
WiresharkFile wiresahrkFile = new Libpcap(file);
My collection:
public ObservableCollection<WiresharkFile> wiresharkFiles { get; set; }
Send packets:
wiresahrkFile.Sendpackets();
At this point all my wiresahrkFile (Libpcap type) properties is changing so i wonder where i need to define this INotifyPropertyChanged.
If your xaml is binded to properties of WiresharkFile then a WiresharkFile have to implement the INotifyPropertyChanged, if not it will lead to the memory leaks (Top 3 Memory Leak Inducing Pitfalls of WPF Programming). If your binding is defined only on a Libpcap class then the Libpcap have to implement the INotifyPropertyChanged interface. In my projects I create a base implementation of the INotifyPropertyChanged interface ,and then each base models and base view models just inherits from that implementation. Here some base code:
1. Base implementation:
public class BaseObservableObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
protected virtual void OnPropertyChanged<T>(Expression<Func<T>> raiser)
{
var propName = ((MemberExpression)raiser.Body).Member.Name;
OnPropertyChanged(propName);
}
protected bool Set<T>(ref T field, T value, [CallerMemberName] string name = null)
{
if (!EqualityComparer<T>.Default.Equals(field, value))
{
field = value;
OnPropertyChanged(name);
return true;
}
return false;
}
}
2. Your model (in my opinion):
public abstract class WiresharkFile:BaseObservableObject
{
private string _fileName;
private int _packets;
private int _packetsSent;
private string _duration;
public int Packets
{
get { return _packets; }
set
{
_packets = value;
OnPropertyChanged();
}
}
public int PacketsSent
{
get { return _packetsSent; }
set
{
_packetsSent = value;
OnPropertyChanged();
}
}
}
regards,
Same answer with IIan but for C# 8 and .Net Framework 4.8.
1. Base Model
public class ObservableObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
protected virtual void OnPropertyChanged<T>(Expression<Func<T>> raiser)
{
string propName = ((MemberExpression)raiser?.Body).Member.Name;
OnPropertyChanged(propName);
}
protected bool Set<T>(ref T field, T value, [CallerMemberName] string name = null)
{
if (!EqualityComparer<T>.Default.Equals(field, value))
{
field = value;
OnPropertyChanged(name);
return true;
}
return false;
}
}
2. Your Model
public class Current : ObservableObject
{
private string _status;
public Current()
{
Status = "Not Connected";
}
public string Status
{
get { return _status; }
set
{
_status = value;
OnPropertyChanged(); // call this to update
}
}
}
3. How to use?
<Label Content="{Binding Status}"/>
What is the best way to create a class with an event that fires when one of its Properties is changed? Specifically, how do you convey to any subscribers which Property was changed?
Ex:
public class ValueChangedPublisher
{
private int _prop1;
private string _prop2;
public static event ValueChangedHandler(/*some parameters?*/);
public int Prop1
{
get { return _prop1; }
set
{
if (_prop1 != value)
{
_prop1 = value;
ValueChangedHandler(/*parameters?*/);
}
}
}
public string Prop2
{
get { return _prop2; }
set
{
if (_prop2 != value)
{
_prop2 = value;
ValueChangedHandler(/*parameters?*/);
}
}
}
}
public class ValueChangedSubscriber
{
private int _prop1;
private string _prop2;
public ValueChangedSubscriber()
{
ValueChangedPublisher.ValueChanged += ValueChanged;
}
private void ValueChanged(/*parameters?*/)
{
/*how does the subscriber know which property was changed?*/
}
}
My goal is to make this as extensible as possible (e.g. I don't want a bunch of huge if/else if/switch statements lumbering around). Does anybody know of a technique to achieve what I'm looking for?
EDIT:
What I'm really looking for is how to utilize the INotifyPropertyChanged pattern on the subscriber side. I don't want to do this:
private void ValueChanged(string propertyName)
{
switch(propertyName)
{
case "Prop1":
_prop1 = _valueChangedPublisher.Prop1;
break;
case "Prop2":
_prop2 = _valueChangedPublisher.Prop2;
break;
// the more properties that are added to the publisher, the more cases I
// have to handle here :/ I don't want to have to do it this way
}
}
.NET provides the INotifyPropertyChanged interface. Inherit and implement it:
public class ValueChangedPublisher : INotifyPropertyChanged
{
private int _prop1;
private string _prop2;
public event PropertyChangedEventHandler ValueChangedHandler;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public int Prop1
{
get { return _prop1; }
set
{
if (_prop1 != value)
{
_prop1 = value;
NotifyPropertyChanged();
}
}
}
public string Prop2
{
get { return _prop2; }
set
{
if (_prop2 != value)
{
_prop2 = value;
NotifyPropertyChanged();
}
}
}
}
Here is what I did to solve my problem. It's a bit large, so maybe not performant, but works for me.
Edit
See this question for performance details: C# using properties with value types with Delegate.CreateDelegate
Base Class for publishing property change stuff:
public abstract class PropertyChangePublisherBase : INotifyPropertyChanged
{
private Dictionary<string, PropertyInfo> _properties;
private bool _cacheProperties;
public bool CacheProperties
{
get { return _cacheProperties; }
set
{
_cacheProperties = value;
if (_cacheProperties && _properties == null)
_properties = new Dictionary<string, PropertyInfo>();
}
}
protected PropertyChangePublisherBase(bool cacheProperties)
{
CacheProperties = cacheProperties;
}
public bool ContainsBinding(PropertyChangedEventHandler handler)
{
if (PropertyChanged == null)
return false;
return PropertyChanged.GetInvocationList().Contains(handler);
}
public object GetPropertValue(string propertyName)
{
if (String.IsNullOrEmpty(propertyName) || String.IsNullOrWhiteSpace(propertyName))
throw new ArgumentException("Argument must be the name of a property of the current instance.", "propertyName");
return ProcessGetPropertyValue(propertyName);
}
protected virtual object ProcessGetPropertyValue(string propertyName)
{
if (_cacheProperties)
{
if (_properties.ContainsKey(propertyName))
{
return _properties[propertyName].GetValue(this, null);
}
else
{
var property = GetType().GetProperty(propertyName);
_properties.Add(propertyName, property);
return property.GetValue(this, null);
}
}
else
{
var property = GetType().GetProperty(propertyName);
return property.GetValue(this, null);
}
}
#region INotifyPropertyChanged Implementation
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
Base Class for receiving property change stuff:
public abstract class PropertyChangeSubscriberBase
{
protected readonly string _propertyName;
protected virtual object Value { get; set; }
protected PropertyChangeSubscriberBase(string propertyName, PropertyChangePublisherBase bindingPublisher)
{
_propertyName = propertyName;
AddBinding(propertyName, this, bindingPublisher);
}
~PropertyChangeSubscriberBase()
{
RemoveBinding(_propertyName);
}
public void Unbind()
{
RemoveBinding(_propertyName);
}
#region Static Fields
private static List<string> _bindingNames = new List<string>();
private static List<PropertyChangeSubscriberBase> _subscribers = new List<PropertyChangeSubscriberBase>();
private static List<PropertyChangePublisherBase> _publishers = new List<PropertyChangePublisherBase>();
#endregion
#region Static Methods
private static void PropertyChanged(object sender, PropertyChangedEventArgs args)
{
string propertyName = args.PropertyName;
if (_bindingNames.Contains(propertyName))
{
int i = _bindingNames.IndexOf(propertyName);
var publisher = _publishers[i];
var subscriber = _subscribers[i];
subscriber.Value = publisher.GetPropertValue(propertyName);
}
}
public static void AddBinding(string propertyName, PropertyChangeSubscriberBase subscriber, PropertyChangePublisherBase publisher)
{
if (!_bindingNames.Contains(propertyName))
{
_bindingNames.Add(propertyName);
_publishers.Add(publisher);
_subscribers.Add(subscriber);
if (!publisher.ContainsBinding(PropertyChanged))
publisher.PropertyChanged += PropertyChanged;
}
}
public static void RemoveBinding(string propertyName)
{
if (_bindingNames.Contains(propertyName))
{
int i = _bindingNames.IndexOf(propertyName);
var publisher = _publishers[i];
_bindingNames.RemoveAt(i);
_publishers.RemoveAt(i);
_subscribers.RemoveAt(i);
if (!_publishers.Contains(publisher))
publisher.PropertyChanged -= PropertyChanged;
}
}
#endregion
}
Actual class to use for subscribing to property change stuff:
public sealed class PropertyChangeSubscriber<T> : PropertyChangeSubscriberBase
{
private PropertyChangePublisherBase _publisher;
public new T Value
{
get
{
if (base.Value == null)
return default(T);
if (base.Value.GetType() != typeof(T))
throw new InvalidOperationException(String.Format("Property {0} on object of type {1} does not match the type Generic type specified {2}.", _propertyName, _publisher.GetType(), typeof(T)));
return (T)base.Value;
}
set { base.Value = value; }
}
public PropertyChangeSubscriber(string propertyName, PropertyChangePublisherBase bindingPublisher)
: base(propertyName, bindingPublisher)
{
_publisher = bindingPublisher;
}
}
Here is an example of the Class with properties that you wish to be notified about:
public class ExamplePublisher: PropertyChangedPublisherBase
{
private string _id;
private bool _testBool;
public string Id
{
get { return _id; }
set
{
if (value == _id) return;
_id = value;
RaisePropertyChanged("Id");
}
}
public bool TestBool
{
get { return _testBool; }
set
{
if (value.Equals(_testBool)) return;
_testBool = value;
RaisePropertyChanged("TestBool");
}
}
}
Here is an example of the Class that will be notified when the properties in the class above change:
public class ExampleReceiver
{
public PropertyChangeSubscriber<string> Id { get; set; }
public PropertyChangeSubscriber<bool> TestBool { get; set; }
public MyExampleClass(PropertyChangePublisherBase publisher)
{
Id = new PropertyChangeSubscriber<string>("Id", publisher);
TestBool = new PropertyChangeSubscriber<bool>("TestBool", publisher);
}
}
I'm trying to create basic class which will allow different controls to bind-in and display some values.
I want to have static list of objects, where each object has some properties like caption, ticks counter, whatever.
Then I want to bind label to last added item to this list and datagridview to allow to see all of them.
Would be great if such solution could be for both winforms and wpf environments.
If you could point me what I'm doing wrong. Thanks.
Draft of the idea (one of many already tested and failed) below.
status class:
public class Status: INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
//implementation of observable collection
private static ObservableCollection<Status> _list;
public static ObservableCollection<Status> List
{
get { return _list ?? (_list = new ObservableCollection<Status>{new Status()}); }
}
//object properties
public string Message { get; set; }
public bool Finished { get; set; }
//object views
public string View
{
get { return Message + "(" + Finished + ")" ; }
}
//object methods
public static Status Add(string message)
{
var result = new Status
{
Message = message,
Finished = false
};
List.Add(result);
return result;
}
public void Finish()
{
Finished = true;
}
}
form:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
label1.DataBindings.Add("Text", Status.List, "Message");
listBox1.DisplayMember = "View";
listBox1.DataSource = Status.List;
}
private void Button1_Click(object sender, EventArgs e)
{
label5.Text = Status.Add(textBox1.Text).Message;
textBox1.Text = "";
}
private void Button2_Click(object sender, EventArgs e)
{
((Status)listBox1.SelectedItem).Finish();
}
}
This did the trick:
using System;
using System.ComponentModel;
using System.Drawing;
using System.Linq;
using System.Runtime.CompilerServices;
using WindowsFormsApplication2.Annotations;
namespace WindowsFormsApplication2
{
public class Item : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
#region Notyfier implementation
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
#region collection implemetation
public BindingList<Item> Items = new BindingList<Item>();
public string Count
{
get { return (Items.Count == 1)
? "1 item."
: Items.Count + " items."; }
}
public Item Current
{
get { return Items.Count == 0
? new Item {Colour = Color.Chartreuse} //default initial item
: Items.Last(); }
}
#endregion
#region object implemetation
protected object ID { get; set; }
public Color Colour { get; set; }
public void NewItem(Color color)
{
Items.Add(new Item
{
ID = Guid.NewGuid(),
Colour = color
});
OnPropertyChanged("Count");
OnPropertyChanged("Current");
}
#endregion
}
}