prism vs mvvm light for wpf - wpf

We are starting a WPF with MVVM project and have to decide on PRISM or MVVM Light (I am new to both these frameworks). I have read through a few posts but still have a few questions. Can someone please throw some light on the following aspects w.r.t. both the frameworks?:
Performance: Will either one framework perform better than the other for any reason?
Communication within the app (viewmodel to viewmodel or between modules etc): I have read that MVVM Light has Messenging Service which appears to be fairly easy as well. But PRISM does not appear to have any equivalent. Is that true? How would PRISM handle interactions?
Unit Testing: Have read that PRISM supports Unit Testing better. Can we still write NUNIT or VSTS tests in MVVM Light also?

I just moved a project from Prism to MvvmLight and it seems to work faster (very subjective).
Both Prism and MvvmLight have Mediator realisation (IEventAggregator in Prism, IMessenger in MvvmLight). But IMessenger has more abilities (for instance, sending messages with tokens) compared to IEventAggregator and is much more convenient to use (see next item).
MvvmLight also has a more powerful ViewModelBase class.
Applications that use MvvmLight are much easier to test than those that use Prism. For instance, IMessenger is easier to mock than IEventAggregator.
PrismViewModel.cs
using System;
using Microsoft.Practices.Prism.Events;
using Microsoft.Practices.Prism.ViewModel;
// An ugly empty event class
public class StringEvent : CompositePresentationEvent<string> { }
public sealed class PrismViewModel : NotificationObject
{
private readonly IEventAggregator _eventAggregator;
private string _name;
public PrismViewModel(IEventAggregator eventAggregator)
{
if (eventAggregator == null)
throw new ArgumentNullException("eventAggregator");
_eventAggregator = eventAggregator;
_eventAggregator.GetEvent<StringEvent>().Subscribe(s => Name = s);
}
public string Name
{
get { return _name; }
set
{
// boiler-plate code
if (value == _name)
return;
_name = value;
RaisePropertyChanged(() => Name);
}
}
public void SendMessage(string message)
{
_eventAggregator.GetEvent<StringEvent>().Publish(message);
}
}
PrismViewModelTestCase.cs
using System;
using FluentAssertions;
using Microsoft.Practices.Prism.Events;
using NSubstitute;
using NUnit.Framework;
public class PrismViewModelTestCase
{
private static PrismViewModel CreateViewModel(IEventAggregator eventAggregator = null)
{
// You can't return Substitute.For<IEventAggregator>()
// because it returns null when PrismViewModel's constructor
// invokes GetEvent<StringEvent>() method which leads to NullReferenceException
return new PrismViewModel(eventAggregator ?? CreateEventAggregatorStub());
}
private static IEventAggregator CreateEventAggregatorStub()
{
var eventAggregatorStub = Substitute.For<IEventAggregator>();
eventAggregatorStub.GetEvent<StringEvent>().Returns(Substitute.For<StringEvent>());
return eventAggregatorStub;
}
[Test]
public void Constructor_WithNonNullEventAggregator_ExpectedSubscribesToStringEvent()
{
// Arrange
var stringEventMock = Substitute.For<StringEvent>();
var eventAggregatorStub = Substitute.For<IEventAggregator>();
eventAggregatorStub.GetEvent<StringEvent>().Returns(stringEventMock);
// Act
CreateViewModel(eventAggregatorStub);
// Assert
// With constrained isolation framework you can only mock virtual members
// CompositePresentationEvent<TPayload> has only one virtual Subscribe overload with four parameters
stringEventMock.Received()
.Subscribe(Arg.Any<Action<string>>(), Arg.Any<ThreadOption>(), Arg.Any<bool>(),
Arg.Any<Predicate<string>>());
}
[Test]
public void Name_ExpectedRaisesPropertyChanged()
{
var sut = CreateViewModel();
sut.MonitorEvents();
sut.Name = "any-value";
sut.ShouldRaisePropertyChangeFor(vm => vm.Name);
}
[Test]
public void SendMessage_ExpectedPublishesStringEventThroughEventAggregator()
{
// Arrange
var stringEventMock = Substitute.For<StringEvent>();
var eventAggregatorStub = Substitute.For<IEventAggregator>();
eventAggregatorStub.GetEvent<StringEvent>().Returns(stringEventMock);
var sut = CreateViewModel(eventAggregatorStub);
const string expectedPayload = "any-string-payload";
// Act
sut.SendMessage(expectedPayload);
// Assert
stringEventMock.Received().Publish(expectedPayload);
}
}
MvvmLightViewModel.cs
using System;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Messaging;
public sealed class MvvmLightViewModel : ViewModelBase
{
private string _name;
public MvvmLightViewModel(IMessenger messenger)
{
if (messenger == null)
throw new ArgumentNullException("messenger");
// ViewModelBase already have field for IMessenger
MessengerInstance = messenger;
MessengerInstance.Register<string>(this, s => Name = s);
}
public string Name
{
get { return _name; }
set { Set(() => Name, ref _name, value); // Chic! }
}
public void SendMessage(string message)
{
MessengerInstance.Send(message);
}
}
MvvmLightViewModelTestCase.cs
using System;
using FluentAssertions;
using GalaSoft.MvvmLight.Messaging;
using NSubstitute;
using NUnit.Framework;
public class MvvmLightViewModelTestCase
{
private static MvvmLightViewModel CreateViewModel(IMessenger messenger = null)
{
return new MvvmLightViewModel(messenger ?? Substitute.For<IMessenger>());
}
[Test]
public void Constructor_WithNonNullMessenger_ExpectedRegistersToStringMessage()
{
var messengerStub = Substitute.For<IMessenger>();
var sut = CreateViewModel(messengerStub);
messengerStub.Received().Register(sut, Arg.Any<Action<string>>());
}
[Test]
public void Name_ExpectedRaisesPropertyChanged()
{
var sut = CreateViewModel();
sut.MonitorEvents();
sut.Name = "any-value";
sut.ShouldRaisePropertyChangeFor(vm => vm.Name);
}
[Test]
public void SendMessage_ExpectedSendsStringMessageThroughMessenger()
{
var messengerMock = Substitute.For<IMessenger>();
var sut = CreateViewModel(messengerMock);
const string expectedMessage = "message";
sut.SendMessage(expectedMessage);
messengerMock.Received().Send(expectedMessage);
}
}
Disadvantages of Prism:
it's non fully open-source project (official Prism repository is read-only)
2015-10-30: now it's fully open-sourced: https://github.com/PrismLibrary/Prism
it no longer actively developed
2015-10-30: new version of Prism: https://github.com/PrismLibrary/Prism
directly using of its classes leads to boiler-plate and less readable code
I think that any new project should be based on modern solutions and approaches.
IMHO, any modern MVVM-framework (like Catel, Caliburn.Micro, MvvmLight, ReactiveUI) is much better than Prism.

You cannot fully compare Prism and MvvmLight.
Prism is more about application architecture even though Prism has been known as MVVM framework. Actually until Prism 5 it had nothing to do with MVVM and It didn't have BaseViewModel class in Prism 4.1 and in prior.
Prism is not a MVVM framework it is application framework it sits higher than that. Prism 5 introduced some support for MVVM and Prism 6 took it futher.
MVVM is just another aspect of problems that prism provides guidance to solve.
It is like comparing Angular vs. Knockout. AngularJS manages the whole application and defines guidelines on how the application code should be structured, whereas with KnockoutJS the application structure is entirely up to you. It is a similar case between Prism and MvvmLight.
Prism provides an implementation of a collection of design patterns that are helpful in writing well structured and maintainable XAML applications, including MVVM, dependency injection, commanding, event aggregation, and more. Prism's core functionality is a shared code base in a Portable Class Library targeting these platforms; WPF, Windows 10 UWP, and Xamarin Forms.
I would recommend to use Prism if you are developing an enterprise level application using wpf.
Please watch this Webinar about MVVM Made Simple with Prism. The presenter is Brian Lagunas:
https://www.youtube.com/watch?v=ZfBy2nfykqY

I don't believe MS has ever promoted PRISM as a "framework" in the same sense that MVVM Light, Caliburn, etc. are "advertised." My understanding is the PRISM was always presented to the "world" as a "practice." Which, I believe, simply means a organized way of building applications. It becomes a bit confusing because of all the code that is supplied with PRISM and one can consider it a "framework" that can be used to build applications. And, it's true, you can use the code that is supplied with PRISM to build applications of your own. But, PRISM, in my mind is much more complicated than the "frameworks" that are available for MVVM and there is a steep learning curve as well as the possibility of "overkill" and making your application more complex than is necessary. If you have the time to learn the latest "framework" or "practice" at the time you are building your application, that's GREAT! My experience has been that I don't have the luxury of factoring in learning some new "practice" or the latest "framework" but have to get the job done. In an ideal world, one would like to be able to use the latest and greatest "framework" or employ the latest "practices" but sometimes you just have to stick with what you already know and get it done.

Related

ReactiveUI ViewModelViewHost Is Very Slow, When using it With HandyControl [closed]

Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 2 years ago.
Improve this question
Hi,
About 6 months ago I started playing with reactiveUI and build simple applications with it
And just four months ago, I started to build an application that monitors the network on low level
So, I implement network part in C++ and then build UI, and database models and logic in C#
Then create an intermediate library to marshal this low-level API,
So as you know this API will provide a huge amount of packets.
So, in C# I decided to use reactiveUI and reactive programming, in general, to work with those streams of data
and Rx works perfectly and save me days of works with this high-performance reactive system
But now I have a big problem:
When I navigate through the application, the initial time of resolving view / ViewModel
is so much, it's about 1200-506 ms on average and this cause a problem because this makes the app look like its frozen
So I try to solve this problem, or get work around it but nothing helps,
I track most/all of the guidelines of reactiveUI, but nothing seems to work
Also, notice a strange behavior described
in this StackOverflow question: WhenActivated is called twice:
And try that solution but does not work.
So i try to implement my custom SimpleViewModelViewHost
SimpleViewModelViewHost.xaml
<UserControl x:Class="Sample.EnhancedViewModelViewHost"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
d:DesignHeight="450" d:DesignWidth="800">
<ContentPresenter
x:Name="MainContent"
Content="{Binding Path=View}"
/>
</UserControl>
SimpleViewModelViewHost.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using ReactiveUI;
namespace Sample
{
// https://stackoverflow.com/questions/36433709/whenactivated-is-called-twice-when-used-in-views-and-viewmodels-hosted-in-viewmo/36813328#36813328
/// <summary>
/// Interaction logic for EnhancedViewModelViewHost.xaml
/// </summary>
public partial class EnhancedViewModelViewHost : UserControl
{
public static readonly DependencyProperty ViewModelProperty = DependencyProperty.Register(
"ViewModel", typeof(object), typeof(EnhancedViewModelViewHost), new PropertyMetadata(default(object)));
public object ViewModel
{
get => GetValue(ViewModelProperty);
set
{
SetValue(ViewModelProperty, value);
if (value == null) { return; }
var view = ViewLocator.Current.ResolveView(value);
if (view != null)
{
View = view;
View.ViewModel = value;
}
else
{
MainContent.Content = value;
}
}
}
public static readonly DependencyProperty ViewProperty = DependencyProperty.Register(
"View", typeof(IViewFor), typeof(EnhancedViewModelViewHost), new PropertyMetadata(default(IViewFor)));
public IViewFor View
{
get => (IViewFor)GetValue(ViewProperty);
set => SetValue(ViewProperty, value);
}
public EnhancedViewModelViewHost()
{
DataContext = this;
InitializeComponent();
}
}
}
I know this leak a lot of features disposing of old view/viewModel .....
But now performance is good: now it just takes about 250-300 ms, but still not good at all
Because human eys can notice this delay
So NowI am in a big problem, so I create another simple app in ReactiveUI with empty View
With no binding
and guess what: the problem still existed
I Used Visual studio profiler to track time between the start of a constructor of ViewModel
and the end of WhenActivated in the View
So my question: is reactive team aware of this problem or just I do something wrong, and if yes, what is the solution
Also Notice:
I try to improve the performance in complex layouts by Creating and implement an interface called IHotReloadViewModel and implement some logic to re-use current ViewModel instead of replacing it
and gain performance from about 1350 ms -> 10 ms
Snippets
Part of ViewModel
public class ManageProjectsViewModel : ReactiveObject, IActivatableViewModel
{
// .....
[Reactive] public EditProjectViewModel SelectedProject { get; set; }
//.....
private AppDbManager AppDbManager { get; set; }
#region Commands
public ReactiveCommand<Unit, Unit> EditProject { get; set; }
public ReactiveCommand<Unit, Unit> CreateNewProject { get; set; }
public ReactiveCommand<Unit, Unit> DeleteProject { get; set; }
// ....
#endregion
private IDisposable LastProjectTrack { get; set; }
private Subject<Unit> FreeSelectedProject { get; set; }
public ManageProjectsViewModel()
{
Activator = new ViewModelActivator();
AppDbManager = Locator.Current.GetService<AppDbManager>();
#region Commands
var canOperateOnProject = this.WhenValueChanged(vm => vm.SelectedProjectLookup).Select(p => p != null);
EditProject = ReactiveCommand.Create(EditProjectImpl, canOperateOnProject);
CreateNewProject = ReactiveCommand.Create(CreateNewProjectImpl);
DeleteProject = ReactiveCommand.Create(DeleteProjectImpl, canOperateOnProject);
#endregion
FreeSelectedProject = new Subject<Unit>();
this.WhenActivated(disposables =>
{
ProjectAddedNotify.ObserveOnDispatcher().Subscribe(ProjectAddedNotifyImpl).DisposeWith(disposables);
FreeSelectedProject.ObserveOnDispatcher().Subscribe(FreeSelectedProjectImpl).DisposeWith(disposables);
});
}
// ...........
Part of View.xaml.cs
public partial class ManageProjectsView : ReactiveUserControl<ManageProjectsViewModel>
{
private bool called = false;
public ManageProjectsView()
{
InitializeComponent();
IDisposable mainDisposable = null;
mainDisposable = this.WhenActivated(disposable =>
{
// ........
this.BindCommand(ViewModel, vm => vm.CreateNewProject, v => v.NewProject).DisposeWith(disposable);
this.BindCommand(ViewModel, vm => vm.EditProject, v => v.EditProject).DisposeWith(disposable);
this.BindCommand(ViewModel, vm => vm.DeleteProject, v => v.DeleteProject).DisposeWith(disposable);
this.Bind(ViewModel, vm => vm.SelectedProject, v => v.SelectedProject.ViewModel).DisposeWith(disposable);
ProjectLookups.Events().SelectionChanged.Subscribe(args =>
{
if (args.AddedItems.Count > 0)
{
ViewModel.SelectedProjectLookup = (NPProjectLookup)args.AddedItems[0];
}
}).DisposeWith(disposable);
ProjectLookups.ApplyHorizontalScrolling();
ProjectLookups.AllowZoom();
mainDisposable.DisposeWith(disposable);
}, this); // either use this or not: performance issue exists
}
}
Most of Views/ViewModels uses the same structure
So why I think this is a problem
Because I test the same UI with my simple implementation of ViewModel-View Locator and everything work instantly
Also tested it with Prism With DryIoC i work with it for a long time and everything works instantly
so know is there any solution for that, or I will need to use a prism in the current reactiveUI app?
Notice
First I did not post this in reactiveUI issues in Github: because I do not want to overflow issues panel with may unnecessary issue, so I need to be sure here that this issue exists on other users devices and if that I will report it to reactiveUI team
Updates (1)
After testing more than one app 5 apps i found that
Problem of calling WhenActivated Twice is related to reactiveUI
Problem of performance Delay of view only occurs when using ReactiveUI with HandyControl UI Library, I still can not be sure about the source of problem but because it's only happens when using HC With RI i decide to Create This Issue at HandyControl in GitHub
Update (2)
I Created This Issue On ReactiveUI Repository
Thanks.
Can you create a usable reproduction of the issue please, put it in a github repository, link it here AND create an issue in https://github.com/reactiveui/ReactiveUI/issues
As for questions about "not providing any performance considerations". There are numerous discussions during pull requests about performance and impact (i.e. https://github.com/reactiveui/ReactiveUI/pull/1311 https://github.com/reactiveui/ReactiveUI/pull/1289 and https://github.com/reactiveui/splat/pull/360). In terms of benchmarks, indeed we're short on them, but we have an open issue https://github.com/reactiveui/ReactiveUI/issues/1734 for them. The documentation could be better, there is a lot of knowledge out there on how to get the best out of ReactiveUI, people are welcome to help us improve how to make that knowledge accessible.
As for confidence in a project that has 5000 stars. That 5000 stars is great as an indication of interest, but only interest. The number of people helping to maintain it equates to ~1% with a few people spending their time and passion on a project, some for almost a decade. They want people to be using the project and want to help you get the best out of it. You want confidence in what you are using which is only sensible, but there are companies using it in real-time applications and\or applications used by thousands of users everyday.
I could point you to posts about the NET framework having a magnitude of stars greater than us, and it has perf\knowledge\usability issues as well. But my point will be the maintainers of projects only learn by customers\communities trying things and feeding back.
For your actual problem, there's a team of people who are willing to help. But we need evidence in a reproducible issue, ideally with a test and possibly trace of what you're seeing. We can then assist and understand if you're project is doing something we can help solve, or whether ReactiveUI or the underlying Splat library needs some investigation.

Login With MVVM and WPF - Login Object must use in other windows. (Globalization)

I am very new to WPF and MVVM Pattern. I even have no experience on windows.
I have Created Simple login window
_ Login.xaml, LoginViewModel.cs
_ Dashboard.xaml, DashboardViewModel.cs
After Login Successfully - ( In Login time we will select Language also )
I am Displaying Username & Selected Language in Dashboard window
I wrote code like this:
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
try
{
var login = new Login();
var loginVM = new LoginViewModel();
Dashboard main = null;
loginVM.LoginCompleted += (sender, args) =>
{
DashboardViewModel dvModel = new DashboardViewModel(loginVM);
main = new Dashboard();
main.DataContext = dvModel;
main.ShowDialog();
login.Hide();
};
login.DataContext = loginVM;
login.ShowDialog();
}
catch (Exception ex)
{
throw ex;
}
}
In Dashboard Window it is displaying username and Language successfully.
But my problem is those two (Username & Language) properties I want to use in dashboard codebehind for update the layout based on language & other xaml files or other viewmodels . How to do that one ?
Technically I want to use Loginviewmodel object in all viewmodels.
Based on Selected Language I want to update Layout.
Note: Is this login approach good ? Is there any alternative for Globalization in MVVM pattern ?
Using a ViewModel for login is perfectly valid. I would perhaps create a token in your loginVM to pass around the system, depending on your needs. That token should be passed into the constructors of your other viewmodels from your main view model (DashboardViewModel?). This can be resolved using any decent IoC container.
For globalization/localization, I would use resources (in satellite assemblies). We've experimented with various things, and found that we didn't like the WPF UUIDs added everywhere when using LocBaml. And storing translation is a database quickly became a performance hog (even when loading in bulk). This does require you to find your labels etc. to a resource manager, but in my opinion, it is worth it.
Take a look at this article, for a nice extension, that enables you to simply write:
<TextBlock Text="{Resx MyText}"/>
And it will be translated using resource files.
An alterative approach is to simply store the Username and Language is a static property. I know most people don't like globals, but something like this is in nature very global, and you will still be able to inject it in if you so desire. The downside of this approach is that your unit tests would have to setup this static variable first.
EDIT An example of the static approach:
public static class RuntimeInfo {
public static string UserName { get; set; }
public static CultureInfo UserCulture { get; set; }
}
In your loginVM, simply store the necessary values in a static class. This can be accessed anywhere needed. This is not as 'correct' as the previous approach, but it can be more pragmatic than having to pass the username into every single ViewModel in your application.
I still recommend injection through an IoC container though.

Need help on layering

I am trying to layer my WP app & following MVVM pattern. I have a VM with an ICommand which runs when a button is clicked on View. A click on button now runs a method as pointed by ICommand which retrieves data from DB using linq.
Here is how my VM looks.
public class CategoryViewModel : INotifyPropertyChanged
{
// Category type is a table in my DB.
private Category _currentCategory;
public Category CurrentCategory
{
get { return _currentCategory; }
set
{
if (value != _currentCategory)
{
_currentCategory = value;
OnPropertyChanged("CurrentCategory");
}
}
}
// Helper method hooked with ICommand via RelayCommand class.
// not posting RelayCommand class code here.
private void GetCategory()
{
using (CategoryDBContext ctx = new CategoryDBContext(CategoryDBContext.ConnectionString))
{
CurrentCategory = ctx.Categories.FirstOrDefault();
}
}
}
Here is how my View looks.
<TextBlock Text="{Binding CurrentCategory.CategoryName}" />
<Button Command="{Binding GetCategoryCommand}" Content="Click me"/>
I am trying to implement a Implementing a Generic Repository and a Unit of Work Class & somewhat following ideas mentioned in this article. If you scroll now a bit there to "Creating a Generic Repository" , you will find use of DbSet< TEntity > because they are using EF. What is equivalent of this in WP ?
How can i do something similar in WP app ? I don't want any data access code in my VM. Where should it go ? Also, the reason i want to Implement a Generic Repository is to avoid creating multiple repositories classes like CategoryRepositoy, ProductRepositoy etc....
I already have all POCO classes in place in my Model.
If i understand correctly you would like to have a single repository for all your get/save methods? If you can do this with Windows Phone? and Where does it go because you don't want it in the VM?
Making a basic interface with a get and set, this is a good place to start
http://www.remondo.net/repository-pattern-example-csharp/
The repository code doesnt need to be in the VM directly, but you should still call it from the vm.
View = ui, Model = data, view model = everything else such as getting/setting/updating/manipulating the data.

WPF: Binding TreeView in MVVM way step by step tutorial

See the next post. This original one question content has been removed, as doesn't have any sense. Briefly, I asked how to bind XML (which I generated by mistake while parsing DLL assembly) to TreeView using XmlDataProvider in MVVM way. But later I understood that this approach was wrong, and I switched to generation of data entity model (just write classes which represent all the entities I would like to expose in the tree) instead of XML.
So, the result in the next post. Currently from time to time I update this "article", so F5, and
Enjoy reading!
Introduction
The right way I had found reading this article
It's a long story, most of you just can skip it :) But those, who want to understand the problem and solution, must read this all !
I'm QA, and some time ago had become responsible for Automation of the product I clicks. Fortunately, this automaton takes place not in some Testing Tool, but in Visual Studio, so it is maximally close to development.
For our automation we use a framework which consist of MbUnit (Gallio as runner) and of MINT (addition to MbUnit, which is written by the customer we work with). MbUnit gives us Test Fixtures and Tests, and MINT adds additional smaller layer -- Actions inside tests. Example. Fixture is called 'FilteringFixture'. It consist of amount of tests like 'TestingFilteringById', or 'TestingFilteringWithSpecialChars', etc. Each test consist of actions, which are atomic unit of our test. Example of actions are - 'Open app (parameter)', 'OpenFilterDialog', etc.
We already have a lot of tests, which contain a lot of actions, it's a mess. They use internal API of the product we QA. Also, we start investigation a new Automation approach - UI automation via Microsoft UI Automation (sorry for tautology). So the necessity of some "exporter", or "reporter" tool became severe for managers.
Some time ago I have got a task to develop some application, which can parse a DLL (which contains all the fixtures, tests and actions), and export its structure in the human readable format (TXT, HTML, CSV, XML, any other). But, right after that, I went to vacation (2 weeks).
It happens so, that my girlfriend went to her family until vacation (she also got it), and I remained at home so alone. Thinking what me to do all this time (2 weeks), I remember about that "write exporter tool task" and how long I have been planning to start learning WPF. So, I decided to make my task during vacation, and also dress a application to WPF. At that time I heard something about MVVM, and I decided to implement it using pure MVVM.
DLL which can parse DLL with fixrtures etc had been written rather fast (~1-2 days). After that I had started with WPF, and this article will show you how it ended.
I have spent a major part of my vacation (almost 8 days!), trying to sorted it out in my head and code, and finally, it is done (almost). My girlfriend would not believe what I was doing all this time, but I have a proof!
Sharing my solution step by step in pseudo code, to help others avoid similar problems. This answer is more looks like tutorial =) (Really?). If you are interested what were the most complicated things while learning WPF from scratch, I would say -- make it all really MVVM and f*g TreeView binding!
If you want an archived file with solution, I can give it a bit later, just when I have made a decision, that it is worth of that. One limitation, I'm not sure I may share the MINT.dll, which brings Actions, as it has been developed by the customer of our company. But I can just remove it, and share the application, which can display information about Fixtures and Tests only, but not about actions.
Boastful words. With just a little C# / WinForms / HTML background and no practice I have been able to implement this version of the application in almost 1 week (and write this article). So, impossible is possible! Just take a vacation like me, and spend it to WPF learning!
Step by step tutorial (w/o attached files yet)
Short repetition of the task:
Some time ago I have got a task to develop an application, which can parse a DLL (which contains test fixtures, test methods and actions - units of our unit testing based automation framework), and export its structure in the human readable format (TXT, HTML, CSV, XML, any other). I decided to implement it using WPF and pure MVVM (both were absolutely new things for me). The 2 most difficult problems for me became MVVM approach itself, and then MVVM binding to TreeView control. I skip the part about MVVM division, it's a theme for separate article. The steps below are about binding to TreeView in MVVM way.
Not so important: Create DLL which can open DLL with unit tests and finds fixtures, test methods and actions (more smaller level of unit test, written in our company) using reflection. If you are interested in how it had been done, look here: Parsing function / method content using Reflection
DLL: Separated classes are created for both fixtures, tests and actions (data model, entity model?).We'll use them for binding. You should think by yourself, what will be an entity model for your tree. Main idea - each level of tree should be exposed by appropriate class, with those properties, which help you to represent the model in the tree (and, ideally, will take right place in your MVVM, as model or part of the model). In my case, I was interested in entity name, list of children and ordinal number. Ordinal number is a number, which represents order of an entity in the code inside DLL. It helps me show ordinal number in the TreeView, still not sure it's right approach, but it works!
public class MintFixutre : IMintEntity
{
private readonly string _name;
private readonly int _ordinalNumber;
private readonly List<MintTest> _tests = new List<MintTest>();
public MintFixutre(string fixtureName, int ordinalNumber)
{
_name = fixtureName;
if (ordinalNumber <= 0)
throw new ArgumentException("Ordinal number must begin from 1");
_ordinalNumber = ordinalNumber;
}
public List<MintTest> Tests
{
get { return _tests; }
}
public string Name { get { return _name; }}
public bool IsParent { get { return true; } }
public int OrdinalNumber { get { return _ordinalNumber; } }
}
public class MintTest : IMintEntity
{
private readonly string _name;
private readonly int _ordinalNumber;
private readonly List<MintAction> _actions = new List<MintAction>();
public MintTest(string testName, int ordinalNumber)
{
if (string.IsNullOrWhiteSpace(testName))
throw new ArgumentException("Test name cannot be null or space filled");
_name = testName;
if (ordinalNumber <= 0)
throw new ArgumentException("OrdinalNumber must begin from 1");
_ordinalNumber = ordinalNumber;
}
public List<MintAction> Actions
{
get { return _actions; }
}
public string Name { get { return _name; } }
public bool IsParent { get { return true; } }
public int OrdinalNumber { get { return _ordinalNumber; } }
}
public class MintAction : IMintEntity
{
private readonly string _name;
private readonly int _ordinalNumber;
public MintAction(string actionName, int ordinalNumber)
{
_name = actionName;
if (ordinalNumber <= 0)
throw new ArgumentException("Ordinal numbers must begins from 1");
_ordinalNumber = ordinalNumber;
}
public string Name { get { return _name; } }
public bool IsParent { get { return false; } }
public int OrdinalNumber { get { return _ordinalNumber; } }
}
BTW, I also created an interface below, which implement all the entities. Such interface can help you in the future. Still not sure, should I was also add there Childrens property of List<IMintEntity> type, or something like that?
public interface IMintEntity
{
string Name { get; }
bool IsParent { get; }
int OrdinalNumber { get; }
}
DLL - building data model: DLL has a method which opens DLL with unit tests and enumerating data. During enumeration, it builds a data model like below. Real method example is given, reflection core + Mono.Reflection.dll are used, don't be confused with complexity. All that you need - look how the method fills _fixtures list with entities.
private void ParseDllToEntityModel()
{
_fixutres = new List<MintFixutre>();
// enumerating Fixtures
int f = 1;
foreach (Type fixture in AssemblyTests.GetTypes().Where(t => t.GetCustomAttributes(typeof(TestFixtureAttribute), false).Length > 0))
{
var tempFixture = new MintFixutre(fixture.Name, f);
// enumerating Test Methods
int t = 1;
foreach (var testMethod in fixture.GetMethods().Where(m => m.GetCustomAttributes(typeof(TestAttribute), false).Length > 0))
{
// filtering Actions
var instructions = testMethod.GetInstructions().Where(
i => i.OpCode.Name.Equals("newobj") && ((ConstructorInfo)i.Operand).DeclaringType.IsSubclassOf(typeof(BaseAction))).ToList();
var tempTest = new MintTest(testMethod.Name, t);
// enumerating Actions
for ( int a = 1; a <= instructions.Count; a++ )
{
Instruction action = instructions[a-1];
string actionName = (action.Operand as ConstructorInfo).DeclaringType.Name;
var tempAction = new MintAction(actionName, a);
tempTest.Actions.Add(tempAction);
}
tempFixture.Tests.Add(tempTest);
t++;
}
_fixutres.Add(tempFixture);
f++;
}
}
DLL: Public property Fixtures of the List<MintFixutre> type is created to return just created data model ( List of Fixtures, which contain lists of tests, which contains lists of Actions ). This will be our binding source for TreeView.
public List<MintFixutre> Fixtures
{
get { return _fixtures; }
}
ViewModel of MainWindow (with TreeView inside): Contains object / class from DLL which can parse unit tests DLLs. Also exposes Fixtures public property from the DLL of List<MintFixutre> type. We will bind to it from XAML of MainWindow. Something like that (simplified):
var _exporter = MySuperDllReaderExporterClass ();
// public property of ViewModel for TreeView, which returns property from #4
public List<MintFixture> Fixtures { get { return _exporter.Fixtures; }}
// Initializing exporter class, ParseDllToEntityModel() is called inside getter
// (from step #3). Cool, we have entity model for binding.
_exporter.PathToDll = #"open file dialog can help";
// Notifying all those how are bound to the Fixtures property, there are work for them, TreeView, are u listening?
// will be faced later in this article, anticipating events
OnPropertyChanged("Fixtures");
XAML of MainWindow - Setup data templates: Inside a Grid, which contains TreeView, we create <Grid.Resources> section, which contains a set of templates for our TreeViewItems. HierarchicalDataTemplate (Fixtures and Tests) is used for those who have child items, and DataTemplate is used for "leaf" items (Actions). For each template, we specify which its Content (text, TreeViewItem image, etc.), ItemsSource (in case of this item has children, e.g. for Fixtures it is {Binding Path=Tests}), and ItemTemplate (again, only in case this item has children, here we set linkage between templates - FixtureTemplate uses TestTemplate for its children, TestTemplate uses ActionTemplate for its children, Action template does not use anything, it is a leaf!). IMPORTANT: Don't forget, that in order to "link" "one" template to "another", the "another" template must be defined in XAML above the "one"! (just enumerating my own blunders :) )
XAML - TreeView linkage: We setup TreeView with: linking with data model from ViewModel (remember public property?) and with just prepared templates, which represent content, appearance, data sources and nesting of tree items! One more important note. Don't define your ViewModel as "static" resource inside XAML, like <Window.Resources><MyViewModel x:Key="DontUseMeForThat" /></Window.Resources>. If you do so, then you won't be able to notify it on property changed. Why? Static resource is static resource, it initializes ones, and after that remains immutable. I might be wrong here, but it was one of my blunders. So for TreeView use ItemsSource="{Binding Fixtures}" instead of ItemsSource="{StaticResource myStaticViewModel}"
ViewModel - ViewModelBase - Property Changed: Almost all. Stop! When user opens an application, then initially TreeView is empty of course, as user hasn't opened any DLL yet! We must wait until user opens a DLL, and only then perform binding. It is done via OnPropertyChanged event. To make life easier, all my ViewModels are inherited from ViewModelBase, which right exposes this functionality to all my ViewModel.
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}
protected virtual void OnPropertyChanged(PropertyChangedEventArgs args)
{
var handler = PropertyChanged;
if (handler != null)
handler(this, args);
}
}
XAML - OnPropertyChanged and commanding. User clicks a button to opens DLL which contains unit tests data. As we using MVVM, then click is handled via commanding. At the end of the OpenDllExecuted handler OnPropertyChanged("Fixtures") is executed, notifying the Tree, that the property, to which it is bind to has been changed, and that now is time to refresh itself. RelayCommand helper class can be taken for example from there). BTW, as I know, there are some helper libraries and toolkits exist Something like that happens in the XAML:
And ViewModel - Commanding
private ICommand _openDllCommand;
//...
public ICommand OpenDllCommand
{
get { return _openDllCommand ?? (_openDllCommand = new RelayCommand(OpenDllExecuted, OpenDllCanExecute)); }
}
//...
// decides, when the <OpenDll> button is enabled or not
private bool OpenDllCanExecute(object obj)
{
return true; // always true for Open DLL button
}
//...
// in fact, handler
private void OpenDllExecuted(object obj)
{
var openDlg = new OpenFileDialog { ... };
_pathToDll = openDlg.FileName;
_exporter.PathToDll = _pathToDll;
// Notifying TreeView via binding that the property <Fixtures> has been changed,
// thereby forcing the tree to refresh itself
OnPropertyChanged("Fixtures");
}
Final UI (but not final for me, a lot of things should be done!). Extended WPF toolkit was used somewhere: http://wpftoolkit.codeplex.com/

