• Winform快速开发组件的实现(一)


        好久好久没有露面了,呵呵,对于写文章都有点生疏了。

        在拿到任何一个项目,不管是b/s的还是c/s,我不会立即开始写代码,我一般会为使这些项目能够快速开发制定一系列的支持组件,虽然可能前期会付出一些代价,但不管是应付当前的任务,还是为以后形成一种可持续改进的开发模式,都是有意义的。

        最近几年都忙于应付b/s方面的项目,所以winform的一些东西已经不是怎么拿得出手了,虽然以前也写过一系列的组件,毕竟技术革新太快了,现在已经不太适应了。

        今天介绍的只是一小部份,主要实现信息编辑窗体中各控件与数据属性之间的绑定、取值与存值、数据验证

        大家知道,这种小型的MIS项目最繁琐的莫过于编辑页面的布局,数据显示和数据保存,这往往会占用一半的时间。

        一、窗体与实体类的映射

        需要定义一个Form基类,并提供一个EntityType属性,这个属性用于绑定一个实体类,因为一个单一的窗体一般只会与一个实体相关联。

    namespace EasyBook.Client.Forms
    {
        /// <summary>
        /// 定义信息编辑的窗体。
        /// </summary>
        public partial class EditForm : FormBase, IEntitySupport
        {
            public EditForm()
            {
                InitializeComponent();
            }
    
            /// <summary>
            /// 获取或设置实体类型。
            /// </summary>
            [Editor(typeof(EntityTypeEditor), typeof(UITypeEditor))]
            [Description("获取或设置实体类型。")]
            public Type EntityType { get; set; }
        }
    }

        IEntitySupport接口只定义了EntityType属性。
        注意到属性上的Editor特性了吗,它提供一种编辑器,可以从当前的程序集中枚举出所有的实体类型,以供我们选择。

    // -----------------------------------------------------------------------
    // <copyright company="Fireasy"
    //      email="faib920@126.com"
    //      qq="55570729">
    //   (c) Copyright Fireasy. All rights reserved.
    // </copyright>
    // -----------------------------------------------------------------------
    using EasyBook.Common;
    using Fireasy.Common.Extensions;
    using Fireasy.Data.Entity;
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Drawing.Design;
    using System.Linq;
    using System.Reflection;
    using System.Windows.Forms;
    using System.Windows.Forms.Design;
    
    namespace EasyBook.Client.Forms
    {
        public class EntityTypeEditor : UITypeEditor
        {
            EntityTypeListBox modelUI;
    
            public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
            {
                if (provider != null)
                {
                    var edSvc = (IWindowsFormsEditorService)provider.GetService(typeof(IWindowsFormsEditorService));
                    var support = (IEntitySupport)context.Instance;
                    if (edSvc == null)
                    {
                        return value;
                    }
    
                    modelUI = new EntityTypeListBox(support);
                    modelUI.Start(edSvc, value);
    
                    edSvc.DropDownControl(modelUI);
                    value = modelUI.Value;
                    modelUI.End();
                }
    
                return value;
            }
    
            public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
            {
                return UITypeEditorEditStyle.DropDown;
            }
    
            public override bool IsDropDownResizable
            {
                get
                {
                    return true;
                }
            }
    
            private class EntityTypeListBox : ListView
            {
                private IEntitySupport support;
                private IWindowsFormsEditorService edSvc;
    
                public EntityTypeListBox(IEntitySupport support)
                {
                    View = System.Windows.Forms.View.Details;
                    Columns.Add(new ColumnHeader { Text = "", Width = 160 });
                    HeaderStyle = ColumnHeaderStyle.None;
                    FullRowSelect = true;
                    HideSelection = false;
                    Height = 400;
    
                    Click += EntityTypeListBox_Click;
                    this.support = support;
                    LoadTypes();
                }
    
                void EntityTypeListBox_Click(object sender, EventArgs e)
                {
                    Value = base.SelectedItems[0].Tag;
                    edSvc.CloseDropDown();
                }
    
                /// <summary>
                /// 加载所有可选择的类型。
                /// </summary>
                private void LoadTypes()
                {
                    //循环所引用的所有程序集
                    foreach (var assemblyName in support.GetType().Assembly.GetReferencedAssemblies())
                    {
                        try
                        {
                            var assembly = Assembly.Load(assemblyName.FullName);
                            if (!IsFireasyEntityAssembly(assembly))
                            {
                                continue;
                            }
    
                            foreach (var type in GetEntityTypes(assembly))
                            {
                                var item = new ListViewItem(type.Name);
                                item.Tag = type;
                                Items.Add(item);
                            }
                        }
                        catch
                        {
                        }
                    }
                }
    
                /// <summary>
                /// 判断程序集是否是 Fireasy Entity 实体程序集。
                /// </summary>
                /// <param name="assembly"></param>
                /// <returns></returns>
                private bool IsFireasyEntityAssembly(Assembly assembly)
                {
                    return assembly.IsDefined<FireasyEntityAssemblyAttribute>();
                }
    
                /// <summary>
                /// 获取指定程序集中的实体类集合。
                /// </summary>
                /// <param name="assembly"></param>
                /// <returns></returns>
                private IEnumerable<Type> GetEntityTypes(Assembly assembly)
                {
                    return assembly.GetExportedTypes().Where(s => s.IsPublic && !s.IsAbstract && typeof(EntityObject).IsAssignableFrom(s));
                }
    
                public void Start(IWindowsFormsEditorService edSvc, object value)
                {
                    SelectedItems.Clear();
                    this.edSvc = edSvc;
                    Value = value;
    
                    if (value == null)
                    {
                        return;
                    }
    
                    //循环所有项,选中
                    foreach (ListViewItem item in base.Items)
                    {
                        if (item.Tag != null && item.Tag.Equals(value))
                        {
                            item.Focused = true;
                            item.Selected = true;
                            item.EnsureVisible();
                            break;
                        }
                    }
                }
    
                public void End()
                {
                    edSvc = null;
                }
    
                public object Value { get; set; }
            }
        }
    }

        为了提高搜索实体程序集的效率,定义了FireasyEntityAssemblyAttribute特性,在实体类所属的程序集中进行修饰。

        二、控件与属性的映射

        基本的思想还是使用IExtenderProvider接口,对输入控件进行扩展,使之与实体类的属性相对应。

    // -----------------------------------------------------------------------
    // <copyright company="Fireasy"
    //      email="faib920@126.com"
    //      qq="55570729">
    //   (c) Copyright Fireasy. All rights reserved.
    // </copyright>
    // -----------------------------------------------------------------------
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Drawing.Design;
    using System.Windows.Forms;
    
    namespace EasyBook.Client.Forms
    {
        /// <summary>
        /// 扩展输入控件,使它们绑定到实体类中的某一个属性,以便能够自动化处理数据显示和数据保存。
        /// </summary>
        [ProvideProperty("PropertyName", typeof(Control))]
        public class EntityPropertyExtend : Component, IExtenderProvider
        {
            //控件与属性名称的键值对
            private Dictionary<Control, string> properties;
    
            public EntityPropertyExtend()
            {
                properties = new Dictionary<Control, string>();
            }
    
            public EntityPropertyExtend(IContainer container)
                : this()
            {
                container.Add(this);
            }
    
            /// <summary>
            /// 获取控件与属性名称的键值对。
            /// </summary>
            /// <returns></returns>
            public Dictionary<Control, string> GetProperties()
            {
                return properties;
            }
    
            /// <summary>
            /// 判断哪些控件能够被扩展。
            /// </summary>
            /// <param name="extendee"></param>
            /// <returns></returns>
            public bool CanExtend(object extendee)
            {
                return ControlEntityMapHelper.IsSupported(extendee.GetType());
            }
    
            /// <summary>
            /// 获取控件所对应的属性的名称。此属性能够使用编辑器选择。
            /// </summary>
            /// <param name="control"></param>
            /// <returns></returns>
            [Editor(typeof(EntityPropertyEditor), typeof(UITypeEditor))]
            public string GetPropertyName(Control control)
            {
                if (properties.ContainsKey(control))
                {
                    return properties[control];
                }
    
                return string.Empty;
            }
    
            /// <summary>
            /// 设置控件所对应的属性名称。
            /// </summary>
            /// <param name="control"></param>
            /// <param name="propertyName"></param>
            public void SetPropertyName(Control control, string propertyName)
            {
                if (properties.ContainsKey(control))
                {
                    if (string.IsNullOrEmpty(propertyName))
                    {
                        properties.Remove(control);
                    }
                    else
                    {
                        properties[control] = propertyName;
                    }
                }
                else if (!string.IsNullOrEmpty(propertyName))
                {
                    properties.Add(control, propertyName);
                }
            }
        }
    }

        注意,属性名称的指定也提供了一个Editor进行选择,这个编辑器比较简单。

    // -----------------------------------------------------------------------
    // <copyright company="Fireasy"
    //      email="faib920@126.com"
    //      qq="55570729">
    //   (c) Copyright Fireasy. All rights reserved.
    // </copyright>
    // -----------------------------------------------------------------------
    using Fireasy.Data.Entity;
    using System;
    using System.ComponentModel;
    using System.Drawing.Design;
    using System.Windows.Forms;
    using System.Windows.Forms.Design;
    
    namespace EasyBook.Client.Forms
    {
        /// <summary>
        /// 实体属性选择编辑器。
        /// </summary>
        public class EntityPropertyEditor : UITypeEditor
        {
            private EntityPropertyListBox modelUI;
    
            public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
            {
                if (provider != null)
                {
                    var edSvc = (IWindowsFormsEditorService)provider.GetService(typeof(IWindowsFormsEditorService));
                    var control = (Control)context.Instance;
                    var form = control.FindForm() as EditForm;
                    if (edSvc == null || form == null || form.EntityType == null)
                    {
                        return value;
                    }
    
                    if (form.EntityType == null)
                    {
                        return value;
                    }
    
                    modelUI = new EntityPropertyListBox(form.EntityType);
                    modelUI.Start(edSvc, value);
    
                    edSvc.DropDownControl(modelUI);
                    value = modelUI.Value;
                    modelUI.End();
                }
    
                return value;
            }
    
            public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
            {
                return UITypeEditorEditStyle.DropDown;
            }
    
            public override bool IsDropDownResizable
            {
                get
                {
                    return true;
                }
            }
    
            private class EntityPropertyListBox : ListView
            {
                private IWindowsFormsEditorService edSvc;
                private Type entityType;
    
                public EntityPropertyListBox(Type entityType)
                {
                    View = System.Windows.Forms.View.Details;
                    Columns.Add(new ColumnHeader { Text = "", Width = 160 });
                    HeaderStyle = ColumnHeaderStyle.None;
                    FullRowSelect = true;
                    HideSelection = false;
                    Height = 400;
    
                    Click += EntityTypeListBox_Click;
                    this.entityType = entityType;
                    LoadProperties();
                }
    
                void EntityTypeListBox_Click(object sender, EventArgs e)
                {
                    Value = base.SelectedItems[0].Text;
                    edSvc.CloseDropDown();
                }
    
                /// <summary>
                /// 加载实体类型中的所有属性。
                /// </summary>
                private void LoadProperties()
                {
                    foreach (var property in PropertyUnity.GetPersistentProperties(entityType))
                    {
                        var item = new ListViewItem(property.Name);
                        Items.Add(item);
                    }
                }
    
                public void Start(IWindowsFormsEditorService edSvc, object value)
                {
                    SelectedItems.Clear();
                    this.edSvc = edSvc;
                    Value = value;
    
                    if (value == null)
                    {
                        return;
                    }
    
                    foreach (ListViewItem item in base.Items)
                    {
                        if (item.Text.Equals(value))
                        {
                            item.Focused = true;
                            item.Selected = true;
                            item.EnsureVisible();
                            break;
                        }
                    }
                }
    
                public void End()
                {
                    edSvc = null;
                }
    
                public object Value { get; set; }
            }
        }
    }


        Fireasy.Data的PropertyUnity类提供了从实体类型中获取所有属性的方法,这个可以参考Fireasy的介绍。

        另外,EntityPropertyExtend的CanExtend方法使用了一个辅助类对控件进行筛选。 

        ControlEntityMapHelper辅助类有一个工厂方法,用于根据不同的控件类型创建一个名叫IControlEntityMapper的实例对象。

        public class ControlEntityMapHelper
        {
            public static bool IsSupported(Type controlType)
            {
                return typeof(TextBox).IsAssignableFrom(controlType) ||
                    typeof(DateTimePicker).IsAssignableFrom(controlType) ||
                    typeof(ComboBox).IsAssignableFrom(controlType);
            }
    
            public static IControlEntityMapper GetMapper(Type controlType)
            {
                if (typeof(TextBox).IsAssignableFrom(controlType))
                {
                    return new TextBoxMapper();
                }
    
                if (typeof(ComboBox).IsAssignableFrom(controlType))
                {
                    return new ComboBoxMapper();
                }
    
                return null;
            }
        }

        IControlEntityMapper接口定义了一个控件与实体属性之间如何进行数据交换,最典型的就是如何将实体的属性填充到控件里,如何将控件的值填充到实体中,以及如何清除控件的值。

        /// <summary>
        /// 提供控件与实体属性之间的数据交换方法。
        /// </summary>
        public interface IControlEntityMapper
        {
            /// <summary>
            /// 从控件中获取值。
            /// </summary>
            /// <param name="control"></param>
            /// <returns></returns>
            object GetValue(Control control);
    
            /// <summary>
            /// 将指定的值填充到控件中。
            /// </summary>
            /// <param name="control"></param>
            /// <param name="value"></param>
            void SetValue(Control control, object value);
    
            /// <summary>
            /// 清除控件的值。
            /// </summary>
            /// <param name="control"></param>
            void Clear(Control control);
        }
    
        public interface IControlEntityMapper<T>
        {
            object GetValue(T control);
    
            void SetValue(T control, object value);
        }

        然后为TextBox、ComboBox等控件定义相应的子类,以实现GetValue和SetValue方法。

        public abstract class ControlEntityMapperBase<T> : IControlEntityMapper, IControlEntityMapper<T> where T : Control
        {
    
            public object GetValue(Control control)
            {
                return GetValue((T)control);
            }
    
            public void SetValue(Control control, object value)
            {
                SetValue((T)control, value);
            }
    
    
            public void Clear(Control control)
            {
                Clear((T)control);
            }
    
            public abstract object GetValue(T control);
    
            public abstract void SetValue(T control, object value);
    
            public abstract void Clear(T control);
        }
    
        public class TextBoxMapper : ControlEntityMapperBase<TextBox>
        {
    
            public override object GetValue(TextBox control)
            {
                return control.Text;
            }
    
            public override void SetValue(TextBox control, object value)
            {
                control.Text = value.ToString();
            }
    
            public override void Clear(TextBox control)
            {
                control.Text = "";
            }
        }
    
    
        public class ComboBoxMapper : ControlEntityMapperBase<ComboBox>
        {
    
            public override object GetValue(ComboBox control)
            {
                return control.SelectedValue;
            }
    
            public override void SetValue(ComboBox control, object value)
            {
                control.SelectedValue = value;
            }
    
            public override void Clear(ComboBox control)
            {
                control.SelectedIndex = -1;
            }
        }

        这样,准备工作就做好了。

        三、业务实现

        现在,新建一个窗体ProductEdit,继承自EditForm。选择EntityType下拉列表中的实体类。

       

        拖一个EntityPropertyExtend控件到窗体上,然后每一个文本框控件被扩展了PropertyName属性。分别为每一个文本框指定对应的属性。

        由于时间太晚了,本来还有如何读取数据填充到窗体上,如何将窗体数据保存到数据库,以及如何进行数据验证等等,只有明天补上了,望见谅。

  • 相关阅读:
    Linux系统调用
    Kubernetes 中强化tab 功能
    Docker镜像构建之案例分享
    网络基础之名词介绍
    网络基础协议之UDP(下篇)
    网络基础协议之UDP(上篇)
    内核升级
    尼恩 Java高并发三部曲 [官方]
    CDN图解(秒懂
    DNS图解(秒懂
  • 原文地址:https://www.cnblogs.com/faib/p/3464558.html
Copyright © 2020-2023  润新知