How to use different behaviours in the same ViewModel? - wpf

I have the view designed for the long running tasks. It has a header and a progress bar.
So the model has the text for a header and a counter for a progress bar and a TotalAmountOfWork field. The model also has
public delegate void TaskCompleted(string resultDescription);
public event TaskCompletedCopyingCompletedEvent;
public event Action UpdateViewState;
When counters change the model invokes UpdateViewState.
ViewModel is subscribed for the events and updates in it's turn the view.
Ok. I have two classes intended to copy files from a hard disk to a flash drive and one class intended for diagnostic information retrieving and this information should be copied to a flash drive finally too.
I want to use them in the same ViewModel, but I can't figure out how to avoid code repeating. I can't figure out how to make it relying on the proper object oriented design.
Those three classes could implement an interface like this:
interface ILongRunningTask {
void DoWork();
}
And then I can implement ViewModel taking ILongRunningTask as an argument.
But look at the name of the name of the interface. It looks too generalized. It seems that something wrong with such an abstraction.
Ok. It seems to me that ViewModel should take a delegate in order to invoke a long running task. But in this case how the ViewModel will interact with the model updating it's properties?
//update
Now, model looks like:
public class FilesCopyingModel : IFilesCopier {
protected int filesCountToCopy;
public int FilesCountToCopy {
get { return filesCountToCopy; }
set {
filesCountToCopy = value;
InvokeUpdateViewState();
}
}
protected int currentProgressValue;
public int CurrentProgressValue {
get { return currentProgressValue; }
set {
currentProgressValue = value;
InvokeUpdateViewState();
}
}
public delegate void CopyingCompleted(string resultDescription);
public event CopyingCompleted CopyingCompletedEvent;
public event Action UpdateViewState;
private readonly IFilesCopier filesCopier;
protected FilesCopyingModel() {
}
public FilesCopyingModel(IFilesCopier filesCopier) {
if (filesCopier == null)
throw new ArgumentNullException("filesCopier");
this.filesCopier = filesCopier;
}
protected static string GetCurrentDateTime() {
return DateTime.Now.ToString("dd.MM.yyyy hh.mm.ss");
}
protected void InvokeCopyCompletedEvent(string resultDescription) {
if (CopyingCompletedEvent != null)
CopyingCompletedEvent(resultDescription);
}
protected void InvokeUpdateViewState() {
if (UpdateViewState != null)
UpdateViewState();
}
protected DriveInfo GetFirstReadyRemovableDrive() {
return
DriveInfo.GetDrives()
.FirstOrDefault(driveInfo => driveInfo.DriveType == DriveType.Removable && driveInfo.IsReady);
}
public void Copy() {
filesCopier.Copy();
}
}
public interface IFilesCopier {
void Copy();
}
public class KFilesCopier : FilesCopyingModel, IFilesCopier {
private string destinationKFilesDirPath;
public new void Copy() {
//some code
}
private static string ComposeDestinationKFilesDirPath(DriveInfo drive) {
//some code
}
}
public class LogsDirCopier : FilesCopyingModel, IFilesCopier {
public readonly string LogsDirPath;
public LogsDirCopier() {
//some code
}
public new void Copy() {
//some code
}
private void InternalCopyLogsDir(string destinationPath) {
//some code
}
private static void CloseStorer(ZipStorer zipStorer) {
//some code
}
private static string ComposeDestinationArchiveFilePath(string destinationPath) {
//some code
}
private void DetermineLogFilesCount() {
//some code
}
ViewModel interact with infrastructure above like this:
public class FilesCopyingViewModel: Screen {
private readonly FilesCopyingModel model;
private readonly IWindowManager windowManager;
public int CurrentProgress {
get { return model.CurrentProgressValue; }
}
public int FilesCountToCopy {
get { return model.FilesCountToCopy; }
}
[ImportingConstructor]
public LongRunningViewModel(IFilesCopier copier) {
model = copier as FilesCopyingModel;
model.CopyingCompletedEvent += CopyingCompletedHandler;
model.UpdateViewState += UpdateViewStateHandler;
windowManager = new WindowManager();
}
private void UpdateViewStateHandler() {
NotifyOfPropertyChange(() => CurrentProgress);
NotifyOfPropertyChange(() => FilesCountToCopy);
}
private void CopyingCompletedHandler(string resultDescription) {
//some code
}
private void RemoveDriveSafely() {
//some code
}
private void PromptEjection(string result) {
//some code
}
private void PromptSuccessEjection() {
//some code
}
private void PromptEjectFlashError() {
//some code
}
protected override void OnActivate() {
try {
var copier = (IFilesCopier) model;
Task.Factory.StartNew(copier.Copy);
}
catch (Exception ex) {
//error handling
}
}
}
These two classes use "Copy" as the name of the method. Now I want to add one more class with very similar behavior, but it seems that it's method should be named like "CollectDiagnosticInfo". Or maybe I can add a class DiagnosticInfoCopier:IFilesCopier and then just do the same. I really don't know, but the sixth sense suggests that there is a smell of some kind.

Related

mvvm update calculated fields

Do you know best practices in wpf+mvvm to update Calculated fields?
What I can do instead OnPropertyChanged(nameof(Summary))?
Also calculated field can be in another viewmodel and this viewmodel should not know about all dependences.
This is my code :
public class Model
{
public int Prop1 { get; set; }
public int Prop2 { get; set; }
public int Summary => Prop1 + Prop2;
}
public class ViewModel : BaseViewModel
{
public Model Model { get; }
public int Prop1
{
get
{
return Model.Prop1;
}
set
{
Model.Prop1 = value;
OnPropertyChanged();
OnPropertyChanged(nameof(Summary));
}
}
public int Prop2
{
get
{
return Model.Prop2;
}
set
{
Model.Prop2 = value;
OnPropertyChanged();
OnPropertyChanged(nameof(Summary));
}
}
public int Summary => Model.Summary;
}
Calling OnPropertyChanged on the calculated property is perfectly acceptable. If you have a relatively simple model like the one you wrote that'll be enough.
If you have multiple calculated properties on the model, you might consider creating a method to call all of them from a single place, instead of calling each one from every property.
Something like this:
public int Prop1
{
get
{
return _prop1;
}
set
{
_prop1 = value;
OnPropertyChanged();
NotifyCalculatedProperties();
}
}
public int Calc1 { get { /* ... */ } }
public int Calc2 { get { /* ... */ } }
public int Calc3 { get { /* ... */ } }
public void NotifyCalculatedProperties()
{
OnPropertyChanged(nameof(Calc1));
OnPropertyChanged(nameof(Calc2));
OnPropertyChanged(nameof(Calc3));
}
In case the calculated properties exist in a different model, you can register in that Model\VM to the source's PropertyChanged event, and then invoke the change notification there.
Like that:
void ModelPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if(e.PropertyName == "Prop1")
OnPropertyChanged(nameof(Calc1));
OnPropertyChanged(nameof(Calc2));
OnPropertyChanged(nameof(Calc3));
}
Just don't forget to unsubscribe when the Model\VM changes, or you'll have a memory leak on your hands.
Lastly, you can always use the Messenger to pass messages between unrelated VMs, though you should use caution since it's a very powerful tool, and can easily be misused.
I don't know what MVVM framework you're using, but each has it's own implementation. You can find more general details on the Messenger pattern here: https://msdn.microsoft.com/en-us/magazine/jj694937.aspx

