Enterprise configuration management

Almost every application requires some form of configuration information. This information can be as simple as a database connection string or as complex as multipart and hierarchical user preference information. How and where to store an application’s configuration data are questions you often face as a developer.

Any large enterprise application has many moving blocks. They all need to be configured for a proper working of the application. As the application size increases or for scalability the same configuration has to be repeated in different applications. For most applications once the configuration has been changed the application needs to be restarted.

Sample Code (create a blank console project, add json.net nuget)

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Collections.Generic;
using System.Linq;

namespace CM
{
    // configuration management API, 
    //1. allow clients specify typesafe models for configuations
    //2. Store flat data on server (table) which is easy to edit
    //3. can be extended to have inheritance of values (overrides)
    //4. can be extended to lock / unlock certain property by admins etc.
    
    class Program
    {
        //Sample configuration model
        internal class SampleConfigModal
        {
            public SampleConfigModal()
            {
                Address = new Address();
            }
            public string Name { get; set; }
            public Address Address { get; set; }
            public int Age { get; set; }


        }
        public class Address
        {
            public string Street { get; set; }

        }

        // this is how client api will look like
        static void Main(string[] args)
        {
            // sample client code
            var data = new SampleConfigModal() { Name = "Rajnish", Age = 18, Address = new Address() { Street = "Oxley" } };

            // save Configuration
            SaveConfiguration("app", "section", data);

            //get Configuration
            var data2 = GetConfiguration("app", "section");
        }

        // Client side framework api -> call to rest end point
        private static T GetConfiguration(string appName, string SectionName) where T : new()
        {
            var defaultValue = new T();
            var samplePayload = JsonConvert.SerializeObject(defaultValue);
            var payload = GetConfiguration(appName, SectionName, samplePayload);
            return JsonConvert.DeserializeObject(payload);
        }

        // Client side framework api -> call to rest end point
        private static void SaveConfiguration(string appName, string SectionName, T data)
        {
            var payload = Newtonsoft.Json.JsonConvert.SerializeObject(data);
            SaveConfigurationa(appName, SectionName, payload);
        }


        //---------------------------------------- Server Code -------------------------- 
        //-------------- server has no knowledge of configuration structure or model

        private static Dictionary<string, string> storage;

        private static void SaveConfigurationa(string appName, string SectionName, string payload, string enumForHierarchyLevel = null)
        {
            // transformer
            var section = string.Format("{0}.{1}", appName, SectionName);
            var data = (JObject)JsonConvert.DeserializeObject(payload);
            var keyValueData = Flatten(data, section);

            // check if user has permission for level overrides
            // store with proper overides

            // store the flat list in sql or data 
            //| KEY |           |Value|            |OverrideType| - default,sysadmin,appadmin,groups,user etc
            //app.section.Name, Rajnish
            //app.section.Address.Street, Oxley
            //app.section.Age, 18

            storage = keyValueData;
        }

        private static string GetConfiguration(string appName, string SectionName, string samplePayload)
        {
            var section = string.Format("{0}.{1}", appName, SectionName);
            var data = (JObject)JsonConvert.DeserializeObject(samplePayload);
            var keyValueSample = Flatten(data, section);
            // update data from sql or data store
            // apply property override rules and get value from overrides if exists
            var keyValueData = keyValueSample.Select(x => new KeyValuePair<string, string>(x.Key, storage[x.Key]));

            //read these
            //app.section.Name, Rajnish
            //app.section.Address.Street, Oxley
            //app.section.Age, 18

            UnFlatten(data, section, keyValueData);

            var formatedData = JsonConvert.SerializeObject(data);
            /*
             * {
                  "Name": "Rajnish",
                  "Address": {
                    "Street": "Oxley"
                  },
                  "Age": "18"
                }
             * */
            return formatedData;

        }
        
        // Server side json helper

