Cast Observable Collection from base Class to inherited class - wpf

I'm writing a WPF application and I'm currently refactoring some reused code to a base ViewModel Class which my other viewmodels can inherit from.
One Property field on this base class is
public class MessageParentBase
{
MessageParentBase() {}
public string Name;
}
internal ObservableCollection<MessageParentBase> _GridData = new ObservableCollection<MessageParentBase>();
I have a subsequent property declaration
public ObservableCollection<MessageParentBase> GridData
{
get { return _GridData; }
set { _GridData = value; }
}
This works great and everything my issue is that the inerited classes actually use the follow class
Public class ChatMessage : MessageParentBase
{
public string Message;
}
and the view contains a grid of data which is bound to this GridData property but the column which should be bound to the Message field from the ChatMessage class is blank and the fields found in the MessageParentBase class are populated.
So I presume there is an issue with the view not knowing to cast up to the ChatMessage from the MessageParentBase class.
Can I inform the view that the objects will be of the type "ChatMessage".
I did try moving the property declaration up to the inherited viewmodel as
public ObservableCollection<ChatMessage> GridData
{
get { return _GridData; }
set { _GridData = value; }
}
but this gives me the following error:-
Cannot implicitly convert type 'System.Collections.ObjectModel.ObservableCollection' to 'System.Collections.ObjectModel.ObservableCollection'
Do I need to cast at the view level or can I change the viewmodels to implement this better?
Any suggestions would be greatly appreciated.
Emlyn

Change the collection to this:
public ObservableCollection<MessageParentBase> GridData { get; set; }
then add into your constructor
this.GridData = new ObservableCollection<MessageParentBase>();
Since WPF uses reflection to retrieve bound data from the data context it should be able to get the values of the derived classes stored in that collection.
Also when you run your application check the output window with Debug selected, the XAML engine will output any binding errors there.

Your ViewModel should contain a list with the type that your grid will show (in this case, the ChatMessage type). You can still use the inheritance to call common methods, but the binded list must be of the ChatMessage type

Related

How do I get the Model instance from inside its wrapping ViewModel?