keeping track of completion of many tasks

I have a WPF application in which lots of event initiate tasks. Here's how I am doing it. But I am not happy about how it looks now
var task = UpdatePersonModelAsync();
taskCollection.Add(task);
RaisePropertyChanged(nameof(IsUpdateInProgress));
await task;
taskCollection.Remove(task);
RaisePropertyChanged(nameof(IsUpdateInProgress));
The property which shows/hides spinner
public bool IsUpdateInProgress => taskCollection.Count > 0;
I was going through the Progress<T> it seems like a call back.
When all the incoming tasks are completed a small spinner will be hidden.
You probably should use await Task.WhenAll(taskCollection.ToArray()); for waiting all the tasks you need to wait. After that, put the code for hiding the spinner below the await statement.
I tried using a CustomTaskScheduler. I got it from https://www.infoworld.com/article/3063560/application-development/building-your-own-task-scheduler-in-c.html
http://www.codeguru.com/csharp/article.php/c18931/Understanding-the-NET-Task-Parallel-Library-TaskScheduler.htm
As tasks are created from various calls, I use the CustomTaskScheduler in Task.Factory.StartNew.
On debugging, I can get hits on QueueTask but Execute isn't getting called. What am I missing?
public sealed class CustomTaskScheduler : TaskScheduler, IDisposable
{
public delegate void TaskStartedHandler(Task sender);
public delegate void AllTasksCompletedHandler(IEnumerable<Task> sender);
public event TaskStartedHandler TaskStarted;
public event AllTasksCompletedHandler AllTasksCompleted;
private BlockingCollection<Task> tasksCollection = new BlockingCollection<Task>();
private readonly Thread mainThread = null;
public CustomTaskScheduler()
{
mainThread = new Thread(new ThreadStart(Execute));
if (!mainThread.IsAlive)
{
mainThread.Start();
}
}
private void Execute()
{
foreach (var task in tasksCollection.GetConsumingEnumerable())
{
var isStarted = TryExecuteTask(task);
if (isStarted && TaskStarted != null)
{
TaskStarted(task);
}
}
if(tasksCollection.GetConsumingEnumerable().All(m => m.IsCompleted))
{
AllTasksCompleted?.Invoke(tasksCollection.GetConsumingEnumerable());
}
}
protected override IEnumerable<Task> GetScheduledTasks()
{
return tasksCollection.ToArray();
}
protected override void QueueTask(Task task)
{
if (task != null)
{
tasksCollection.Add(task);
}
}
protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
{
return false;
}
private void Dispose(bool disposing)
{
if (!disposing)
{
return;
}
tasksCollection.CompleteAdding();
tasksCollection.Dispose();
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
The code is not yet complete. It would be great if someone can point to right direction
Here's a new version
public class TaskTracker
{
public delegate void AllTasksCompletedHandler(object sender);
public delegate void TaskStartedHandler();
public event AllTasksCompletedHandler AllTasksCompleted;
public event TaskStartedHandler TaskStarted;
private object syncLock = new object();
private SynchronizedCollection<Task> tasksCollection;
private bool isTaskStartedNotified;
private readonly uint delay;
public TaskTracker(uint delayBeforeRemovingTasks)
{
tasksCollection = new SynchronizedCollection<Task>();
delay = delayBeforeRemovingTasks;
}
public void Add(Task task)
{
if (!isTaskStartedNotified)
{
isTaskStartedNotified = true;
TaskStarted?.Invoke();
}
task.ContinueWith(t =>
{
RemoveTask(t);
});
tasksCollection.Add(task);
}
private async void RemoveTask(Task task)
{
await Task.Delay(300);
await Task.Run(() =>
{
tasksCollection.Remove(task);
if (tasksCollection.Count == 0)
{
isTaskStartedNotified = false;
AllTasksCompleted?.Invoke(tasksCollection);
}
});
}
}

How to best propagate change notifications upwards a hierarchical structure for binding?

If i have a folder-like structure that uses the composite design pattern and i bind the root folder to a TreeView. It would be quite useful if i can display certain properties that are being accumulated from the folder's contents. The question is, how do i best inform the folder that changes occurred in a child-element so that the accumulative properties get updated?
The context in which i need this is a small RSS-FeedReader i am trying to make. This are the most important objects and aspects of my model:
Composite interface:
public interface IFeedComposite : INotifyPropertyChanged
{
string Title { get; set; }
int UnreadFeedItemsCount { get; }
ObservableCollection<FeedItem> FeedItems { get; }
}
FeedComposite (aka Folder)
public class FeedComposite : BindableObject, IFeedComposite
{
private string title = "";
public string Title
{
get { return title; }
set
{
title = value;
NotifyPropertyChanged("Title");
}
}
private ObservableCollection<IFeedComposite> children = new ObservableCollection<IFeedComposite>();
public ObservableCollection<IFeedComposite> Children
{
get { return children; }
set
{
children.Clear();
foreach (IFeedComposite item in value)
{
children.Add(item);
}
NotifyPropertyChanged("Children");
}
}
public FeedComposite() { }
public FeedComposite(string title)
{
Title = title;
}
public ObservableCollection<FeedItem> FeedItems
{
get
{
ObservableCollection<FeedItem> feedItems = new ObservableCollection<FeedItem>();
foreach (IFeedComposite child in Children)
{
foreach (FeedItem item in child.FeedItems)
{
feedItems.Add(item);
}
}
return feedItems;
}
}
public int UnreadFeedItemsCount
{
get
{
return (from i in FeedItems
where i.IsUnread
select i).Count();
}
}
Feed:
public class Feed : BindableObject, IFeedComposite
{
private string url = "";
public string Url
{
get { return url; }
set
{
url = value;
NotifyPropertyChanged("Url");
}
}
...
private ObservableCollection<FeedItem> feedItems = new ObservableCollection<FeedItem>();
public ObservableCollection<FeedItem> FeedItems
{
get { return feedItems; }
set
{
feedItems.Clear();
foreach (FeedItem item in value)
{
AddFeedItem(item);
}
NotifyPropertyChanged("Items");
}
}
public int UnreadFeedItemsCount
{
get
{
return (from i in FeedItems
where i.IsUnread
select i).Count();
}
}
public Feed() { }
public Feed(string url)
{
Url = url;
}
Ok, so here is the thing, if i bind a TextBlock.Text to the UnreadFeedItemsCount there won't be simple notifications when an item is marked unread, so one of my approaches has been to handle the PropertyChanged event of every FeedItem and if the IsUnread-Property is changed i have my Feed make a notification that the property UnreadFeedItemsCount has been changed. With this approach i also need to handle all PropertyChanged events of all Feeds and FeedComposites in Children of FeedComposite, from the sound of it, it should be obvious that this is not such a very good idea, you need to be very careful that items never get added to or removed from any collection without having attached the PropertyChanged event handler first.
Also: What do i do with the CollectionChanged-Events which necessarily also cause a change in the sum of the unread items count? Sounds like more event handling fun.
It is such a mess; it would be great if anyone has an elegant solution to this since i do not want the feed-reader to end up as awful as my first attempt years ago when i did not even know about DataBinding...
Well I thought I'd give your question a go, to see what I would come up with. Its untested and its is kinda along the same lines as what you already had. The major difference I made is added methods to handle the add and removal of feeds which handle the event binding needed for it to work. Theres a bit of code so here goes,
I all my code is in a single file, youll need to modify slightly if you want in in separate files.
First the groovy extension method for the PropertyChangedEventHandler
You dont need to use it, but I like it alot.
public static class NotifyPropertyChangedExtention
{
public static void Raise<T, TP>(this PropertyChangedEventHandler pc, T source, Expression<Func<T, TP>> pe)
{
if (pc != null)
{
pc.Invoke(source, new PropertyChangedEventArgs(((MemberExpression)pe.Body).Member.Name));
}
}
}
Second the FeedItem minus the feed stuff :) I have a check to raise a change event only when the value actually changes. You can see the Raise extension method in use here, no strings lovely.
class FeedItem : INotifyPropertyChanged
{
private bool _isUnread;
public event PropertyChangedEventHandler PropertyChanged;
public bool IsUnread
{
get { return _isUnread; }
set
{
if (_isUnread != value)
{
_isUnread = value;
PropertyChanged.Raise(this, x => x.IsUnread);
}
}
}
}
Now the Interfaces, I made mine differ slightly, in that my folders can contain other folders as well as feeds.
internal interface IFeedComposite : INotifyPropertyChanged
{
string Title { get; set; }
int UnreadFeedItemsCount { get; }
}
internal interface IFeedFolder : IFeedComposite
{
ObservableCollection<IFeedFolder> FeedFolders { get; }
ObservableCollection<IFeed> Feeds { get; }
void AddFeed(IFeed newFeed);
void RemoveFeed(IFeed feedToRemove);
void AddFeedFolder(IFeedFolder newFeedFolder);
void RemoveFeedFolder(IFeedFolder feedFolderToRemove);
}
internal interface IFeed : IFeedComposite
{
ObservableCollection<FeedItem> FeedItems { get; }
void AddFeedItem(FeedItem newFeedItem);
void RemoveFeedItem(FeedItem feedItemToRemove);
}
Now the Feed Class, the AddFeedItem method hooks the property changed event for you, and if it is marked as unread, raises the property changed event for the count. You could overload this method, to accept a list of items, then once they have been added to the list, if any where unread, raise a single property changed event for them all.
class Feed : IFeed
{
private readonly ObservableCollection<FeedItem> _feedItems = new ObservableCollection<FeedItem>();
public event PropertyChangedEventHandler PropertyChanged;
public string Title { get; set; }
public int UnreadFeedItemsCount
{
get
{
return (from i in FeedItems
where i.IsUnread
select i).Count();
}
}
public ObservableCollection<FeedItem> FeedItems
{
get { return _feedItems; }
}
public void AddFeedItem(FeedItem newFeed)
{
newFeed.PropertyChanged += NewFeedPropertyChanged;
_feedItems.Add(newFeed);
PropertyChanged.Raise(this, x => x.FeedItems);
if (newFeed.IsUnread)
{
PropertyChanged.Raise(this, x => x.UnreadFeedItemsCount);
}
}
public void RemoveFeedItem(FeedItem feedToRemove)
{
_feedItems.Remove(feedToRemove);
PropertyChanged.Raise(this, x => x.FeedItems);
if (feedToRemove.IsUnread)
{
PropertyChanged.Raise(this, x => x.UnreadFeedItemsCount);
}
}
void NewFeedPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "IsUnread")
{
PropertyChanged.Raise(this, x => x.UnreadFeedItemsCount);
}
}
}
Now the FeedFolder class, much the same as the feed class but this one can hold a list of feeds and a list of feed folders (which hold their own feeds). You can easily add a method or property to return all feeditems from feeds and feedfolders if you need. Again, various checks to only raise change events if needed.
class FeedFolder : IFeedFolder
{
private readonly ObservableCollection<IFeedFolder> _feedFolders = new ObservableCollection<IFeedFolder>();
private readonly ObservableCollection<IFeed> _feeds = new ObservableCollection<IFeed>();
public event PropertyChangedEventHandler PropertyChanged;
public string Title { get; set; }
public int UnreadFeedItemsCount
{
get { return Feeds.Sum(x => x.UnreadFeedItemsCount) + FeedFolders.Sum(x => x.UnreadFeedItemsCount); }
}
public ObservableCollection<IFeedFolder> FeedFolders
{
get { return _feedFolders; }
}
public ObservableCollection<IFeed> Feeds
{
get { return _feeds; }
}
public void AddFeed(IFeed newFeed)
{
newFeed.PropertyChanged += NewFeedPropertyChanged;
_feeds.Add(newFeed);
PropertyChanged.Raise(this, x => x.Feeds);
if (newFeed.UnreadFeedItemsCount > 0)
{
PropertyChanged.Raise(this, x => x.UnreadFeedItemsCount);
}
}
void NewFeedPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "UnreadFeedItemsCount")
{
PropertyChanged.Raise(this, x => x.UnreadFeedItemsCount);
}
}
public void RemoveFeed(IFeed feedToRemove)
{
_feeds.Remove(feedToRemove);
PropertyChanged.Raise(this, x => x.Feeds);
if (feedToRemove.UnreadFeedItemsCount > 0)
{
PropertyChanged.Raise(this, x => x.UnreadFeedItemsCount);
}
}
public void AddFeedFolder(IFeedFolder newFeedFolder)
{
newFeedFolder.PropertyChanged += NewFeedPropertyChanged;
_feedFolders.Add(newFeedFolder);
PropertyChanged.Raise(this, x => x.FeedFolders);
if (newFeedFolder.UnreadFeedItemsCount > 0)
{
PropertyChanged.Raise(this, x => x.UnreadFeedItemsCount);
}
}
public void RemoveFeedFolder(IFeedFolder feedFolderToRemove)
{
_feedFolders.Remove(feedFolderToRemove);
PropertyChanged.Raise(this, x => x.FeedFolders);
if (feedFolderToRemove.UnreadFeedItemsCount > 0)
{
PropertyChanged.Raise(this, x => x.UnreadFeedItemsCount);
}
}
}
Now for usage, remember I havent tested this, but it should be mostly right.
var myFolder = new FeedFolder();
var myFeed = new Feed();
var myFeedItem = new FeedItem();
myFeedItem.IsUnread = true;
myFeed.AddFeedItem(myFeedItem);
myFolder.AddFeed(myFeed);
var mySecondFeedItem = new FeedItem();
//add a second feeditem to feed, but it is marked as read, so no notifications raised for unread count.
myFeed.AddFeedItem(mySecondFeedItem);
//this should fire off change events all the way up to the folder
mySecondFeedItem.IsUnread = true;

