Need a reusable SelectAll ViewModel - wpf

I have often run into situations where I want to have a 3-State checkbox to select all/deselect all of my view models. I have run into difficulties creating a reusable class to do this with.
I tried to create the following base classes:
public class SelectAllListVM
{
public List<SelectableVM> ChildList = new List<SelectableVM>();
public bool? SelectAll // call UpdateSelectedChildren on set
internal void UpdateSelectAll ()
// Set SelectAll based on ChildList elements
// true = all selected, false = non selected, null = some selected
private void UpdateSelectedChildren () {
foreach ( SelectableVM vm in ChildList )
vm.SetIsSelected( SelectAll.Value );
}
}
public class SelectableVM
{
public SelectableVM (SelectAllListVM parentVM) {}
public bool IsSelected // call parentVM.UpdateSelectAll
}
The problem with this is every time I want to iterate through the ChildList I have to cast the SelectableVM to it's concrete implementation. How can I reuse this behavior and avoid casting?

I did some attached behavior trickery back in the day to do this. Everything is handled by the view no need for the VM to care about select all logic. I wrote the details of how to do it in three parts (part 1, part 2, part 3) I made the code available on fileden...and subsequently lost it because I didn't log into the account enough times. But all the code is there throughout the three posts.

You could create an Interface ISelectableVM
public interface ISelectableVM
{
public bool IsSelected;
}
and implement it in your derived classes.
public class SelectableVM : ISelectableVM
{}
public class OtherSelectableVM : ISelectableVM
{}
Then in your ParentVM you don't have to cast:
public class SelectAllListVM
{
public List<ISelectableVM> ChildList = new List<ISelectableVM>();
public bool? SelectAll; // call UpdateSelectedChildren on set
private void UpdateSelectedChildren()
{
foreach (ISelectableVM vm in ChildList)
vm.IsSelected = SelectAll.Value;
}
}
Instead of an interface you could also use
public class BaseSelectableVM
{
public bool IsSelected;
}
and derive from it.

Related

PropertyChanged for an extended class

My VS2015 solution consists of two projects: DataModel and DesktopClient.
DataModel has a Customer class - thats an EntityFramework 6 DB entity. Customer has a FirstName property.
In DesktopClient there is an extended class CustomerExt.
In DesktopClient, is it possible to have a notification to CustomerExt.FirstName changes? Defining a partial Customer across two projects won't work - DataModel is compiled first and it won't have partial properties defined in DesktopClient.
public class CustomerExt : Customer, INotifyPropertyChanged
{
public object Clone()
{
return this.MemberwiseClone();
}
private bool _isChecked;
public bool IsChecked
{
get { return _isChecked; }
set
{
this._isChecked = value;
NotifyPropertyChanged("IsChecked");
}
}
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(info));
}
}
Unfortunately, if your base class does not implement INotifyPropertyChanged the safest way is to just write a wrapper class and only use that in your software. You can fit this in with your CustExt, or make it separate if you feel you want the extra layer.
This also assumes that while you may not control the Customer class, you control all of the code creating/editing the Customer instances, so that you can use this new class instead, then convert it to the original Customer class only when needed (such as a database transaction).
public class CustomerExt: INotifyPropertyChanged
{
Customer _customer = new Customer();
public object Clone()
{
return this.MemberwiseClone();
}
private bool _isChecked;
public bool IsChecked
{
get { return _isChecked; }
set
{
this._isChecked = value;
NotifyPropertyChanged("IsChecked");
}
}
#region WrapperProperties
public bool FirstName
{
get { return _customer.FirstName; }
set
{
_customer.FirstName= value;
NotifyPropertyChanged("FirstName");
}
}
#endregion
public Customer ToCustomer()
{
// returning a copy of the _customer instance here is safer than returning
// the reference, otherwise the properties could be altered directly
}
#region INotifyPropertyChanged
...
}
Some of this gets a little easier if you have an ICustomer interface and that is used during the database calls, then you can skip the formality of retaining a Customer instance.
I remember there being some third party libraries that have tried to automate this process - but I have never tried them and/or didn't trust them to work properly.
Let me see if I understand, you want update the View when your date is updated on the database?
You have to find a way to request this information from your ViewModel.
some kind of RefreshFirstNameAsync
private string _firstName;
public string FirstName
{
get { return _firstName; }
set
{
this._firstName= value;
NotifyPropertyChanged("FirstName"); // There is better ways to implement that line
}
}
private void RefreshFirstName(){
FirstName = _userRepo.GetFirstNameAsync();
}

WPF binding Datasource is updated through reference

