Click here to Skip to main content
15,895,746 members
Articles / Programming Languages / C#

Simple Model-View-ViewModel in Windows Forms

Rate me:
Please Sign up or sign in to vote.
4.83/5 (4 votes)
31 Jan 2013CPOL3 min read 29K   400   16  
Just a tip about using data bindings in Windows Forms.
// Screencast Capture, free screen recorder
// http://screencast-capture.googlecode.com
//
// Copyright © César Souza, 2012-2013
// cesarsouza at gmail.com
//
//    This program is free software; you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation; either version 2 of the License, or
//    (at your option) any later version.
//
//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with this program; if not, write to the Free Software
//    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
// 

namespace ScreenCapture
{
    using System;
    using System.Windows.Forms;
    using System.Linq.Expressions;

    /// <summary>
    ///   Extension methods for binding.
    /// </summary>
    /// 
    /// <remarks>
    ///   The methods available in this class provide an easier way to configure windows forms
    ///   databindings using lambda functions. It is possible to direct configure the binding
    ///   (such as format and parsing options for two-way binding) in a single, one-line call
    ///   at the time of the binding.
    /// </remarks>
    /// 
    /// <example>
    /// <para>
    ///   The simplest usage scenario is:
    /// </para>
    /// <code>
    ///   myButton.Bind("Visible", viewModel, "SomeModelViewProperty");
    /// </code>
    /// 
    /// <para>
    ///   If you wish to have control over the binding, you can apply a transformation
    ///   (similar to what WPF converters do). Supposing you have a boolean property,
    ///   you can use:</para>
    /// <code>
    ///   myButton.Bind("Visible", viewModel, "SomeModelViewProperty", (bool value) => !value);
    /// </code>
    /// 
    /// <para>
    ///   In case you desire type-safety or are using obfuscation, you can use:
    /// </para>
    /// <code>
    ///   myButton.Bind(b => b.Enabled, viewModel, m => m.SomeViewModelProperty);
    /// </code>
    /// 
    /// <para>
    ///   And again you can fine control the behavior of the binding by using:
    /// </para>
    /// <code>
    ///   myButton.Bind(b => b.Enabled, viewModel, m => m.SomeViewModelProperty, value => !value);
    /// </code>
    /// </example>
    /// 
    public static class BindingExtensions
    {


        /// <summary>
        ///   Attaches a binding to a component, using the given format transformation.
        /// </summary>
        /// 
        /// <typeparam name="TSource">The type of the source property.</typeparam>
        /// <typeparam name="TDestination">The type of the destination.</typeparam>
        /// 
        /// <param name="component">The component to which the binding applies (such as a control in the form).</param>
        /// <param name="binding">The binding object which defines the property being bound (such as a ViewModel property).</param>
        /// <param name="format">The format transformation which transforms the model's property to a control's value.</param>
        /// 
        public static void Bind<TSource, TDestination>(this IBindableComponent component, Binding binding, Func<TSource, TDestination> format)
        {
            if (component == null) throw new ArgumentNullException("component");
            if (binding == null) throw new ArgumentNullException("binding");
            binding.Format += (sender, e) => e.Value = format((TSource)e.Value);
            component.DataBindings.Add(binding);
        }

        /// <summary>
        ///   Attaches a binding to a component, using the given format transformation.
        /// </summary>
        /// 
        /// <typeparam name="TSource">The type of the source property.</typeparam>
        /// <typeparam name="TDestination">The type of the destination.</typeparam>
        /// 
        /// <param name="component">The component to which the binding applies (such as a control in the form).</param>
        /// <param name="binding">The binding object which defines the property being bound (such as a ViewModel property).</param>
        /// <param name="format">The format transformation which transforms the model's property to a control's value.</param>
        /// <param name="parse">The parse transformation which transforms the control's value into the model's property.</param>
        /// 
        public static void Bind<TSource, TDestination>(this IBindableComponent component, Binding binding, Func<TSource, TDestination> format, Func<TDestination, TSource> parse)
        {
            if (component == null) throw new ArgumentNullException("component");
            if (binding == null) throw new ArgumentNullException("component");
            binding.Parse += (sender, e) => e.Value = parse((TDestination)e.Value);
            binding.Format += (sender, e) => e.Value = format((TSource)e.Value);
            component.DataBindings.Add(binding);
        }

