Client -II-

xhdo17c
My previous post explained a bit the overall concept of this client development series. The essence of MVVM pattern was discussed, as well the base line of the project structure was created. Now I pick it up and extend it by some implementations step by step until we get a running version of the client app which is able to connect successfully to the previous covered server application.

Like in previous Server App series, I will follow the path to implement each projects file in step by step and explain the interaction with other not present entities if necessary. So lets start with the Client project and implement the start up logic.

Project “Client”

This is the Startup project with program entry point. In my previous post I has some comments about how WPF applications handle startup.

Program Start Entry Point

Here we find the App.xaml.cs were the startup logic will be present. During start, an instance of App() is created by the static void Main() entry point.

using System;
using System.Diagnostics;
using System.Globalization;
using System.Threading;
using System.Windows;
using ViewModels;
using Views;

namespace Client
{
    public partial class App
    {
        private const string DefaultCulture = "en-US";

        public App()
        {
            PresentationTraceSources.DataBindingSource.Listeners.Add(new ConsoleTraceListener());
            PresentationTraceSources.DataBindingSource.Switch.Level = SourceLevels.Warning;
        }

        protected override void OnStartup(StartupEventArgs e)
        {
            AppDomain.CurrentDomain.UnhandledException += CurrentDomainOnUnhandledException;
            base.OnStartup(e);

            ShutdownMode = ShutdownMode.OnMainWindowClose;

            var core = new Core.Core { ViewDispatcher = new ViewDispatcher() };

            try
            {
                if (string.IsNullOrEmpty(core.Settings.CurrentCulture))
                    core.Settings.CurrentCulture = DefaultCulture;

                var culture = new CultureInfo(core.Settings.CurrentCulture);
                Thread.CurrentThread.CurrentUICulture = culture;
            }
            catch (Exception ex)
            {
            }

            var vm = new MainViewModel(core);
            MainWindow = new MainView { DataContext = vm };

            MainWindow.Show();
        }

        private void CurrentDomainOnUnhandledException(object sender, UnhandledExceptionEventArgs args)
        {
        }
    }
}

 It is important to mention that this need to be the only “App.xaml” class from all projects of the entire solution! If Visual Studio creates another App.xaml in any class library, just delete it to avoid them interfering.

Ok,what it’s mainly do during startup, is just creating a new instance of the main settings class “Core”. This Core instance is passed to the MainViewModel to create the main window with all necessary settings for initialization during startup.

View Dispatcher

This class exists to keep the overview about all Views and ViewModels and their connection. The best way to keep the overview is – right – a simple list.

using Client.VMDispatcher;
using Client.ViewModels;
using Client.Views;

namespace Client
{
    internal class ViewDispatcher : ViewDispatcherBase
    {
        ///
<summary>
        ///     Initialize view to view model dictionary
        /// </summary>

        public ViewDispatcher()
        {
            ViewModelToView.Add(new ResolveItem(typeof(LoginViewModel), null, typeof(LoginView)));
        }
    }
}

bild1Currently there is only one element named LoginView <=> LoginViewModel in our list type of , cause the first target is just to connect to the server application – nothing else. Later if more and more entities become relevant, we see why its great to have this list…

Now the Solution should look like in the picture , with one class implemented and marked as startup project.

Now lets move to the 2nd project I want to cover in this post. After talked about a View Dispatcher, it makes sense to continue with ViewModel Dispatcher.

Project “VMDispatcher”

This project handles all about the View and ViewModel connection to apply the MVVM pattern. For now the project contains two classes ViewDispatcherBase.cs and ViewModelDispatcherBase.cs.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Threading;
using Client.Interfaces;
using Microsoft.Win32;
using MessageBox = Xceed.Wpf.Toolkit.MessageBox;

namespace Client.VMDispatcher
{
    public abstract class ViewDispatcherBase : IViewDispatcher
    {
        protected static readonly List&lt;ResolveItem&gt; ViewModelToView = new List&lt;ResolveItem&gt;();
        private readonly Dispatcher _dispatcher = Application.Current.Dispatcher;

        private bool IsSatisfy(ResolveItem item, Type fromType, string context)
        {
            if (item.IncludeInheritance)
                return item.FromType.IsAssignableFrom(fromType) &amp;&amp; item.Context == context;

            return item.FromType == fromType &amp;&amp; item.Context == context;
        }

        ///
&lt;summary&gt;
        ///     Get currently active window in application
        /// &lt;/summary&gt;