        private static void UnFlatten(JObject jsonObject, string prefix, IEnumerable<KeyValuePair<string, string>> data)
        {
            foreach (var item in data)
            {
                var keyName = item.Key.Substring(prefix.Length + 1);
                var storageValue = item.Value;
                if (keyName.Contains("."))
                {
                    var keys = keyName.Split('.');
                    var jtoken = (JToken)jsonObject;
                    foreach (var k in keys)
                    {
                        jtoken = jtoken.SelectToken(k);
                    }
                    ((JValue)jtoken).Value = storageValue;
                }
                else
                {
                    jsonObject[keyName] = storageValue;
                }
            }
        }

        private static Dictionary<string, string> Flatten(JObject jsonObject, string prefix)
        {

            IEnumerable jTokens = jsonObject.Descendants().Where(p => p.Count() == 0);
            Dictionary<string, string> results = jTokens.Aggregate(new Dictionary<string, string>(), (properties, jToken) =>
            {
                properties.Add(string.Format("{0}.{1}", prefix, jToken.Path), jToken.ToString());
                return properties;
            });
            return results;
        }
    }
}

WPF Multithreading

WPF have the concept of the UI thread and background (or secondary) threads. Code on a background thread is not allowed to interact with UI elements (controls, etc.) on the foreground thread.  What happens when you need to add and remove items from a collection from that background thread? That’s pretty common in applications which must keep a list updated . When the collection raises the change notification events, the binding system and listeners, on the UI thread, have code executed.

Before WPF 4.5 we have to use dispatcher to enable this feature in observable collection or we end up creating thread safe observable collection and also expose Add Range function which is not available in observable collections.

WPF 4.5 includes a number of key targeted performance and capability features, one of which is cross-thread collection change notification. Enabling the change notification is as simple as the inclusion of a lock object and a single function call. Versus the manual dispatching approach, this can be a real performance win, not to mention save you some coding time.

Use BindingOperations.EnableCollectionSynchronization to enable this cross thread access of observable collections.

So inside your view model you call this method and provide the lock to synchronise the collection from multiple threads. BindingOperations.EnableCollectionSynchronization, the WPF data binding engine participates in locking. The default behavior is to acquire a lock on the object specified in the aforementioned call, but you also have the option to use more complex locking schemes.

something like

BindingOperations.EnableCollectionSynchronization(this.items, itemsSyncLock);

where

private readonly object itemsSyncLock = new object();

private readonly ObservableCollection<string> items = new ObservableCollection<string>();

Now when you have to add data into collection from background thread

simply lock the collection and update it

lock (itemsSyncLock)
{
// Once locked, you can manipulate the collection safely from another thread
items.Add(value);
}

This means now you don’t need IDispatcher in you application anymore..

There are also other binding properties like IsAsync (to get the data asynchronously) , Delay (provides delay before committing binding values etc)..

Testing with Dispatcher

The threading model in the Windows Presentation Foundation (WPF) is based on the concept of a “dispatcher”. Every thread has a Dispatcher object associated with it. Dispatcher is responsible for policing cross-thread operations and provides a way for threads to marshal method invocations to each other, via its BeginInvoke and Invoke methods.

Dispatcher contains the WPF version of a Windows message queue and message pump. It provides a prioritized message queue, from which the message pump can pull messages and process them. Most WPF classes have an affinity for the thread on which they are instantiated. That thread affinity is based on an association between the object and the thread’s Dispatcher.