is it possible to set a Datasource through a reference?
public partial class GraphView : UserControl
{
public ObservableCollection<ChartCollection<long>> signals { get; set; }
public GraphView()
{
UCGraph.DataSource = this.signals;
}
}
and if I set the signals property should it update the Datasource?
MyGraphUC.signals = mySignals;
It doesn't seem to be working for me. Why?
No you can't directly because the variables UCDataGraph.DataSource and signals are not connected by any means. They just happen to point to the same instance after you assign them in your constructor (actually they will both point to null which is not an instance at all). That being said, you can leverage the setter to do your bidding like so:
public partial class GraphView : UserControl
{
private ObservableCollection<ChartCollection<long>> _signals
public ObservableCollection<ChartCollection<long>> signals
{
get
{
return _signals;
}
set
{
this._signals = value;
UCGraph.DataSource = this._signals;
}
}
public GraphView()
{
UCGraph.DataSource = this.signals;
}
}
Alternatively you can just .Clear() the observable collection and refill it with the elements instead of changeing the collection itself if that is an feasible scenario for you.

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

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

MVVM - Filling a combobox and how to relay commands from window to user control

I have an ItemType that is coming from EF. This ItemType is wrapped in a ItemTypeViewModel. Many ItemTypes are wrapped in ItemTypeViewModels and are being put in a ObservableCollection in the ViewModel for the user control that will display them:
I use the CollectionView so I can page through them. The screen looks like this:
Now I'm thinking that the buttons that are used for paging that are in the user control could better be placed in the Window that will contain the user control. So, in my user control I know have commands like this:
But I want them to be in the window. I don't know if this will be good design, but if I will go through with this, how to relay the commands from the window to the usercontrol?
Another question I have is how to fill the combobox in the user control. They will always have the same values, but the selected item will change per ItemType.
I know two ways how to do this.
1) Add new class, for example, MainWindowViewModel and add there 2 commands and an instance of UserControlViewModel (you haven't said the title, so I will call it in this way). Here is a part of example:
public class MainWindowViewModel
{
public UserControlViewModel ChildControlViewModel { get; set; }
private Lazy<RelayCommand> nextCommand = new Lazy<RelayCommand>(() =>
new RelayCommand(
() => this.ChildControlViewModel.CollectionView.MoveCurrentToNext(),
() => this.ChildControlViewModel.CollectionView.CurrentPosition < this.ChildControlViewModel.ItemTypes.Count - 1));
public ICommand NextCommand
{
get { return nextCommand.Value; }
}
//prev command...
}
I have used the Lazy class, but the main idea is clear: the code is the same, except the call this.ChildControlViewModel.CollectionView instead of CollectionView.
2) Use the Messenger class.
This way isn't so obvious and it has only one advantage: the viewmodels are loosely connected.
public class MainWindowViewModel
{
public const string NextCommandNotification = "NextCommand";
public const string PreviousCommandNotification = "PreviousCommand";
private bool isNextCommandEnabled;
private bool isPreviousCommandEnabled;
public MainWindowViewModel()
{
this.NextCommand = new RelayCommand(
() => Messenger.Default.Send(new NotificationMessage<MainWindowViewModel>(this, NextCommandNotification)),
() => this.isNextCommandEnabled);
//prev command...
Messenger.Default.Register<NotificationMessage<UserControlViewModel>>(this,
msg =>
{
if (msg.Notification == UserControlViewModel.CurrentItemChangedNotification)
{
this.isNextCommandEnabled = msg.Content.CollectionView.CurrentPosition < msg.Content.ItemTypes.Count - 1;
this.NextCommand.RaiseCanExecuteChanged();
//prev command...
}
});
}
public ICommand NextCommand { get; private set; }
//prev command...
}
public class UserControlViewModel
{
public const string CurrentItemChangedNotification = "CurrentItemChanged";
public UserControlViewModel()
{
Messenger.Default.Register<NotificationMessage<MainWindowViewModel>>(this,
msg =>
{
if (msg.Notification == MainWindowViewModel.NextCommandNotification)
this.CollectionView.MoveCurrentToNext();
else if (msg.Notification == MainWindowViewModel.PreviousCommandNotification)
this.CollectionView.MoveCurrentToPrevious();
});
this.CollectionView.CurrentChanged += (s,e) => Messenger.Default.Send(new NotificationMessage<UserControlViewModel>(this, CurrentItemChangedNotification))
}
}
I'm not sure whether this code will work correctly. And it is not easy to explain.
The MainWindowViewModel class send the message when a user press the button. The UserControlViewModel class process the message, change the position of the current item, and send the CurrentItemChangedNotification message. The MainWindowViewModel class process this message and updates the CanExecute part of the command.
1st solution is better for me, but at the same time I use the Messenger class quite often. It depends on the situation.

MVVM with aggregated model classes - how to wrap in ViewModels?

