Catel.MVVM + MahApps.Metro

Topics: Questions
Jul 10, 2012 at 12:50 AM

Hello!

First, thanks for the really great work in developing Catel!

I started to develop a new application, and, after some research, decided to give MahApps.Metro ( http://mahapps.com/MahApps.Metro/ ) a try. The controls and the default window are really gorgeous.. But then I ran into a problem. I have to derive my window's codebehind from the mahapps control..

Is it possible to use their style, <Control:MetroWindow>, and still have the view models automatically discovered? As well as using the 'cascading' dynamic view models?

Thank you for your time!

Greetings from Brazil.

Coordinator
Jul 11, 2012 at 8:47 AM

Hi there,

this is "of course" possible. Just derive your own base controls from the MahApps.Metro stuff, and use this technique to apply Catel to them.

Then you can use Catel with any external control and apply your own custom logic, but keep the power of view model location and nested user controls.

Jul 11, 2012 at 5:32 PM

I feel somewhat dumb for not having seen it before..

I wrote the whole class, but I can't compile. Without inheriting DataWindow, the GetViewModelType() is not available to me. Here's my code.

namespace iCOD.WPF.Infrastructure
{
    using System;
    using System.ComponentModel;
    using System.Windows;
    using System.Windows.Interactivity;
    using Catel;
    using Catel.IoC;
    using Catel.MVVM;
    using Catel.Windows.Controls.MVVMProviders;
    using MahApps.Metro.Controls;

    public class WindowBase : MetroWindow, IViewModelContainer
    {
        private readonly WindowBehavior _mvvmBehavior;

        public WindowBase() : this(null) { }

        public WindowBase(IViewModel viewModel)
        {
            var viewModelType = (viewModel != null) ? viewModel.GetType() : GetViewModelType();
            if (viewModelType == null)
            {
                var viewModelLocator = ServiceLocator.Instance.ResolveType<IViewModelLocator>();
                viewModelType = viewModelLocator.ResolveViewModel(GetType());
                if (viewModelType == null)
                {
                    const string error = "The view model of the view could not be resolved. Use either the GetViewModelType() method or IViewModelLocator";
                    throw new NotSupportedException(error);
                }
            }

            _mvvmBehavior = new WindowBehavior(viewModel);
            _mvvmBehavior.ViewModelType = viewModelType;
            Interaction.GetBehaviors(this).Add(_mvvmBehavior);

            _mvvmBehavior.ViewModelChanged += (sender, e) => ViewModelChanged.SafeInvoke(this, e);
            _mvvmBehavior.ViewModelPropertyChanged += (sender, e) => ViewModelPropertyChanged.SafeInvoke(this, e);
        }

        public IViewModel ViewModel
        { get { return _mvvmBehavior.ViewModel; } }

        public event EventHandler<EventArgs> ViewModelChanged;
        public event EventHandler<PropertyChangedEventArgs> ViewModelPropertyChanged;
        public event EventHandler<PropertyChangedEventArgs> PropertyChanged;

        protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
        {
            base.OnPropertyChanged(e);

            PropertyChanged.SafeInvoke(this, new PropertyChangedEventArgs(e.Property.Name));
        }

        private void OnViewModelChanged()
        {
            if (ViewModel != null && !ViewModel.IsClosed)
            {
                ViewModel.Closed += ViewModelClosed;
            }
        }

        private void ViewModelClosed(object sender, ViewModelClosedEventArgs e)
        {
            Close();
        }
    }
}

Coordinator
Jul 12, 2012 at 7:26 AM

Add this method. I will update the docs as well:

        /// <summary>
        /// Gets the type of the view model. If this method returns <c>null</c>, the view model type will be retrieved by naming 
        /// convention using the <see cref="IViewModelLocator"/> registered in the <see cref="IServiceLocator"/>.
        /// </summary>
        /// <returns>The type of the view model or <c>null</c> in case it should be auto determined.</returns>
        protected virtual Type GetViewModelType()
        {
            return null;
        }