Using IDispatcher in your view models

 /// <summary>
 /// The Dispatcher interface.
 /// </summary>
 public interface IDispatcher
 {
 /// <summary>
 /// The invoke.
 /// </summary>
 /// <param name="callback">
 /// The callback.
 /// </param>
 void Invoke(Delegate callback);

 /// <summary>
 /// The invoke.
 /// </summary>
 /// <param name="method">
 /// The method.
 /// </param>
 /// <param name="priority">
 /// The priority.
 /// </param>
 /// <param name="args">
 /// The args.
 /// </param>
 /// <returns>
 /// The <see cref="object"/>.
 /// </returns>
 object Invoke(Delegate method, DispatcherPriority priority, params object[] args);

 /// <summary>
 /// The begin invoke.
 /// </summary>
 /// <param name="callback">
 /// The callback.
 /// </param>
 /// <returns>
 /// The <see cref="DispatcherOperation"/>.
 /// </returns>
 DispatcherOperation BeginInvoke(Delegate callback);

 /// <summary>
 /// The begin invoke.
 /// </summary>
 /// <param name="method">
 /// The method.
 /// </param>
 /// <param name="args">
 /// The args.
 /// </param>
 /// <returns>
 /// The <see cref="DispatcherOperation"/>.
 /// </returns>
 DispatcherOperation BeginInvoke(Delegate method, params object[] args);

 /// <summary>
 /// The begin invoke.
 /// </summary>
 /// <param name="method">
 /// The method.
 /// </param>
 /// <param name="priority">
 /// The priority.
 /// </param>
 /// <param name="args">
 /// The args.
 /// </param>
 /// <returns>
 /// The <see cref="DispatcherOperation"/>.
 /// </returns>
 DispatcherOperation BeginInvoke(Delegate method, DispatcherPriority priority, params object[] args);

 /// <summary>
 /// The invoke async.
 /// </summary>
 /// <param name="callback">
 /// The callback.
 /// </param>
 /// <returns>
 /// The <see cref="DispatcherOperation"/>.
 /// </returns>
 DispatcherOperation InvokeAsync(Action callback);

 /// <summary>
 /// The invoke async.
 /// </summary>
 /// <param name="callback">
 /// The callback.
 /// </param>
 /// <param name="priority">
 /// The priority.
 /// </param>
 /// <returns>
 /// The <see cref="DispatcherOperation"/>.
 /// </returns>
 DispatcherOperation InvokeAsync(Action callback, DispatcherPriority priority);

 /// <summary>
 /// The invoke async.
 /// </summary>
 /// <param name="callback">
 /// The callback.
 /// </param>
 /// <typeparam name="TResult">type of result
 /// </typeparam>
 /// <returns>
 /// The <see cref="DispatcherOperation"/>.
 /// </returns>
 DispatcherOperation<TResult> InvokeAsync<TResult>(Func<TResult> callback);

 /// <summary>
 /// The invoke async.
 /// </summary>
 /// <param name="callback">
 /// The callback.
 /// </param>
 /// <param name="priority">
 /// The priority.
 /// </param>
 /// <typeparam name="TResult">type of result
 /// </typeparam>
 /// <returns>
 /// The <see cref="DispatcherOperation"/>.
 /// </returns>
 DispatcherOperation<TResult> InvokeAsync<TResult>(Func<TResult> callback, DispatcherPriority priority);
 }

The real dispatcher

public class DispatcherFactory
 {
 public static IDispatcher GetDispatcher()
 {
 return Application.Current != null ? new AppDispatcher(Application.Current.Dispatcher) : new AppDispatcher();
 }

 private class AppDispatcher : IDispatcher
 {
 private readonly Dispatcher _safeDispatcher;
 private readonly Dispatcher _currentDispatcher;

 public AppDispatcher(Dispatcher dispatcher):this()
 {
 this._safeDispatcher = dispatcher ?? this._currentDispatcher;
 }

 public AppDispatcher()
 {
 this._currentDispatcher = Dispatcher.CurrentDispatcher;
 this._safeDispatcher = this._currentDispatcher;
 }

 private Dispatcher Dispatcher
 {
 get { return this._currentDispatcher.CheckAccess() ? this._currentDispatcher : this._safeDispatcher; }
 }

 void IDispatcher.Invoke(Delegate callback)
 {
 this.Dispatcher.Invoke(callback);
 }

 object IDispatcher.Invoke(Delegate method, DispatcherPriority priority, params object[] args)
 {
 return this.Dispatcher.Invoke(method, priority, args);
 }

 DispatcherOperation IDispatcher.BeginInvoke(Delegate callback)
 {
 return this.Dispatcher.BeginInvoke(callback);
 }

 DispatcherOperation IDispatcher.BeginInvoke(Delegate method, params object[] args)
 {
 return this.Dispatcher.BeginInvoke(method, args);
 }

 DispatcherOperation IDispatcher.BeginInvoke(Delegate method, DispatcherPriority priority, params object[] args)
 {
 return this.Dispatcher.BeginInvoke(method, priority, args);
 }


 DispatcherOperation IDispatcher.InvokeAsync(Action callback)
 {
 return this.Dispatcher.InvokeAsync(callback);
 }

 DispatcherOperation IDispatcher.InvokeAsync(Action callback, DispatcherPriority priority)
 {
 return this.Dispatcher.InvokeAsync(callback, priority);
 }

 DispatcherOperation<TResult> IDispatcher.InvokeAsync<TResult>(Func<TResult> callback)
 {
 return this.Dispatcher.InvokeAsync(callback);
 }

 DispatcherOperation<TResult> IDispatcher.InvokeAsync<TResult>(Func<TResult> callback, DispatcherPriority priority)
 {
 return this.Dispatcher.InvokeAsync(callback, priority);
 }
 }
 }