I'm currently trying to create a small application using the MVVM pattern. However I don't really know how to correctly wrap up aggregated Model classes in my ViewModel. From what little I know about MVVM, you're not supposed to expose Models in your ViewModel as properties or else you could directly bind to the Model from your View. So it seems I have to wrap the nested Model in another ViewModel, but this imposes some problems while synching Model and ViewModel later on.
So how do you do that efficiently?
I'll give a short example. Let's suppose I have the following model classes:
public class Bar
{
public string Name { get; set; }
}
public class Foo
{
public Bar NestedBar { get; set; }
}
Now I create two ViewModel classes accordingly, wrapping the Models, but run into problems with the FooViewModel:
public class BarViewModel
{
private Bar _bar;
public string Name
{
get { return _bar.Name; }
set { _bar.Name = value; }
}
}
public class FooViewModel
{
private Foo _foo;
public BarViewModel Bar
{
get { return ???; }
set { ??? = value; }
}
}
Now what do I do with the Bar-property of FooViewModel? For "get" to work I need to return a BarViewModel instance. Do I create a new field of that type in FooViewModel and just wrap the _foo.NestedBar object in there? Changes to that field's properties should propagate down to the underlying Bar instance, right?
What if I need to assign another BarViewModel instance to that property, like so:
foo.Bar = new BarViewModel();
Now that won't propagate down to the model, which still holds the old instance of type Bar. I'd need to create a new Bar object based on the new BarViewModel and assing it to _foo, but how do you do that elegantly? It's pretty trivial in this sample, but if Bar is much more complex with lots of properties, that'll be a lot of typing... not to mention it'd be very prone to errors, if you forget to set one of the properties.
#Goblin
There are some flaws with your code: e.g. what if I get a list of Foo objects from database and I want to wrap each of them in an ObservableCollection?
then your Constructor of FooViewModel should accept the Foo model as parameter and not create it inside the Constructor!
Normally you do this to wrap a model into a viewmodel and put it the same time into a bindable Collection:
IEnumerable<Foo> foos = fooRepository.GetFoos();
foos.Select( m => viewmodelCollection.Add(new ViewModel(m,e.g.Service)));
The models properties are not copied to the ViewModel hell no!!! The ViewModel does delegate its properties to the model properties like:
public class FooViewModel
{
private Foo _foo;
public FooViewModel(Foo foo,IService service)
{
_foo = foo;
}
public string FoosName
{
get{return _foo.Name };
set
{
if(_foo.Name == value)
return;
_foo.Name = value;
this.NotifyPropertyChanged("FoosName");
}
}
}
And like Goblin said all UI-Specific interfaces like:
IDataErrorInfo
INotifyPropertyChanged
IEditableObject
etc...
are implemented the by the ViewModel ONLY.
My above answer only makes sense if you are doing DDD - if you are not - you can solve your problem like this - simply 'flattening' the model:
public class FooViewModel
{
private Foo _foo;
public string Name
{
get { return _foo.Name; }
set { _foo.Name = value; }
}
public string BarProperty
{
get { return _foo.Bar.Property; }
set { _foo.Bar.Property = value; }
}
}
Or you could do like I showed in the prior example - just ignore everything about Aggregates... should still work.
Okay - first things first - using the term Aggregate implies you are adhering to DDD? If you are - you are doing an encapsulation no-no :-). One Aggregate should never be allowed to edit another Aggregate. If what you have is that both are really Aggregate they would become associated (which is perfectly 'legal' in a DDD-sense - but then your propety on the FooViewModel wouldn't be of type BarViewModel, but rather type Bar. That way Bar would (as it should) be responsible for updating itself - and we only maintain the link in FooViewModel.
However, if what you are doing is AggregateRoot with a ValueType child - then here is what you could do given a slightly modified domain model:
public class Foo
{
public string SomeProperty { get; set; }
public Bar Bar { get; set; }
public void Save()
{
//Magically saves to persistent storage...
}
}
public class Bar
{
public Bar(string someOtherProperty)
{
SomeOtherProperty = someOtherProperty;
}
public string SomeOtherProperty { get; private set; }
}
And then for the ViewModels:
public class FooViewModel
{
private Foo _foo;
public FooViewModel()
{
Bar = new BarViewModel();
}
public BarViewModel Bar { get; private set; }
public void SetFoo(Foo foo)
{
_foo = foo;
SomeProperty = foo.SomeProperty;
Bar.SetBar(foo.Bar);
}
public string SomeProperty { get; set; }
public void SaveChanges()
{
_foo.SomeProperty = SomeProperty;
_foo.Bar = Bar.CreateUpdatedBar();
_foo.Save();
}
}
public class BarViewModel
{
public string SomeOtherProperty { get; set; }
public void SetBar(Bar bar)
{
SomeOtherProperty = bar.SomeOtherProperty;
}
public Bar CreateUpdatedBar()
{
return new Bar(SomeOtherProperty);
}
}
This way - the FooViewModel is now capable of controlling the BarViewModel (which does nothing but accept a valuetype - and create a new one when asked). This also solves a common UI-problem ('How do we edit an object that has no setters?' - answer: 'We don't - we create a new one'). A lot of fleshing out is missing (INotifyPropertyChanged, dirty-tracking etc., but those are easy if you get through this leap of thinking :-).
I hope this makes a wee bit of sense :-) Otherwise, I'll be happy to elaborate.

Resources