Click here to Skip to main content
Email Password   helpLost your password?

Contents

Introduction

I have been working with WPF both for fun and for a job for about 2 years now, and have followed the evolution of WPF patterns and am luckly enough to be close to some of the biggest movers and shakers in the WPF world. It seems to be the majority of the smart WPF folks out there have settled for a pattern called ModelView-ViewModel (MVVM for short). This pattern allows a view to be abstracted to a ViewModel, where by the View is passive an simply binds (using WPF Binding) to associated ViewModel(s). The beuty of the pattern is that if you have done the whole MVVM thing correctly you should be able to test your UI without the need for the actual UI. You can test the ViewModels. In fact you should be able to examine the ViewModels for an application without even seeing the View(s) and know what is going on.

Now the holy grail of MVVM WPF development, would be to have no code behind at all, and have the View simply bind to a ViewModel. Unfortunately there are always some things that prevent the beauty of this. For example how does a ViewModel show a MessageBox, or disable a control. It is quite a challenge, but luckily WPF provides such help, by the use of the routed commanding, which allows ViewModels to have commands which the UI controls that support the ICommand interface to run. So what about MessageBoxes/Dialogs (such as OpenFileDialog/SaveFileDialog etc etc) and things like that, how does a ViewModel deal with that.

Well it is not that easy using WPF out of the box, you need to put some effort into it, if you do not wich to end up with a slew of untestable code behind. Believe me, this can easily happen, and still happens to me on occassions, and it makes me really mad, as I am breaking what I consider to be a nice pattern. GRRR

So you either have to do quite a birt of custom work yourelf, or maybe let someone else put the efforts in and use their work. As luck would have it one of my WPF Disciples buddies Bill Kempf has done some nice work on a cool MVVM WPF framework that he is calling Onyx. Bill is not finished with the Onyx framework, and it is evolving, but I just wanted to give it some exposure, and see what it is capable of right now.

So in this article I am going to talk about what Bills rather nifty MVVM WPF framework can do. So if this interests you please read on.

Show It

Ok so what does the demo app look like, well I did want to do something quite dull, so it would be clear which parts were the Onyx framework, and which bits were not, but I just couldn't help myself, and had to come up with something that pleased me. So I came up with a little 3D app that lets you view some of the WPF Disciples in 3D space, and flip the 3D to show a larger image of your chosen WPF Disciples, and it will also show you their blog using the .NET 3.5 WebBrowser control.

So here is a screen shot or 6

STEP1 : Click the main button at the top

STEP2 : Pick the Disciples.xml file from the DEBUG folder of where you downloaded the code to

STEP3 : Enjoy the 3D Animation

STEP4 : Pick someone

STEP5 : Wait for slide in with the chosen persons blog

STEP6 : Read their blog in you want to.

 

Now I am acutely aware that this is a pretty useless app, and it is, I am under no illusions about that. The demo app is not the point, it is simple demo of what is really important, and that is the actual Onyx framework, so this is what this article is all about really. I think it is my job to try and spread the word about something that will help you, and I think the Onyx framework will help you. Good one Bill. Ok so now let's get on with the main thrust of the article shall we.

PreRequisites

The only thing you really need to run this code is the Onyx Dll, and .NET3.5 SP1. Which is included in the downloaded code, but if you want to dive into the guts of it you will also needs Bills other opensource project Specificity, which Onyx uses for testing. That is also worth a look. It is not often I am bowled over by someones code, but I really like both of Bills open source projects.

Getting Into The Onyx Framework

This section will go into how Onyx helps you out in your day to day WPF MVVM development. It it worth pointing out that I am not the author of Onyx, so anything I mention here is by way of me looking through the source code that Bill wrote.

Setting Up The ViewModel

One requirement Onyx does have is that you MUST associate a particular View with its associated ViewModel using an Attached DependencyProperty. This is easily done as follows:

<Window x:Class="FlipTile3D.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:onyx="http://schemas.onyx.com/2009/fx/presentation"
    xmlns:local="clr-namespace:FlipTile3D"
    onyx:View.Model="{x:Type local:MainWindowViewModel}">

So what is actually happening here. Well quite a lot actually. The onyx:View.Model attached DP is declared as such

public static readonly DependencyProperty ModelProperty =
    DependencyProperty.RegisterAttached(
        "Model",
        typeof(object),
        typeof(View),
        new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.Inherits, OnModelChanged, CoerceModel));
		
