Simple status message implementation - c#

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
}
}

Related

Wpf binded property not updating in 2-way-mode

I have simple gallery with notes and text box to add new one by title.
I use mvvm, other bindings work correcty.
When I set title of note and click add property NewNote.Title is updated and object saved in database. After that I clear NewNote.Title but UI is not updated.
here is short video https://youtu.be/l3vFwI-a4TQ
xaml
<TextBox Text="{Binding NewNote.Title}" />
page view model
class NotesPageViewModel : INotifyPropertyChanged
{
public ObservableCollection<NoteViewModel> Notes { get; set; }
public NoteViewModel NewNote { get; set; }
public NotesPageViewModel()
{
NewNote = new NoteViewModel();
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
internal void LoadNotes()
{
using (var uow = new UnitOfWork())
{
Notes.Clear();
uow.NotesRepository.OrderBy(n => n.Position).ToList()
.ForEach(note => Notes.Add((NoteViewModel)note));
}
}
internal void AddNote()
{
if (string.IsNullOrWhiteSpace(NewNote.Title))
return;
using (var uow = new UnitOfWork())
{
uow.NotesRepository.Add((Note)NewNote);
uow.Complete();
}
NewNote.Title = "";
LoadNotes();
}
}
object view model
class NoteViewModel : INotifyPropertyChanged
{
public int Id { get; set; }
public string Title { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
You're not calling the OnPropertyChanged on the viewmodel when you're setting the property
class NoteViewModel : INotifyPropertyChanged
{
private int _id;
public int Id
{
get { return _id; }
set
{
_id = value;
OnPropertyChanged("Id");
}
}
private string _title;
public string Title
{
get { return _title; }
set
{
_title = value;
OnPropertyChanged("Title");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Also, the LoadNotes and AddNotes should be called via a command, and the new title should be sent as a parameter.
https://msdn.microsoft.com/en-us/library/ms752308(v=vs.110).aspx

Collection in ViewModel does not notify my View

I set the data context of my View to the ViewModel like this
PersonVM pvm = null;
public MainPage()
{
this.InitializeComponent();
pvm = new PersonVM();
this.DataContext = pvm;
}
then on a Button click I want to add more items to my collection
private void Btn_PointerPressed(object sender, Windows.UI.Xaml.Input.TappedRoutedEventArgs e)
{
pvm.DataSource.Add(new PersonVMWrapper(new PersonModel() { Name = "asdasd", Age = 23 }));
}
Here is my ViewModel where I obviously do something wrong, but cannot figure it out..
namespace App3vv.ViewModel
{
public class PersonVMWrapper : INotifyPropertyChanged
{
PersonModel _pm = null;
public PersonVMWrapper(PersonModel pm)
{
_pm = pm;
}
public string Name
{
get
{
return "mr." + _pm.Name;
}
set { RaisePropertyChanged("Name"); }
}
public string Age
{
get
{
return _pm.Age.ToString() + " years";
}
set { RaisePropertyChanged("Age"); }
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
public class PersonVM : INotifyPropertyChanged
{
private ObservableCollection<PersonVMWrapper> personDataSource;
public PersonVM()
{
this.DataSource.Add(new PersonVMWrapper(new PersonModel() { Name = "John", Age = 32 }));
this.DataSource.Add(new PersonVMWrapper(new PersonModel() { Name = "Kate", Age = 27 }));
this.DataSource.Add(new PersonVMWrapper(new PersonModel() { Name = "Sam", Age = 30 }));
DataSource.CollectionChanged += DataSource_CollectionChanged;
}
void DataSource_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
this.RaisePropertyChanged("DataSource");
}
public ObservableCollection<PersonVMWrapper> DataSource
{
get
{
if (this.personDataSource == null)
{
this.personDataSource = new ObservableCollection<PersonVMWrapper>();
}
return this.personDataSource;
}
set
{
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
EDIT: I implemented CollectionChange and still but my View still does not get the new item added..

UnitTest and PostSharp Toolkits, Domain and Threading

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.

Collection changed event for child property

I have use the following code snippet for Creating ObservableCollection binded to the DataGrid.
public class Test:INotifyPropertyChanged
{
private string _name;
public string Name
{
get { return _name; }
set { _name = value;OnpropertyChanged("Name"); }
}
private string _city;
public string City
{
get { return _city; }
set
{
_city = value;OnpropertyChanged("City");}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
public void OnpropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
var e = new PropertyChangedEventArgs(propertyName);
handler(this, e);
}
}
#endregion
}
class Data:INotifyPropertyChanged
{
private int customerID;
public int CustomerID
{
get { return customerID; }
set { customerID = value; OnpropertyChanged("CustomerID"); }
}
private bool isSelected;
public bool IsSelected
{
get { return isSelected; }
set { isSelected = value; OnpropertyChanged("IsSelected"); }
}
private ObservableCollection<Test> _collection;
public ObservableCollection<Test> Collection
{
get { return _collection; }
set { _collection = value;OnpropertyChanged("Collection" +
""); }
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnpropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
var e = new PropertyChangedEventArgs(propertyName);
handler(this, e);
}
}
}
class ViewModel:NotificationObject
{
public ViewModel()
{
this.GDCSource = Getsource();
}
private ObservableCollection<Data> _gdcsource;
public ObservableCollection<Data> GDCSource
{
get { return _gdcsource; }
set { _gdcsource = value; RaisePropertyChanged("GDCSource");}
}
private ObservableCollection<Data> Getsource()
{
ObservableCollection<Data> items = new ObservableCollection<Data>();
if (items != null)
{
items.Add(new Data()
{
IsSelected = true,
CustomerID = 1,
});
items.Add(new Data()
{
IsSelected = true,
CustomerID = 2,
});
}
return items;
}
}
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
ViewModel vmModel = new ViewModel();
this.datagrid.ItemsSource = vmModel.GDCSource;
vmModel.GDCSource.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(GDCSource_CollectionChanged);
}
void GDCSource_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
//Listen the collection changed event for underlying source
}
// add the object to the Collection property
private void Test_OnClick(object sender, RoutedEventArgs e)
{
(this.DataContext as ViewModel).GDCSource[0].Collection.Add(new Test() { Name = "Name1", City = "City1" });
(this.DataContext as ViewModel).GDCSource[0].Collection.Add(new Test() { Name = "Name1", City = "City1" });
(this.DataContext as ViewModel).GDCSource[0].Collection.Add(new Test() { Name = "Name1", City = "City1" });
}
}
It is possible to listen while adding Collection property in any event.
Thanks in advance
Regards,
Rajasekar
If you mean you want to register for event that is raised when item is added/deleted in observable collection you should look at CollectionChanged event
ObservableCollection<T>.CollectionChanged Event
Occurs when an item is added, removed, changed, moved, or the entire
list is refreshed.
You can extend your own version of ObservableCollection if you want and override the add method,
There you can fire any delegates or whatever you may want to register, the UI will update automatically using ObservableCollection with items added/removed you don't need to do anything for that,

What is the best way of refreshing dependency tree of calculated properties?

I've got some classes here that all more or less rely on each other. The relationships form kinda like a dependency tree:
class A {
List<B> _bs = new List<B>();
public int ValueOfA {
get {
return _bs.Sum(p => p.ValueOfB);
}
}
class B {
List<C> _cs = new List<C>();
public int ValueOfB {
get {
return _cs.Where(p => p.ValueOfC > 1).Sum(p => p.ValuOfC);
}
}
class C {
public int ValueOfC { get; set }
}
So, whenever _bs, _cs or ValueOfC change, every property relating to them should be notified as has changed, too, and hence be recalculated.
What's the best way of consistently and reliably achieving this goal? Is there by any chance a way to do this automatically?
You'll want to implement INotifyPropertyChanged with your class C. In the set of ValueOfC, you'll fire the event:
class C : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private int valueOfC;
public int ValueOfC
{
get { return valueOfC; }
set
{
valueOfC = value;
OnPropertyChanged(PropertyChanged);
}
}
protected virtual void OnPropertyChanged(PropertyChangedEventHandler handler)
{
if (handler != null)
handler(this, new PropertyChangedEventArgs("ValueOfC"));
}
}
I just tested it, and it works perfectly.
Having a protected virtual method fire the event for you is just common practice.
As a side note, if you want to do something if the lists change, you may want to look into using a BindingList or an ObservableCollection.
EDIT
I've written up a small example that does refresh the whole tree:
public class Bank : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private BindingList<Customer> customers = new BindingList<Customer>();
public int Worth
{
get { return customers.Sum(cust => cust.FullBalance); }
}
public Bank()
{
customers.ListChanged += new ListChangedEventHandler(customers_ListChanged);
}
void customers_ListChanged(object sender, ListChangedEventArgs e)
{
Console.WriteLine("A customer has changed.");
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs("Worth"));
}
public void Add(Customer c) { customers.Add(c); }
}
public class Customer : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private BindingList<Account> accounts = new BindingList<Account>();
public int FullBalance
{
get { return accounts.Sum(acc => acc.Balance); }
}
public Customer()
{
accounts.ListChanged += new ListChangedEventHandler(accounts_ListChanged);
}
void accounts_ListChanged(object sender, ListChangedEventArgs e)
{
Console.WriteLine("An account has changed.");
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs("FullBalance"));
}
public void Add(Account a) { accounts.Add(a); }
}
public class Account : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private int balance = 0;
public int Balance
{
get { return balance; }
set
{
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs("Balance"));
}
}
}
class Program
{
static void Main(string[] args)
{
Account a1 = new Account() { Balance = 5 };
Account a2 = new Account() { Balance = 10 };
Account a3 = new Account() { Balance = 15 };
Customer c1 = new Customer(); c1.Add(a1); c1.Add(a2);
Customer c2 = new Customer(); c2.Add(a3);
Bank b = new Bank(); b.Add(c1); b.Add(c2);
Console.WriteLine();
a1.Balance += 100;
}
}
Now you can write something like if (e.ListChangedType == ListChangedType.ItemChanged) in the event handlers, or something similar.

Categories

Resources