NHibernate and weird casting exception - wpf

I'm fighting it the second day and I'm just fed up.
I'm getting weird exceptions connected with my UI.
First things first.
My model looks basically like that:
Base class:
public class DbItem: ObservableModel
{
public virtual Document ParentDocument { get; set; }
Guid id;
public virtual Guid Id
{
get { return id; }
set
{
if (id != value)
{
id = value;
NotifyPropertyChanged();
}
}
}
string name = string.Empty;
public virtual string Name
{
get { return name; }
set
{
if (value == null || name != value)
{
name = value;
NotifyPropertyChanged();
}
}
}
}
Next we have PeriodBase class:
public enum PeriodType
{
Year,
Sheet
}
public abstract class PeriodBase : DbItem
{
public virtual Period ParentPeriod { get; set; }
public virtual PeriodType PeriodType { get; set; }
}
There are some more properties, but I just deleted them here for clarity.
Next, we have Period class that inherits from PeriodBase:
public class Period : PeriodBase
{
IList<PeriodBase> periods = new ObservableCollection<PeriodBase>();
public virtual IList<PeriodBase> Periods
{
get { return periods; }
set
{
if (periods != value)
{
periods = value;
NotifyPropertyChanged();
}
}
}
}
Now, Period can have other periods and Sheets (which also inherites from PeriodBase):
public class Sheet : PeriodBase
{
DateTimeOffset startDate;
public override DateTimeOffset StartDate
{
get { return startDate; }
set
{
if (startDate != value)
{
startDate = value;
NotifyPropertyChanged();
}
}
}
DateTimeOffset endDate;
public override DateTimeOffset EndDate
{
get { return endDate; }
set
{
if (endDate != value)
{
endDate = value;
NotifyPropertyChanged();
}
}
}
}
And finally we have document class, that is made up of Periods:
public class Document: DbItem
{
IList<Period> periods = new ObservableCollection<Period>();
public virtual IList<Period> Periods
{
get { return periods; }
set
{
if (periods != value)
{
periods = value;
NotifyPropertyChanged();
}
}
}
}
As you may guess, I get a tree hierarchy like that:
- Document
- Period 1
- Sheet 1
My bindings look like this:
public class DocumentMap : DbItemMap<Document>
{
public DocumentMap()
{
Table("documents");
HasMany(x => x.Periods).ForeignKeyConstraintName("ParentDocument_id");
}
}
public class PeriodBaseMap: DbItemMap<PeriodBase>
{
public PeriodBaseMap()
{
UseUnionSubclassForInheritanceMapping();
References(x => x.ParentPeriod);
Map(x => x.Name).Not.Nullable();
Map(x => x.PeriodType).CustomType<PeriodType>();
}
}
public class PeriodMap : SubclassMap<Period>
{
public PeriodMap()
{
Table("periods");
Abstract();
References(x => x.ParentDocument);
HasMany(x => x.Periods).Inverse().Not.LazyLoad();
}
}
public class SheetMap : SubclassMap<Sheet>
{
public SheetMap()
{
Table("sheets");
Abstract();
Map(x => x.StartDate);
Map(x => x.EndDate);
}
}
For now, I just do eager loading everywhere. Just for simplicity.
Now WPF. This is how I create my TreeView (I'm using syncfusion controls):
<sf:TreeViewAdv>
<sf:TreeViewItemAdv
Header="Document"
LeftImageSource="../Resources/database.png"
ItemsSource="{Binding Periods}"
IsExpanded="True"
>
<sf:TreeViewItemAdv.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Periods}"> <!-- Period -->
<TextBlock Text="{Binding Name}"/>
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/> <!-- Sheet -->
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</sf:TreeViewItemAdv.ItemTemplate>
</sf:TreeViewItemAdv>
</sf:TreeViewAdv>
And everything works until I save the records. It's just simple SaveAsync's in one transaction.
Everything gets saved but then I get a weird error. Application crashes with message: Cannot cast TreeViewItemAdv to PeriodBase.
What the heck? I can't even find the place when it's really throws.
This is stacktrace from exception info:
in NHibernate.Collection.Generic.PersistentGenericBag`1.System.Collections.IList.IndexOf(Object value)
in System.Windows.Data.ListCollectionView.InternalIndexOf(Object item)
in Syncfusion.Windows.Tools.Controls.TreeViewItemAdv.Initialize(FrameworkTemplate template)
in Syncfusion.Windows.Tools.Controls.TreeViewItemAdv.TreeViewItemAdv_Loaded(Object sender, RoutedEventArgs e)
in System.Windows.EventRoute.InvokeHandlersImpl(Object source, RoutedEventArgs args, Boolean reRaised)
in System.Windows.UIElement.RaiseEventImpl(DependencyObject sender, RoutedEventArgs args)
in System.Windows.BroadcastEventHelper.BroadcastEvent(DependencyObject root, RoutedEvent routedEvent)
in System.Windows.BroadcastEventHelper.BroadcastLoadedEvent(Object root)
in MS.Internal.LoadedOrUnloadedOperation.DoWork()
in System.Windows.Media.MediaContext.FireLoadedPendingCallbacks()
in System.Windows.Media.MediaContext.FireInvokeOnRenderCallbacks()
in System.Windows.Media.MediaContext.RenderMessageHandlerCore(Object resizedCompositionTarget)
in System.Windows.Media.MediaContext.RenderMessageHandler(Object resizedCompositionTarget)
in System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
in System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Int32 numArgs, Delegate catchHandler)
What's important, I get the same error after I start the application and load the document and click on the expander in treeview to expand Period. But everything works fine when I run the app for the first time, until I save the document.
What can be the problem?
In reply to Mark Feldman's post
I decided to reply in an answer as this is too long to comment. This is my first meeting with ORM, so I may have some wrong thoughts about this. I have just one model in my solution. Normally (using SQL) it would work. I would take an object, INSERT it into DB, and the other way also.
So I did the same way here. I just have one business model which has some simple business rules. It is used in ViewModels, and it's stored in db. Is it bad solution? Should I have another model and somewhat break DRY principle?
In my head it was suppose to work like this: User clicks "Create new Sheet". Here you are (this is part of my ViewModel -> method that is called from command):
void CreateNewSheetInActiveDocument()
{
Sheet sh = ActiveDocument.CreateItem<Sheet>();
ActiveDocument.LastPeriod.Periods.Add(sh);
}
This is more like pseudocode but it keeps the idea. Active document creates my sheet. This is done so because document signs to PropertyChanged event just to know if it was modified. Periods is ObservableCollection, so that I can react to adding and removing elements. Thanks to that period can set parentPeriod for my sheet automatically.
And then user saves it to db:
async Task SaveDocument(Document doc)
{
foreach(var item in doc.ModifiedItems)
db.SaveOrUpdate(item);
}
ModifiedItems is simply just a dictionary that keeps items that were modified. Thanks to this I don't have to save the whole document, just modified items.
So as far as I understand you this is not the way it should be. So what would be the PROPER way to do that? Or maybe ORM is not suitable here?

Unless there have been major changes to NHibernate in the years since I've used it you can't just derive your model classes from ObservableModel and expect it to work. It appears that your reasoning for this is to give INPC to your DB models, which some would argue isn't good separation of concerns and suggests that your view model layer hasn't been designed properly.
That said, if you really are adamant about doing it then instead of deriving your entities from ObservableModel try using something like Castle Dynamic Proxy to inject INPC into your entities when NHibernate first creates them. Ayende Rahien's post NHibernate & INotifyPropertyChanged shows how to do this and also provides the code you'll need.
The next problem you'll face is the issue of collections. Again, you can't just assign an ObservableCollection<T> to an IList<T> property and expect it to work, NHibernate replaces the entire list when it deserializes collections back in rather than using add/remove on an existing collection that you've already assigned. It's possible to replace the list with an ObserveableCollection<T> after its been loaded, but if you do that then NHibernate will think the entire list has changed, irrespective of whether it has or not, and serialize the whole thing back out again. You'll get away with it at first, but pretty soon the performance hit is going to start to hurt.
To work around that problem you're going to have to use a convention so that NHibernate creates collection entities that support INotifyCollectionChanged. Unfortunately the page where I originally read about this has long since disappeared, so I'll have to just post the code here (regrettably without attribution). I've only used conventions with NHibernate Fluent, so I'll leave you to find out how to apply them in your own case, but here's what you need...
public class ObservableBagConvention : ICollectionConvention
{
public void Apply(ICollectionInstance instance)
{
Type collectionType = typeof(ObservableBagType<>)
.MakeGenericType(instance.ChildType);
instance.CollectionType(collectionType);
instance.LazyLoad();
}
}
public class ObservableBagType<T> : CollectionType, IUserCollectionType
{
public ObservableBagType(string role, string foreignKeyPropertyName, bool isEmbeddedInXML)
: base(role, foreignKeyPropertyName, isEmbeddedInXML)
{
}
public ObservableBagType()
: base(string.Empty, string.Empty, false)
{
}
public IPersistentCollection Instantiate(ISessionImplementor session, ICollectionPersister persister)
{
return new PersistentObservableGenericBag<T>(session);
}
public override IPersistentCollection Instantiate(ISessionImplementor session, ICollectionPersister persister, object key)
{
return new PersistentObservableGenericBag<T>(session);
}
public override IPersistentCollection Wrap(ISessionImplementor session, object collection)
{
return new PersistentObservableGenericBag<T>(session, (ICollection<T>)collection);
}
public IEnumerable GetElements(object collection)
{
return ((IEnumerable)collection);
}
public bool Contains(object collection, object entity)
{
return ((ICollection<T>)collection).Contains((T)entity);
}
protected override void Clear(object collection)
{
((IList)collection).Clear();
}
public object ReplaceElements(object original, object target, ICollectionPersister persister, object owner, IDictionary copyCache, ISessionImplementor session)
{
var result = (ICollection<T>)target;
result.Clear();
foreach (var item in ((IEnumerable)original))
{
if (copyCache.Contains(item))
result.Add((T)copyCache[item]);
else
result.Add((T)item);
}
return result;
}
public override object Instantiate(int anticipatedSize)
{
return new ObservableCollection<T>();
}
public override Type ReturnedClass
{
get
{
return typeof(PersistentObservableGenericBag<T>);
}
}
}
That's the code for the convention, you use it with this collection class:
public class PersistentObservableGenericBag<T> : PersistentGenericBag<T>, INotifyCollectionChanged,
INotifyPropertyChanged, IList<T>
{
private NotifyCollectionChangedEventHandler _collectionChanged;
private PropertyChangedEventHandler _propertyChanged;
public PersistentObservableGenericBag(ISessionImplementor sessionImplementor)
: base(sessionImplementor)
{
}
public PersistentObservableGenericBag(ISessionImplementor sessionImplementor, ICollection<T> coll)
: base(sessionImplementor, coll)
{
CaptureEventHandlers(coll);
}
public PersistentObservableGenericBag()
{
}
#region INotifyCollectionChanged Members
public event NotifyCollectionChangedEventHandler CollectionChanged
{
add
{
Initialize(false);
_collectionChanged += value;
}
remove { _collectionChanged -= value; }
}
#endregion
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged
{
add
{
Initialize(false);
_propertyChanged += value;
}
remove { _propertyChanged += value; }
}
#endregion
public override void BeforeInitialize(ICollectionPersister persister, int anticipatedSize)
{
base.BeforeInitialize(persister, anticipatedSize);
CaptureEventHandlers(InternalBag);
}
private void CaptureEventHandlers(ICollection<T> coll)
{
var notificableCollection = coll as INotifyCollectionChanged;
var propertyNotificableColl = coll as INotifyPropertyChanged;
if (notificableCollection != null)
notificableCollection.CollectionChanged += OnCollectionChanged;
if (propertyNotificableColl != null)
propertyNotificableColl.PropertyChanged += OnPropertyChanged;
}
private void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
{
PropertyChangedEventHandler changed = _propertyChanged;
if (changed != null) changed(this, e);
}
private void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
NotifyCollectionChangedEventHandler changed = _collectionChanged;
if (changed != null) changed(this, e);
}
}
And that's it! Now NHibernate will deserialize your collections as type PersistentObservableGenericBag<T>.
So that's how you inject INPC into entities at runtime, but there are a couple of ways to accomplish what you need without actually having to do that. Apart from being easier to implement they also don't require the use of reflection, which is a factor if you ever need to migrate your code to something that doesn't allow it (e.g. Xamarin.iOS). Adding basic INPC can be achieved by simply adding ProprtyChanged.Fody which will add it to your class properties IL automatically at build time. For change collection you're better off keeping your collections as type IList<T>, representing them with classes of type ObserveableCollection<T> in your view models and then just writing a bit of code, or a helper function, to keep the two synchronized.
UPDATE: I managed to track down the original project where I got that code, it's part of Fabio Maulo's uNhAddIns project.

After Mark Feldman's changes, the error still occures. But when I changed the tree control to standard one, the problem went away. That means there is an error in Syncfusion control. I have reported it.

Related

WinForms binding Generic List - checkable business object to a Grid

We all like how easy it is to bind with WPF. Now I am back working with Winforms and I am looking for a nice way to bind my grid to a List of Checkable of BusinessObject (I am sticking with BindingList for Winforms). So I am essentially just adding a checkable to my business object.
I am using a grid as there will be multiple columns where the user would edit (in this scenario Name and Description on the business object) - as well as adding new objects to the grid and removing from it. Checked list box does not fit for this purpose as I want to edit columns.
For this I am using .NET 4.
I basically want to reduce the amount of UI code in the scenario so I am using a view model based approach which will populate the list. I want the user to be able to check a box alongside each of the business object properties.
Sure I can use inheritance, but if I want to apply the same mechanism against a lot of business objects (having lots of different screens where you check items in a list for the different business objects). Maybe this would be the way to go - but I have my doubts.
Now depending upon the choice of grid - I am using Infragistics - the functionality would hopefully be pretty similar conceptually.
I thought about wrapping the business object up in a Checkable generic class:
using System;
using System.Collections.Generic;
public class Checkable<T> : ModelBase
{
public Checkable(T value)
{
_value = value;
}
private T _value;
public T Value
{
get
{
return _value;
}
set
{
if (!EqualityComparer<T>.Default.Equals(_value, value))
{
_value = value;
OnPropertyChanged("Value");
}
}
}
private bool _checked;
public bool Checked
{
get { return _checked; }
set
{
if (_checked != value)
{
_checked = value;
OnPropertyChanged("Checked");
}
}
}
}
I have made up a business object for this scenario:
public class BusinessObject : ModelBase
{
public BusinessObject()
{
}
public BusinessObject(RepairType repairType)
{
_name = repairType.Name;
_id = repairType.Id;
}
private string _name;
public string Name
{
get { return _name; }
set
{
if (_name != value)
{
_name = value;
OnPropertyChanged("Name");
}
}
}
private string _description;
public string Description
{
get { return _description; }
set
{
if (description != value)
{
description = value;
OnPropertyChanged("Description");
}
}
}
private int _id;
public int Id
{
get { return _id; }
set
{
if (_id != value)
{
_id = value;
OnPropertyChanged("Id");
}
}
}
}
Where ModelBase just implements the INotifyPropertyChanged:
public abstract class ModelBase : INotifyPropertyChanged, IDisposable
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
protected bool SetProperty<T>(ref T field, T value, string propertyName = null)
{
if (object.Equals(field, value)) { return false; }
field = value;
OnPropertyChanged(propertyName);
return true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
public virtual void Dispose(bool disposing)
{
if (disposing)
{
PropertyChanged = null;
}
}
}
So potentially for my grid datasource I would define:
// in view model
var datasource = new BindingList<Checkable<BusinessObject>>();
... populate list
grid.DataSource = viewmodel.DataSource;
So of course my scenario fails at the minute as Value is the BusinessObject reference which has the properties I want to bind to, and Checked is the property for a checkbox which I also want to bind to.
I am trying to kick start the old grey matter with some ideas on this. I don't really like writing code to define grid columns. However, the Infragistics grid has been ok for data binding directly to the BusinessObject at design time. Its possible to add an unbound column (checkbox for my scenario) and handle the checking/unchecking of items manually (which I might potentially have to do).
I am wondering if I am missing any neat tricks with Winform binding of late having missed out with Linq and Entity Framework when they appeared many years ago.

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");
}
};
}

making async calls using mvvm in silverlight

I am trying to make a call to a wcf service with my silverlight application and I am having some trouble understanding how the model returns the result back to the view model. Within my view model I have the following command:
public DelegateCommand GetSearchResultCommand
{
get
{
if (this._getSearchResultCommand == null)
this._getSearchResultCommand = new DelegateCommand(GetSearchResultCommandExecute, CanGetSearchResultsCommandExecute);
return this._getSearchResultCommand;
}
}
private void GetSearchResultCommandExecute(object parameter)
{
this.SearchResults = this._DataModel.GetSearchResults(this.SearchTerm);
}
/// <summary>
/// Bindable property for SearchResults
/// </summary>
public ObservableCollection<QueryResponse> SearchResults
{
get
{
return this._SearchResults;
}
private set
{
if (this._SearchResults == value)
return;
// Set the new value and notify
this._SearchResults = value;
this.NotifyPropertyChanged("SearchResults");
}
}
then within my model I have the following code
public ObservableCollection<QueryResponse> GetSearchResults(string searchQuery)
{
//return type cannot be void needs to be a collection
SearchClient sc = new SearchClient();
//******
//TODO: stubbed in placeholder for Endpoint Address used to retreive proxy address at runtime
// sc.Endpoint.Address = (clientProxy);
//******
sc.QueryCompleted += new EventHandler<QueryCompletedEventArgs>(sc_QueryCompleted);
sc.QueryAsync(new Query { QueryText = searchQuery });
return LastSearchResults;
}
void sc_QueryCompleted(object sender, QueryCompletedEventArgs e)
{
ObservableCollection<QueryResponse> results = new ObservableCollection<QueryResponse>();
results.Add(e.Result);
this.LastSearchResults = results;
}
When I insert breakpoints within the model I see where the query is being executed and a result is returned within the model (this.LastSearchResults = results) however I cannot seem to get this collection to update/ notify the view model of the result. I've generated and run a similar test using just a method and dummy class and it seems to work so I suspect the issue is due to the async call /threading. I have INotifyPropertyChanged within the ViewModel to sync the View and ViewModel. Do I need to also implement INotifyPropChng within the model as well? I'm new to mvvm so any help / example of how I should approach this would be appreciated.
Thank you,
UPDATE
In further testing I added INotifyPropertyChanged to the model and changed the Completed event as follows:
void sc_QueryCompleted(object sender, QueryCompletedEventArgs e)
{
ObservableCollection<QueryResponse> results = new ObservableCollection<QueryResponse>();
results.Add(e.Result);
//this.LastSearchResults = results;
SearchResults = results;
}
Placing a watch on Search Results I now see it is updated with results from teh WCF. My question is still around is this teh correct approach? It seems to work right now however I am curious if I am missing something else or if I should not be placing INotify within the Model.
Thank you,
I've found that it's best to encapsulate my WCF services in an additional layer of Service classes. This allows me to more easily Unit Test my ViewModels. There are several patterns when doing this, though this is the simplest I've used. The pattern is to create a method that matches the definition of the service call, though also contains an Action that can be invoked after the service call completes.
public class Service : IService
{
public void GetSearchResults(string searchQuery, Action<ObservableCollection<QueryResponse>> reply)
{
//return type cannot be void needs to be a collection
SearchClient sc = new SearchClient();
//******
//TODO: stubbed in placeholder for Endpoint Address used to retreive proxy address at runtime
// sc.Endpoint.Address = (clientProxy);
//******
sc.QueryCompleted += (s,e) =>
{
ObservableCollection<QueryResponse> results = new ObservableCollection<QueryResponse>();
results.Add(e.Result);
reply(results);
};
sc.QueryAsync(new Query { QueryText = searchQuery });
}
}
You can also provide an interface that your ViewModel can use. This makes Unit Testing even easier, though is optional.
public interface IService
{
void GetSearchResults(string searchQuery, Action<ObservableCollection<QueryResponse>> reply);
}
Your ViewModel would then look something like this:
public class MyViewModel : INotifyPropertyChanged
{
private IService _service;
public MyViewModel()
: this(new Service())
{ }
public MyViewModel(IService service)
{
_service = service;
SearchResults = new ObservableCollection<QueryResponse>();
}
private ObservableCollection<QueryResponse> _searchResults
public ObservableCollection<QueryResponse> SearchResults
{
get { return _searchResults; }
set
{
_searchResults = value;
NotifyPropertyChanged("SearchResults");
}
}
public void Search()
{
_service.GetSearchResults("abcd", results =>
{
SearchResults.AddRange(results);
});
}
protected void NotifyPropertyChanged(string property)
{
var handler = this.PropertyChanged;
if(handler != null)
handler(new PropertyChangedEventArgs(property));
}
}
An additional reason for encapsulating your service calls into another class like this is that it can provide a single place for such things as logging and error handling. That way your ViewModel itself doesn't need to take care of those things specifically related to the Service.
I would likely use something along the lines of:
public class ViewModel : INotifyPropertyChanged
{
private readonly IModel model;
private readonly DelegateCommand getSearchResultsCommand;
public DelegateCommand GetSearchResultsCommand
{
get { return getSearchResultsCommand; }
}
public ObservableCollection<QueryResponse> SearchResults
{
get { return model.SearchResults; }
}
public ViewModel(IModel model)
{
this.model = model;
this.model.SearchResultsRetrieved += new EventHandler(model_SearchResultsRetrieved);
this.getSearchResultsCommand = new DelegateCommand(model.GetSearchResultCommandExecute, model.CanGetSearchResultsCommandExecute);
}
private void model_SearchResultsRetrieved(object sender, EventArgs e)
{
this.NotifyPropertyChanged("SearchResults");
}
}
public interface IModel
{
event EventHandler SearchResultsRetrieved;
void GetSearchResultCommandExecute(object parameter);
bool CanGetSearchResultsCommandExecute(object parameter);
ObservableCollection<QueryResponse> SearchResults { get; }
}
With the SearchResultsRetrieved event being fired by the Model when its SearchResults collection has been filled with the appropriate data. I prefer to have custom events rather than implement INotifyPropertyChanged on my models, particularly if there are only one, or a few, events that need to be communicated to the viewmodel.

Refactoring multiple interfaces to a common interface using MVVM, MEF and Silverlight4

I am just learning MVVM with MEF and already see the benefits but I am a little confused about some implementation details. The app I am building has several Models that do the same with with different entities (WCF RIA Services exposing a Entity framework object) and I would like to avoid implementing a similar interface/model for each view I need and the following is what I have come up with though it currently doesn't work.
The common interface has a new completed event for each model that implements the base model, this was the easiest way I could implement a common class as the compiler did not like casting from a child to the base type.
The code as it currently sits compiles and runs but the is a null IModel being passed into the [ImportingConstructor] for the FaqViewModel class.
I have a common interface (simplified for posting) defined as follows, this should look familiar to those who have seen Shawn Wildermuth's RIAXboxGames sample.
public interface IModel
{
void GetItemsAsync();
event EventHandler<EntityResultsArgs<faq>> GetFaqsComplete;
}
A base method that implements the interface
public class ModelBase : IModel
{
public virtual void GetItemsAsync() { }
public virtual event EventHandler<EntityResultsArgs<faq>> GetFaqsComplete;
protected void PerformQuery<T>(EntityQuery<T> qry, EventHandler<EntityResultsArgs<T>> evt) where T : Entity
{
Context.Load(qry, r =>
{
if (evt == null) return;
try
{
if (r.HasError)
{
evt(this, new EntityResultsArgs<T>(r.Error));
}
else if (r.Entities.Count() > 0)
{
evt(this, new EntityResultsArgs<T>(r.Entities));
}
}
catch (Exception ex)
{
evt(this, new EntityResultsArgs<T>(ex));
}
}, null);
}
private DomainContext _domainContext;
protected DomainContext Context
{
get
{
if (_domainContext == null)
{
_domainContext = new DomainContext();
_domainContext.PropertyChanged += DomainContext_PropertyChanged;
}
return _domainContext;
}
}
void DomainContext_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
switch (e.PropertyName)
{
case "IsLoading":
AppMessages.IsBusyMessage.Send(_domainContext.IsLoading);
break;
case "IsSubmitting":
AppMessages.IsBusyMessage.Send(_domainContext.IsSubmitting);
break;
}
}
}
A model that implements the base model
[Export(ViewModelTypes.FaqViewModel, typeof(IModel))]
public class FaqModel : ModelBase
{
public override void GetItemsAsync()
{
PerformQuery(Context.GetFaqsQuery(), GetFaqsComplete);
}
public override event EventHandler<EntityResultsArgs<faq>> GetFaqsComplete;
}
A view model
[PartCreationPolicy(CreationPolicy.NonShared)]
[Export(ViewModelTypes.FaqViewModel)]
public class FaqViewModel : MyViewModelBase
{
private readonly IModel _model;
[ImportingConstructor]
public FaqViewModel(IModel model)
{
_model = model;
_model.GetFaqsComplete += Model_GetFaqsComplete;
_model.GetItemsAsync(); // Load FAQS on creation
}
private IEnumerable<faq> _faqs;
public IEnumerable<faq> Faqs
{
get { return _faqs; }
private set
{
if (value == _faqs) return;
_faqs = value;
RaisePropertyChanged("Faqs");
}
}
private faq _currentFaq;
public faq CurrentFaq
{
get { return _currentFaq; }
set
{
if (value == _currentFaq) return;
_currentFaq = value;
RaisePropertyChanged("CurrentFaq");
}
}
public void GetFaqsAsync()
{
_model.GetItemsAsync();
}
void Model_GetFaqsComplete(object sender, EntityResultsArgs<faq> e)
{
if (e.Error != null)
{
ErrorMessage = e.Error.Message;
}
else
{
Faqs = e.Results;
}
}
}
And then finally the Silverlight view itself
public partial class FrequentlyAskedQuestions
{
public FrequentlyAskedQuestions()
{
InitializeComponent();
if (!ViewModelBase.IsInDesignModeStatic)
{
// Use MEF To load the View Model
CompositionInitializer.SatisfyImports(this);
}
}
[Import(ViewModelTypes.FaqViewModel)]
public object ViewModel
{
set
{
DataContext = value;
}
}
}
It seems as though I am going down the wrong path with trying to refactor into multiple models. As seen here, http://msdn.microsoft.com/en-us/magazine/dd458800.aspx#id0090019 if would appear the best thing to do would be to think about the model as an instance of the EDMX class referenced via RIA Services. As such, the model should contain all methods and event handlers needed to access the DomainContext.
If anybody has other thoughts I would be open to them.
As a fellow noob, I am just starting to play with MEF, and I think I have identified a possible problem with your code. From your question, it sounds like your main problem is the null IModel reference.
Try changing this:
private readonly IModel _model;
to this:
[Import]
public IModel _model { get; set; }
I haven't yet played with how MEF likes private and readonly properties, so try setting to public and then verify that _model isn't null when you first try to use it.

PropertyChanged notification for calculated properties

I'm developing an application in Silverlight2 and trying to follow the Model-View-ViewModel pattern. I am binding the IsEnabled property on some controls to a boolean property on the ViewModel.
I'm running into problems when those properties are derived from other properties. Let's say I have a Save button that I only want to be enabled when it's possible to save (data has been loaded, and we're currently not busy doing stuff in the database).
So I have a couple of properties like this:
private bool m_DatabaseBusy;
public bool DatabaseBusy
{
get { return m_DatabaseBusy; }
set
{
if (m_DatabaseBusy != value)
{
m_DatabaseBusy = value;
OnPropertyChanged("DatabaseBusy");
}
}
}
private bool m_IsLoaded;
public bool IsLoaded
{
get { return m_IsLoaded; }
set
{
if (m_IsLoaded != value)
{
m_IsLoaded = value;
OnPropertyChanged("IsLoaded");
}
}
}
Now what I want to do is this:
public bool CanSave
{
get { return this.IsLoaded && !this.DatabaseBusy; }
}
But note the lack of property-changed notification.
So the question is: What is a clean way of exposing a single boolean property I can bind to, but is calculated instead of being explicitly set and provides notification so the UI can update correctly?
EDIT: Thanks for the help everyone - I got it going and had a go at making a custom attribute. I'm posting the source here in case anyone's interested. I'm sure it could be done in a cleaner way, so if you see any flaws, add a comment or an answer.
Basically what I did was made an interface that defined a list of key-value pairs to hold what properties depended on other properties:
public interface INotifyDependentPropertyChanged
{
// key,value = parent_property_name, child_property_name, where child depends on parent.
List<KeyValuePair<string, string>> DependentPropertyList{get;}
}
I then made the attribute to go on each property:
[AttributeUsage(AttributeTargets.Property, AllowMultiple = true, Inherited = false)]
public class NotifyDependsOnAttribute : Attribute
{
public string DependsOn { get; set; }
public NotifyDependsOnAttribute(string dependsOn)
{
this.DependsOn = dependsOn;
}
public static void BuildDependentPropertyList(object obj)
{
if (obj == null)
{
throw new ArgumentNullException("obj");
}
var obj_interface = (obj as INotifyDependentPropertyChanged);
if (obj_interface == null)
{
throw new Exception(string.Format("Type {0} does not implement INotifyDependentPropertyChanged.",obj.GetType().Name));
}
obj_interface.DependentPropertyList.Clear();
// Build the list of dependent properties.
foreach (var property in obj.GetType().GetProperties())
{
// Find all of our attributes (may be multiple).
var attributeArray = (NotifyDependsOnAttribute[])property.GetCustomAttributes(typeof(NotifyDependsOnAttribute), false);
foreach (var attribute in attributeArray)
{
obj_interface.DependentPropertyList.Add(new KeyValuePair<string, string>(attribute.DependsOn, property.Name));
}
}
}
}
The attribute itself only stores a single string. You can define multiple dependencies per property. The guts of the attribute is in the BuildDependentPropertyList static function. You have to call this in the constructor of your class. (Anyone know if there's a way to do this via a class/constructor attribute?) In my case all this is hidden away in a base class, so in the subclasses you just put the attributes on the properties. Then you modify your OnPropertyChanged equivalent to look for any dependencies. Here's my ViewModel base class as an example:
public class ViewModel : INotifyPropertyChanged, INotifyDependentPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyname)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyname));
// fire for dependent properties
foreach (var p in this.DependentPropertyList.Where((x) => x.Key.Equals(propertyname)))
{
PropertyChanged(this, new PropertyChangedEventArgs(p.Value));
}
}
}
private List<KeyValuePair<string, string>> m_DependentPropertyList = new List<KeyValuePair<string, string>>();
public List<KeyValuePair<string, string>> DependentPropertyList
{
get { return m_DependentPropertyList; }
}
public ViewModel()
{
NotifyDependsOnAttribute.BuildDependentPropertyList(this);
}
}
Finally, you set the attributes on the affected properties. I like this way because the derived property holds the properties it depends on, rather than the other way around.
[NotifyDependsOn("Session")]
[NotifyDependsOn("DatabaseBusy")]
public bool SaveEnabled
{
get { return !this.Session.IsLocked && !this.DatabaseBusy; }
}
The big caveat here is that it only works when the other properties are members of the current class. In the example above, if this.Session.IsLocked changes, the notification doesnt get through. The way I get around this is to subscribe to this.Session.NotifyPropertyChanged and fire PropertyChanged for "Session". (Yes, this would result in events firing where they didnt need to)
The traditional way to do this is to add an OnPropertyChanged call to each of the properties that might affect your calculated one, like this:
public bool IsLoaded
{
get { return m_IsLoaded; }
set
{
if (m_IsLoaded != value)
{
m_IsLoaded = value;
OnPropertyChanged("IsLoaded");
OnPropertyChanged("CanSave");
}
}
}
This can get a bit messy (if, for example, your calculation in CanSave changes).
One (cleaner? I don't know) way to get around this would be to override OnPropertyChanged and make the call there:
protected override void OnPropertyChanged(string propertyName)
{
base.OnPropertyChanged(propertyName);
if (propertyName == "IsLoaded" /* || propertyName == etc */)
{
base.OnPropertyChanged("CanSave");
}
}
You need to add a notification for the CanSave property change everywhere one of the properties it depends changes:
OnPropertyChanged("DatabaseBusy");
OnPropertyChanged("CanSave");
And
OnPropertyChanged("IsEnabled");
OnPropertyChanged("CanSave");
How about this solution?
private bool _previousCanSave;
private void UpdateCanSave()
{
if (CanSave != _previousCanSave)
{
_previousCanSave = CanSave;
OnPropertyChanged("CanSave");
}
}
Then call UpdateCanSave() in the setters of IsLoaded and DatabaseBusy?
If you cannot modify the setters of IsLoaded and DatabaseBusy because they are in different classes, you could try calling UpdateCanSave() in the PropertyChanged event handler for the object defining IsLoaded and DatabaseBusy.

Resources