/// <summary>
/// Called when the <see cref="ModelProperty"/> attached dependency property is 
/// changed on the <paramref name="source"/>.
/// </summary>
/// <param name="source">The source object.</param>
/// <param name="e">The <see cref="DependencyPropertyChangedEventArgs"/> 
/// instance containing the event data.</param>
private static void OnModelChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
    if (source.AsFrameworkObject() == null)
    {
        return;
    }

    object model = e.NewValue;
    if (model == null)
    {
        return;
    }

    if (DependencyPropertyHelper.GetValueSource(source, ModelProperty).
	        BaseValueSource == BaseValueSource.Local &&
        DependencyPropertyHelper.GetValueSource(source, FrameworkElement.DataContextProperty).
	        BaseValueSource != BaseValueSource.Local)
    {
        source.SetValue(FrameworkElement.DataContextProperty, model);
    }

}		

What Onyx does behind the scenes when you set the onyx:View.Model Attached DP changes is that the associated View (the source of the onyx:View.Model Attached DP) will have its DataContext property set to the ViewModel that is set the onyx:View.Model Attached DP value to.

So this one property is enough to set up all the binding between the View and its associated ViewModel.

Commanding Support

Onyx makes use of RoutedCommands which are as standard WPF mechanisms. What RoutedCommands allow you to do is seperate the command and the implementation. For example it is possible to have a ViewModel expose RoutedCommands which are then used to bind to by the View, and the command implementation will then be done in the ViewModel, again allowing for clean testable code.

Here is how Onyx works with commands, all you have to do is expose a Command and define the command implementation within your ViewModel:

public class MainWindowViewModel : ViewModel
{

    #region Ctor
    /// <summary>
    /// Initializes a new instance of the <see cref="MainWindowViewModel"/> class.
    /// </summary>
    /// <param name="view">The view to associate with this instance.</param>
    public MainWindowViewModel(View view)
        : base(view)
    {
        this.CommandBindings.Add(new CommandBinding(OpenDisciplesXMLFileCommand,
            this.OpenDisciplesXMLFileExecuted,
            new CanExecuteRoutedEventHandler(this.OpenDisciplesXMLFileCanExecute)));
    }
    #endregion

    #region Commands
    /// <summary>
    /// The OpenDisciplesXMLFile routed command.
    /// </summary>
    private static readonly RoutedUICommand cmdOpenDisciplesXMLFile =
        new System.Windows.Input.RoutedUICommand(
            "Open Disciples XML File",
            "OpenDisciplesXMLFileCommand",
            typeof(MainWindowViewModel));

    /// <summary>
    /// Gets the OpenDisciplesXMLFile command.
    /// </summary>
    /// <value>The OpenDisciplesXMLFile command.</value>
    public static RoutedUICommand OpenDisciplesXMLFileCommand
    {
        get { return cmdOpenDisciplesXMLFile; }
    }


    /// <summary>
    /// Determines if the OpenDisciplesXMLFileCommand can Execute
    /// </summary>
    private void OpenDisciplesXMLFileCanExecute(object sender, CanExecuteRoutedEventArgs e)
    {
	//Allow command to run
        e.CanExecute = true;
    }

    /// <summary>
    /// Handles the execution of the <see cref="MainWindow.OpenDisciplesXMLFile"/> command.
    /// </summary>
    /// <param name="sender">The sender.</param>
    /// <param name="args">The <see cref="System.Windows.RoutedEventArgs"/> 
    ///	instance containing the event data.</param>
    private void OpenDisciplesXMLFileExecuted(object sender, RoutedEventArgs args)
    {
	//DO SOMETHING
	//DO SOMETHING
    }

}

So all we then need to do is wire up the View to the exposed ViewModel commands. Here is an example.

<Button x:Name="btnOpenFile" 
    Command="local:MainWindowViewModel.OpenDisciplesXMLFileCommand"/>

That is it. This means the ViewModel has the actual command and its implementation, and the View simply binds to these exposed commands. This also enables Unit testing, where by the unit tests can execute the ViewModel commands something like:

ViewModel x = new ViewModel();
x.SomeCommand.Execute();

ServiceLocator

At its heart Onyx is a ServiceProvider. Internally it registers new services, which can then be fetch using the Microsoft System.IServiceProvider interface, which is defined as follows:

namespace System
{
    // Summary:
    //     Defines a mechanism for retrieving a service object; that is, an object that
    //     provides custom support to other objects.
    public interface IServiceProvider
    {
        // Summary:
        //     Gets the service object of the specified type.
        //
        // Parameters:
        //   serviceType:
        //     An object that specifies the type of service object to get.
        //
        // Returns:
        //     A service object of type serviceType.  -or- null if there is no service object
        //     of type serviceType.
        object GetService(Type serviceType);
    }
}

