MVVM Tips: How to close the current window / dialog?

To keep your ViewModel separated from the View we would like to make sure the ViewModel knows as little about the UI as possible, this includes no hard references. So how do we close the window?

I like to create a abstract ViewModel class specifically for this purpose which extends from my ViewModel base class and adds an event called RequestClose. Lets call it CloseableViewModel.

public abstract class CloseableViewModel : ViewModel
{
    public event EventHandler RequestClose;

    protected OnRequestClose()
    {
        if (RequestClose != null)
            RequestClose(this, EventArgs.Empty);
    }
}

Now just make sure your view model extends the CloseableViewModel. When you are linking your ViewModel to your view add a listener to the event

public partial class DialogView : ChildWindow
{
    public DialogView(DialogViewModel model)
    {
        this.DataContext = model;
        model.RequestClose += delegate (object sender, EventArgs args) { this.Close(); }
    }
}

When you want to close the window from your ViewModel call OnRequestClose();

Easy as that, and your ViewModel doesn’t need to know about your View 🙂

Advertisements

MVVM Tips: How to show a message box or dialog in MVVM Silverlight?

The key to successfully separating the concerns of your application is all in the abstraction.

Of course we do not want to call MessageBox.Show() from within our ViewModel as this will not allow us to automatically test it. We will need to abstract the call to MessageBox.Show() so that you can swap it out for testing. A simple way to do this is to use a service implementing an interface that we can swap out depending on whether we are testing or running the application.


public interface IMsgBoxService
{
    ShowNotification(string message);
    bool AskForConfirmation(string message);
    //... etc
}

public class MsgBoxService : IMsgBoxService
{
    public void ShowNotification(string message)
    {
        MessageBox.Show(message, "Notification", MessageBoxButton.OK)
    }

    public bool AskForConfirmation(string message)
    {
            MessageBoxResult result = MessageBox.Show(message, "Are you sure?", MessageBoxButton.OKCancel);
            return result.HasFlag(MessageBoxResult.OK);
    }
}

Now that we have implemented our message box service we just need to get an instance of it and call the appropriate method in our ViewModel


private IMsgBoxService messageService;
public TestViewModel(IMsgBoxService msgboxService)
{
   this.messageService = msgboxService;
}

public void SomeMethod()
{
    messageService.ShowNotification("Hey!");
}

There it is, a few lines of code and you have a message box you can safely use throughout your application in a MVVM way that will allow for testability and seperation of concerns.

Lots more MVVM tips to come so subscribe if you would like to stay updated. Thanks!

MVVM Tips: ViewModel INotifyPropertyChanged compile time errors with Visual Studio Refactoring

Ok so I’m not too much of a fan of strings used to represent variable/property/method names. Say, INotifyPropertyChanged, no Visual Studio Refactor support for renaming and you could accidentally type the wrong string, resulting in no compile error as shown below.

    private string name;
    public string Name
    {
        get
        {
            return name;
        }
        set
        {
            name = value;
            RaisePropertyChanged("Names");
        }
    }

I came across a great little helper function created by the Microsoft Patterns and Practices guys to fix this problem. In your ViewModel base class, try adding this:


        protected void RaisePropertyChanged<T>(Expression<Func<T>> propertyExpresssion)
        {
            var propertyName = ExtractPropertyName(propertyExpresssion);
            this.RaisePropertyChanged(propertyName);
        }

        private string ExtractPropertyName<T>(Expression<Func<T>> propertyExpression)
        {
            if (propertyExpression == null)
            {
                throw new ArgumentNullException("propertyExpression");
            }

            var memberExpression = propertyExpression.Body as MemberExpression;
            if (memberExpression == null)
            {
                throw new ArgumentException("The expression is not a member access expression.", "propertyExpression");
            }

            var property = memberExpression.Member as PropertyInfo;
            if (property == null)
            {
                throw new ArgumentException("The member access expression does not access a property.", "propertyExpression");
            }

            if (!property.DeclaringType.IsAssignableFrom(this.GetType()))
            {
                throw new ArgumentException("The referenced property belongs to a different type.", "propertyExpression");
            }

            var getMethod = property.GetGetMethod(true);
            if (getMethod == null)
            {
                // this shouldn't happen - the expression would reject the property before reaching this far
                throw new ArgumentException("The referenced property does not have a get method.", "propertyExpression");
            }

            if (getMethod.IsStatic)
            {
                throw new ArgumentException("The referenced property is a static property.", "propertyExpression");
            }

            return memberExpression.Member.Name;
        }