        /// <summary>
        ///   Attaches a binding to a component, using the given format transformation.
        /// </summary>
        /// 
        /// <typeparam name="TSource">The type of the source property.</typeparam>
        /// <typeparam name="TDestination">The type of the destination.</typeparam>
        /// 
        /// <param name="component">The component to which the binding applies (such as a control in the form).</param>
        /// <param name="propertyName">The name of the control property to bind.</param>
        /// <param name="dataSource">An object that represents the data source.</param>
        /// <param name="dataMember">The property or list to bind to.</param>
        /// <param name="format">The format transformation which transforms the model's property to a control's value.</param>
        /// 
        public static void Bind<TSource, TDestination>(this IBindableComponent component,
            string propertyName, object dataSource, string dataMember, Func<TSource, TDestination> format)
        {
            if (component == null) throw new ArgumentNullException("component");
            Bind(component, new Binding(propertyName, dataSource, dataMember), format);
        }

        /// <summary>
        ///   Attaches a binding to a component, using the given format transformation.
        /// </summary>
        /// 
        /// <typeparam name="TSource">The type of the source property.</typeparam>
        /// <typeparam name="TDestination">The type of the destination.</typeparam>
        /// 
        /// <param name="component">The component to which the binding applies (such as a control in the form).</param>
        /// <param name="propertyName">The name of the control property to bind.</param>
        /// <param name="dataSource">An object that represents the data source.</param>
        /// <param name="dataMember">The property or list to bind to.</param>
        /// <param name="format">The format transformation which transforms the model's property to a control's value.</param>
        /// <param name="parse">The parse transformation which transforms the control's value into the model's property.</param>
        /// 
        public static void Bind<TSource, TDestination>(this IBindableComponent component,
            string propertyName, object dataSource, string dataMember,
            Func<TSource, TDestination> format, Func<TDestination, TSource> parse)
        {
            if (component == null) throw new ArgumentNullException("component");
            Bind(component, new Binding(propertyName, dataSource, dataMember, true), format, parse);
        }

        /// <summary>
        ///   Attaches a binding to a component, using the given format transformation.
        /// </summary>
        /// 
        /// <param name="component">The component to which the binding applies (such as a control in the form).</param>
        /// <param name="binding">The binding object which defines the property being bound (such as a ViewModel property).</param>
        /// 
        public static void Bind(this IBindableComponent component, Binding binding)
        {
            if (component == null) throw new ArgumentNullException("component");
            component.DataBindings.Add(binding);
        }

        /// <summary>
        ///   Attaches a binding to a component, using the given format transformation.
        /// </summary>
        /// 
        /// <param name="component">The component to which the binding applies (such as a control in the form).</param>
        /// <param name="propertyName">The name of the control property to bind.</param>
        /// <param name="dataSource">An object that represents the data source.</param>
        /// <param name="dataMember">The property or list to bind to.</param>
        /// 
        public static void Bind(this IBindableComponent component,
            string propertyName, object dataSource, string dataMember)
        {
            if (component == null) throw new ArgumentNullException("component");
            Bind(component, new Binding(propertyName, dataSource, dataMember));
        }

        /// <summary>
        ///   Attaches a binding to a component.
        /// </summary>
        /// 
        /// <param name="component">The component to which the binding applies (such as a control in the form).</param>
        /// <param name="propertyName">The name of the control property to bind.</param>
        /// <param name="dataSource">An object that represents the data source.</param>
        /// <param name="dataMember">The property or list to bind to.</param>
        /// 
        public static void Bind<TControl, TModel, TSource>(this TControl component,
            Expression<Func<TControl, object>> propertyName, TModel dataSource,
            Expression<Func<TModel, TSource>> dataMember)
            where TControl : IBindableComponent
        {
            string nameString, dataString;
            getNames(propertyName, dataMember, out nameString, out dataString);

            if (nameString == null) throw new ArgumentException("Unexpected expression.", "propertyName");
            if (dataString == null) throw new ArgumentException("Unexpected expression.", "dataMember");

            Bind(component, nameString, dataSource, dataString);
        }