So what happens is that the Onyx View object is used to expose its services, which translated internally to calling the System.IServiceProvider.GetService() method. Here is how this is done within the Onyx View object

/// <summary>
/// Gets the service object of the specified type.
/// </summary>
/// <param name="serviceType">An object that specifies the type of service object to get.</param>
/// <returns>
/// A service object of type <paramref name="serviceType"/>.
/// -or-
/// null if there is no service object of type <paramref name="serviceType"/>.
/// </returns>
/// <exception cref="ObjectDisposedException">This instance has been disposed.</exception>
public object GetService(Type serviceType)
{
    if (serviceType == null)
    {
        throw new ArgumentNullException(Reflect.GetField(() => serviceType).Name);
    }

    return this.serviceContainer.GetService(serviceType);
}

So now we know that we can obtain services for a View. So what sort of services does Onyx provide. Well in order to understand that we need to look into how the Onyx View object gets created in the first place, here is what happens:

/// <summary>
/// Gets the view.
/// </summary>
/// <param name="source">The source.</param>
/// <returns>
/// The <see cref="View"/> associated with the <paramref name="source"/>.
/// </returns>
/// <remarks>
/// If there currently is no <see cref="View"/> associated with the <paramref name="source"/>
/// then a new <see cref="View"/> is created.
/// </remarks>
/// <exception cref="ArgumentNullException"><paramref name="source"/> is null.</exception>
public static View GetView(DependencyObject source)
{
    if (source == null)
    {
        throw new ArgumentNullException(Reflect.GetField(() => source).Name);
    }

    View view = (View)source.GetValue(ViewProperty);
    if (view == null)
    {
        view = new View(source);
        source.SetValue(ViewProperty, view);
        IServiceRegistry container = View.ServiceRegistryActivator.CreateServiceRegistry();
        View.ViewCreated(view, new ViewCreatedEventArgs(view.ViewElement, container));
        container.RegisterServicesFor(view.ViewElement);
        view.serviceContainer = container.CreateServiceProvider();
    }

    return view;
}

It can be seen that this method 1st examines the Onyx View object to see if it needs to be instantiated, and if the Onyx View object does need creating, that is when all the services are registered. So this brings up back to the question of what services are registered and will be available. The key to this lies in looking at the way the ViewCreated event works. Lets see the resultant code that is run when the Onyx View is created.

/// <summary>
/// Called when a <see cref="View"/> is created.
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The <see cref="ViewCreatedEventArgs"/> 
/// instance containing the event data.</param>
/// <remarks>
/// Adds common framework services to the <see cref="View"/>.
/// </remarks>
private static void OnViewCreated(object sender, ViewCreatedEventArgs e)
{
    UIElement uiElement = e.ViewElement as UIElement;
    if (uiElement != null)
    {
        UIElementServices services = new UIElementServices(uiElement);
        services.AddServices(e.ServiceContainer);
    }

    IFrameworkObject frameworkObject = e.ViewElement.AsFrameworkObject();
    if (frameworkObject != null)
    {
        CommonFrameworkElementServices services = 
          new CommonFrameworkElementServices(frameworkObject);
        services.AddServices(e.ServiceContainer);
    }
}

It can be seen that Onyx uses a UIElementServices object, and a CommonFrameworkElementServices object to add services from. So let us examine those 2 Onyx objects.

UIElementServices

internal class UIElementServices : ServicesBase, ICommandTarget, 
        IFocusSuggested, IDisplayMessage, ICommonDialogProvider

This Onyx object provides the following services:

CommonFrameworkElementServices

internal class CommonFrameworkElementServices : ServicesBase, IElementLifetime

This Onyx object provides the following services:

For now all that is important to know is that the Onyx View object exposes services, and that you are able to use these services from a ViewModel. I will now carry on and show you how to use some typical Onyx services.

MessageBox (Service)

One of the things that catches people when they are trying to create a typical WPF MVVM app is that they get part way there, and then something goes wrong in the ViewModel and they need to tell the user about this. Oh crap. This typically means showing a MessageBox to the user. So you could either create a new MessageBox showing interface on the View and get the ViewModel to talk to the View using this new interface, Or you ingore the fact that your ViewModel should be UI agnostic, and just let it reference the nessecary Dlls, and have the ViewModel show a MessageBox to the user.

Both these approaches do work, but the 2nd one, just doesn't feel right. The specific interface approach sounds nicer. Luckily Onyx caters for this approach quite nicely.