Now you can do this:

    private string name;
    public string Name
    {
        get  {  return name; }
        set
        {
            name = value;
            RaisePropertyChanged(() => this.Name);
        }
    }

and get compile time error handling of INotifyPropertyChanged

Silverlight ChildWindow Close() leaves background disabled

Found an issue today where the background was left disabled after calling Close() on a Silverlight ChildWindow (Reproduced on SL4 intermittently after many open/closes). A thread following the issue can be found here http://forums.silverlight.net/forums/t/176423.aspx.
Quick workaround was to replace the call to Close() with DialogResult = true which will then close the window without problem.

Deploying Silverlight WCF RIA Services on IIS 6

OK so you’ve just created a great Silverlight application, it works like a charm and it has come time to finally deploy. That should be the easy part.. think again!

Tim Heuer has a great post on his blog about this http://timheuer.com/blog/archive/2009/12/10/tips-to-deploy-ria-services-troubleshoot.aspx. If you search Google you will find tons of people with RIA deployment issues, however if you were like me you have to really sift through it to find any information that relates to your situation. Before reading the following make sure you have read Tim’s post and installed the RiaServices.msi with the server flag as noted. This will place the RIA DLL’s into your global assembly cache so that all applications running on the server have access to the files.

With RIA and IIS 6 you want to look for a few gotchas, once you know them they are easy. To find these you will first need to download a great piece of software called Fiddler (Google it) and whilst running fiddler, open your favourite browser and try to run your Silverlight application.

Now at this point you may get a 404 Not Found error shown in Silverlight. That doesn’t mean much as you may need fiddler to see the exact exception. Check the request for the web service in fiddler, copy the URL and load it into your browser. If you still get a 404 then most likely something is disallowing you from accessing the generated WCF service. Otherwise you should get an exception which well help you identify the source of your problem (your one of the lucky ones).

If you still cannot access the web service its time to try the web.config file. RIA services uses WCF in the back-end (The WCF services are dynamically generated) and as such you will need

<serviceHostingEnvironment 
     aspNetCompatibilityEnabled="true"
     multipleSiteBindingsEnabled="true" />

in your web.config. An easy way to do this (and add the appropriate references to RIA DLL’s is to create a new DomainService in your application and then simply delete it. Visual studio will handle all of the work for you. Reload your Silverlight app and try again.

If you are sure all of this is working next check your IIS virtual directory and ensure that the mapping extension .svc is mapped to the aspnet_isapi for which .NET framework you are using. To check this go to the properties of your virtual directory and click on the “Configuration…” button on the “Virtual Directory” tab. Make sure that when you update the mapping for .svc you UNCHECK the “verify file exists” check box. This is a very important step as the WCF service is dynamically served and does not exist on the file system. You should be able to find your asp.net_isapi should be able to be found in C:\Windows\Microsoft.NET\Framework\[Version]\aspnet_isapi.dll.

If you are still having issues getting to your RIA service at this point I would check the isapi wildcard extension. This is also a very important step, I have been caught out with this one way too many times. Ensure that the wildcard extension if any points to the correct aspnet_isapi and that the “verify file exists” checkbox is again UNCHECKED.

At this point your IIS virtual directory should be serving your WCF services, your web.config should allow WCF and RIA to do its thing and your RIA dll’s are in the GAC ( try copy local if cannot find the dll’s). By now if you still have problems post them in the comments. You could try manually creating the “.svc” files which map to their RIA domain service counterparts as a temporary solution.

Hopefully by now you’ve got the problem fixed and this post may have saved you a little time. If It has or you would like to point out more tips or correct something I have said please leave a comment

First Post

Welcome to Refactor(this)!

I’m basically starting this to write down all the tips and gotchas I run into everyday as I work as a .NET developer. Currently the blog will probably focus on Silverlight MVVM and PRISM development as that is what I am currently working with.

Hope to have some helpful posts up soon