How to register custom module manager in PRISM?

I've created some simple custom ModuleManager in my silverlight application based on PRISM. I also registered this type in bootstrapper, but PRISM still use the default manager. The constructor of my CustomModuleManager is called, but the property ModuleTypeLoaders is never accessed. I can't figure it out, how can I make it work properly?
Here is bootstrapper.cs
protected override void ConfigureContainer()
{
Container.RegisterType<IShellProvider, Shell>();
Container.RegisterType<IModuleManager, CustomModuleManager>();
base.ConfigureContainer();
}
CustomModuleManager.cs
public class CustomModuleManager : ModuleManager
{
IEnumerable<IModuleTypeLoader> _typeLoaders;
public CustomModuleManager(IModuleInitializer moduleInitializer,
IModuleCatalog moduleCatalog,
ILoggerFacade loggerFacade)
: base(moduleInitializer, moduleCatalog, loggerFacade)
{
MessageBox.Show("ctor");
}
public override IEnumerable<IModuleTypeLoader> ModuleTypeLoaders
{
get
{
MessageBox.Show("getter");
if (_typeLoaders == null)
{
_typeLoaders = new List<IModuleTypeLoader>
{
new CustomXapModuleTypeLoader()
};
}
return _typeLoaders;
}
set
{
MessageBox.Show("setter");
_typeLoaders = value;
}
}
}
CustomXapModuleTypeLoader.cs
public class CustomXapModuleTypeLoader : XapModuleTypeLoader
{
protected override IFileDownloader CreateDownloader()
{
return new CustomFileDownloader();
}
}
CustomFileDownloader.cs
public class CustomFileDownloader : IFileDownloader
{
public event EventHandler<DownloadCompletedEventArgs> DownloadCompleted;
readonly FileDownloader _dler = new FileDownloader();
public CustomFileDownloader()
{
_dler.DownloadCompleted += DlerDownloadCompleted;
}
void DlerDownloadCompleted(object sender, DownloadCompletedEventArgs e)
{
_dler.DownloadCompleted -= DlerDownloadCompleted;
if (DownloadCompleted != null)
{
if (e.Cancelled || e.Error != null)
{
DownloadCompleted(this, e);
}
else
{
DownloadCompleted(this,
new DownloadCompletedEventArgs(e.Result,
e.Error,
e.Cancelled,
e.UserState));
}
}
}
public void DownloadAsync(Uri uri, object userToken)
{
_dler.DownloadAsync(uri, userToken);
}
}
Reorder your call to base.ConfigureContainer so that yours wins (last one wins):
protected override void ConfigureContainer()
{
base.ConfigureContainer();
Container.RegisterType<IShellProvider, Shell>();
Container.RegisterType<IModuleManager, CustomModuleManager>();
}

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.

Resources