To show a MessageBox to the user from the ViewModel we simply obtain the correct type of service (View implemented interface), and do something with this obtained service within our ViewModel.

Onyx exposes a MessageBox service as IDisplayMessage, which can be used as follows:

var messageBoxService = this.View.GetService<IDisplayMessage>();
messageBoxService.ShowMessage("Welcome to a small Onyx Demo.\r\n\r\n " +
	"Please pick the Disciples.xml file from the debug directory","Onyx demo",
	MessageBoxButton.OK,MessageBoxImage.Information);
	

Simple, and clean.

Common Dialogs (Service)

Showing common dialogs such as OpenFileDialog/SaveFileDialog etc etc, are another real concern, that could lead to problems, as these are typically done in code behind, which is something we are trying to avoid.

Again Onyx handles these with ease. Lets have a look at what Onyx caters for this. Within Onyx there is a class called UIElementServices that is used to provide UI services we touched on this earlier), recall it looked like this:

internal class UIElementServices : ServicesBase, ICommandTarget, 
        IFocusSuggested, IDisplayMessage, ICommonDialogProvider

It can be seen that one of the Interfaces that the UIElementServices class implements is ICommonDialogProvider which looks like this:

namespace Onyx.Windows
{
    /// <summary>
    /// Provides factory methods for creating common dialogs.
    /// </summary>
    public interface ICommonDialogProvider
    {
        /// <summary>
        /// Creates the open file dialog.
        /// </summary>
        /// <returns>
        /// A newly created <see cref="IOpenFileDialog"/> instance.
        /// </returns>
        IOpenFileDialog CreateOpenFileDialog();

        /// <summary>
        /// Creates the save file dialog.
        /// </summary>
        /// <returns>
        /// A newly created <see cref="ISaveFileDialog"/> instance.
        /// </returns>
        ISaveFileDialog CreateSaveFileDialog();
    }
}

So it can be seen that Onyx currently caters for OpenFileDialog and SaveFileDialog. So how do we use a OpenFileDialog and SaveFileDialog from our ViewModel, well it is as easy as this:

var openFileDialogService = this.View.GetService<ICommonDialogProvider>().CreateOpenFileDialog();
openFileDialogService.InitialDirectory = System.IO.Directory.GetCurrentDirectory();
openFileDialogService.Filter = "XML files (*.xml)|*.xml;|All files (*.*)|*.*";


if (openFileDialogService.ShowDialog() == true)
{
    string fileName = openFileDialogService.FileName;
    ....
    ....
    ....
    ....
}

INotifyPropertyChanged Goodness

A typical ViewModel will contain properties that the View will bind to. In order for a View to receive changes in bound values the ViewModel will typically implment the INotifyPropertyChanged interface (When implemented, the interface communicates to a bound control the property changes on a object) , which is usually implmented like this:

// This class implements a simple customer type 
// that implements the IPropertyChange interface.
public class DemoCustomer : INotifyPropertyChanged
{

    private string customerName = String.Empty;

    public event PropertyChangedEventHandler PropertyChanged;


    private void NotifyPropertyChanged(String propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }


    // The constructor is private to enforce the factory pattern.
    private DemoCustomer()
    {
        companyNameValue = "no data";
    }


    // This is the public factory method.
    public static DemoCustomer CreateNewCustomer()
    {
        return new DemoCustomer();
    }


    public string CompanyName
    {
        get {return this.companyNameValue;}

        set
        {
            if (value != this.companyNameValue)
            {
                this.companyNameValue = value;
                NotifyPropertyChanged("CompanyName");
            }
        }
    }

}

Where binding changes are notified by the use of the INotifyPropertyChanged interfaces NotifyPropertyChanged(String propertyName) method. The problem with the standard implementation is that it uses magic strings in the code. Now there is a potential for these magical strings to get missed when refactoring, or the magic strings could be wrongly cased, or incorrectly spelt, which would mean that the associated bound object would miss a notification of a change in the bound object properties.

Lately there has been many people working with creating static reflection lookups, which is typically done using LINQ Expression trees. Onyx is no different, it also uses LINQ Expression trees to assist with the INotifyPropertyChanged interface. What this means is that there are no magic strings. Let us see an example:

public List DiscipleInfos
{
    get { return discipleInfos; }
    set
    {
        this.discipleInfos = value;
        OnPropertyChanged(() => DiscipleInfos);
    }
}

