MVPVM - Model View Presenter View Model

"Those who cannot remember the past are condemned to repeat it."
   - George Santayana

Our newly activated open source Electronic Health Record System (http://ehr.codeplex.com/) will be based on MVP-VM.  The architecture will implement both MVP and MVVM as intended while combining them for reasons noted in this blog.

History would tell us that the evolution of architecture (because of smart controls, newer technologies and limitations of older architectures) evolved as follows: 

MVC -> Presentation/Application Model ->  MVP  ->  Supervising Controller/Passive View

Understanding that the PresentationModel and MVVM are synonymous, the question to ask yourself when deciding to use MVVM alone is "what were the limitations that inspired the birth of MVP via the Potel and Twisting the Triad papers?".   For our environment the answer  was simple - we need a presenter. 

----------------------------------------------------------------------------------------------------

Although the following equation is true there are subtle differences that warranted a new name for the reasons John Gossman notes.  We'll reference authoritative sources on the differences.

Presentation Model = Application Model = MVVM  

In John Gossman (founding father of MVVM) "PresentationModel and WPF" John writes:

  • "On naming, my opinion at this point is the the Model-View-ViewModel pattern is a WPF-specific version of the PresentationModel pattern.  PresentationModel is a lot easier to say and to write, and is more widely known...but for now I'm sticking to M-V-VM terminology in my own thinking in order to capture the WPF-ness and to avoid any interpretation conflicts."

In Martin Fowlers "Presentation Model", 2004 article he defines the Presentation Model as follows:

  • "Presentation Model pulls the state and behavior of the view out into a model class that is part of the presentation. The Presentation Model coordinates with the domain layer and provides an interface to the view that minimizes decision making in the view. The view either stores all its state in the Presentation Model or synchonizes its state with Presentation Model frequently

    Presentation Model may interact with several domain objects, but Presentation Model is not a GUI friendly facade to a specific domain object. Instead it is easier to consider Presentation Model as an abstract of the view that is not dependent on a specific GUI framework. While several views can utilize the same Presentation Model, each view should require only one Presentation Model."

In Martin Fowlers "Gui Architectures", 2006 article he states the following about Application and Presentation Models:

  • "The main difference between using an application model and classic MVC is that we now have an intermediate class between the domain model class (Reader) and the widget - this is the application model class. The widgets don't access the domain objects directly - their model is the application model." - para below figure 9.
  • "Application models allow us to separate behavior and state that's particular to the UI from real domain logic." - 2nd para above Figure 11
  • "Directly updating the widgets like this is not part of Presentation Model, which is why the visual works application model isn't truly a Presentation Model. This need to manipulate the widgets directly was seen by many as a bit of dirty work-around and helped develop the Model-View-Presenter approach".  - para below figure 11.

----------------------------------------------------------------------------------------------------

So why a new name - MVPVM?

"to avoid any interpretation conflicts" - John Gossman

Why is MVPVM necessary?

Short version - for reusability and to have a clear separation of concerns.

Longer version - For all the reasons we evolved to both MVP and MVVM; they are still valid and the same problems are still being solved.  The only difference is that neither can solve the problem alone "easily".

What is the problem being solved?

We are using MEF/PRISM/Unity for the dependency injection of reusable components.  We may have a need to update the widget (view) directly depending on the complexity of the requirement (will one line of code remove the need for complex bindings); we want an extensible framework that will provide us the option.  Let's use the following Visual Studio 2010 use case diagram as the basis of this discussion.

 

Reusable component:  Person View; it is a UserControl that is resolved (instantiated) by the applicable presenter (its relation to the presenters are not shown in diagram to reduce complexity) 

Business rule:  Only the Admin Staff can view the Social Security Number field of the Person View.

Assumption: There will be numerous business rules that will affect the behavior of the view.

THE PROBLEM / SOLUTION SCENARIO

We're starting our sprint and I'm handed the "Add Patient" requirement.  It has a business rule that states only Admin Staff can have access to the SSN field.

I would start by grabbing the available reusable views, user controls created by designer(s), to compose my main screen with.  In our example we'll focus solely on the "Person View".   From the use case diagram I can see that this view has a "Person View model" that it binds to with a provision for the SSN Visibility rule; the Person View will reflect the state of this behavior {collapsed, hidden, visible} effectively showing/hiding the SSN field based on its settings.

Problem:  I can see by the high level use case that I won't be the only presenter that implements this view (and view model).  I can't pollute the ViewModel with any business logic or data access logic.

Solution:  Have my presenter retrieve Patient Records (where personType=Patient) and use it to populate a ComboBox.  The selected index change event of this Combo will notify the presenter when a user has changed and the Presenter will update the PersonViewModel fields as applicable.   The presenter will set the SSNVisibility property to "Visible" and our job with the PersonView and it's presenter is done.  

Could a switch statement for visibility be placed on the PatientViewModel?  Yes and it will also have to know who the actor (parent) is that is consuming it.   Can it retrieve the user record?  Yes and it would require knowledge of the patient primary key (dependency property that retrieves data).   Will every process require data access to get the record?   Probably not - the information could reside in another control.  

In our environment we want to remain loosely coupled to minimize, if not remove, any impact on other users of a view (and its view model).

 


Tags: ,
Categories: MVPVM


Actions: E-mail | Permalink |  Grammar/Typo/Better way? Please let me know

WPFToolkit won't build - Type type exists in both 'assembly1' and 'assembly2'

You'll probably receive the following error if you have Expression Blend (in my case Blend 3) and Visual Studio 2008 installed on your development box and now you are attempting to compile the newly downloaded WPFToolkit.   The problem is that Design and Extensibility references are pointing to two separate folders, one to Visual Studio and the other to Expression Blend.  Since the Project is called VisualStudio.Design (there is also an Expression.Design) it is safe to assume we should have both assemblies pointing to the same folder (Visual Studio); once you do this you will be able to compile with no issues.

  1. Remove the Design Extensibility from references
  2. Add the Extensibility assembly from the Visual Studio folder as indicated by the arrow. 


Tags: ,
Categories:


Actions: E-mail | Permalink |  Grammar/Typo/Better way? Please let me know

Azure - the OutputPath property is not set for this project

I'm not sure what I did to start receiving the error "the OutputPath property is not set for this project" but I suspect it had something to do with me trying to move the EHRDataProviderRole out of the root into a peer folder with the CloudDataProvider (which I just learned is refered to as a Database - I assumed it to be a provider).   This did not go well so I had to recreate the EHRDataProviderRole (back in the root) and then manually attach it to the database again.

The fix actually came from the following link Jamie Laflen MSFT

The only difference was that I had both a Debug and Release configuration.   I also had the following: <OutputPath>bin\Debug\</OutputPath> in both locations.  I changed the "bin" to a period as shown by the two arrows below and then all was well.

Tip: I started by right clicking on the project and selecting "Unload".  I then right clicked on the unloaded project and selected "Edit" as shown in the bottom pane. 


Tags: ,
Categories: Azure


Actions: E-mail | Permalink |  Grammar/Typo/Better way? Please let me know

MSBUILD Copy Task - Upgrading PRISM to Unity 2.0

I'm preparing to start an Electronic Health Record (EHR) Management System (http://EHR.CodePlex.com). To ensure that the application's infrastructure is not obsolete before it is completed I am using the newest techologies available (VS2010, Azure, PRISM, MEF, Unity, etc).  

 

The process that follows will ensure all Third-Party libraries are built and available for the new EHR application. 

 

Unity 2.0 and MEF Beta 2 are not yet released but I'll want to take advantage of their new features.  When they are released I'll have to perform this process again so I'll simplify it now.  Since PRISM is dependent on Unity I'll have to upgrade it so that it is compatible with Unity 2.0.  There are no dependencies on MEF but some changes have to be made because Beta 2 clashes with the version of MEF that resides in .NET 4.0.

 

Once I get all issues resolved so that the various projects and solutions will build I'll use a MSBuild's Copy task to transfer the .DLLs to a unified Binaries folder (c:\_\source\Binaries\Desktop).  My EHR application will reference this Binaries folder as required. 


I could simply place the ItemGroup and Target element directly into the applicable projects but it will be far easier to create a "CopyToBin.Desktop.Targets" file and reference it with one line of code. 

 

 

[CopyToBin.Desktop.Targets] contents follow: 

http://msdn.microsoft.com/en-us/library/3e54c37h.aspx

 

<Project DefaultTargets="AfterBuild"

    xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

  <ItemGroup>

      <AllDll Include=".\**\*.dll" />

    <CopyToBin Include="c:\_\source\Binaries\Desktop"/>

  </ItemGroup>

 

  <Target Name="AfterBuild">

    <Message Text="Processing Copy Task"/>

      <MakeDir Directories="@(CopyToBin)"/>

      <Copy

          SourceFiles="@(AllDll)"

          DestinationFolder="@(CopyToBin)"  />

  </Target>

</Project>

 

I'll place the .targets files in the root of my source folder (where all my third-party source code files reside).

To easily edit the project file simply right click on the project and "Unload" it.  Once unloaded you can right click on the unloaded project and select "Edit" (reference right pane in the image below).   Note that I have a snippet in my Toolbox so that I can simply go to the end of the project file and insert the location of my CopyToBin.Desktop.Targets file.   I'll then "Reload" the project

I'll do the above steps for all of the Unity projects and then build the project.   The resulting DLLs will be copied to the Binaries\Desktop folder where I can now transfer them to the applicable PRISM folder.

PRISM stores the Unity files in it's LIB\Desktop\Unity folder.  You'll have to delete the existing files and copy the newly created Unity DLLs from the Binaries folder to the above PRISM folder (later I'll have a batch file do this).

You are now ready to load the PRISM solution and compile it.  There are only a few differences in Unity 1.2 and Unity 2.0 that will break the build.

  1. Two projects will complain that you require a reference to System.Xaml (assuming you are also upgrading PRISM to VS2010).
  2. There are a few Mock objects that do not implement interfaces correctly - simply implement the interfaces as applicable.
  3. There will be two sections where you'll have to remark out code for objects that no longer exists (this is okay - I verified it with the PRISM team)

You should now be able to build PRISM using the newly compiled Unity 2.0 DLLs.

Note: To build MEF under VS2010 you'll need to follow the few steps I noted in the MEF Developers forum (CLICK HERE)

Once I have all of my projects setup with the new CopyToBin.Desktop.target I can run the batch files I created.   The first, BuildBinaries.cmd, will launch the Visual Studio 2010 command window - its contents follow:

@ECHO OFF
echo Type - and hit ENTER
call %comspec% /k ""C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\vcvarsall.bat"" x86

This batch file is the one provided by Visual Studio which sets up the environment (otherwise the second batch file won't find MSBuild).   It effectively opens a Visual Studio Command window so that you can easily run the second batch file "-.cmd"  (dash - keep it quick/simple). 

The -.cmd batch file is based on folder structures compatible with the third-party applications.  The batch file will change directories to each of the applicable solutions and then run MSBuild against it.

Note: I renamed the folders since doing the above screenshot to ensure I wouldn't have to modify the batch file later (I'll just simply ensure updates use the names used).

The end results is a I have a Binaries folder that is populated with all of the Third-Party DLLs that I will be using for my application.  When new releases are provided I simply have to ensure they compile and then run my batch file again.


Tags: ,
Categories: CompositeWPF | MVC


Actions: E-mail | Permalink |  Grammar/Typo/Better way? Please let me know

Access WCF Service via SSL - Method not found

Assuming you have SSL configured correctly and now want to consume a webservice using https, the following will sum it up nicely.   I beat my head against the wall, as others have, far longer than I cared to - particularly since a few changes were all that were required to make it work.  

The following links were instrumental in creating the Server side Web.Config (they were not complete): 

The arrows point out two places that caused me issues - the first arrow (service1.svc) resulted in a "Method not allowed" error (the second link above did not have the service1.svc as as part of the endpoint address causing the error).

With the following changes made to the default Web.Config. I was then able to add service reference using the https://www.MyService.net/WCFServiceHost/service1.svc url  (note MyService.net is bogus).

Good news is that once you make the following updates to your Server side Web.Config you'll be able to consume your WCF Service using https://youraddress.com/service.svc.

 


Tags:
Categories: WCF


Actions: E-mail | Permalink |  Grammar/Typo/Better way? Please let me know

ASP.NET MVC 2 - Passing Parameters to Silverlight application

In our SDMS application (source code here) we pass parameters from our ASP.NET MVC 2 application to our Silverlight application.   We have a ISystemConfiguration implementation that contains the following information from our ASP.NET MVC 2 Web.Config file (watch window below).  I should note here that we'll have one configuration file to manage our entire application (MVC 2 and Silverlight).


Since config on line 78 is a singleton we have effectively updated our unity container to contain the same parameter object that our MVC 2 application contains (the contents of it's Web.Config file).   See THIS BLOG for more information on the Serialization/Deserialization process.

You'll find in the Library.Demo.ASPMVC2 application of our SDMS solution that we can easily upgrade our MVC 2 application to have Unity support by merely changing the classes that the Global.asax and Controllers derive from using our MVCContrib project (see the LibraryDoc.chm file for details).   Unity allows us to easily support Multi-targeting which means our ASP.NET MVC2, WPF and Silverlight applications all share the same codebase.   Below we see the only Silverlight specific code that was required in our Bootstrapper class was to deserialize the XML String back into our ISystemConfiguration object.   We don't have to do this in WPF (which also shares this code) because it's Unity container already contains it . 

 

Note for PRISM readers: We have to configure our container, and deserialize our parameter object, in GetModuleCatalog because it is called from the UnityBootstrapper's CreateContainer command which occurs prior to CreateShell (see sequence diagram here); we want to ensure we have everything registered before we buildup our view, presenter and call the presenters OnGetModuleCatalog() where the application specific work is done.  

An important part of passing our parameter object is to first make it available to MVC 2.   You'll find that we didn't have to make any changes to a default MVC 2 application (outside of the classes derived from) which indicates that the MVCContrib infrastructure did all of the work of pulling the configuration information out of the Web.Config file into our parameter object for us.  

Below you'll see that we tap into the Master page to call our own html extension SetContainer() on line 57 of the top window in the image below.   Our extension simply takes the parameter object, Serializes it using our object extension, and then passes the resulting xml string into ViewData["InitParams"]



Next we have to get our ViewData["InitParams"] that was set in line 54 above (bottom window) into Silverlight.  The following excerpt from our Library.Demo.ASPMVC2\Views\Home\Silverlight.aspx shows us how this is done:

As a result of the above the Silverlight's App.xaml.cs file will receive the serialized parameter (xml string) in it's Application_Startup method on it's StartupEventArgs (e.InitParams).

private void Application_Startup(object sender, StartupEventArgs e)
{
    BootStrapper bootstrapper = new BootStrapper(new MainPage(), e);
    bootstrapper.Run();
    // The following was moved into MainPage.Show()
    // => this.RootVisual = new MainPage();
}

We'll pass this into our Bootstrapper which it will store it in an args field which brings us full cycle to our first image where our serialized string is retrieved via 

      var para = args.InitParams["InitParams"]

private StartupEventArgs args;
public BootStrapper(object sender, StartupEventArgs e)
{
    view = sender
as IShellView;
    args = e;
}


Tags: , , ,
Categories: MVC | Unity


Actions: E-mail | Permalink |  Grammar/Typo/Better way? Please let me know

Serialize / Deserialize POCO objects - Silverlight and WPF (multi-targeting code)

In our SDMS application (source code here) we have a library that is multi-targeting (shared codebase) for ASP.NET MVC2, Silverlight and WPF, as you'll see in the image below the "only" differences will be the App.xaml and Mainxxxx.xaml files.   To pull this off we give the CAG Bootstrapper a little more work to do.

One of the jobs of the GetModuleCatalog() method in the Bootstrapper is to deserialize our parameter object (more on parameter object in my next blog).   We have to do this in GetModuleCatalog because it is called within the UnityBootstrapper's CreateContainer command which occurs prior to CreateShell (see sequence diagram here).  Note that we do this with the object extension DeserializeObject

The following code will permit us to serialize and deserialize our POCO object.   The code resides in this applications UnityContrib project in the Extensions\ObjectExtension class.

 


Tags: , , , ,
Categories: Enterprise Library | Unity | Visual Studio 2010 | WPF


Actions: E-mail | Permalink |  Grammar/Typo/Better way? Please let me know

Migrating Health Common User Interface from VS2008 to VS2010

Initial efforts to convert/migrate the Microsoft Health Common User Interface from VS2008 to VS2010 failed miserably.   There were so many errors it wasn't real apparent where to start (after a few hours I gave up).  

After having converted a number of other programs I found there are a few consistent problems that once resolved move things along nicely.   They revolve around "Treat warnings as errors", Code Analysis and missing references.  If one of these occurs on an assembly that multiple projects have references to the error rate is compounded (making it harder to find).

After make the following few changes I found the Health Common User Interface compiles under VS2010:

Microsoft.Cui.SamplePages --- Add reference to System.Net

Microsoft.Cui.SampleWinForm --- Select None for "Treat warning as errors"

 

Uncheck "Enable Code Analysis on Build" for the following files:

  • Microsoft.Cui.ShowcaseControls
  • Microsoft.Cui.SamplePages
  • Microsoft.Cui.Roadmap
  • Microsoft.Cui.IsvDataProvider
  • Microsoft.Cui.Data
  • Microsoft.Cui.Controls

You'll want to remove the following three Unit Test

  • NhsCui.Toolkit.Test
  • NhsCui.Toolkit.Web.Test
  • NhsCui.Toolkit.WinForms.Test

If you don't you'll be prompted to convert the solution each time you load it as it will attempt to convert these and will fail.

Once the steps are complied with you can compile successfully!


Tags: , ,
Categories: Visual Studio 2010 | Health


Actions: E-mail | Permalink |  Grammar/Typo/Better way? Please let me know

Unable to load DLL 'sqlceme35.dll': The specified module could not be found

If you are running x64 then you'll need to install the following:

Microsoft SQL Server Compact 3.5 Service Pack 1 and Synchronization Services for ADO.NET version 1.0 Service Pack 1 for Windows Desktop

This threw me for a loop because my form would simply load without any data.  As a result I would be stepping through code (stepping over my data layer) and my form would activate with nothing in it.   Drilling down deeper I discovered the view would activate on line 228 below:

  222 public DataBindingList<T> ExecuteList<T>(string sqlCommand, ListDelegate<T> listDelegate)
  223 {
  224     try
  225     {
  226         DataBindingList<T> dataList = new DataBindingList<T>();
  227 
  228         using (SqlCeConnection conn = new SqlCeConnection(ConnString))
  229         {

This had me scratching my head wondering if some of the Async processes were messing with my debugger.   Since it worked on the 32-bit development box (which I don't currently have with me) I had no reason to suspect foul play on my x64 development box - this misconception resullted in much head-banging time.   

I should have payed attention to this error earlier, as it would reveal itself, but more towards the end of the debugging session and not on line 228 above; it would reveal itself as a messagebox not attached to any particular line of code.  I wrongly had suspected corrupt dlls as I am dabbling in a multi-targeting application (code shared between mobile and winforms).   It wasn't until I placed the try/catch (shown above) that the error revealed itself within the scope of the process.

Hopefully you'll hit this blog before the others because Head-banging occurred longer than it needed as I ended up chasing rouge blogs on this topic that had me going in circles until I figured out they were dead-ends. In the end the fix was quite simple - simply install SP1 and everything started working. 


Tags: , , ,
Categories: Compact Framework | HeadBanger | SQL Server 2008


Actions: E-mail | Permalink |  Grammar/Typo/Better way? Please let me know

Creating Desktop Skins in WPF for PRISM Desktop

Based on Creating a Skinned User Interface in WPF (excellent blog on the topic!), adapted for PRISM and dependency injection.

Creating SKINS in WPF Desktop for PRISM is relatively easy.   The first thing we want to do is create our styles.  Note in my WPF Project (Demo.WPF second arrow) that I use an Assets folder - this is to be consistent with the .NET RIA Services auto generated styles (top arrow). 

My Styles.xaml file will contain styles common to all skins - in this case I have a StylesDefault (Blue) and StylesGreen (Green).

With the Styles (skins) out of the way we can start wiring up PRISM.   We do this by Loading both of the Stylesxxxxx.xaml files and registering them in our Container - the type will be ResourceDictionary with the names set to "Blue" and Default".  The code for the Windows.cs (Shell) follows:

   22 /// <summary>
   23 /// Interaction logic for Window1.xaml
   24 /// </summary>
   25 public partial class Window1 : Window
   26 {
   27     /// <summary>
   28     /// Load resources delegate
   29     /// </summary>
   30     public delegate void LoadResourcesDelegate();
   31 
   32     [Dependency]
   33     public IUnityContainer Container { get; set; }
   34 
   35     [Dependency]
   36     public ILoggerFacade Logger { get; set; }
   37 
   38     public Window1()
   39     {
   40         InitializeComponent();
   41 
   42         this.Loaded += new RoutedEventHandler(Window1_Loaded);
   43     }
   44 
   45     void Window1_Loaded(object sender, RoutedEventArgs e)
   46     {
   47         // Load asynchronously
   48         Dispatcher.BeginInvoke(new LoadResourcesDelegate(LoadResources));
   49     }
   50 
   51     private void LoadResources()
   52     {
   53         // Load secondary styles when the XAML Build Action
   54         // is set to "Content"
   55         Container.RegisterInstance<ResourceDictionary>("Blue",
   56             (ResourceDictionary)Application.LoadComponent(
   57             new Uri("Assets/StylesGreen.xaml", UriKind.Relative)));
   58 
   59         // Load default styles (so we can restore).  The XAML
   60         // Build Action is "Page" - required because the App.XAML
   61         // shows this as the MergeDictionary
   62         Container.RegisterInstance<ResourceDictionary>("Default", 
   63             (ResourceDictionary) Application.LoadComponent(
   64             new Uri("Assets/StylesDefault.xaml", UriKind.Relative)));
   65     }
   66 }

Note how we are loading the skins on their own thread (line 48).  We don't have an immediate need for the skins since our App.xaml code is loading the StylesDefault.xaml resources for it's own use (line 8 below); we don't have to affect performance by loading skins on the UI thread.

    1 <Application x:Class="Demo.WPF.App"
    2     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    3     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    4 
    5     <Application.Resources>
    6         <ResourceDictionary>
    7             <ResourceDictionary.MergedDictionaries>
    8                 <ResourceDictionary Source="Assets/StylesDefault.xaml"/>
    9             </ResourceDictionary.MergedDictionaries>
   10         </ResourceDictionary>
   11     </Application.Resources>
   12 
   13 </Application>

At this point we have successfully loaded our skins into our Unity Container and are ready to wire-up a view to utilize them.

We'll now go to our Modules folder and select the ModuleClient.WPF (WPF for Desktop) and wire-up our MenuRegion's view (MainMenu) to process the available skins.  Note below we create a context menu with two menu options "Blue" (default) and "Green".

Our code behind will handle the ContextMenu_Click by using the EventAggregator to raise a MenuItem click event (ClickEventArgs handles MenuItem, Button and CheckBox clicks).   We're done on the UI side - now we'll turn our attention to the Presenter which will handle the click event.

The PrismContrib.xxxx assemblies abstract much of the framework from us (KISS).   Within these libraries resides the PresenterBase<T> which wires everything up on lines 27-32.   Once wired-up we have virtual methods that we can override to simplify our lives, e.g., below we override the OnButtonClickEventHandler to handle our MenuItem click.

The magic happens on lines 45 and 50 as applicable.  They utilize a Extensions class which extends the IUnityContainer interface.  Note how we can use the same code (line 29 below) for both Silverlight and WPF Desktop by using the "using" statements on lines 5 and 7 (below).

Line 26 will resolve our ResourceDictionary by the name effectively giving us a reference to the applicable skin dictionary.

On line 29 we get a reference to the current resource and clear it on line 33.   All that remains is to add our updated MergedDictionary and walla!  We have successfully applied a new skin.

    1 using System.Windows;
    2 using Microsoft.Practices.Unity;
    3 
    4 #if SILVERLIGHT
    5 using wpfCollection = System.Windows.PresentationFrameworkCollection<System.Windows.ResourceDictionary>;
    6 #else
    7 using wpfCollection = System.Collections.ObjectModel.Collection<System.Windows.ResourceDictionary>;
    8 #endif
    9 
   10 
   11 using System.Collections.ObjectModel;namespace Infrastructure.Resource
   12 {
   13     /// <summary>
   14     /// Resource Utility
   15     /// </summary>
   16     public static class ResourceUtil
   17     {
   18         /// <summary>
   19         /// Sets the skin.
   20         /// </summary>
   21         /// <param name="rd">The rd.</param>
   22         /// <param name="skinName">Name of the skin.</param>
   23         public static void SetSkin(this IUnityContainer container, string skinName)
   24         {
   25 #if !WinForm
   26             ResourceDictionary skin =
   27                 container.Resolve<ResourceDictionary>(skinName);
   28 
   29             wpfCollection mergedDicts =
   30                 Application.Current.Resources.MergedDictionaries;
   31 
   32             if (mergedDicts.Count > 0)
   33                 mergedDicts.Clear();
   34 
   35             // Apply the selected skin so that all elements in the
   36             // application will honor the new look and feel.
   37             mergedDicts.Add(skin);
   38 #endif
   39 
   40         }
   41     }
   42 }

Right clicking on the Menu bar (top sky-blue region) reveals our context menu. 


Tags: , ,
Categories: WPF


Actions: E-mail | Permalink |  Grammar/Typo/Better way? Please let me know