The testing dispatcher

 /// <summary>
 /// The stub dispatcher.
 /// </summary>
 public class TestingDispatcher : IDispatcher
 {
 private readonly Dispatcher currentDispatcher;

 /// <summary>
 /// Initializes a new instance of the <see cref="TestingDispatcher"/> class.
 /// </summary>
 public TestingDispatcher()
 {
 this.currentDispatcher = Dispatcher.CurrentDispatcher;
 }

 /// <summary>
 /// The invoke.
 /// </summary>
 /// <param name="callback">
 /// The callback.
 /// </param>
 public void Invoke(Delegate callback)
 {
 try
 {
 this.currentDispatcher.Invoke(callback);
 }
 finally
 {
 this.DoEvents();
 }
 }

 /// <summary>
 /// The invoke.
 /// </summary>
 /// <param name="method">
 /// The method.
 /// </param>
 /// <param name="priority">
 /// The priority.
 /// </param>
 /// <param name="args">
 /// The args.
 /// </param>
 /// <returns>
 /// The <see cref="object"/>.
 /// </returns>
 public object Invoke(Delegate method, DispatcherPriority priority, params object[] args)
 {
 try
 {
 return this.currentDispatcher.Invoke(method, priority, args);
 }
 finally
 {
 this.DoEvents();
 }
 }

 /// <summary>
 /// The begin invoke.
 /// </summary>
 /// <param name="callback">
 /// The callback.
 /// </param>
 /// <returns>
 /// The <see cref="DispatcherOperation"/>.
 /// </returns>
 public DispatcherOperation BeginInvoke(Delegate callback)
 {
 try
 {
 return this.currentDispatcher.BeginInvoke(callback);
 }
 finally
 {
 this.DoEvents();
 }
 }

 /// <summary>
 /// The begin invoke.
 /// </summary>
 /// <param name="method">
 /// The method.
 /// </param>
 /// <param name="args">
 /// The args.
 /// </param>
 /// <returns>
 /// The <see cref="DispatcherOperation"/>.
 /// </returns>
 public DispatcherOperation BeginInvoke(Delegate method, params object[] args)
 {
 try
 {
 return this.currentDispatcher.BeginInvoke(method, args);
 }
 finally
 {
 this.DoEvents();
 }
 }

 /// <summary>
 /// The begin invoke.
 /// </summary>
 /// <param name="method">
 /// The method.
 /// </param>
 /// <param name="priority">
 /// The priority.
 /// </param>
 /// <param name="args">
 /// The args.
 /// </param>
 /// <returns>
 /// The <see cref="DispatcherOperation"/>.
 /// </returns>
 public DispatcherOperation BeginInvoke(Delegate method, DispatcherPriority priority, params object[] args)
 {
 try
 {
 return this.currentDispatcher.BeginInvoke(method, priority, args);
 }
 finally
 {
 this.DoEvents();
 }
 }

 /// <summary>
 /// The invoke async.
 /// </summary>
 /// <param name="callback">
 /// The callback.
 /// </param>
 /// <returns>
 /// The <see cref="DispatcherOperation"/>.
 /// </returns>
 public DispatcherOperation InvokeAsync(Action callback)
 {
 try
 {
 return this.currentDispatcher.InvokeAsync(callback);
 }
 finally
 {
 this.DoEvents();
 }
 }

 /// <summary>
 /// The invoke async.
 /// </summary>
 /// <param name="callback">
 /// The callback.
 /// </param>
 /// <param name="priority">
 /// The priority.
 /// </param>
 /// <returns>
 /// The <see cref="DispatcherOperation"/>.
 /// </returns>
 public DispatcherOperation InvokeAsync(Action callback, DispatcherPriority priority)
 {
 try
 {
 return this.currentDispatcher.InvokeAsync(callback, priority);
 }
 finally
 {
 this.DoEvents();
 }
 }

 /// <summary>
 /// The invoke async.
 /// </summary>
 /// <param name="callback">
 /// The callback.
 /// </param>
 /// <typeparam name="TResult">type of result
 /// </typeparam>
 /// <returns>
 /// The <see cref="DispatcherOperation"/>.
 /// </returns>
 public DispatcherOperation<TResult> InvokeAsync<TResult>(Func<TResult> callback)
 {
 try
 {
 return this.currentDispatcher.InvokeAsync(callback);
 }
 finally
 {
 this.DoEvents();
 }
 }

 /// <summary>
 /// The invoke async.
 /// </summary>
 /// <param name="callback">
 /// The callback.
 /// </param>
 /// <param name="priority">
 /// The priority.
 /// </param>
 /// <typeparam name="TResult">type of result
 /// </typeparam>
 /// <returns>
 /// The <see cref="DispatcherOperation"/>.
 /// </returns>
 public DispatcherOperation<TResult> InvokeAsync<TResult>(Func<TResult> callback, DispatcherPriority priority)
 {
 try
 {
 return this.currentDispatcher.InvokeAsync(callback, priority);
 }
 finally
 {
 this.DoEvents();
 }
 }

 private void DoEvents()
 {
 DispatcherFrame frame = new DispatcherFrame();
 this.currentDispatcher.BeginInvoke(new Action(() => frame.Continue = false), DispatcherPriority.ContextIdle);
 Dispatcher.PushFrame(frame);
 }
 }