Can you see that we are using a LINQ Expression here (which if you dived into Onyx you would see is declared as an Expression<Func<T>> expression. Here is what happens within Onyx. As previously stated Onyx uses a LINQ Expression tree, and does some static reflection on that to work out the property that is forming part of the Expression tree, and then it fires the normal INotifyPropertyChanged interface NotifyPropertyChanged(String propertyName) method.

/// <summary>
/// Called when a property changes, using static reflection to determine the property.
/// </summary>
/// <typeparam name="T">The type of the property.</typeparam>
/// <param name="expression">Expression that accesses the property that changed.</param>
protected void OnPropertyChanged<T>(Expression<Func<T>> expression)
{
    this.OnPropertyChanged(Reflect.GetProperty(expression).Name);
}

/// <summary>
/// Called when the property <paramref name="propertyName"/> has changed.
/// </summary>
/// <param name="propertyName">Name of the property.</param>
protected virtual void OnPropertyChanged(string propertyName)
{
    this.VerifyPropertyName(propertyName);
    this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}

Can you in the protected void OnPropertyChanged<T>(Expression<Func<T>> expression) method that there is a Reflect.GetProperty() method call. Let us examine that in a bit more detail. This is as follows:

//-----------------------------------------------------------------------------
// <copyright file="Reflect.cs" company="William E. Kempf">
//     Copyright (c) William E. Kempf.
// </copyright>
//-----------------------------------------------------------------------------

namespace Onyx.Reflection
{
    using System;
    using System.Linq.Expressions;
    using System.Reflection;

    /// <summary>
    /// Performs static reflection.
    /// </summary>
    public static class Reflect
    {
        /// <summary>
        /// Gets a <see cref="MemberInfo"/> using static reflection.
        /// </summary>
        /// <param name="expression">An expression that uses member access.</param>
        /// <returns>
        /// A <see cref="MemberInfo"/> instance for the member accessed in the 
        /// <paramref name="expression"/>.
        /// </returns>
        public static MemberInfo GetMember(Expression<Action> expression)
        {
            if (expression == null)
            {
                throw new ArgumentNullException(
                    GetMember(() => expression).Name);
            }

            return GetMemberInfo(expression as LambdaExpression);
        }

        /// <summary>
        /// Gets a <see cref="MemberInfo"/> using static reflection.
        /// </summary>
        /// <typeparam name="T">The type of the member accessed in the 
        /// <paramref name="expression"/>.</typeparam>
        /// <param name="expression">An expression that uses member access.</param>
        /// <returns>
        /// A <see cref="MemberInfo"/> instance for the member accessed in the 
        /// <paramref name="expression"/>.
        /// </returns>
        public static MemberInfo GetMember<T>(Expression<Func<T>> expression)
        {
            if (expression == null)
            {
                throw new ArgumentNullException(
                    GetMember(() => expression).Name);
            }

            return GetMemberInfo(expression as LambdaExpression);
        }

        /// <summary>
        /// Gets a <see cref="MethodInfo"/> using static reflection.
        /// </summary>
        /// <param name="expression">An expression that uses member access.</param>
        /// <returns>
        /// A <see cref="MethodInfo"/> instance for the member accessed in the 
        /// <paramref name="expression"/>.
        /// </returns>
        public static MethodInfo GetMethod(Expression<Action> expression)
        {
            MethodInfo method = GetMember(expression) as MethodInfo;
            if (method == null)
            {
                throw new ArgumentException(
                    "Not a method call expression", GetMember(() => expression).Name);
            }

            return method;
        }

        /// <summary>
        /// Gets a <see cref="PropertyInfo"/> using static reflection.
        /// </summary>
        /// <typeparam name="T">The type of the member accessed in the 
        /// <paramref name="expression"/>.</typeparam>
        /// <param name="expression">An expression that uses member access.</param>
        /// <returns>
        /// A <see cref="PropertyInfo"/> instance for the member accessed in the 
        /// <paramref name="expression"/>.
        /// </returns>
        public static PropertyInfo GetProperty<T>(Expression<Func<T>> expression)
        {
            PropertyInfo property = GetMember(expression) as PropertyInfo;
            if (property == null)
            {
                throw new ArgumentException(
                    "Not a property expression", GetMember(() => expression).Name);
            }

            return property;
        }

        /// <summary>
        /// Gets a <see cref="FieldInfo"/> using static reflection.
        /// </summary>
        /// <typeparam name="T">The type of the member accessed in the 
        /// <paramref name="expression"/>.</typeparam>
        /// <param name="expression">An expression that uses member access.</param>
        /// <returns>
        /// A <see cref="FieldInfo"/> instance for the member accessed in the 
        /// <paramref name="expression"/>.
        /// </returns>
        public static FieldInfo GetField<T>(Expression<Func<T>> expression)
        {
            FieldInfo field = GetMember(expression) as FieldInfo;
            if (field == null)
            {
                throw new ArgumentException(
                    "Not a field expression", GetMember(() => expression).Name);
            }

            return field;
        }

        internal static MemberInfo GetMemberInfo(LambdaExpression lambda)
        {
            if (lambda == null)
            {
                throw new ArgumentNullException(
                    GetMember(() => lambda).Name);
            }

            MemberExpression memberExpression = null;
            if (lambda.Body.NodeType == ExpressionType.Convert)
            {
                memberExpression = ((UnaryExpression)lambda.Body).Operand as MemberExpression;
            }
            else if (lambda.Body.NodeType == ExpressionType.MemberAccess)
            {
                memberExpression = lambda.Body as MemberExpression;
            }
            else if (lambda.Body.NodeType == ExpressionType.Call)
            {
                return ((MethodCallExpression)lambda.Body).Method;
            }

            if (memberExpression == null)
            {
                throw new ArgumentException(
                    "Not a member access", GetMember(() => lambda).Name);
            }

            return memberExpression.Member;
        }
    }
}

Bill talks a bit more about this over at his blog : Reflecting on Code : Static reflection. Its all the rage.

Listening To Typical View Methods

One of the very nice things that Onyx provides is that is exposes some nice methods (which are raised as results of the associated View raising its own events) that one would expect to find on a typical Window, such as

Where Oyxn does the following to create these methods

protected ViewModel(View view)
{
    this.view = view;

    IElementLifetime lifetime = this.view.GetService<IElementLifetime>();
    if (lifetime != null)
    {

        lifetime.Initialized += (s, e) => this.OnViewInitialized();
        lifetime.Unloaded += (s, e) => this.OnViewUnloaded();
        lifetime.Loaded += (s, e) => this.OnViewLoaded();
    }
}

IElementLifetime is just an interface that Onyx uses to make a further abstraction for the View to be only these 3 methods.

So what does this mean to you if you are working in your ViewModel. Well it means that we can simply override these methods and do things within ouor ViewModel when one of these events occurs within the View that the ViewModel is abstracting.

Here is an example, that the attached demo code uses :

/// <summary>
/// Occurs when the associated View is loaded
/// </summary>
protected override void OnViewLoaded()
{
    base.OnViewLoaded();
    //run messagebox on Background give view some time for view to render properly
    base.View.ViewElement.Dispatcher.BeginInvoke((Action)delegate
    {
        var messageBoxService = this.View.GetService<IDisplayMessage>();
        messageBoxService.ShowMessage("Welcome to a small Onyx Demo.\r\n\r\n " +
            "Please pick the Disciples.xml file from the debug directory","Onyx demo",
            MessageBoxButton.OK,MessageBoxImage.Information);

    }, DispatcherPriority.Background);
}

In this section of code, I am using Onyx to locate a service called IDisplayMessage which can be used to display a MessageBox to the user via the associated View. Also of note here is that I am using the Dispatcher associated with the View, which again is exposed very easily in Onyx, you simply have to use the base.View.ViewElement.Dispatcher to obtain the correct Views Dispatcher.

Future Onyx Work

I mentioned to Bill that I thought there was something missing which was the ability to open new actual WPF windows from a ViewModel and the whole issue of navigation. Bill is aware of this, but it is a complex subject, however it is something that Bill mentioned would appear in Onyx within the future. So all I can about that one is stay tuned and keep checking the Onyx codeplex site.

That's It Folks

I would just like to close by stating that if you are doing an sort of WPF development, it will be well worth your while looking into Onyx, which as I stated is still a work in progress, but it is a good work in progress, that I have found to be very intuitive, and it actually caters for most nasties that would otherwise be a headache. Nice one Bill, it is really very very cool. If I wasn't so far into my current WPF project at work, I would be using this framework for sure, and certainly will for future projects.

Oh and another thing, of late the WPF Disciples have all been having heavy discussions about a new codeplex site for the commuity. This site is available at :

http://www.codeplex.com/mvvmref

Here is what this site will be aiming to cover

M-V-VM Reference Application

Project Description
Community created reference application for M-V-VM frameworks to use for demonstration purposes, similar in concept to Pet Shop for web frameworks.

This project isn't going to give any solutions for following the Model-View-ViewModel pattern.

What it is going to do is provide a sample application that does "typical" things that cause problems when following an M-V-VM architecture. Then, given this reference application and supporting documentation, other implementations can be made using specific M-V-VM libraries.

Those other implementations won't be part of this project, though we'll provide links to them.

Instead, they'll be reference implementations provided by the specific M-V-VM library's author(s) or interested third parties. This makes it easier on the library developers, since they don't have to dream up their own reference implementation sample. It benefits the users, since they can compare various implementations using "competing" libraries, and they can learn "best practices" of how to use a given library. Again, if you're at all familiar, this is "Pet Shop" for WPF and Silverlight. 

 

Check it out


Useful links

Onyx : By Bill Kempf

MVVM Exemplar / Reference : WPF Disciples

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
GeneralGreat Article
Dan Wts
10:46 18 Sep '09  
This is a excellent article. Thanks. 5/5
GeneralRe: Great Article
Sacha Barber
20:43 18 Sep '09  
Thanks

Sacha Barber
  • Microsoft Visual C# MVP 2008/2009
  • Codeproject MVP 2008/2009
Your best friend is you.
I'm my best friend too. We share the same views, and hardly ever argue

My Blog : sachabarber.net

GeneralNavigation for Onyx framework
Sam Safonov
1:41 6 Sep '09  
Good day Sacha.
Thank you for your articles.
Could approach presented in Navigation in MVVM applications (Onyx) be acceptable solution of Navigation issue for Onyx framework.
GeneralIs this correct?
jh1111
8:04 22 Jul '09  
In "Setting Up The ViewModel" section,

....
onyx:View.Model="{x:Type local:MainWindowViewModel}"

Here the type of MainWindowViewModel is assigned to onyx:View.Model.


I thought in

private static void OnModelChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
object model = e.NewValue;
...........
source.SetValue(FrameworkElement.DataContextProperty, model);

}