        /// &lt;returns&gt;Active window&lt;/returns&gt;
        protected Window GetActiveOrMainWindow()
        {
            if (!_dispatcher.CheckAccess())
            {
                Window w = null;
                _dispatcher.Invoke(new Action(() =&gt; w = GetActiveOrMainWindow()));
                return w;
            }

            var window = Application.Current.Windows.OfType&lt;Window&gt;().FirstOrDefault(i =&gt; i.IsActive);
            if (window == null &amp;&amp; Application.Current.MainWindow != null)
                window = Application.Current.MainWindow.IsVisible ? Application.Current.MainWindow : null;
            return window;
        }

        protected class ResolveItem
        {
            public ResolveItem(Type fromType, string context, Type toType)
            {
                FromType = fromType;
                Context = context;
                ToType = toType;
            }

            public ResolveItem(Type fromType, string context, Type toType, bool includeInheritance)
                : this(fromType, context, toType)
            {
                IncludeInheritance = includeInheritance;
            }

            public Type FromType { get; private set; }
            public string Context { get; private set; }
            public Type ToType { get; private set; }
            public bool IncludeInheritance { get; set; }
        }

        #region IViewDispatcher Implementation
        ///
&lt;summary&gt;
        ///     Show system message box in dispatcher thread
        /// &lt;/summary&gt;

        /// &lt;remarks&gt;
        ///     Might not always show up correctly for versions below 2.7:
        ///     https://wpftoolkit.codeplex.com/workitem/21777
        /// &lt;/remarks&gt;
        public void ShowMessageBox(string message)
        {
            if (!_dispatcher.CheckAccess())
            {
                _dispatcher.Invoke(new Action(() =&gt; ShowMessageBox(message)));
                return;
            }

            if (String.IsNullOrWhiteSpace(message))
                return;

            var w = GetActiveOrMainWindow();
            if (w == null)
                MessageBox.Show(message, "Info", MessageBoxButton.OK, MessageBoxImage.Information);
            else
                MessageBox.Show(w, message, "Info", MessageBoxButton.OK, MessageBoxImage.Information);
        }

        ///
&lt;summary&gt;
        ///     Show system message box in dispatcher thread
        /// &lt;/summary&gt;

        /// &lt;remarks&gt;
        ///     Might not always show up correctly for versions below 2.7:
        ///     https://wpftoolkit.codeplex.com/workitem/21777
        /// &lt;/remarks&gt;
        public MessageBoxResult ShowMessageBox(string message, string caption, MessageBoxButton buttons, MessageBoxImage image)
        {
            if (!_dispatcher.CheckAccess())
            {
                var res = default(MessageBoxResult);
                _dispatcher.Invoke(new Action(() =&gt; { res = ShowMessageBox(message, caption, buttons, image); }));
                return res;
            }

            var w = GetActiveOrMainWindow();
            if (w == null)
                return MessageBox.Show(null, message, caption, buttons, image);

            return MessageBox.Show(w, message, caption, buttons, image);
        }

        ///
&lt;summary&gt;
        ///     Show view according to T - type of view model
        /// &lt;/summary&gt;

        /// &lt;typeparam name="T"&gt;Type  of view model&lt;/typeparam&gt;
        /// &lt;param name="viewModel"&gt;Instance of view model&lt;/param&gt;
        /// &lt;param name="closeAction"&gt;Delegate that will be called after the view is closed&lt;/param&gt;
        /// &lt;param name="context"&gt;View context&lt;/param&gt;
        public void ShowView&lt;T&gt;(T viewModel, Action closeAction = null, string context = null) where T : class
        {
            if (viewModel == null)
                throw new ArgumentNullException("viewModel");

            if (!_dispatcher.CheckAccess())
            {
                _dispatcher.BeginInvoke(new Action(() =&gt; ShowView(viewModel, closeAction, context)));
                return;
            }

            var viewType = ViewModelToView.Single(item =&gt; IsSatisfy(item, viewModel.GetType(), context)).ToType;
            var view = (Window)Activator.CreateInstance(viewType);
            view.DataContext = viewModel;

            view.Closed += (sender, args) =&gt;
            {
                if (closeAction != null)
                    closeAction.Invoke();
            };

            view.Show();
        }

        ///
&lt;summary&gt;
        ///     Show view according to T - type of view model in dialog mode
        /// &lt;/summary&gt;

        /// &lt;typeparam name="T"&gt;Type  of view model&lt;/typeparam&gt;
        /// &lt;param name="viewModel"&gt;Instance of view model&lt;/param&gt;
        /// &lt;param name="context"&gt;View context&lt;/param&gt;
        /// &lt;returns&gt;Result of dialog&lt;/returns&gt;
        public bool? ShowDialogView&lt;T&gt;(T viewModel, string context = null) where T : class
        {
            if (viewModel == null)
                throw new ArgumentNullException("viewModel");

            if (!_dispatcher.CheckAccess())
            {
                bool? res = null;
                _dispatcher.Invoke(new Action(() =&gt; { res = ShowDialogView(viewModel, context); }));
                return res;
            }

            var viewType = ViewModelToView.Single(item =&gt; IsSatisfy(item, viewModel.GetType(), context)).ToType;
            var view = (Window)Activator.CreateInstance(viewType);
            view.DataContext = viewModel;

            return view.ShowDialog();
        }