ViewModel First – ViewCaching

With View model first approach, view are usually created via DataTemplate, and each time a view model is injected in the content control, the corresponding view is recreated.

If you have complex view where it take bit time to create view, you may see the performance hits. To avoid the performance hit you may want to cache the view and use the cached view when available. This can be done via creating CacheContentControl and you choice of view factory.

The basic idea is to wrap the view inside CacheContentControl and delegate the view creation logic to your view factory. The attached sample uses weakreference view factory,say if GC has not collected the view, you may use the cached view instead of creating view each time. This will work with controls like tab control docking controls etc.

The CacheContentControl  code

public class CacheContentControl : ContentControl
    {
        public CacheContentControl()
        {
            Unloaded += ViewCache_Unloaded;
            ViewFactory = WeakReferenceViewFactory.Instance;
        }

        void ViewCache_Unloaded(object sender, RoutedEventArgs e)
        {
            Content = null;
        }

        private Type _contentType;
        public Type ContentType
        {
            get { return _contentType; }
            set
            {
                _contentType = value;
               //  use you favorite factory
                Content = ViewFactory.GetView(value);
            }
        }

        public IViewFactory ViewFactory { get; private set; }
    }

In the DataTemplate, use the ViewCache, pass the type of the real view you want to use:

<DataTemplate DataType=”{x:Type moduleB:Panel3Vm}”>

<Border Background=”Green”>

<local:CacheContentControl ContentType=”{x:Type moduleB:Pane3 }” Margin=”5″/>

</Border>

</DataTemplate>

The example uses standard data template (red option) to demonstrate each time you navigate to panes the corresponding views are recreated with new hash code.