You should create an instance of MainWindowViewModel, and then assign it to DataContext.

Did I miss something here?

Thanks
GeneralRe: Is this correct?
Sacha Barber
22:46 22 Jul '09  
hey. thats is exactly what is being done

Sacha Barber
  • Microsoft Visual C# MVP 2008/2009
  • Codeproject MVP 2008/2009
Your best friend is you.
I'm my best friend too. We share the same views, and hardly ever argue

My Blog : sachabarber.net

GeneralRun Spell Check before Final.
Wade Beasley
11:49 17 May '09  
There are some typos in the document you might want to fix before finalizing this great work.

Thanks,
Wade

Wade Beasley
Solution Designers, LLC
www.vtmag.com

General[My vote of 2] Hey images are not visible.
ww.mubshir.com
4:13 15 May '09  
I would love to see this after review.

Mubshir Raza Ali
^^^^^^^^^^^^^^^^^^^^
http://www.mubshir.com

GeneralRe: [My vote of 2] Hey images are not visible.
Sacha Barber
4:35 15 May '09  
Not sure what you mean?

Sacha Barber
  • Microsoft Visual C# MVP 2008/2009
  • Codeproject MVP 2008/2009
Your best friend is you.
I'm my best friend too. We share the same views, and hardly ever argue

My Blog : sachabarber.net