Short version:
If I have ViewModel, containing its Model object and exposing its properties, how do I get the model "back" after it has been edited? If the Model-inside-ViewModel is public, it violates encapsulation, and if it is private, I cannot get it (right?).
Longer version:
I am implementing a part of an application which displays collections of objects. Let's say the objects are of type Gizmo, which is declared in the Model layer, and simply holds properties and handle its own serialization/deserialization.
In the Model layer, I have a Repository<T> class, which I use to handle collections of MasterGizmo and DetailGizmo. One of the properties of this repository class is an IEnumerable<T> Items { get; } where T will be some of the Gizmo subtype.
Now since Gizmo doesn't implement INPC, I have created the following classes in ViewModel layer:
GizmoViewModel, which wraps every public property of a Gizmo so that setting any property raises PropertyChanged accordingly;
[**] RepositoryViewModel<T>, which has an ObservableCollection<GizmoViewModel> whose CollectionChanged is listened to by a method that handles Adds, Removes and Updates to the repository.
Notice that the Model layer has a "Repository of Models", while the ViewModel layer has a "ViewModel with an ObservableCollection of ViewModels".
The doubt is related to the [**] part above. My RepositoryViewModel.CollectionChangedHandler method is as follows:
void CollectionChangedHandler(object sender, NotifyCollectionChangedEventArgs e)
{
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
foreach (var added in e.NewItems)
{
var gvm = added as GizmoViewModel;
if (gvm != null)
{
//// IS ANY OF THE ALTERNATIVES BELOW THE RIGHT ONE?
// Gizmo g = gvm.RetrieveModel(); ?? proper getter ??
// Gizmo g = GetModelFromViewModel(gvm); ?? external getter ??
// Gizmo g = gvm.Model; ?? public model property ??
_gizmo_repository.Add(g);
}
}
break;
....
Besides that, if anyone can detect any MVVM smell here, I'll be happy to know.
We can deal with our Models even outside the View and ViewModel layers, so leaving the model publicly accessible from ViewModel is I believe acceptable.
Let say you are creating the Models in "DataLayer" you can pass the instance of the Model to the ViewModel. To illustrate my point:
///Models ////////////////////////////
public interface IGizmo{}
public class Gizmo:IGizmo{}
public class SuperGizmo : IGizmo {}
public class SuperDuperGizmo : IGizmo { }
//////////////////////////////////////
public interface IGizmoViewModel<out T>
{
T GetModel();
}
public abstract class GizmoViewModelBase : IGizmoViewModel<IGizmo>
{
protected GizmoViewModelBase(IGizmo model)
{
_Model = model;
}
private readonly IGizmo _Model;
public IGizmo GetModel()
{
return _Model;
}
}
public class GizmoViewModel : GizmoViewModelBase
{
public GizmoViewModel(Gizmo model)
: base(model) { }
}
public class SuperDuperGizmoViewModel : GizmoViewModelBase
{
public SuperDuperGizmoViewModel(SuperDuperGizmo model)
: base(model){}
}
Your repository of Models will be updated on whatever updates it get from the ViewModel as long as you passed the same instance. So there is no need to have a repository of ViewModels to get the updates.
Reading your code, I think there is something of a mixup regarding your ViewModel and Model separation.
So, as I understand it, when your ObservableCollection of GizmoViewModel's changes, you are trying to add the Gizmo instance of the new item back to your Model?
I would approach this differently. You should create your Gizmo instances inside your Model layer, and when you do this you should add it to the Repository.
Otherwise, you haven't provided enough information - or rather, you have provided too much but it is the wrong sort of information. You need to describe the situation in which you want to do this, where these GizmoViewModels are created, etc.
From what I can see here, your GizmoViewModel has a dependency to your Repository<T>, so why not pass in the repository when you create your view model?
public class GizmoViewModel
{
private IRepository<Gizmo> _Repo;
//Underlying model (Doesn't implement INotifyPropertyChanged)
private Gizmo _Model;
//Wrapping properties
public int MyProperty
{
get { return _Model.Property; }
set
{
_Model.Property = value;
NotifyOfPropertyChange();
}
}
...
public GizmoViewModel(IRepository<Gizmo> repo)
{
_Repo = repo;
}
public void AddToRepo()
{
_Repo.Add(_Model);
}
...
It would be even better if these methods are inside the RepositoryViewModel base class. You can really go crazy with inheritance here. Perhaps something like this:
var gvm = added as IRepositoryViewModel;
if (gvm != null)
gvm.AddToRepo();
You can then simply call AddToRepo when you need to add the view model's underlying model to the repository.
Perhaps not the most elegant solution, however if encapsulation is what's worrying you, then you need to ensure that your dependencies are properly managed.
"If the Model-inside-ViewModel is public, it violates encapsulation"
Your assertion above is completely wrong and is killing your code.
By setting the Model property in ViewModel as private, you are forced to repeat your self ( code smells ), as you will need to define in your ViewModel, the same properties as you did for your Model, effectively transforming it into a Model class that mimics the Model it is supposed to expose to the View.
In MVVM the ViewModel role is to provide the View with all the presentation data and logic that it needs and for sure the Model is fundamental part of this data, by hidding it from the View you are killing MVVM.

Databinding to an ObservableCollection of unknown object type

In my main project I have an ObservableCollection<DataValue>, where DataValue is a type from a 3rd party library, and it looks like this (simplified):
public class DataValue : IFormattable, ICloneable
{
private object m_value;
private TypeInfo m_typeInfo;
public object Value
{
get { return this.m_value; }
}
public TypeInfo TypeInfo
{
get
{
if (this.m_typeInfo == (TypeInfo) null)
return TypeInfo.Unknown;
else
return this.m_typeInfo;
}
}
}
In a different project in my solution I have a view and viewmodel to display the data in this collection, and it works fine when binding to a ListBox (it correctly displays the value, if the DataValue.Value is an integer, a byte[] or whatever), so it somehow figures out of the actual type automatically?
I want the same behavior in a custom control I have created, and if the value is a collection of byte[], it should process the data.
How can I accomplish this? I tried fooling around with a ValueConverter and some reflection and casting, but it seems unnecessary since the native controls seem to figure this out automatically.

Update a wpf label periodically

I am new to WPF and C# im trying to understand how can I update a UI element from a BL class (to keep a seperation between the logic and the UI) the bl gets periodic updates from a c++ network component and should update the form once a new argument comes in (I read on the msdn website but I want to see some concrete examples to make sure I got it right)
Because of your gets periodic updates from a c++ network component comment, I am assuming that you already have a system to update your property. I would expose that property from your business class in a view model class, a class with public properties and ICommand functions designed specifically to supply all of the required data to a view, or UserControl.
To be honest, I wouldn't have that (or any) functionality in a business class (depending what you mean by business class)... I'd personally put it straight into the view model, or have a manager/service class that exposed it.
If you insist on keeping it where it is, you'll have to implement either an event or a delegate in your business class so that users of that class can be alerted as to when the value changes. Then you could simply attach a handler to your event/delegate from the view model class and easily update the exposed property whenever the actual property changes.
So it would go a little something like this... in your business class (I am assuming that your value is an int, but you can change this if it is incorrect... the principal is the same):
public delegate void FieldUpdate(int value);
public FieldUpdate OnFieldUpdate { get; set; }
...
private int field;
public int Field
{
get { return field; }
set
{
if (value != field)
{
field = value;
if (OnFieldUpdate != null) OnFieldUpdate(field);
}
}
}
Then in your view model:
private YourBusinessClass instance = new YourBusinessClass();
public YourBusinessClass Instance
{
get { return instance; }
set { instance = value; NotifyPropertyChanged("Instance"); }
}
Attach a handler:
instance.OnFieldUpdate += OnBusinessClassFieldUpdate;
...
public void OnBusinessClassFieldUpdate(int value)
{
Instance = value;
}
Now whenever the field is updated in the business class, the view model (and data bound UI controls) will automatically update via the delegate.

How to write a commom class for two ViewModel classes?

I have two viewmodel classes called ChangePwdViewModel.cs and ExpiringPwdViewModel.cs.
ChangPwd.xaml binds to ChangePwdViewModel and ExpiringPwd.xaml binds to ExpiringPwdViewModel.
Both have the property as below.
private string _message;
public string Message
{
get { return _message; }
set { _message = value; OnPropertyChanged("Message"); }
}
In each class, there's a function called ValidatePwd() to validate the new password.
In this function, Message property is updated.
Eg.
if (IsAlphaNumeric(this.NewPassword) == false || IsAlphaNumeric(this.CfmPassword) == false)
{
this.Message = "Invalid new password, only characters and numbers are accepted, password must contain at least one character and one number";
this.ResetPasswordFields();
return false;
}
I want to create a common class to write this function and used by two viewmodel. But, How can I update the Message Property of the viewmodels from this class?
How about putting it in a base class:
class ViewModelBase
{
private string _message;
public string Message
{
get { return _message; }
set { _message = value; OnPropertyChanged("Message"); }
}
public bool VerifyPassword(string newPassword)
{
....
}
}
class ChangePwdViewModel : ViewModelBase
{
}
class ExpiringPwdViewModel : ViewModelBase
{
}
Update:
If you can't use a base class because your view models already have a base class then you could use an interface as suggested by others. However this means that you will still have to implement the interface in all your view model classes so you don't gain that much in terms of avoiding multiple implementations (except that you have a contract for your view models then which is usually a good thing to have).
You can achieve some kind of "multiple inheritance" in C# by using a tool like Dynamic Proxy which allows you to create mixins. So you could implement the Message property and password verification in one class and then create a mixin proxy which merges the view model with that implementation. It's not as nice as you will have to create all your view model instances via the proxy generator but it can be made to work. Have a look at this tutorial if it sounds like an option for you.
You could have the two ViewModel classes implement a common interface, say IMessage that implemented a single property - Message.
Then your common class or a function would take a parameter of type IMessage that it could use to update the message.
I would suggest to avoid base classes (could cause potential design issues in future) in such cases, I would rather suggest to pass through constructor an algorithm of validation, smth like this:
public class MyViewModel
{
public MyViewModel(Func<bool> validationAlgorithm)
{
// ... save function to use later for a validation
}
}

Requirements for design-time data source in Report Viewer 2010

What are the requirements for a custom data source to be listed in the 'Data Source' drop-down list when adding a Dataset to a .rdlc report in Report Viewer 2010?
As can been seen from the screen grab, for some reason it is listing potential sources from a variety of referenced assemblies, but I can't see an obvious pattern as to why it is selecting these.
The 'GeneralDataSet' makes sense as that is a strongly-typed Dataset class, but I'm pretty sure most of the others are not, yet the design dialog still lists them.
I'm looking to roll my own custom data source and would prefer it to be selectable from this list.
I think it scans your project file looking for methods that return Lists<> and so on.
So something like:
public class Person
{
public string name { get; set; }
public int age { get; set; }
}
public class GetPeople
{
public List<Person> GetPeopleList()
{
return null;
}
public IEnumerable<Person> GetPeopleIEnumerable()
{
return null;
}
public IQueryable<Person> GetPeopleIQueryable()
{
return null;
}
}
All three show up, so take your pick. (Code is just thrashed out, ignore bad names/practices :))
But when you use a ReportViewer, you will need to manually set the datasets. Selecting it inside the report from what I have found just basically tells it what data to expect. So add an ObjectDataSource or just set it in the code behind.
I noticed the dataset does not appear if the source is exposed as a Property and not a method.
ie this fails to be a selectable data source.
public class FooData
{
public List<string> Data {get;set;}
}
but this will show up as a data source
public class FooData
{
public List<string> GetData();
}
I just had a problem with this also,
my class was returning Lists but would not show up in the datasources list.
I then added a parameterless constructor and it started to show up ( there was not one before ) I assmume this is so the reportviewer can create and instance of it.
eg:
public MyObject()
{
}
I've had a similar problem with custom lists which inherit from List.
You can work around it if your system will allow you to inherit without using interfaces. Ours doesn't.
The project containing this class WILL appear in the DataSource dropdown, and the class itself appears in the DataSet dropdown:
public class AccountList : List<AccountData>
{}
This class will NOT appear as a Dataset, which prevents its project from appearing as a DataSource (notice the "I" in front of AccountData):
public class AccountList : List<IAccountData>
{}
This is a pain because other aspects of our system require the lists to inherit from an interface not a concrete class. I don't know why it doesn't work.

Resources