If you select CacheContentControl (green option) and navigate the view is only created once. If you click on button to collected the GC then view are created on navigation if they are claimed by GC.

Why ViewModel first ?

I prefer to use view model first approach. For many reasons:

  • Vms are your application containing most of logic apart from glue code in form of behaviors or triggers.
  • If you creates views then you are responsible for its life and cleanup code. You have to deal with threading and other issues which are difficult to test. On the other hand of you create vms and leave the view creation logic with WPF via data template.. you don’t have to worry about threading issue. And there will be better separation of concerns.
  • With vm first approach zero code behind.
  • With a project level isolation for view and vms you can restrict developers using view specific things like dispatcher in the view model leaving more cleaner and testable code base. I.e view project sprojec to vm. And vm project should not refer to any presentation lib.
  • If there is clear boundary between view and vm. Both can evolve and will be less fragile.
  • Allows more complete testing of logic to open new Views and ViewModels
  • Tends to be DRYer (don’t repeat yourself ) as applications get larger
  • View and ViewModel are more independent and can be worked on separately more easily.

For more details see attached source code (download and rename docx to zip)

ViewCache Source Code

RadDocking With Prism + Mvvm

Features

  • Support MVVM with Prism
  • Supports ViewModel First or ViewFirst Approach
  • Supports styling RadPane in xaml rather than DockingPansFactory
  • Code RadDocking – SourceCode

The XAML

  • Defiine the region names at RadPaneGroup level
  • Set Rad Panel Style via ItemContainerStyle
  • Bind Header property of RadPane with Title Property in View Model
 <Window.Resources>
 <Style x:Key="RadPaneFactoryStyle" TargetType="{x:Type telerik:RadPane}">
 <Setter Property="Header" Value="{Binding Title}"/>
 </Style>
 </Window.Resources>
 <Grid>
 <telerikDocking:RadDocking>
 <telerikDocking:RadDocking.DocumentHost>
 <telerikDocking:RadSplitContainer >
 <telerikDocking:RadPaneGroup regions:RegionManager.RegionName="MainRegion"
 ItemContainerStyle="{StaticResource RadPaneFactoryStyle}"
 >
 
 </telerikDocking:RadPaneGroup>
 </telerikDocking:RadSplitContainer>
 </telerikDocking:RadDocking.DocumentHost>

 <telerikDocking:RadSplitContainer InitialPosition="DockedRight">
 <telerikDocking:RadPaneGroup regions:RegionManager.RegionName="MainRegion1" >
 
 </telerikDocking:RadPaneGroup>
 </telerikDocking:RadSplitContainer>
 
 </telerikDocking:RadDocking> 
 </Grid>

Configure Region Adapter Mappings in Bootstrap

protected override RegionAdapterMappings ConfigureRegionAdapterMappings()
 {
 var mappings = base.ConfigureRegionAdapterMappings();

 mappings.RegisterMapping(typeof(RadPaneGroup), this.Container.Resolve<RadPaneGroupRegionAdapter>());

 return mappings;
 }