        /// <summary>
        ///   Attaches a binding to a component, using the given format transformation.
        /// </summary>
        /// 
        /// <param name="component">The component to which the binding applies (such as a control in the form).</param>
        /// <param name="propertyName">The name of the control property to bind.</param>
        /// <param name="dataSource">An object that represents the data source.</param>
        /// <param name="dataMember">The property or list to bind to.</param>
        /// <param name="format">The format transformation which transforms the model's property to a control's value.</param>
        /// 
        public static void Bind<TControl, TModel, TSource, TDestination>(this TControl component,
           Expression<Func<TControl, TDestination>> propertyName, TModel dataSource,
           Expression<Func<TModel, TSource>> dataMember, Func<TSource, TDestination> format)
            where TControl : IBindableComponent
        {
            string nameString, dataString;
            getNames(propertyName, dataMember, out nameString, out dataString);

            if (nameString == null) throw new ArgumentException("Unexpected expression.", "propertyName");
            if (dataString == null) throw new ArgumentException("Unexpected expression.", "dataMember");

            Bind(component, nameString, dataSource, dataString, format);
        }

        /// <summary>
        ///   Attaches a binding to a component, using the given format transformation.
        /// </summary>
        /// 
        /// <param name="component">The component to which the binding applies (such as a control in the form).</param>
        /// <param name="propertyName">The name of the control property to bind.</param>
        /// <param name="dataSource">An object that represents the data source.</param>
        /// <param name="dataMember">The property or list to bind to.</param>
        /// <param name="format">The format transformation which transforms the model's property to a control's value.</param>
        /// <param name="parse">The parse transformation which transforms the control's value into the model's property.</param>
        /// 
        public static void Bind<TControl, TModel, TSource, TDestination>(this TControl component,
           Expression<Func<TControl, TDestination>> propertyName, TModel dataSource,
           Expression<Func<TModel, TSource>> dataMember, Func<TSource, TDestination> format,
            Func<TDestination, TSource> parse)
            where TControl : IBindableComponent
        {
            string nameString, dataString;
            getNames(propertyName, dataMember, out nameString, out dataString);

            if (nameString == null) throw new ArgumentException("Unexpected expression.", "propertyName");
            if (dataString == null) throw new ArgumentException("Unexpected expression.", "dataMember");

            Bind(component, nameString, dataSource, dataString, format, parse);
        }



        private static void getNames<TControl, TModel, TSource, TDestination>(
            Expression<Func<TControl, TDestination>> propertyNameExpression,
            Expression<Func<TModel, TSource>> dataMemberExpression,
            out string propertyName, out string dataMember)
        {
            propertyName = null;
            dataMember = null;

            MemberExpression exp1 = memberInfo(propertyNameExpression);

            if (exp1 == null) return;
            propertyName = exp1.Member.Name;

            MemberExpression exp2 = memberInfo(dataMemberExpression);

            if (exp2 == null) return;
            dataMember = exp2.Member.Name;
        }

        private static MemberExpression memberInfo(LambdaExpression exp)
        {
            MemberExpression memberExp = null;

            if (exp.Body.NodeType == ExpressionType.MemberAccess)
                memberExp = exp.Body as MemberExpression;

            else if (exp.Body.NodeType == ExpressionType.Convert)
                memberExp = ((UnaryExpression)exp.Body).Operand as MemberExpression;

            return memberExp;
        }

    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Engineer NAVER LABS Europe
France France
Computer and technology enthusiast, interested in artificial intelligence and image processing. Has a Master's degree on Computer Science specialized on Image and Signal Processing, with expertise on Machine Learning, Computer Vision, Pattern Recognition and Data Mining systems. Author of the Accord.NET Framework for developing scientific computing applications.

If you would like to hire good developers to build your dream application, please check out DaitanGroup, one of the top outsourcing companies in Brazil. This company, located in Brazil's Sillicon Valley but with US-based offices, has huge experience developing telecommunications software for large and small companies worldwide.

Comments and Discussions