Is this an acceptable practice of managing views in a WPF application using Prism?

I am writing an WPF MVVM application using Prism. A couple days ago I asked about best practices for managing different views and didn't get a whole lot of feedback. Sense then I have come up with a system that seems to work, but I want to make sure I wont get bit down the road.
I followed the tutorials at http://development-guides.silverbaylabs.org/ to get my shell setup and am confident that my modules are being registered well.
However, nowhere in those tutorials was an example of a view being replaced with a different view within a given region. This in general, seems to be fairly hard to find a good example of. So, today I rolled my own solution to the problem.
Essentially the module has a controller that keeps track of the current view, then when the user wants to switch views, it calls the Regions.Remove command and then the add command to replace it with the current view. It seems like there must be a more elegant solution to just switch between different registered views, but I haven't found it.
All the different possible views for a module are registered with the Unity container when the module is initialized.
The controller and the view switching function follows:
namespace HazardModule
{
public class HazardController : IHazardController
{
private object CurrentView;
public IRegionManager RegionManager { get; set; }
private IUnityContainer _container;
public HazardController(IUnityContainer container)
{
_container = container;
}
/// <summary>
/// Switches the MainRegion view to a different view
/// </summary>
/// <typeparam name="T">The class of the view to switch to</typeparam>
public void SiwthToView<T>()
{
if (CurrentView != null)
{
RegionManager.Regions["MainRegion"].Remove(CurrentView);
}
CurrentView = _container.Resolve<T>();
RegionManager.Regions["MainRegion"].Add(CurrentView);
}
}
}
Any feedback or other better solutions would be appreciated.
I have pretty much the same approach, so does a co-worker who has a bit more Prism experience than myself.
Basically I have a ViewController class which is a property in my ViewModelBase class. This enables all my ViewModels to have access to it in one go. Then in my ViewController class I have a few display management methods. The correctness of this approach is probably debatable but I found it to work quite well in my case
public TView ShowViewInRegion<TView>(string regionName, string viewName, bool removeAllViewsFromRegion)
{
var region = regionManager.Regions[regionName];
var view = region.GetView(viewName) ?? container.Resolve<TView>();
if (removeAllViewsFromRegion)
{
RemoveViewsFromRegion(region);
}
region.Add(view, viewName);
region.Activate(view);
if (regionName == RegionNames.OverlayRegion)
{
eventAggregator.GetEvent<PopupWindowVisibility>().Publish(true);
}
return (TView)view;
}
public void RemoveViewsFromRegion(string regionName)
{
RemoveViewsFromRegion(regionManager.Regions[regionName]);
}
private void RemoveViewsFromRegion(IRegion region)
{
for (int i = 0; i < region.Views.Count() + i; i++)
{
var view = region.Views.ElementAt(0);
region.Remove(view);
}
if (region.Name == RegionNames.OverlayRegion)
{
eventAggregator.GetEvent<PopupWindowVisibility>().Publish(false);
}
}
private static void DeactivateViewsInRegion(IRegion region)
{
for (var i = 0; i < region.ActiveViews.Count(); i++)
{
var view = region.ActiveViews.ElementAt(i);
region.Deactivate(view);
}
}
Then whenever I need to switch out a view or whatever I can just call from my ViewModel
public void ExecuteCreateUserCommand()
{
ViewController.ShowViewInRegion<IUserCreateView>(RegionNames.ContentRegion, ViewNames.UserCreateView, true);
}

Resources