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 = });
HeaderStyle = ColumnHeaderStyle.None;
FullRowSelect = true;
HideSelection = false;
Height = ; Click += EntityTypeListBox_Click;
this.support = support;
LoadTypes();
} void EntityTypeListBox_Click(object sender, EventArgs e)
{
Value = base.SelectedItems[].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 = });
HeaderStyle = ColumnHeaderStyle.None;
FullRowSelect = true;
HideSelection = false;
Height = ; Click += EntityTypeListBox_Click;
this.entityType = entityType;
LoadProperties();
} void EntityTypeListBox_Click(object sender, EventArgs e)
{
Value = base.SelectedItems[].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 = -;
}
}
这样,准备工作就做好了。
三、业务实现
现在,新建一个窗体ProductEdit,继承自EditForm。选择EntityType下拉列表中的实体类。

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

由于时间太晚了,本来还有如何读取数据填充到窗体上,如何将窗体数据保存到数据库,以及如何进行数据验证等等,只有明天补上了,望见谅。
Winform快速开发组件的实现(一)的更多相关文章
- Winform快速开发组件的实现(二)
		
昨天我们一直在做准备工作,最终表单数据需要从数据库里提取,并保存到数据库,今天接着介绍如何做提取.保存和验证. 四.提取并显示信息 在EditForm我们定义一个InfoId属性,用于接收在列表页面打 ...
 - winform快速开发平台 -> 工作流组件(仿GooFlow)
		
对于web方向的工作流,一直在用gooflow对于目前我的winform开发平台却没有较好的工作流组件. 针对目前的项目经验告诉我们.一个工作流控件是很必要的. 当然在winform方面的工作流第三 ...
 - winform快速开发平台 -> 基础组件之分页控件
		
一个项目控件主要由及部分的常用组件,当然本次介绍的是通用分页控件. 处理思想:我们在处理分页过程中主要是针对数据库操作. 一般情况主要是传递一些开始位置,当前页数,和数据总页数以及相关关联的业务逻辑. ...
 - winform快速开发平台->让有限的资源创造无限的价值!
		
最近一直在维护一套自己的快速开发平台. 主要应对针对C/S架构下的项目.然而对winform这快,还真没有看到过相对好的快速开发平台, 何为快速,在博客园逛了了好久, 预览了很多通用权限管理系统. 确 ...
 - winform快速开发平台 -> 通用权限管理之动态菜单
		
这几个月一直忙APP的项目,没来得及更新项目,想想该抽出时间整理一下开发思路,跟大家分享,同时也希望得到宝贵的建议. 先说一下我们的权限管理的的设计思路,首先一个企业信息化管理系统一定会用到权限管理, ...
 - winform快速开发平台 -> 快速绑定ComboBox数据控件
		
通常我们在处理编辑窗体时.往往会遇到数据绑定.例如combobox控件绑定数据字典可能是我们经常用到的.然而在我的winform快速开发平台中我是如何处理这个频繁的操作呢? 首先,我们要绑定combo ...
 - BOLT.NET 学习笔记(一) 开篇 用.net winform 快速开发 炫酷的界面
		
BOLT.NET 学习笔记(一) 开篇 用.net winform 快速开发 炫酷的界面 bolt 基本介绍 Bolt界面引擎是迅雷公司从2009年开始开发的第四代界面库.迅雷7是首个采用该引擎成功开 ...
 - C#.NET Winform 快速开发平台
		
C/S系统开发框架-企业版 V4.0 (Enterprise Edition) 简介: http://www.csframework.com/cs-framework-4.0.htm 适用软件:适合开 ...
 - SNF开发平台WinForm之六-上传下载组件使用-SNF快速开发平台3.3-Spring.Net.Framework
		
6.1运行效果: 6.2开发实现: 1.先在要使用的项目进行引用,SNF.WinForm.Attachments.dll文件. 2.在工具箱内新建选项卡->选择项,浏览找到文件SNF.WinFo ...
 
随机推荐
- Atitit.列表页面and条件查询的实现最佳实践(1)------设置查询条件and提交查询and返回json数据
			
Atitit.列表页面and条件查询的实现最佳实践(1)------设置查询条件and提交查询and返回json数据 1. 1. 配置条件字段@Conditional 1 1 2. 2. 配置条件字段 ...
 - DL 小记之序
			
在开通博客的1小时03分钟50秒,我写下了第一篇文字.不知道从什么时候开始,什么东西都有个期限,在写下几小时几分几秒之后,我总是习惯性的加上不知道从什么时候开始. 本博客不含惊天动地的故事,想看故事请 ...
 - ucos操作系统的内核有哪些调度方法
			
1)时间片轮番调度法 假设系统中有5个任务,T1,T2,T3,T4,T5,这个时候,操作系统为每一个任务分配时间,比如说我们为T1任务分配10毫秒,为T2任务分配20毫秒,为T3任务分配5毫秒,为T4 ...
 - Revit自定义快递访问工具栏
			
Revit快速访问工具栏提供了了一些常用的绘图工具,Revit默认的快速访问工具栏在Revit界面标题栏最左边,我们可以对快速访问工具栏进行控制,比如添加删除绘图命令,让其显示在功能区下方,编辑分组, ...
 - php加密解密功能类
			
这两天突发奇想想要用php写一个对日常项目加密以及解密的功能,经过努力简单的封装了一个对php代码进行加密解密的类,一些思想也是来自于网络,初步测试用着还行,可以实现对指定项目的加密以及解密(只针对本 ...
 - View 以Diaglog 方式展示
			
OutAdmissionBookingEditor admissionBookingEditor = this.LayoutManager.OpenDialog<OutAdmissionBook ...
 - cer pfx格式数字证书区别
			
作为文件形式存在的证书一般有这几种格式: 1.带有私钥的证书 由Public Key Cryptography Standards #12,PKCS#12标准定义,包含了公钥和私钥的二进制格式的证书形 ...
 - Scala深入浅出实战经典之 List伴生对象操作方法代码实战.
			
王家林亲授<DT大数据梦工厂>大数据实战视频 Scala 深入浅出实战经典(1-64讲)完整视频.PPT.代码下载:百度云盘:http://pan.baidu.com/s/1c0noOt6 ...
 - 安装python官方的mysql库“mysql-connector-python”
			
$ echo https://cdn.mysql.com/Downloads/Connector-Python/mysql-connector-python-2.1.3.tar.gz >> ...
 - Selenium实战脚本集(4)--简单的开发者头条客户端
			
描述 去开发者头条抓取本日的top 10内容,也就是排在前面的10个,需要抓取标题和url 将这些内容保存在数据库,推荐使用sqlite 写个简单的客户端,要求可以展示每日的内容,点击标题后可以打开浏 ...