        ///
&lt;summary&gt;
        ///     Show system file save dialog
        /// &lt;/summary&gt;

        /// &lt;param name="filter"&gt;Extension filters&lt;/param&gt;
        /// &lt;param name="initialDirectory"&gt;Initial directory&lt;/param&gt;
        /// &lt;returns&gt;Save  file path&lt;/returns&gt;
        public string ShowSaveFileDialog(string filter = null, string initialDirectory = null)
        {
            var dialog = new SaveFileDialog { Filter = filter, InitialDirectory = initialDirectory };
            var res = dialog.ShowDialog(GetActiveOrMainWindow());
            return res == true ? dialog.FileName : null;
        }

        ///
&lt;summary&gt;
        ///     Show system open file dialog
        /// &lt;/summary&gt;

        /// &lt;param name="filter"&gt;Extension filters&lt;/param&gt;
        /// &lt;param name="initialDirectory"&gt;Initial directory&lt;/param&gt;
        /// &lt;returns&gt;Open file path&lt;/returns&gt;
        public string ShowOpenFileDialog(string filter = null, string initialDirectory = null)
        {
            var dialog = new OpenFileDialog { Filter = filter, InitialDirectory = initialDirectory };
            var res = dialog.ShowDialog(GetActiveOrMainWindow());
            return res == true ? dialog.FileName : null;
        }

        ///
&lt;summary&gt;
        ///     Method that invoke delegate asynchronously in dispatcher thread
        /// &lt;/summary&gt;

        /// &lt;param name="action"&gt;Invocation delegate&lt;/param&gt;
        public void BeginInvokeInUI(Action action)
        {
            if (!_dispatcher.CheckAccess())
                _dispatcher.BeginInvoke(action);
            else
                action();
        }

        ///
&lt;summary&gt;
        ///     Method that invoke delegate synchronously in dispatcher thread
        /// &lt;/summary&gt;

        /// &lt;param name="action"&gt;Invocation delegate&lt;/param&gt;
        public void InvokeInUI(Action action)
        {
            if (!_dispatcher.CheckAccess())
                _dispatcher.Invoke(action);
            else
                action();
        }

        ///
&lt;summary&gt;
        ///     Method that called when application is closing
        /// &lt;/summary&gt;

        /// &lt;param name="exitCode"&gt;Exit code&lt;/param&gt;
        public void ApplicationExit(int exitCode)
        {
            if (!_dispatcher.CheckAccess())
            {
                _dispatcher.BeginInvoke(new Action(() =&gt; ApplicationExit(exitCode)));
                return;
            }

            Application.Current.Shutdown(exitCode);
        }

        #endregion
    }
}

And here we have the event handler for the ViewModel data to provide them to the View if they become updated. All ViewModels inherit this class:

using System;
using System.ComponentModel;

namespace Client.VMDispatcher
{
    public abstract class ViewModelDispatcherBase : INotifyPropertyChanged, IDisposable
    {
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        public event PropertyChangedEventHandler PropertyChanged;

        [NotifyPropertyChangedInvocator]
        protected virtual void OnPropertyChanged(string propertyName)
        {
            var handler = PropertyChanged;
            if (handler != null)
                handler(this, new PropertyChangedEventArgs(propertyName));
        }

        ~ViewModelDispatcherBase()
        {
            Dispose(false);
        }

        protected virtual void Dispose(bool disposing)
        {
        }
    }

    [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
    public sealed class NotifyPropertyChangedInvocatorAttribute : Attribute
    {
        public NotifyPropertyChangedInvocatorAttribute()
        {
        }

        public NotifyPropertyChangedInvocatorAttribute(string parameterName)
        {
            ParameterName = parameterName;
        }

        public string ParameterName { get; private set; }
    }
}

Finally the solution should look like in the picture with two projects and their implemented classes.

Bild2.png

In my next post I will demystify the settings instance for the main window object. This is just a trick to use default folders and setting during application startup.

If you have suggestions or comments, feel free to let me know. To get a preview about upcoming topics or what was posted in past, please have a look here: Content++. If you like this Blog and don’t want to miss any update, just follow here at Twitter or Facebook.

Funny Movie:

http://img-9gag-fun.9cache.com/photo/a84ZjPQ_460sv.mp4

quantocracy-badge-130

Advertisements

2 thoughts on “Client -II-

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s