GeneralRe: [My vote of 2] Hey images are not visible.
Tefik Becirovic
6:25 16 May '09  
I don't see them, either:

http://www.codeproject.com/KB/WPF/WPFOnyxApp%5Cdemo1.png
.
.
.
http://www.codeproject.com/KB/WPF/WPFOnyxApp%5Cdemo6.png

Regards
Tefik
GeneralRe: [My vote of 2] Hey images are not visible.
Sacha Barber
3:37 17 May '09  
All fixed now

Sacha Barber
  • Microsoft Visual C# MVP 2008/2009
  • Codeproject MVP 2008/2009
Your best friend is you.
I'm my best friend too. We share the same views, and hardly ever argue

My Blog : sachabarber.net

GeneralAgain
Dr.Luiji
22:15 13 May '09  
Great article, thanks for the info.Thumbs Up
Many thanks to Bill Kempf for the Onyx framework.

Question:
With 'yourelf' you mean my elf? Shucks

Dr.Luiji

Trust and you'll be trusted.

Try iPhone UI [^] a new fresh face for your Windows Mobile, here on Code Project.

GeneralRe: Again
Sacha Barber
22:33 13 May '09  
Er yeah, my spelling is bad. Ooops

Sacha Barber
  • Microsoft Visual C# MVP 2008/2009
  • Codeproject MVP 2008/2009