Define the RadPaneGroup RegionAdapter

 
Behavior
public class RadPaneGroupRegionAdapter : RegionAdapterBase<RadPaneGroup>
 {
 public RadPaneGroupRegionAdapter(IRegionBehaviorFactory regionBehaviorFactory)
 : base(regionBehaviorFactory)
 {
 }

 protected override void Adapt(IRegion region, RadPaneGroup regionTarget)
 {
 var docking = regionTarget.ParentOfType<RadDocking>();
 if (docking.DockingPanesFactory == null)
 {
 docking.DockingPanesFactory = new CustomDockingPanesFactory();
 }

 var store = DocumentsStoreAttachment.GetPanesStore(docking);
 if (store == null)
 {
 store = new PanesStore();
 DocumentsStoreAttachment.SetPanesStore(docking, store);
 BindingOperations.SetBinding(
 docking,
 RadDocking.PanesSourceProperty,
 new Binding("PanesSource") { Source = store });
 }
 }

 protected override IRegion CreateRegion()
 {
 return new SingleActiveRegion();
 }

 protected override void AttachBehaviors(IRegion region, RadPaneGroup regionTarget)
 {
 if (region == null)
 throw new System.ArgumentNullException("region");

 var docking = regionTarget.ParentOfType<RadDocking>();
 
 // Add the behavior that syncs the items source items with the rest of the items
 region.Behaviors.Add(RadPaneGroupSyncBehavior.BehaviorKey, new RadPaneGroupSyncBehavior()
 {
 HostControl = regionTarget,
 Docking = docking
 });

 base.AttachBehaviors(region, regionTarget);
 }

 private class PanesStore
 {
 public readonly ObservableCollection<object> Panes = new ObservableCollection<object>();
 ReadOnlyObservableCollection<object> _readonlyDocumentsList;
 public ReadOnlyObservableCollection<object> PanesSource
 {
 get
 {
 return _readonlyDocumentsList ??
 (_readonlyDocumentsList = new ReadOnlyObservableCollection<object>(Panes));
 }
 }
 }

 private static class DocumentsStoreAttachment
 {
 public static readonly DependencyProperty PanesStoreProperty = DependencyProperty.RegisterAttached(
 "PanesStore",
 typeof(PanesStore),
 typeof(DocumentsStoreAttachment),
 new PropertyMetadata(null)
 );
 public static void SetPanesStore(UIElement element, PanesStore value)
 {
 element.SetValue(PanesStoreProperty, value);
 }
 public static PanesStore GetPanesStore(UIElement element)
 {
 return (PanesStore)element.GetValue(PanesStoreProperty);
 }
 }

 private class CustomDockingPanesFactory : DockingPanesFactory
 {
 public RadPaneGroup ActiveGroup { get; internal set; }

 protected override void AddPane(RadDocking radDocking, RadPane pane)
 {
 var group = ActiveGroup;

 pane.SetValue(RadPane.HeaderProperty, DependencyProperty.UnsetValue);

 var style = group.ItemContainerStyle;
 if (style != null)
 {
 pane.SetValue(RadPane.StyleProperty, style);
 }
 group?.Items.Add(pane);
 }
 }

 private class RadPaneGroupSyncBehavior : RegionBehavior, IHostAwareRegionBehavior
 {
 public static readonly string BehaviorKey = "RadPaneGroupSyncBehavior";
 private bool _updatingActiveViewsInManagerActiveContentChanged;
 private RadPaneGroup _radPaneGroup;
 private PanesStore _documentStore;

 public DependencyObject HostControl
 {
 get
 {
 return _radPaneGroup;
 }

 set
 {
 _radPaneGroup = value as RadPaneGroup;
 }
 }



 public RadDocking Docking { get; internal set; }

 /// <summary>
 /// Starts to monitor the <see cref="IRegion"/> to keep it in synch with the items of the <see cref="HostControl"/>.
 /// </summary>
 protected override void OnAttach()
 {
 bool itemsSourceIsSet = _radPaneGroup.ItemsSource != null;


 if (itemsSourceIsSet)
 {
 throw new InvalidOperationException();
 }

 SynchronizeItems();

 Docking.ActivePaneChanged += ManagerActiveContentChanged;
 Region.ActiveViews.CollectionChanged += ActiveViews_CollectionChanged;
 Region.Views.CollectionChanged += Views_CollectionChanged;
 Docking.Close += _dockingManager_DocumentClosed;

 }

 private void _dockingManager_DocumentClosed(object sender, StateChangeEventArgs e)
 {
 foreach (var pane in e.Panes)
 {
 if (Region.Views.Contains(pane.Content))
 {
 Region.Remove(pane.Content);
 }
 }

 }


 private void Views_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
 {
 if (e.Action == NotifyCollectionChangedAction.Add)
 {
 int startIndex = e.NewStartingIndex;

 foreach (var newItem in e.NewItems)
 {
 var panesFactory = ((CustomDockingPanesFactory)Docking.DockingPanesFactory);
 panesFactory.ActiveGroup = _radPaneGroup;
 _documentStore.Panes.Insert(0, newItem);
 }

 }
 else if (e.Action == NotifyCollectionChangedAction.Remove)
 {
 foreach (object oldItem in e.OldItems)
 {
 _documentStore.Panes.Remove(oldItem);
 }
 }
 }

 private void SynchronizeItems()
 {
 var store = DocumentsStoreAttachment.GetPanesStore(Docking);



 _documentStore = store;
 foreach (object view in Region.Views)
 {
 ((CustomDockingPanesFactory)Docking.DockingPanesFactory).ActiveGroup = _radPaneGroup;
 _documentStore.Panes.Insert(0, view);

 }
 }

 private void ActiveViews_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
 {
 if (_updatingActiveViewsInManagerActiveContentChanged)
 {
 return;
 }

 if (e.Action == NotifyCollectionChangedAction.Add)
 {
 if (Docking.ActivePane != null
 && Docking.ActivePane != e.NewItems[0]
 && Region.ActiveViews.Contains(Docking.ActivePane.Content))
 {
 Region.Deactivate(Docking.ActivePane.Content);
 }

 var pane = Docking.Panes.FirstOrDefault(x => x.Content.Equals(e.NewItems[0]));
 Docking.ActivePane = pane;
 }
 else if (e.Action == NotifyCollectionChangedAction.Remove &&
 e.OldItems.Contains(Docking.ActivePane))
 {
 Docking.ActivePane = null;
 }
 }

 private void ManagerActiveContentChanged(object sender, EventArgs e)
 {
 try
 {
 _updatingActiveViewsInManagerActiveContentChanged = true;

 if (Docking.Equals(sender))
 {
 var activePane = Docking.ActivePane;
 if (activePane != null)
 {
 var activeViews = Region.ActiveViews.Where(it => it != activePane.Content).ToList();
 foreach (var item in activeViews)
 {
 Region.Deactivate(item);
 }

 if (Region.Views.Contains(activePane.Content) && !Region.ActiveViews.Contains(activePane.Content))
 {
 Region.Activate(activePane.Content);
 }
 }



 }
 }
 finally
 {
 _updatingActiveViewsInManagerActiveContentChanged = false;
 }
 }

 }
 }