Your best friend is you.
I'm my best friend too. We share the same views, and hardly ever argue

My Blog : sachabarber.net

GeneralCritical spelling failure
ohnAskew
9:37 6 May '09  
Sacha, please see if you can correct your spelling of Onyx (you've spelled it Oynx throughout the article).

http://wpfonyx.codeplex.com/

You can see from the codeplex link that you've misspelled the project name to such an extent that this article may never be found by search engines when looking for WPFOnyx articles.

Onyx is a mineral -- http://en.wikipedia.org/wiki/Onyx

Oynx are sounds that pigs make.

Wink

Thanks for the great article, your contributions to our community are extensive and valuable.
GeneralRe: Critical spelling failure
William E. Kempf
4:01 11 May '09  
You know, I didn't even notice the misspellings the first time around. Won't make much difference when it comes to searching, because it's spelled correctly about as often as it's spelled incorrectly, but a quick fix by Sacha wouldn't hurt. Smile

William E. Kempf

GeneralRe: Critical spelling failure
Sacha Barber
0:30 13 May '09  
Ill try and fix this as soon as I can. Thanks for pointing it out, Ive been on hols, so didnt pick up this comment until now, I'll sort it soon.

Sacha Barber
  • Microsoft Visual C# MVP 2008/2009
  • Codeproject MVP 2008/2009
Your best friend is you.
I'm my best friend too. We share the same views, and hardly ever argue

My Blog : sachabarber.net

GeneralRe: Critical spelling failure
Sacha Barber
22:36 13 May '09  
DONE,JBIO

Sacha Barber
  • Microsoft Visual C# MVP 2008/2009
  • Codeproject MVP 2008/2009
Your best friend is you.
I'm my best friend too. We share the same views, and hardly ever argue

My Blog : sachabarber.net

QuestionCommand Registration - where does this happen?
npcomplete1
6:40 30 Apr '09  
I am curious where this line of code happens if it is there at all?
Every view model have a collection of commands but I don't see the registration anywhere.
CommandManager.RegisterClassCommandBinding(typeof (Control), commandBinding);

How is it done?
Thanks.
AnswerRe: Command Registration - where does this happen?
Sacha Barber
7:03 30 Apr '09  
It is inside the Oynx framework.

Sacha Barber
  • Microsoft Visual C# MVP 2008/2009
  • Codeproject MVP 2008/2009
Your best friend is you.
I'm my best friend too. We share the same views, and hardly ever argue

My Blog : sachabarber.net

QuestionRe: Command Registration - where does this happen?
npcomplete1
7:53 30 Apr '09  
Thank you for the reply.
Now I know it happens
The other question is:
MainWindowViewModel constructor has a parameter View
How does this happen?
Is that because View has a static constructor?
Is there a place to see Onyx code?
AnswerRe: Command Registration - where does this happen?
Sacha Barber
10:51 30 Apr '09  
This is done via the attached DP in the MainWindow.xaml, and Oynx takes care of the rest. Oynx rocks man. I only need to write one more service (for showing extra windows) and I think it does the rest. Bill is one smart dude. The more I look the more I like it.

Sacha Barber
  • Microsoft Visual C# MVP 2008/2009
  • Codeproject MVP 2008/2009
Your best friend is you.
I'm my best friend too. We share the same views, and hardly ever argue

My Blog : sachabarber.net

GeneralRe: Command Registration - where does this happen?
npcomplete1
4:22 1 May '09  
Yes, I finally read http://wpfonyx.codeplex.com/Wiki/View.aspx?title=Introduction[^]
It explains the model creation. It is reflection.
GeneralRe: Command Registration - where does this happen?
Sacha Barber
4:33 1 May '09  
Yeah it uses Reflection a bit for sure. I dont know many apps these days that don't

Sacha Barber
  • Microsoft Visual C# MVP 2008/2009
  • Codeproject MVP 2008/2009
Your best friend is you.
I'm my best friend too. We share the same views, and hardly ever argue

My Blog : sachabarber.net

Generalgood
Member 3682371
0:56 17 Apr '09  
exellente good job friend you merite 100/5
GeneralRe: good
Sacha Barber
1:22 17 Apr '09  
Cheers

Sacha Barber
  • Microsoft Visual C# MVP 2008/2009
  • Codeproject MVP 2008/2009
Your best friend is you.
I'm my best friend too. We share the same views, and hardly ever argue

My Blog : sachabarber.net

GeneralGood
zhujinlong19840913
21:34 16 Apr '09  
Good


Last Updated 21 May 2009 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2010