Prism Navigation – ViewModel first

Navigating to view using prism navigation service is straight forward and there are lot of examples on web around this.With MVVM i always found ViewModel first approach as best because of lot of reasons, some of them are

  • With MVVM, ViewModels is your application
  • No threading issues as you will always create view models which can be created on any thread.
  • More responsive , you are not dealing with dispatcher context to create views and let WPF system decide when to create views.
  • No Memory leaks, The life cycle of views is controlled by WPF system in most efficient way
  • More testable application, Since you app lives in view models and they don’t have UI beasts,
  • And lot more..

This post cover a small trick to implement the ViewModel first navigation using Prism 5.0 for WPF.

1. What ever IoC you are using (the code is based on StructuralMap) expose the RegisterForNavigation<T> method and from module initialization, register interface of viewModels participating in navigation process. The RegisterForNavigation<T> will look like

public static void RegisterTypeForNavigation<T>(this ConfigurationExpression reg)
{
reg.For<object>().Use(() => ServiceLocator.Current.GetInstance(typeof(T)))
.Named(typeof(T).FullName);
}

And some where in module initialization you call this method

configurationExpression
.RegisterTypeForNavigation<ICustomerDetailViewModel>();

And to navigate to this view model just use

var parameters = new NavigationParameters { { "Activity", SelectedActivity }, { "ColumnName", columnName } };
var uri = new Uri(typeof(ICustomerDetailViewModel).FullName, UriKind.RelativeOrAbsolute);
regionManager.RequestNavigate(ShellRegions.Workspace, uri, parameters);

And obviously some where in you data template definition there should a (DataTemplate) mapping which WPF uses to swap the view model with corresponding view (and set data context too)..