2023年2月

我们知道,一般都导出的Word文档或者Excel文档,基本上分为两类,一类是动态生成全部文档的内容方式,一种是基于固定模板化的内容输出,后者在很多场合用的比较多,这也是企业报表规范化的一个体现。

我的博客介绍过几篇关于Aspose.Word控件和Aspose.Cell控件的使用操作,如下所示。


使用Aspose.Cell控件实现Excel高难度报表的生成(一)


使用Aspose.Cell控件实现Excel高难度报表的生成(二)


使用Aspose.Cell控件实现Excel高难度报表的生成(三)


使用Aspose.Cell控件实现多个Excel文件的合并

以及关于Word的操作的《
利用Aspose.Word控件实现Word文档的操作

这些都是我本人的总结,是实实在在的从项目中来,到博客中去的经验总结,本篇主要介绍在Web的模板化文档输出中,Aspose.Word控件和Aspose.Cell控件的使用操作。

1、Word模板化的文档导出

模板化的输出,首先是在预定义好固定的模板,然后绑定数据源或者替换相关的文字实现模板化文档的导出操作。

在Aspose.Word的操作对象中,我们可以使用文本替换的方式实现模板内容的修改,简单的操作如下所示。

Document doc = new Document(MyDir + "Document.doc");
doc.Range.Replace(
"wuhuacong", "伍华聪", false, true);

也可以以书签引用替换的方式实现,首先要定义好相应的标签引用,操作如下。

在一个Word文档中某固定位置插入一个标签引用,在Word(2007、2010)的【插入】-【书签】中插入指定位置的书签引用,如下所示。

替换标签内容的代码如下所示。

Aspose.Words.Bookmark bookmark = doc.Range.Bookmarks["ACCUSER_SEX"];if (bookmark != null)
{
bookmark.Text
= "";
}

为了较好展示操作效果,我们先来固定一个Word的模板文档,如下所示。

实现模板化的文档导出,我这里结合两种方式实现内容的替换操作,一个是使用文字替换,一个是使用标签引用,两部分的代码如下所示。

        protected void btnGenWord_Click(objectsender, EventArgs e)
{
Dictionary
<string, string> dictSource = new Dictionary<string, string>();
dictSource.Add(
"TIS_HANDLE_NO", "T0001");
dictSource.Add(
"ACCUSE_INDUSTRY", "出租车");
dictSource.Add(
"ACCUSER_NAME", "张三");string templateFile = Server.MapPath("./Templates/Advice.doc");
Aspose.Words.Document doc
= newAspose.Words.Document(templateFile);//使用文本方式替换 foreach (string name indictSource.Keys)
{
doc.Range.Replace(name, dictSource[name],
true, true);
}
#region 使用书签替换模式Aspose.Words.Bookmark bookmark= doc.Range.Bookmarks["ACCUSER_SEX"];if (bookmark != null)
{
bookmark.Text
= "";
}
bookmark
= doc.Range.Bookmarks["ACCUSER_TEL"];if (bookmark != null)
{
bookmark.Text
= "1862029207*";
}
#endregiondoc.Save(Response,"testAdvice.doc", Aspose.Words.ContentDisposition.Attachment,
Aspose.Words.Saving.SaveOptions.CreateSaveOptions(Aspose.Words.SaveFormat.Doc));
}

基于Asp.NET的界面如下所示。

生成的模板化文档如下所示,整个文档是基于固定模板输出,因此比较标准和统一化。

2、Aspose.Cell模板化的文档导出

Aspose.Cell的模板化文档也是类似,也有两种方式可以操作,一种是替换文本,一种是绑定数据源的方式实现,具体可以参考一下我前面的列出随笔,对于Apsose.Cell来说,绑定数据源的功能非常强大。

绑定数据源是通过在模板中设置变量对象方式,变量
对象是通过&=来引用,对象的属性或者列名,通过如&=Customer.City方式引用,非常直观方便。

这种方式的变量对象支持简单对象,如下所示。

也支持复杂的集合对象,如下所示。

为了演示模板化的文档导出,我这里设计了一个固定的Excel模板,通过两种方式实现数据的替换,先定义Excel模板如下所示。

后台绑定数据的代码如下所示。

        protected void btnGenExcel_Click(objectsender, EventArgs e)
{
Dictionary
<string, string> dictSource = new Dictionary<string, string>();
dictSource.Add(
"TIS_HANDLE_NO", "T0001");
dictSource.Add(
"ACCUSE_INDUSTRY", "出租车");
dictSource.Add(
"ACCUSER_NAME", "张三");string templateFile = Server.MapPath("./Templates/Advice.xls");
WorkbookDesigner designer
= newWorkbookDesigner();
designer.Open(templateFile);

Aspose.Cells.Worksheet worksheet
= designer.Workbook.Worksheets[0];//使用文本替换 foreach (string name indictSource.Keys)
{
worksheet.Replace(name, dictSource[name]);
}
//使用绑定数据方式替换 designer.SetDataSource("ACCUSER_SEX", "");
designer.SetDataSource(
"ACCUSER_TEL", "1862029207*");
designer.Process();

designer.Save(
"testAdvice.xls", SaveType.OpenInExcel, FileFormatType.Excel2003, Response);
}

以上就是使用Aspose.Word控件和Aspose.Cell控件,实现Word文档和Excel文档的模板化导出的相关操作过程及代码,希望对大家有帮助,也作为我的一个总结,以后参考,呵呵,欢迎互相沟通研究。

俗话说,一个好汉十个帮,众人拾柴火焰高等都说明一个道理,有更多的资源,更丰富的积累,都是助你走向成功,走向顶峰的推动力。

本篇的公用类库的介绍主题是程序开发中常用到的一些辅助类,在帮助文档中归类到其他目录下面,本篇主要介绍有注册表的操作、反射操作、正则表达式操作、Zip压缩操作、AD操作、字符串Unicode转换操作等常见但有些凌乱的类库。

本篇继续继续整理优化已有的共用类库,并继续发表随笔介绍公用类库的接口方法以及详细使用操作,力求给自己继续优化,积攒更丰富的公用类库资源,加深了解的同时,也给大家展现公用类库好的方面。

厚积薄发,丰富的公用类库积累,助你高效进行系统开发(12)--- 网络相关操作辅助类

厚积薄发,丰富的公用类库积累,助你高效进行系统开发(11)---各种线程相关操作类
厚积薄发,丰富的公用类库积累,助你高效进行系统开发(10)---各种线程同步的集合类
厚积薄发,丰富的公用类库积累,助你高效进行系统开发(9)----各种常用辅助类
厚积薄发,丰富的公用类库积累,助你高效进行系统开发(8)----非对称加密、BASE64加密、MD5等常用加密处理

厚积薄发,丰富的公用类库积累,助你高效进行系统开发(7)-----声音播放、硬件信息、键盘模拟及钩子、鼠标模拟及钩子等设备相关

厚积薄发,丰富的公用类库积累,助你高效进行系统开发(6)----全屏截图、图标获取、图片打印、页面预览截屏、图片复杂操作等

厚积薄发,丰富的公用类库积累,助你高效进行系统开发(5)----热键、多线程、窗体动画冻结等窗体操作

厚积薄发,丰富的公用类库积累,助你高效进行系统开发(4)----CSV、Excel、INI文件、独立存储等文件相关

厚积薄发,丰富的公用类库积累,助你高效进行系统开发(3)----数据库相关操作

厚积薄发,丰富的公用类库积累,助你高效进行系统开发(2)----常用操作

厚积薄发,丰富的公用类库积累,助你高效进行系统开发(1)----开篇总结

1、 注册表操作辅助类RegistryHelper,通过默认指定注册表的前缀路径,减少调用复杂性。

1)辅助类提供的方法接口如下所示:

/// <summary>    
///获取注册表项的值。如果该键不存在,则返回空字符串。/// </summary>    
/// <param name="key">注册表键</param>    
/// <returns>指定键返回的值</returns>    
public static string GetValue(stringkey)/// <summary>    
///获取注册表项的值。如果该键不存在,则返回空字符串。/// </summary>    
/// <param name="softwareKey">注册表键的前缀路径</param>    
/// <param name="key">注册表键</param>    
/// <returns>指定键返回的值</returns>    
public static string GetValue(string softwareKey, stringkey)/// <summary>    
///获取注册表项的值。如果该键不存在,则返回空字符串。/// </summary>    
/// <param name="softwareKey">注册表键的前缀路径</param>    
/// <param name="key">注册表键</param>    
/// <param name="rootRegistry">开始的根节点(Registry.CurrentUser或者Registry.LocalMachine等)</param>    
/// <returns>指定键返回的值</returns>    
public static string GetValue(string softwareKey, stringkey, RegistryKey rootRegistry)/// <summary>    
///保存键值到注册表/// </summary>    
/// <param name="key">注册表键</param>    
/// <param name="value">键的值内容</param>    
/// <returns>如果保存成功返回true,否则为false</returns>    
public static bool SaveValue(string key, stringvalue)/// <summary>    
///保存键值到注册表/// </summary>    
/// <param name="softwareKey">注册表键的前缀路径</param>    
/// <param name="key">注册表键</param>    
/// <param name="value">键的值内容</param>    
/// <returns>如果保存成功返回true,否则为false</returns>    
public static bool SaveValue(string softwareKey, string key, stringvalue)/// <summary>    
///保存键值到注册表/// </summary>    
/// <param name="softwareKey">注册表键的前缀路径</param>    
/// <param name="key">注册表键</param>    
/// <param name="value">键的值内容</param>    
/// <param name="rootRegistry">开始的根节点(Registry.CurrentUser或者Registry.LocalMachine等)</param>    
/// <returns>如果保存成功返回true,否则为false</returns>    
public static bool SaveValue(string softwareKey, string key, stringvalue, RegistryKey rootRegistry)#endregion   
  
#region 自动启动程序设置    
   
/// <summary>    
///检查是否随系统启动/// </summary>    
/// <returns></returns>    
public static boolCheckStartWithWindows()/// <summary>    
///设置随系统启动/// </summary>    
/// <param name="startWin"></param>    
public static void SetStartWithWindows(boolstartWin)#endregion  

2)辅助类的使用例子代码如下所示

private void btnRegistryHelper_Click(objectsender, EventArgs e)    
{
string result = string.Empty;
result
+= "使用RegistryHelper注册表访问辅助类:" + "\r\n";string softwareKey = @"Software\DeepLand\OrderWater";bool sucess = RegistryHelper.SaveValue(softwareKey, "Test", DateTime.Now.ToString());if(sucess)
{
result
+= RegistryHelper.GetValue(softwareKey, "Test") + "\r\n";
}

RegistryHelper.SaveValue(softwareKey,
"Test", "测试内容", Microsoft.Win32.Registry.LocalMachine);
result
+= RegistryHelper.GetValue(softwareKey, "Test", Microsoft.Win32.Registry.LocalMachine);

MessageUtil.ShowTips(result);
}

演示代码效果如下所示。


2、 反射操作辅助类ReflectionUtil,如获取或设置字段、属性的值等反射信息。

本辅助类主要是用来方便实现反射操作辅助类,如获取或设置字段、属性的值等反射信息。 在通用的赋值操作(属性或者字段)、执行方法或者需要获取特定对象属性的时候,就需要大量用到反射操作。

1)辅助类提供的方法接口如下所示:

#region 属性字段设置    
/// <summary>    
///绑定标识/// </summary>    
public static BindingFlags bf = BindingFlags.DeclaredOnly | BindingFlags.Public |BindingFlags.NonPublic| BindingFlags.Instance |BindingFlags.Static;/// <summary>    
///执行方法/// </summary>    
/// <param name="obj">对象实例</param>    
/// <param name="methodName">方法名称</param>    
/// <param name="args">参数</param>    
/// <returns></returns>    
public static object InvokeMethod(object obj, string methodName, object[] args)/// <summary>    
///设置对象实例的字段值/// </summary>    
/// <param name="obj">对象实例</param>    
/// <param name="name">字段名称</param>    
/// <param name="value">字段值</param>    
public static void SetField(object obj, string name, objectvalue)/// <summary>    
///获取对象实例的字段值/// </summary>    
/// <param name="obj">对象实例</param>    
/// <param name="name">字段名称</param>    
/// <returns></returns>    
public static object GetField(object obj, stringname)/// <summary>    
///获取对象实例的字段集合/// </summary>    
/// <param name="obj">对象实例</param>    
/// <returns></returns>    
public static FieldInfo[] GetFields(objectobj)/// <summary>    
///设置对象实例的属性值/// </summary>    
/// <param name="obj">对象实例</param>    
/// <param name="name">属性名称</param>    
/// <param name="value">属性值</param>    
public static void SetProperty(object obj, string name, objectvalue)/// <summary>    
///获取对象实例的属性值/// </summary>    
/// <param name="obj">对象实例</param>    
/// <param name="name">属性名称</param>    
/// <returns></returns>    
public static object GetProperty(object obj, stringname)/// <summary>    
///获取对象实例的属性列表/// </summary>    
/// <param name="obj">对象实例</param>    
/// <returns></returns>    
public static PropertyInfo[] GetProperties(objectobj)/// <summary>    
///把对象的属性和值,输出一个键值的字符串,如A=1&B=test/// </summary>    
/// <param name="obj">实体对象</param>    
/// <param name="includeEmptyProperties">是否包含空白属性的键值</param>    
/// <returns></returns>    
public static string ToNameValuePairs(object obj, bool includeEmptyProperties = true)#endregion   
  
#region 获取Description    
   
/// <summary>    
///获取枚举字段的Description属性值/// </summary>    
/// <param name="value">The value.</param>    
/// <returns>return description or value.ToString()</returns>    
public static stringGetDescription(Enum value)/// <summary>    
///Get The Enum Field Description using Description Attribute and///objects to format the Description./// </summary>    
/// <param name="value">Enum For Which description is required.</param>    
/// <param name="args">An Object array containing zero or more objects to format.</param>    
/// <returns>return null if DescriptionAttribute is not found or return type description</returns>    
public static string GetDescription(Enum value, params object[] args)/// <summary>    
///获取字段的Description属性值/// </summary>    
/// <param name="member">Specified Member for which Info is Required</param>    
/// <returns>return null if DescriptionAttribute is not found or return type description</returns>    
public static stringGetDescription(MemberInfo member)/// <summary>    
///Get The Type Description using Description Attribute and///objects to format the Description./// </summary>    
/// <param name="member">Specified Member for which Info is Required</param>    
/// <param name="args">An Object array containing zero or more objects to format.</param>    
/// <returns>return<see cref="String.Empty"/>if DescriptionAttribute is///not found or return type description</returns>    
public static string GetDescription(MemberInfo member, params object[] args)#endregion   
  
#region 获取Attribute信息    
   
/// <summary>    
///获取指定对象实例的attributes内容/// </summary>    
/// <param name="attributeType">The attribute Type for which the custom attributes are to be returned.</param>    
/// <param name="assembly">the assembly in which the specified attribute is defined</param>    
/// <returns>Attribute as Object or null if not found.</returns>    
public static objectGetAttribute(Type attributeType, Assembly assembly)/// <summary>    
///获取指定对象实例的attributes内容/// </summary>    
/// <param name="attributeType">The attribute Type for which the custom attributes are to be returned.</param>    
/// <param name="type">the type on which the specified attribute is defined</param>    
/// <returns>Attribute as Object or null if not found.</returns>    
public static objectGetAttribute(Type attributeType, MemberInfo type)/// <summary>    
///Gets the specified object attributes for type as specified by type with option to serach parent/// </summary>    
/// <param name="attributeType">The attribute Type for which the custom attributes are to be returned.</param>    
/// <param name="type">the type on which the specified attribute is defined</param>    
/// <param name="searchParent">if set to<see langword="true"/>[search parent].</param>    
/// <returns>    
///Attribute as Object or null if not found./// </returns>    
public static object GetAttribute(Type attributeType, MemberInfo type, boolsearchParent)/// <summary>    
///Gets the collection of all specified object attributes for type as specified by type/// </summary>    
/// <param name="attributeType">The attribute Type for which the custom attributes are to be returned.</param>    
/// <param name="type">the type on which the specified attribute is defined</param>    
/// <returns>Attribute as Object or null if not found.</returns>    
public static object[] GetAttributes(Type attributeType, MemberInfo type)/// <summary>    
///Gets the collection of all specified object attributes for type as specified by type with option to serach parent/// </summary>    
/// <param name="attributeType">The attribute Type for which the custom attributes are to be returned.</param>    
/// <param name="type">the type on which the specified attribute is defined</param>    
/// <param name="searchParent">The attribute Type for which the custom attribute is to be returned.</param>    
/// <returns>    
///Attribute as Object or null if not found./// </returns>    
public static object[] GetAttributes(Type attributeType, MemberInfo type, boolsearchParent)#endregion   
  
#region 资源获取    
   
/// <summary>    
///根据资源名称获取图片资源流/// </summary>    
/// <param name="ResourceName"></param>    
/// <returns></returns>    
public static Stream GetImageResource(stringResourceName)/// <summary>    
///获取程序集资源的位图资源/// </summary>    
/// <param name="assemblyType">程序集中的某一对象类型</param>    
/// <param name="resourceHolder">资源的根名称。例如,名为“MyResource.en-US.resources”的资源文件的根名称为“MyResource”。</param>    
/// <param name="imageName">资源项名称</param>    
public static Bitmap LoadBitmap(Type assemblyType, string resourceHolder, stringimageName)/// <summary>    
///获取程序集资源的文本资源/// </summary>    
/// <param name="assemblyType">程序集中的某一对象类型</param>    
/// <param name="resName">资源项名称</param>    
/// <param name="resourceHolder">资源的根名称。例如,名为“MyResource.en-US.resources”的资源文件的根名称为“MyResource”。</param>    
public static string GetStringRes(Type assemblyType, string resName, stringresourceHolder)/// <summary>    
///获取程序集嵌入资源的文本形式/// </summary>    
/// <param name="assemblyType">程序集中的某一对象类型</param>    
/// <param name="charset">字符集编码</param>    
/// <param name="ResName">嵌入资源相对路径</param>    
/// <returns>如没找到该资源则返回空字符</returns>    
public static string GetManifestString(Type assemblyType, string charset, stringResName)#endregion   
  
#region 创建对应实例    
/// <summary>    
///创建对应实例/// </summary>    
/// <param name="type">类型</param>    
/// <returns>对应实例</returns>    
public static object CreateInstance(stringtype)/// <summary>    
///创建对应实例/// </summary>    
/// <param name="type">类型</param>    
/// <returns>对应实例</returns>    
public static objectCreateInstance(Type type)#endregion  

2)辅助类的使用例子代码如下所示,例子是Socket框架里面用来反序列化对象的操作,通过反射把字符串的值赋值到对象里面。

/// <summary>    
///转换Socket接收到的信息为对象信息/// </summary>    
/// <param name="data">Socket接收到的信息</param>    
public BaseEntity(stringdata)    
{
string[] dataArray =NetStringUtil.UnPack(data);if (dataArray != null && dataArray.Length > 0)
{
int i = 0;
FieldInfo[] fieldArray
= ReflectionUtil.GetFields(this);if (fieldArray == null || dataArray.Length !=fieldArray.Length)
{
throw new ArgumentException("收到的信息和字段信息不一致");
}
if (fieldArray != null)
{
foreach (FieldInfo info infieldArray)
{
string strValue = dataArray[i++];try{if (!string.IsNullOrEmpty(strValue))
{
ReflectionUtil.SetField(
this, info.Name, strValue);
}
}
catch(Exception ex)
{
Communication.Model.Log.WriteError(
string.Format("转换数据发生错误:{0}【{1}】",
ex.Message, data));
break;
}
}
}
}
}

例如两个对象的属性基本相同,可以进行对象的拷贝赋值。

#region 表结构一样,复制相关的内容    PilotInfo pilotInfo= BLLFactory<Pilot>.Instance.FindByID(txtPilot.PilotID);if (pilotInfo != null)    
{
PropertyInfo[] outPropertyArray
=ReflectionUtil.GetProperties(info);
PropertyInfo[] pilotPropArray
=ReflectionUtil.GetProperties(pilotInfo);if (pilotPropArray != null && outPropertyArray != null)
{
foreach (PropertyInfo propInfo inpilotPropArray)
{
try{object value =ReflectionUtil.GetProperty(pilotInfo, propInfo.Name);foreach (PropertyInfo outInfo inoutPropertyArray)
{
if (outInfo.Name ==propInfo.Name)
{
ReflectionUtil.SetProperty(info, propInfo.Name, value);
}
}
}
catch(Exception ex)
{
}
}
}
}
#endregion

3、程序集反射创建对象实例辅助类 Reflect

本辅助类主要是用来方便实现程序集反射创建对象实例的操作。 该程序集反射春节对象实例的操作,在我的WInform开发框架中用于对象的创建操作。

1)辅助类提供的方法接口如下所示:

/// <summary>    
///程序集反射创建对象实例辅助类/// </summary>    
/// <typeparam name="T">对象类型</typeparam>    
public class Reflect<T> where T : class{/// <summary>    
    ///缓存的hash表集合/// </summary>    
    public staticHashtable ObjCache/// <summary>    
    ///根据参数创建对象实例/// </summary>    
    /// <param name="sName">对象全局名称</param>    
    /// <param name="sFilePath">文件路径</param>    
    /// <returns></returns>    
    public static T Create(string sName, stringsFilePath)/// <summary>    
    ///根据参数创建对象实例/// </summary>    
    /// <param name="sName">对象全局名称</param>    
    /// <param name="sFilePath">文件路径</param>    
    /// <param name="bCache">缓存集合</param>    
    /// <returns></returns>    
    public static T Create(string sName, string sFilePath, boolbCache)/// <summary>    
    ///创建程序集对象/// </summary>    
    /// <param name="sFilePath">文件路径</param>    
    /// <returns></returns>    
    public static Assembly CreateAssembly(stringsFilePath)    
}

2)辅助类的使用例子代码如下所示(摘自我的Winform开发框架里面的对象反射操作)。
在下面的例子中,通过传入的类型T,在程序集中构造制定类型的对象,并放入到缓存当中。

public class BLLFactory<T> where T : class{private static Hashtable objCache = newHashtable();private static object syncRoot = newObject();public staticT Instance    
{
get{string CacheKey = typeof(T).FullName;
T bll
= (T)objCache[CacheKey];  //从缓存读取 if (bll == null)
{
lock(syncRoot)
{
if (bll == null)
{
bll
= Reflect<T>.Create(typeof(T).FullName, "WHC.WareHouseMis"); //反射创建,并缓存//bll = Reflect<T>.Create(typeof(T).FullName, System.Reflection.Assembly.GetExecutingAssembly().GetName().Name);//反射创建,并缓存 objCache.Add(typeof(T).FullName, bll);
}
}
}
returnbll;
}
}
}

4、 正则表达式辅助类 CRegex

本辅助类主要是用来方便实现对正则表达式的封装使用,通过利用该类库,可以简化C#对正则表达式的操作,但不能替代掌握正则表达式的知识。
正则表达式可以通过The Reguator等正则表达式调试工具来验证编写的表达式,基本的操作是匹配,替换,分割等,调试工具界面如下:

1)软件包含下面基础通用的操作正则表达式方法的定义,以及一些封装好的正则表达式操作,如获取HTML标题、Body等内容。

/// <summary>    
///内容是否匹配指定的表达式/// </summary>    
/// <param name="sInput">输入内容</param>    
/// <param name="sRegex">表达式字符串</param>    
/// <returns></returns>    
public static bool IsMatch(string sInput, stringsRegex)/// <summary>    
///多个匹配内容/// </summary>    
/// <param name="sInput">输入内容</param>    
/// <param name="sRegex">表达式字符串</param>    
/// <param name="iGroupIndex">第几个分组, 从1开始, 0代表不分组</param>    
public static List<string> GetList(string sInput, string sRegex, intiGroupIndex)/// <summary>    
///多个匹配内容/// </summary>    
/// <param name="sInput">输入内容</param>    
/// <param name="sRegex">表达式字符串</param>    
/// <param name="sGroupName">分组名, ""代表不分组</param>    
public static List<string> GetList(string sInput, string sRegex, stringsGroupName)/// <summary>    
///单个匹配内容/// </summary>    
/// <param name="sInput">输入内容</param>    
/// <param name="sRegex">表达式字符串</param>    
/// <param name="iGroupIndex">分组序号, 从1开始, 0不分组</param>    
public static string GetText(string sInput, string sRegex, intiGroupIndex)/// <summary>    
///单个匹配内容/// </summary>    
/// <param name="sInput">输入内容</param>    
/// <param name="sRegex">表达式字符串</param>    
/// <param name="sGroupName">分组名, ""代表不分组</param>    
public static string GetText(string sInput, string sRegex, stringsGroupName)/// <summary>    
///替换指定内容/// </summary>    
/// <param name="sInput">输入内容</param>    
/// <param name="sRegex">表达式字符串</param>    
/// <param name="sReplace">替换值</param>    
/// <param name="iGroupIndex">分组序号, 0代表不分组</param>    
public static string Replace(string sInput, string sRegex, string sReplace, intiGroupIndex)/// <summary>    
///替换指定内容/// </summary>    
/// <param name="sInput">输入内容</param>    
/// <param name="sRegex">表达式字符串</param>    
/// <param name="sReplace">替换值</param>    
/// <param name="sGroupName">分组名, "" 代表不分组</param>    
public static string Replace(string sInput, string sRegex, string sReplace, stringsGroupName)/// <summary>    
///分割指定内容/// </summary>    
/// <param name="sInput">输入内容</param>    
/// <param name="sRegex">表达式字符串</param>    
/// <param name="iStrLen">最小保留字符串长度</param>    
public static List<string> Split(string sInput, string sRegex, int iStrLen)  

5、 压缩文本、字节或者文件的压缩辅助类GZipUtil。

本辅助类主要是用来方便实现压缩文本、字节或者文件的压缩操作。本辅助类使用.NET内置的类库实现压缩操作。

1)辅助类提供的方法接口如下所示:

/// <summary>    
///压缩字符串/// </summary>    
/// <param name="text">待压缩的文本</param>    
/// <returns>压缩后的文本内容</returns>    
public static string Compress(stringtext)/// <summary>    
///解压字符串/// </summary>    
/// <param name="compressedText">待解压缩的文本内容</param>    
/// <returns>解压后的原始文本内容</returns>    
public static string Decompress(stringcompressedText)/// <summary>    
///压缩流对象/// </summary>    
/// <typeparam name="T">对象类型</typeparam>    
/// <param name="stream">流数据</param>    
/// <param name="mode">压缩类型</param>    
/// <returns></returns>    
public static T GZip<T>(Stream stream, CompressionMode mode) whereT : Stream/// <summary>    
///压缩字节/// </summary>    
/// <param name="bytData">待压缩字节</param>    
/// <returns>压缩后的字节数组</returns>    
public static byte[] Compress(byte[] bytData)/// <summary>    
///解压字节/// </summary>    
/// <param name="bytData">待解压缩字节</param>    
/// <returns>解压后的原始字节内容</returns>    
public static byte[] Decompress(byte[] bytData)/// <summary>    
///压缩Object对象到字节数组/// </summary>    
/// <param name="obj">待压缩对象</param>    
/// <returns>压缩后的字节数组</returns>    
public static byte[] ObjectToGZip(objectobj)/// <summary>    
///从压缩的字节数组转换到Object对象/// </summary>    
/// <param name="byteArray">待解压缩的字节数据</param>    
/// <returns>对象</returns>    
public static object GZipToObject(byte[] byteArray)/// <summary>    
///压缩文件/// </summary>    
/// <param name="lpSourceFolder">包括在zip文件中的文件夹路径,所有文件,包括子文件夹中的文件将包括在内。</param>    
/// <param name="lpDestFolder">写入到Zip文件的目标文件夹</param>    
/// <param name="zipFileName">zip文件名称</param>    
public static GZipResult Compress(string lpSourceFolder, string lpDestFolder, stringzipFileName)/// <summary>    
///压缩文件/// </summary>    
/// <param name="lpSourceFolder">包括在zip文件中的源文件夹路径</param>    
/// <param name="searchPattern">搜索模式 (例如 "*.*" or "*.txt" or "*.gif") 以标识那些文件将被包含到Zip文件里面</param>    
/// <param name="searchOption">指定是搜索当前目录,还是搜索当前目录及其所有子目录</param>    
/// <param name="lpDestFolder">写入zip目标文件夹的路径</param>    
/// <param name="zipFileName">Zip文件名</param>    
/// <param name="deleteTempFile">布尔值,如果为true则删除中间临时文件,false则在lpDestFolder保留临时文件(调试用)</param>    
public static GZipResult Compress(string lpSourceFolder, string searchPattern, SearchOption searchOption, string lpDestFolder, string zipFileName, booldeleteTempFile)/// <summary>    
///压缩文件/// </summary>    
/// <param name="files">在zip文件中包含的FileInfo对象数组</param>    
/// <param name="folders">文件夹字符串数组</param>    
/// <param name="lpBaseFolder">    
///基础文件夹,在创建的zip文件中存储的文件的相对路径。例如, 如果lpBaseFolder 是 'C:\zipTest\Files\',///当存在一个文件 'C:\zipTest\Files\folder1\sample.txt' 在数组'files'里面, 则 sample.txt 的相对路径是 'folder1/sample.txt'/// </param>    
/// <param name="lpDestFolder">写入Zip文件的文件夹</param>    
/// <param name="zipFileName">Zip文件名</param>    
public static GZipResult Compress(FileInfo[] files, string[] folders, string lpBaseFolder, string lpDestFolder, stringzipFileName)/// <summary>    
///压缩文件/// </summary>    
/// <param name="files">在zip文件中包含的FileInfo对象数组</param>    
/// <param name="lpBaseFolder">    
///基础文件夹,在创建的zip文件中存储的文件的相对路径。例如, 如果lpBaseFolder 是 'C:\zipTest\Files\',///当存在一个文件 'C:\zipTest\Files\folder1\sample.txt' 在数组'files'里面, 则 sample.txt 的相对路径是 'folder1/sample.txt'/// </param>    
/// <param name="lpDestFolder">写入Zip文件的文件夹</param>    
/// <param name="zipFileName">Zip文件名</param>    
public static GZipResult Compress(FileInfo[] files, string lpBaseFolder, string lpDestFolder, stringzipFileName)/// <summary>    
///压缩文件/// </summary>    
/// <param name="files">在zip文件中包含的FileInfo对象数组</param>    
/// <param name="lpBaseFolder">    
///基础文件夹,在创建的zip文件中存储的文件的相对路径。例如, 如果lpBaseFolder 是 'C:\zipTest\Files\',///当存在一个文件 'C:\zipTest\Files\folder1\sample.txt' 在数组'files'里面, 则 sample.txt 的相对路径是 'folder1/sample.txt'/// </param>    
/// <param name="lpDestFolder">写入Zip文件的文件夹</param>    
/// <param name="zipFileName">Zip文件名</param>    
/// <param name="deleteTempFile">布尔值,如果为true则删除中间临时文件,false则在lpDestFolder保留临时文件(调试用)</param>    
public static GZipResult Compress(FileInfo[] files, string lpBaseFolder, string lpDestFolder, string zipFileName, booldeleteTempFile)/// <summary>    
///解压文件/// </summary>    
/// <param name="lpSourceFolder">zip文件的源目录</param>    
/// <param name="lpDestFolder">解压到的目录</param>    
/// <param name="zipFileName">Zip文件名</param>    
/// <returns></returns>    
public static GZipResult Decompress(string lpSourceFolder, string lpDestFolder, stringzipFileName)/// <summary>    
///解压文件/// </summary>    
/// <param name="lpSourceFolder">zip文件的源目录</param>    
/// <param name="lpDestFolder">解压到的目录</param>    
/// <param name="zipFileName">Zip文件名</param>    
/// <param name="writeFiles"></param>    
/// <param name="addExtension">增加后缀名</param>    
/// <returns></returns>    
public static GZipResult Decompress(string lpSourceFolder, string lpDestFolder, string zipFileName, bool writeFiles, stringaddExtension)/// <summary>    
///解压文件/// </summary>    
/// <param name="lpSourceFolder">zip文件的源目录</param>    
/// <param name="lpDestFolder">解压到的目录</param>    
/// <param name="zipFileName">Zip文件名</param>    
/// <param name="deleteTempFile">布尔值,如果为true则删除中间临时文件,false则在lpDestFolder保留临时文件(调试用)</param>    
/// <param name="writeFiles"></param>    
/// <param name="addExtension">增加后缀名</param>    
/// <param name="htFiles"></param>    
/// <param name="bufferSize"></param>    
/// <returns></returns>    
public static GZipResult Decompress(string lpSrcFolder, string lpDestFolder, string zipFileName, bool deleteTempFile, bool writeFiles, string addExtension, Hashtable htFiles, int bufferSize)   

2)辅助类的使用例子代码如下所示

//压缩解压缩文本内容
string zippedContent = GZipUtil.Compress("wuhuacong");string original =GZipUtil.Decompress(zippedContent);    

GZipUtil.Compress(Application.StartupPath, Application.StartupPath,
"cityroad.zip");
GZipUtil.Decompress(Application.StartupPath, Path.Combine(Application.StartupPath,
"cityroad"), "cityroad.zip");

MessageUtil.ShowTips(
"操作完成");

6、字符串Unicode转换操作辅助类 UnicodeHelper。

1)辅助类提供的方法接口如下所示:

/// <summary>    
///将原始字串转换为unicode,格式为\u.\u./// </summary>    
/// <param name="str">待转换字符串</param>    
/// <returns></returns>    
public static string StringToUnicode(stringstr)/// <summary>    
///将Unicode字串\u.\u.格式字串转换为原始字符串/// </summary>    
/// <param name="str">待转换字符串</param>    
/// <returns></returns>    
public static string UnicodeToString(stringstr)/// <summary>    
///GB2312转换成unicode编码/// </summary>    
/// <param name="str">待转换字符串</param>    
/// <returns></returns>    
public static string GBToUnicode(stringstr)/// <summary>    
///转换一个字符,输入如"Π"中的"03a0"/// </summary>    
/// <param name="unicodeSingle">待转换字符串</param>    
/// <returns></returns>    
public static string ConvertSingle(stringunicodeSingle)/// <summary>    
///unicode编码转换成GB2312汉字/// </summary>    
/// <param name="str">待转换字符串</param>    
/// <returns></returns>    
public static string UnicodeToGB(string str)  

2)辅助类的使用例子代码如下所示

private void btnUnicodeHelper_Click(objectsender, EventArgs e)    
{
string str = "\u821e\u7fbd\u6e05\u548c \u5c71\u7f8a\u4e4b\u89d2";string test =UnicodeHelper.UnicodeToString(str);string result = test + "\r\n";
result
+= UnicodeHelper.StringToUnicode(test) + "\r\n";

MessageUtil.ShowTips(result);
}

俗话说,一个好汉十个帮,众人拾柴火焰高等都说明一个道理,有更多的资源,更丰富的积累,都是助你走向成功,走向顶峰的推动力。

本篇的公用类库的介绍主题是程序开发中常用到的一些辅助类,在帮助文档中归类到其他目录下面,本篇主要介绍在Winform开发用,常用到的一些类库,包括Windows服务操作、DOS操作、串口操作、POS打印操作以及一些参加的界面控件的辅助类。

本篇继续继续整理优化已有的共用类库,并继续发表随笔介绍公用类库的接口方法以及详细使用操作,力求给自己继续优化,积攒更丰富的公用类库资源,加深了解的同时,也给大家展现公用类库好的方面。

厚积薄发,丰富的公用类库积累,助你高效进行系统开发(13)--- 各种常用的辅助类2

厚积薄发,丰富的公用类库积累,助你高效进行系统开发(12)--- 网络相关操作辅助类

厚积薄发,丰富的公用类库积累,助你高效进行系统开发(11)---各种线程相关操作类

厚积薄发,丰富的公用类库积累,助你高效进行系统开发(10)---各种线程同步的集合类

厚积薄发,丰富的公用类库积累,助你高效进行系统开发(9)----各种常用辅助类
厚积薄发,丰富的公用类库积累,助你高效进行系统开发(8)----非对称加密、BASE64加密、MD5等常用加密处理

厚积薄发,丰富的公用类库积累,助你高效进行系统开发(7)-----声音播放、硬件信息、键盘模拟及钩子、鼠标模拟及钩子等设备相关
厚积薄发,丰富的公用类库积累,助你高效进行系统开发(6)----全屏截图、图标获取、图片打印、页面预览截屏、图片复杂操作等

厚积薄发,丰富的公用类库积累,助你高效进行系统开发(5)----热键、多线程、窗体动画冻结等窗体操作

厚积薄发,丰富的公用类库积累,助你高效进行系统开发(4)----CSV、Excel、INI文件、独立存储等文件相关

厚积薄发,丰富的公用类库积累,助你高效进行系统开发(3)----数据库相关操作

厚积薄发,丰富的公用类库积累,助你高效进行系统开发(2)----常用操作

厚积薄发,丰富的公用类库积累,助你高效进行系统开发(1)----开篇总结

至此,整个厚积薄发的类库整理也落下帷幕,整个过程历时一年多,整理优化近两百多个类库,帮助说明近两百个。本篇作为本系列的终结篇,贴出最终的
CHM帮助文档
,以及最有的
类库DLL
,如果需要进一步了解整个类库的信息,可以随时联系我。

CHM帮助文档持续更新中,统一下载地址是:
http://www.iqidi.com/download/commonshelp.rar

最新公用类库DLL+XML注释文件下载地址是:
https://files.cnblogs.com/wuhuacong/WHC.OrderWater.Commons.rar

整个帮助类库的CHM文件概况如下所示。

1、 Window服务辅助类WinServiceHelper,包括安装、卸载、启动、停止、重新启动、判断服务是否存在等操作。

本辅助类主要是用来方便实现Window服务的各种操作,包括安装、卸载、启动、停止、重新启动、判断服务是否存在等操作。 Window服务适用于个各种定时服务,或者数据同步的操作中。

1)辅助类提供的方法接口如下所示:

///安装Windows服务/// </summary>    
/// <param name="serviceName">服务名称</param>    
/// <param name="serviceFileName">服务文件路径</param>    
/// <returns></returns>    
public static bool InstallService(string serviceName, stringserviceFileName)/// <summary>    
///卸载Windows服务/// </summary>    
/// <param name="serviceName">服务名称</param>    
/// <param name="serviceFileName">服务文件路径</param>    
public static bool UnInstallService(string serviceName, stringserviceFileName)/// <summary>    
///另外一种安装、卸载Windows服务的方法/// </summary>    
/// <param name="install">安装还是卸载,true为安装,false为卸载</param>    
/// <param name="serviceFileName"></param>    
public static void InstallService2(bool install, stringserviceFileName)/// <summary>    
///判断window服务是否存在/// </summary>    
/// <param name="serviceName">window服务名称</param>    
/// <returns></returns>    
public static bool ServiceIsExisted(stringserviceName)/// <summary>    
///等待某种预期的状态(如运行,停止等)/// </summary>    
/// <param name="serviceName">window服务名称</param>    
/// <param name="status">预期的状态</param>    
/// <param name="second">如果获取不到预期的状态,则等待多少秒</param>    
/// <returns></returns>    
public static bool WaitForStatus(string serviceName, ServiceControllerStatus status, intsecond)/// <summary>    
///启动window服务/// </summary>    
/// <param name="serviceName"></param>    
public static bool StartService(stringserviceName)/// <summary>    
///停止服务/// </summary>    
/// <param name="serviseName"></param>    
/// <returns></returns>    
public static bool StopService(stringserviseName)/// <summary>    
///修改服务的启动项 2为自动,3为手动/// </summary>    
/// <param name="startType"></param>    
/// <param name="serviceName"></param>    
/// <returns></returns>    
public static bool ChangeServiceStartType(int startType, stringserviceName)/// <summary>    
///获取服务启动类型 2为自动 3为手动 4 为禁用/// </summary>    
/// <param name="serviceName"></param>    
/// <returns></returns>    
public static string GetServiceStartType(stringserviceName)/// <summary>    
///验证服务是否启动/// </summary>    
/// <returns></returns>    
public static bool ServiceIsRunning(string serviceName)   

2)辅助类的使用例子代码如下所示

private voidCheckServcieStatus()    
{
string serviceName = this.txtServiceName.Text;bool exist =WinServiceHelper.ServiceIsExisted(serviceName);if(exist)
{
#region 获取转换状态 string startType =WinServiceHelper.GetServiceStartType(serviceName);string startTypeStatus = "";if (startType == "2")
{
startTypeStatus
= "自动";
}
else if (startType == "3")
{
startTypeStatus
= "手动";
}
else if (startType == "4")
{
startTypeStatus
= "禁用";
}
#endregion bool running =WinServiceHelper.ServiceIsRunning(serviceName);this.txtServiceStatus.Text = string.Format("启动类型:{0} 状态:{1}", startTypeStatus, running ? "正在运行" : "已停止");this.txtServiceStatus.BackColor = Color.FromArgb(255, 255, 192);this.btnStartService.Enabled = !running;this.btnStopService.Enabled =running;this.btnReset.Enabled = true;this.btnInstallService.Enabled = false;this.btnUnInstallService.Enabled = true;
}
else{this.txtServiceStatus.BackColor =Color.Red;this.txtServiceStatus.Text = string.Format("{0} 服务不存在", serviceName);this.btnStartService.Enabled = false;this.btnStopService.Enabled = false;this.btnReset.Enabled = false;this.btnInstallService.Enabled = true;this.btnUnInstallService.Enabled = false;
}
}

以上代码摘自我的《
Winform开发框架之通用定时服务管理

2、DOS操作封装辅助类 DosHelper。

本辅助类主要是用来方便实现DOS操作。DOS操作在自定义安装数据库脚本、运行特殊命令、后台注册控件、操作Windows Service等方面都有用到。在另一个辅助类SqlScriptHelper,就是采用运行DOS命令方式进行Sql脚本的数据库安装。

1)辅助类提供的方法接口如下所示

/// <summary>    
///后台执行DOS文件/// </summary>    
/// <param name="fileName">文件名(包含路径)</param>    
/// <param name="argument">运行参数</param>    
/// <param name="hidden">是否隐藏窗口</param>    
public static void RunDos(string fileName, string argument, boolhidden)/// <summary>       
///运行指定DOS命令行/// </summary>       
/// <param name="fileName">命令</param>       
/// <param name="argument">命令行参数</param>       
/// <param name="hidden">是否隐藏窗口</param>     
/// <param name="confirm">写入命令行的确认信息</param>       
/// <returns></returns>       
public static string ExecuteCMD(string fileName, string argument, bool hidden, stringconfirm)/// <summary>    
///同步方式执行DOS命令/// </summary>    
/// <param name="command">DOS命令</param>    
public static void ExecuteCommandSync(objectcommand)/// <summary>    
///异步方式执行DOS命令/// </summary>    
/// <param name="command">DOS命令字符串</param>    
public static void ExecuteCommandAsync(string command)  

2)辅助类的使用例子代码如下所示

private void btnInstallService_Click(objectsender, EventArgs e)    
{
if(CheckInput())
{
DosHelper.RunDos(servicefile,
"-i", false);

WinServiceHelper.WaitForStatus(
this.txtServiceName.Text, ServiceControllerStatus.Running, 10);

CheckServcieStatus();
}
}
private void btnUnInstallService_Click(objectsender, EventArgs e)
{
if(CheckInput())
{
DosHelper.RunDos(servicefile,
"-u", false);
Thread.Sleep(
1000);

CheckServcieStatus();
}

以上代码摘自我的《
Winform开发框架之通用定时服务管理

3、串口开发辅助类 SerialPortUtil

一般人可能开发C#相关的项目很多年,不一定会接触到串口的开发,了解熟悉对硬件串口的开发,串口也不再是什么神秘的东西,利用SerailPort组件,对串口的各种操作也非常的方便,由于本人总是喜欢把一些常用的东西封装成可供重复利用的类库,因此,在阅百家代码,项目积累总结的基础上,提炼总结优化,把对串口的操作封装成一个公用的类库,应付日常的串口编程开发,也算是工作的一个阶段性总结吧。

微软在 .NET FrameWork2.0中对串口通讯进行了封装,我们可以在.net2.0及以上版本开发时直接使用SerialPort类对串口进行读写操作。
SerialPort类的属性主要包括:
1)串口名称(PortName)
2)波特率(BaudRate)
3)数据位 DataBits
4)停止位 StopBits
5)奇偶校验 Parity
SerialPort类的事件主要包括:
DataReceived:用于异步接收串口数据事件

ErrorReceived:错误处理事件

SerialPort类的方法主要包括:

Open();Close();Read();Write()、DiscardInBuffer()、DiscardOutBuffer()等

从上面的测试例子图中,我们可以看到,一般对串口的操作,需要提供串口号、波特率、数据位、停止位、奇偶校验的参数,用来构造一个串口操作类,以便实现具体的串口操作,而这些参数有的是系统内置的枚举参数,我们可以通过遍历枚举对象来绑定下来列表(如停止位、奇偶校验);但有些参数却不是系统内置的枚举类型,例如波特率、数据位等,而且对这些参数操作也是串口开发经常用到的,因此,第一步,我对这些参数的绑定做了一个简单的封装。

1)辅助类的接口代码如下所示。

    /// <summary>
    ///串口开发辅助类/// </summary>
    public classSerialPortUtil
{
/// <summary> ///接收事件是否有效 false表示有效/// </summary> public bool ReceiveEventFlag = false;/// <summary> ///结束符比特/// </summary> public byte EndByte = 0x23;//string End = "#"; /// <summary> ///完整协议的记录处理事件/// </summary> public eventDataReceivedEventHandler DataReceived;/// <summary> ///严重错误事件处理/// </summary> public eventSerialErrorReceivedEventHandler Error;/// <summary> ///串口号/// </summary> public stringPortName/// <summary> ///波特率/// </summary> publicSerialPortBaudRates BaudRate/// <summary> ///奇偶校验位/// </summary> publicParity Parity/// <summary> ///数据位/// </summary> publicSerialPortDatabits DataBits/// <summary> ///停止位/// </summary> publicStopBits StopBits#endregion #region 构造函数 /// <summary> ///参数构造函数(使用枚举参数构造)/// </summary> /// <param name="baud">波特率</param> /// <param name="par">奇偶校验位</param> /// <param name="sBits">停止位</param> /// <param name="dBits">数据位</param> /// <param name="name">串口号</param> public SerialPortUtil(stringname, SerialPortBaudRates baud, Parity par, SerialPortDatabits dBits, StopBits sBits)/// <summary> ///参数构造函数(使用字符串参数构造)/// </summary> /// <param name="baud">波特率</param> /// <param name="par">奇偶校验位</param> /// <param name="sBits">停止位</param> /// <param name="dBits">数据位</param> /// <param name="name">串口号</param> public SerialPortUtil(string name, string baud, string par, string dBits, stringsBits)/// <summary> ///默认构造函数/// </summary> publicSerialPortUtil()#endregion /// <summary> ///端口是否已经打开/// </summary> public boolIsOpen/// <summary> ///打开端口/// </summary> /// <returns></returns> public voidOpenPort()/// <summary> ///关闭端口/// </summary> public voidClosePort()/// <summary> ///丢弃来自串行驱动程序的接收和发送缓冲区的数据/// </summary> public voidDiscardBuffer()#region 数据写入操作 /// <summary> ///写入数据/// </summary> /// <param name="msg"></param> public void WriteData(stringmsg)/// <summary> ///写入数据/// </summary> /// <param name="msg">写入端口的字节数组</param> public void WriteData(byte[] msg)/// <summary> ///写入数据/// </summary> /// <param name="msg">包含要写入端口的字节数组</param> /// <param name="offset">参数从0字节开始的字节偏移量</param> /// <param name="count">要写入的字节数</param> public void WriteData(byte[] msg, int offset, intcount)/// <summary> ///发送串口命令/// </summary> /// <param name="SendData">发送数据</param> /// <param name="ReceiveData">接收数据</param> /// <param name="Overtime">重复次数</param> /// <returns></returns> public int SendCommand(byte[] SendData, ref byte[] ReceiveData, intOvertime)#endregion #region 常用的列表数据获取和绑定操作 /// <summary> ///封装获取串口号列表/// </summary> /// <returns></returns> public static string[] GetPortNames()/// <summary> ///设置串口号/// </summary> /// <param name="obj"></param> public static voidSetPortNameValues(ComboBox obj)/// <summary> ///设置波特率/// </summary> public static voidSetBauRateValues(ComboBox obj)/// <summary> ///设置数据位/// </summary> public static voidSetDataBitsValues(ComboBox obj)/// <summary> ///设置校验位列表/// </summary> public static voidSetParityValues(ComboBox obj)/// <summary> ///设置停止位/// </summary> public static voidSetStopBitValues(ComboBox obj)#endregion /// <summary> ///检查端口名称是否存在/// </summary> /// <param name="port_name"></param> /// <returns></returns> public static bool Exists(stringport_name)/// <summary> ///格式化端口相关属性/// </summary> /// <param name="port"></param> /// <returns></returns> public static stringFormat(SerialPort port)
}

2)辅助类的使用代码如下所示。

绑定相应的数据源(包括端口、波特率、数据位、校验位等等)代码如下所示,这样我们在窗体界面代码中,绑定相关参数的数据源就很方便了,如下所示。

private void Form1_Load(objectsender, EventArgs e)    
{
BindData();
}
private voidBindData()
{
//绑定端口号 SerialPortUtil.SetPortNameValues(txtPort);
txtPort.SelectedIndex
= 0;//波特率 SerialPortUtil.SetBauRateValues(txtBaudRate);
txtBaudRate.SelectedText
= "57600";//数据位 SerialPortUtil.SetDataBitsValues(txtDataBits);this.txtDataBits.SelectedText = "8";//校验位 SerialPortUtil.SetParityValues(txtParity);this.txtParity.SelectedIndex = 0;//停止位 SerialPortUtil.SetStopBitValues(txtStopBit);this.txtStopBit.SelectedIndex = 1;this.btnSend.Enabled =isOpened;
}

辅助类实现连接特定的窗口并处理数据的操作代码如下所示。

 private void btnConnect_Click(objectsender, EventArgs e)    
{
try{if (serial == null)
{
try{string portname = this.txtPort.Text;
SerialPortBaudRates rate
= (SerialPortBaudRates)Enum.Parse(typeof(SerialPortBaudRates), this.txtBaudRate.Text);//int.Parse(this.txtBaudRate.Text); SerialPortDatabits databit = (SerialPortDatabits)int.Parse(this.txtDataBits.Text);
Parity party
= (Parity)Enum.Parse(typeof(Parity), this.txtParity.Text);
StopBits stopbit
= (StopBits)Enum.Parse(typeof(StopBits), this.txtStopBit.Text);//使用枚举参数构造//serial = new SerialPortUtil(portname, rate, party, databit, stopbit);//使用字符串参数构造 serial = new SerialPortUtil(portname, this.txtBaudRate.Text, this.txtParity.Text, this.txtDataBits.Text, this.txtStopBit.Text);
serial.DataReceived
+= newDataReceivedEventHandler(serial_DataReceived);

}
catch(Exception ex)
{
MessageBox.Show(ex.Message);
serial
= null;return;
}
}
if (!isOpened)
{
serial.OpenPort();
btnConnect.Text
= "断开";
}
else{
serial.ClosePort();
serial
= null;

btnConnect.Text
= "连接";
}

isOpened
= !isOpened;this.btnSend.Enabled =isOpened;this.lblTips.Text = isOpened ? "已连接" : "未连接";
}
catch(Exception ex)
{
this.lblTips.Text =ex.Message;
MessageBox.Show(ex.Message);
}
}

4、POS打印机操作

本小节包括两个POS打印类,USB接口方式的POS小票打印的打印预览管理界面 FrmPosPrintPreview和基于并口接口方式的打印辅助类POSPrinter。

基于USB接口方式的POS小票打印该操作。很多基本上采用了GP5860这种POS打印机进行小票打印了。

执行打印预览,USB接口方式的小票可以实现效果的预览,如果是并口的只有直接输出,没有预览效果。

1)USB的POS打印的辅助类FrmPosPrintPreview提供了几个常用的参数进行配置,代码如下所示。

/// <summary>    
///POS小票打印的打印预览管理界面/// </summary>    
public partial classFrmPosPrintPreview : Form    
{
/// <summary> ///设置待打印的内容/// </summary> public string PrintString = "";/// <summary> ///指定默认的小票打印机名称,用作快速POS打印/// </summary> public string PrinterName = "GP-5860III";/// <summary> ///POS打印机的边距,默认为2/// </summary> public int POSPageMargin = 2;/// <summary> ///POS打印机默认横向还是纵向,默认设置为纵向(false)/// </summary> public bool Landscape = false;

2)并口的POS打印辅助类POSPrinter提供了两个构造函数和一个打印操作,代码如下。

/// <summary>    
 ///并口POS打印机操作辅助类/// </summary>    
 public classPOSPrinter    
{
/// <summary> ///默认构造函数,打印并口名称为LPT1/// </summary> publicPOSPrinter()/// <summary> ///以指定的并口打印名称构造函数/// </summary> /// <param name="prnPort">打印并口名称,如LPT1</param> public POSPrinter(stringprnPort)/// <summary> ///打印内容/// </summary> /// <param name="str">待打印的字符串</param> /// <returns></returns> public string PrintLine(string str)

3)辅助类的使用例子代码如下所示

private void btnPosPrint_Click(objectsender, EventArgs e)    
{
if (this.lvwDetail.Items.Count == 0)
{
MessageUtil.ShowTips(
"没有消费记录");return;
}

StringBuilder sb
= newStringBuilder();
sb.AppendFormat(
"{0}\r\n\r\n", Portal.gc.gAppUnit);
sb.AppendFormat(
"客房消费\r\n");
sb.AppendFormat(
"结账单号:{0}\r\n", this.lblCheckOutNo.Text);
sb.AppendFormat(
"客户姓名:{0}\r\n", this.txtName.Text);
sb.AppendFormat(
"结账客房:{0}\r\n", RoomInfo.RoomNo);
sb.AppendFormat(
"打印时间:{0}\r\n\r\n", DateTime.Now);

sb.AppendFormat(
"项目 单价 数量 金额\r\n");for (int i = 0; i < lvwDetail.Items.Count; i++)
{
ListViewItem item
=lvwDetail.Items[i];string itemName = item.SubItems[1].Text;if (itemName.Contains("买断消费"))
{
itemName
= "买断消费";
}
sb.AppendFormat(
"{0}", itemName);
sb.AppendFormat(
"{0}", item.SubItems[2].Text);
sb.AppendFormat(
"{0}", item.SubItems[6].Text);
sb.AppendFormat(
"{0}\r\n", item.SubItems[7].Text);
}
sb.AppendFormat(
"\r\n\r");
sb.AppendFormat(
"总 金 额:{0}\r\n", Convert.ToDecimal(this.lblConsume.Tag).ToString("C2"));
sb.AppendFormat(
"实收金额:{0}\r\n", Convert.ToDecimal(this.txtAmount.Text).ToString("C2"));
sb.AppendFormat(
"实收大写:{0}\r\n", RMBUtil.CmycurD(this.txtAmount.Text));
sb.AppendFormat(
"现 金:{0}\r\n", Convert.ToDecimal(this.txtPay.Text).ToString("C2"));
sb.AppendFormat(
"找 零:{0}\r\n", Convert.ToDecimal(this.lblChange.Text).ToString("C2"));
sb.AppendFormat(
"\r\n\r");

Portal.gc.PosPrint(sb.ToString());
}
/// <summary>    
///提供通用的POS打印函数/// </summary>    
public void PosPrint(stringprintString)    
{
bool useUSB =SystemConfig.Default.IsUSBPOSPrinter;if (!useUSB)
{
if (MessageUtil.ShowYesNoAndTips("您是否确定进行POS打印?") ==DialogResult.No)
{
return;
}
POSPrinter print
= newPOSPrinter(SystemConfig.Default.PosPrintPort);string error =print.PrintLine(printString);if (error != "")
{
MessageUtil.ShowError(error);
}
}
else{
FrmPosPrintPreview dlg
= newFrmPosPrintPreview();
printString
= "\r\n\r\n\r\n\r\n" +printString;
printString
+= string.Format("\r\n签单金额:\r\n");
printString
+= string.Format("\r\n签单单位:\r\n");
printString
+= string.Format("\r\n签 单 人:\r\n");
dlg.PrintString
=printString;
dlg.ShowDialog();
}
}

5、玻璃效果图片按钮控件 VistaButton

本辅助类主要是用来方便实现美观的图片按钮,类似玻璃效果的那种,图片放上去可以带有动态阴影效果。 这个是一个Vista样式的控件,其代码是在Codeproject上有的:
http://www.codeproject.com/KB/buttons/VistaButton.aspx

Screenshot - Screenshot.jpg

通过改变其颜色,就可以实现不同的效果,而且鼠标靠近或者离开都有特殊的效果,比较酷。例如我加上颜色图片后,得到的效果如下所示:

使用说明:

在VS工具箱里面添加该共用类库的程序集应用,然后添加VistaButton控件的使用即可。
可以在窗体的可视化设计界面中,编辑控件的背景色等图片效果,实现更多美妙的特效。
该控件属于界面控件,直接拖动到界面即可使用。

6、TreeView的包装类,实现树的DragDrop拖拉的操作的辅助类 TreeViewDrager

1)辅助类提供的方法接口如下所示:

public classTreeViewDrager    
{
/// <summary> ///设置拖动树的时候,显示的图片,显示其中第一个/// </summary> publicImageList TreeImageList/// <summary> ///默认构造函数/// </summary> publicTreeViewDrager()/// <summary> ///参数构造函数,设置操作的TreeView/// </summary> /// <param name="treeView"></param> publicTreeViewDrager(TreeView treeView)/// <summary> ///处理拖动节点后的事件/// </summary> public eventProcessDragNodeEventHandler ProcessDragNode;
}

2)辅助类的使用例子代码如下所示

private voidInit()    
{
TreeViewDrager treeViewDrager
= new TreeViewDrager(this.treeView1);
treeViewDrager.TreeImageList
= this.imageList1;//不设置这个也可以,只是拖动的时候没图标。 treeViewDrager.ProcessDragNode += newProcessDragNodeEventHandler(treeViewDrager_ProcessDragNode);
}
private bool treeViewDrager_ProcessDragNode(TreeNode from, TreeNode to)
{
//这里根据from/to两个节点记录的信息去进行数据库持久化的工作。//根据持久化的结果决定节点是否会最终实现拖动操作。 return true;
}

实际上,我们需要处理拖动后的事件,一般来说,我们需要调整他们的父目录就可以了,如我的通用字典管理模块,就是利用这个控件实现拖动效果。

publicFrmDictionary()    
{
InitializeComponent();

TreeViewDrager drager
= new TreeViewDrager(this.treeView1);
drager.TreeImageList
= this.imageList1;
drager.ProcessDragNode
+= newProcessDragNodeEventHandler(drager_ProcessDragNode);
}
booldrager_ProcessDragNode(TreeNode dragNode, TreeNode dropNode)
{
if (dragNode != null && dragNode.Text == "数据字典管理")return false;if (dropNode != null && dropNode.Tag != null)
{
string dropTypeId =dropNode.Tag.ToString();string dragTypeId =dragNode.Tag.ToString();//MessageUtil.ShowTips(string.Format("dropTypeId:{0} dragTypeId:{1}", dropTypeId, drageTypeId)); try{
DictTypeInfo dragTypeInfo
= BLLFactory<DictType>.Instance.FindByID(dragTypeId);if (dragTypeInfo != null)
{
dragTypeInfo.PID
=dropTypeId;
BLLFactory
<DictType>.Instance.Update(dragTypeInfo, dragTypeInfo.ID);
}
}
catch(Exception ex)
{
LogHelper.Error(ex);
MessageUtil.ShowError(ex.Message);
return false;
}
}
return true;
}

WCF寄宿方式是一种非常灵活的操作,可以在IIS服务、Windows服务、Winform程序、控制台程序中进行寄宿,从而实现WCF服务的运行,为调用者方便、高效提供服务调用。本文分别对这几种方式进行详细介绍并开发例子进行说明,以求大家对WCF寄宿的方式进行全面的认识和了解。

1、 WCF服务的IIS服务寄宿

我在我前面几篇WCF开发框架的介绍文章中,介绍过了WCF常用的一种寄宿方式,IIS服务寄宿。这种寄宿方式是最为方便的方式,而且由于服务只需要IIS运行就能自动运行起来,因此广为使用。

创建这种方式IIS寄宿方式的,只需要在解决方案里面,添加WCF服务应用程序,就可以生成这种的服务模块了。

这个是一个基于Web的应用程序,创建项目后会生成一个Service1.svc的服务页面,以及相关的WCF服务接口和实现,如下图所示。

这个就是简单的WCF服务,当然如果是复杂的实际应用,会考虑和数据库打交道,而且可能项目会分成几个进行管理,从而实现更好的逻辑分离操作。

2、 创建WCF服务库为多种寄宿做准备

除了上面常用的IIS服务寄宿,一般还会有各种各样的寄宿方式,不过如果采用其他方式的寄宿方式,一般会把WCF服务和寄宿方式进行项目的分离,实现更好的重用操作,特别WCF需要考虑多种寄宿方式的情况下。下面是WCF服务库和WCF服务应用程序的介绍说明,先了解一下基础。

WCF服务库,可以认为是一个包含WCF服务以及契约定义的类库。这里WCF服务库还不能直接运行,你可以在其他项目里引用,在宿主里启用托管这个库。

而WCF应用程序,是一个可以执行的程序,它有独立的进程,WCF服务类契约的定义,可以直接看到运行的效果。此项目模板应该是基于IIS托管的程序。

前者一般考虑WCF服务设计的时候,服务类的定义为单独的库,可以为其它项目使用。提高代码的复用性。后者在开发基于IIS托管的WCF服务程序时,比较多见,自学的时候也可以使用这种类型。当然你也可以修改这些代码,比如把WCF服务程序里的类,移到一个单独的类库里。

创建WCF服务库,可以理解为我们开发.NET程序时创建的一个类库模块,不含界面,如下所示,创建一个WCF服务库。

确定后就只有一个示例服务Service1生成了。

这里,我们为了演示,就不修改任何它们的代码,原始的代码如下所示。

usingSystem;usingSystem.Collections.Generic;usingSystem.Linq;usingSystem.Runtime.Serialization;usingSystem.ServiceModel;usingSystem.Text;namespaceWcfServiceLibrary
{
public classService1 : IService1
{
public string GetData(intvalue)
{
return string.Format("You entered: {0}", value);
}
publicCompositeType GetDataUsingDataContract(CompositeType composite)
{
if (composite == null)
{
throw new ArgumentNullException("composite");
}
if(composite.BoolValue)
{
composite.StringValue
+= "Suffix";
}
returncomposite;
}
}
}

3、 WCF服务的控制台程序寄宿

这种也是一种常见的WCF服务寄宿方式,通过启动一个类似DOS窗口的控制台软件,实现WCF服务的动态寄宿,关闭控制台程序,服务就自然终止。

这种方式很简单,创建一个控制台程序,然后添加WCF服务类库的项目应用,在Main函数里面添加下面代码即可实现。

namespaceWcfService_HostConsole
{
classProgram
{
static void Main(string[] args)
{
try{
ServiceHost serviceHost
= new ServiceHost(typeof(Service1));if (serviceHost.State !=CommunicationState.Opened)
{
serviceHost.Open();
}

Console.WriteLine(
"WCF 服务正在运行......");
Console.WriteLine(
"输入回车键 <ENTER> 退出WCF服务");
Console.ReadLine();
serviceHost.Close();

}
catch(Exception ex)
{
Console.WriteLine(ex);
}
}
}
}

4、 WCF服务的Winform程序寄宿

和控制台程序一样,我们创建一个Winform项目,然后在窗体启动代码里面添加寄宿方式的代码即可,为了较好的响应体验,可以使用后台线程程序进行服务启动,如下所示。

namespaceWcfService_HostWinform
{
public partial classFrmMain : Form
{
ServiceHost serviceHost
= null;
BackgroundWorker worker
= null;publicFrmMain()
{
InitializeComponent();

worker
= newBackgroundWorker();
worker.RunWorkerCompleted
+= newRunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
worker.DoWork
+= newDoWorkEventHandler(worker_DoWork);if (!worker.IsBusy)
{
tssTips.Text
= "正在启动......";
lblTips.Text
=tssTips.Text;
worker.RunWorkerAsync();
}
}
void worker_DoWork(objectsender, DoWorkEventArgs e)
{
try{
serviceHost
= new ServiceHost(typeof(Service1));if (serviceHost.State !=CommunicationState.Opened)
{
serviceHost.Open();
}

e.Result
= "正常";
}
catch(Exception ex)
{
e.Result
=ex.Message;
}
}
void worker_RunWorkerCompleted(objectsender, RunWorkerCompletedEventArgs e)
{
if (e.Result != null)
{
if (e.Result.ToString() == "正常")
{
tssTips.Text
= "服务正在进行侦听......";
}
else{
tssTips.Text
= string.Format("错误:{0}", e.Result);
}

lblTips.Text
=tssTips.Text;
}
}
...........................
}
}

当然为了防止Winform程序被不小心关闭,可以添加托盘代码,把程序缩小到托盘图标里面。

5、 WCF服务的Windows 服务程序寄宿

这种方式的服务寄宿,和IIS一样有一个一样的优点,系统启动后,WCF服务也会跟着启动了,不用人工干预,也是一种较好的寄宿方式。

为了实现这种方式的寄宿,我们创建一个控制台程序,然后添加响应的Window服务和安装程序类

然后在服务类启动里面添加WCF的寄宿代码,如下所示。

    public classServiceManager : ServiceBase 
{
private static object syncRoot = new Object();//同步锁 private ServiceHost serviceHost = null; //寄宿服务对象 publicServiceManager()
{
this.ServiceName =Constants.ServiceName;
}
/// <summary> ///设置具体的操作,以便服务可以执行它的工作。/// </summary> protected override void OnStart(string[] args)
{
try{
serviceHost
= new ServiceHost(typeof(Service1));if (serviceHost.State !=CommunicationState.Opened)
{
serviceHost.Open();
}
}
catch(Exception ex)
{
LogTextHelper.Error(ex);
}

LogTextHelper.Info(Constants.ServiceName
+ DateTime.Now.ToShortTimeString() + "已成功调用了服务一次。");

LogTextHelper.Info(Constants.ServiceName
+ "已成功启动。");
}

为了实现通过该控制台程序实现参数化安装和卸载服务,我们需要拦截控制台的参数,并进行相应的操作,如下所示。

        /// <summary>
        ///应用程序的主入口点。/// </summary>
[STAThread]static void Main(string[] args)
{
ServiceController service
= newServiceController(Constants.ServiceName);//运行服务 if (args.Length == 0)
{
ServiceBase[] ServicesToRun;
ServicesToRun
= new ServiceBase[] { newServiceManager() };
ServiceBase.Run(ServicesToRun);
}
else if (args[0].ToLower() == "/i" || args[0].ToLower() == "-i")
{
#region 安装服务 if (!ServiceIsExisted(Constants.ServiceName))
{
try{string[] cmdline ={ };string serviceFileName =System.Reflection.Assembly.GetExecutingAssembly().Location;

TransactedInstaller transactedInstaller
= newTransactedInstaller();
AssemblyInstaller assemblyInstaller
= newAssemblyInstaller(serviceFileName, cmdline);
transactedInstaller.Installers.Add(assemblyInstaller);
transactedInstaller.Install(
newSystem.Collections.Hashtable());

TimeSpan timeout
= TimeSpan.FromMilliseconds(1000 * 10);
service.Start();
service.WaitForStatus(ServiceControllerStatus.Running, timeout);
}
catch(Exception ex)
{
LogTextHelper.Info(ex);
throw;
}
}
#endregion}else if (args[0].ToLower() == "/u" || args[0].ToLower() == "-u")
{
#region 删除服务 try{if(ServiceIsExisted(Constants.ServiceName))
{
string[] cmdline ={ };string serviceFileName =System.Reflection.Assembly.GetExecutingAssembly().Location;

TransactedInstaller transactedInstaller
= newTransactedInstaller();
AssemblyInstaller assemblyInstaller
= newAssemblyInstaller(serviceFileName, cmdline);
transactedInstaller.Installers.Add(assemblyInstaller);
transactedInstaller.Uninstall(
null);
}
}
catch(Exception ex)
{
LogTextHelper.Info(ex);
throw;
}
#endregion}
}

编译程序成功后,我们添加两个批处理的DOS脚本来实现执行程序的自动安装和卸载,如下所示。

安装脚本

"WcfService_HostWinService.exe"-ipause

卸载脚本

"WcfService_HostWinService.exe"-upause

顺利执行脚本后,服务列表里面就增加一个服务项目了。

卸载操作,直接执行脚本就会卸载服务,非常方便哦。

6、 WCF服务的Web寄宿

当然,除了以上几种方式,这种以WCF服务库的方式,也可以在Web方式进行寄宿(IIS方式),这种方式更简单,添加一个后缀名的svc的文件,只需要一行代码即可,如下所示。

7、 使WCF服务支持GET方式调用

有时候,我们为了需要,可能通过一个小程序发布一个服务,然后供其他程序进行调用,可能是Web,也可以是Winform,但是我们是想提供一个基于HTTP,GET或者POST方式来实现接口的调用的,例如,提供一个JSON格式或者文本格式的内容返回操作。

如果是整合在Winform里面,那么我们在Winform里面添加一个WCF的项,修改里面的代码就可以了,如下所示。

首先要在使用GET方式的WCF服务接口的添加说明。如果是POS方式,增加设置有点不同([WebInvoke(Method = "POST", BodyStyle = WebMessageBodyStyle.Wrapped, ResponseFormat = WebMessageFormat.Json)])。

namespaceWcfServiceForWinform
{
[ServiceContract]
public interfaceIService1
{
[OperationContract]
voidDoWork();

[OperationContract]
[WebInvoke(Method
= "GET", ResponseFormat =WebMessageFormat.Json)]string GetData(intvalue);
}
}

第二,在实现类里面添加相应的设置

namespaceWcfServiceForWinform
{
[AspNetCompatibilityRequirements(RequirementsMode
=AspNetCompatibilityRequirementsMode.Allowed)]public classService1 : IService1
{
public voidDoWork()
{
}
public string GetData(intvalue)
{
return string.Format("You entered: {0}", value);
}
}
}

配置文件如下所示,颜色标注特别的要注意:

<?xml version="1.0"?>
<configuration>

  <system.web>
    <compilationdebug="true"/>
  </system.web>

  <system.serviceModel>
    <services>
      <servicename="WcfServiceForWinform.Service1"behaviorConfiguration="ServiceConfig">
        <endpointaddress=""binding="webHttpBinding"bindingConfiguration="webHttpBindingWithJsonP"behaviorConfiguration="webHttpBehavior"contract="WcfServiceForWinform.IService1">
        </endpoint>
        <endpointaddress="mex"binding="mexHttpBinding"contract="IMetadataExchange" />
        <host>
          <baseAddresses>
            <addbaseAddress="http://localhost:9000/Service1/" />
          </baseAddresses>
        </host>
      </service>
    </services>

    <bindings>
<webHttpBinding>
<bindingname="webHttpBindingWithJsonP"crossDomainScriptAccessEnabled="true" />
</webHttpBinding>
</bindings> <behaviors> <endpointBehaviors> <behaviorname="webHttpBehavior"> <webHttp/> </behavior> </endpointBehaviors> <serviceBehaviors> <behaviorname="ServiceConfig"> <serviceMetadatahttpGetEnabled="True"policyVersion="Policy15"/> <serviceDebugincludeExceptionDetailInFaults="False"/> </behavior> </serviceBehaviors> </behaviors> <serviceHostingEnvironmentaspNetCompatibilityEnabled="true" /> </system.serviceModel> <startup> <supportedRuntimeversion="v4.0"sku=".NETFramework,Version=v4.0"/> </startup> </configuration>

运行Winform程序,启动了WCF服务后,我们可以在浏览器(Chrome)上进行操作,如下结果所示。

从上图我们可以看到,这个通过Winform启动起来的WCF服务,连接也能通过GET方式进行接口调用了,接口可以通过参数进行传递,对于一些方便传输数据的接口如JSON接口,就是一种非常方便的调用了。

我在随笔《
Winform开发框架之通用短信邮件通知模块
》和《
Winform开发框架之通用附件管理模块
》等多篇文章中都有介绍我总体的一个Winform开发框架的规划,其实就是开发一个集成度高的Winform开发框架,然后尽可能开发一些日常项目用到的模块,我称之为通用模块(可重复利用,或稍微调整可以适应项目需求),这样就形成了一个良好的生态体系,能够极快提高开发效率,同时也能规范化项目产品的开发工作,松耦合的集成能提供更高层的整合和利用。本文主要介绍我的Winform开发框架体系里面的一个新的模块,通用人员信息管理模块,这个模块其实在很多场合都可能用到,如企业员工管理、科室员工管理等等,这些要求登记人员详细资料及图片等信息的系统模块,就是我这里提到的人员信息管理模块。

首先我们来回顾一下,我的Winform开发框架体系,以及人员信息管理模块在其中所处的位置。
Winform开发框架是我集多年开发经验以及积累而成,很多细节之处润物细无声,但却是精粹心得所至,很多地方都希望是精益求精,力求把框架中的模块当成一把把神兵利器,用到的时候,马上就可以派生用场解决问题,这样可以避免给客户开发业务的时候,延误战机,或者因为事无巨细,都要从头来过,效率大打折扣,而且时间和金钱的大投入也未见得取得好的效果。

1、模块设计

整个人员模块,我们预想需要管理人员基本信息,学习情况,职称情况,履历情况,出国情况,家庭情况,受奖情况,以及相关的附件信息,当然人员数据还有一些字典管理,因此该模块同时也整合通用附件管理模块,以及通用字典管理模块,是一个小综合性的集成模块了。

人员信息管理,一般需要导出一个标准型的Word文档,方便人员资料打印操作,我们预想是导出这样的文档的。


因此设计模块的数据库如下所示,其中包括了通用字典管理和通用附件模块的数据库设计。

完成以上的文档规划以及数据库的模型设计,就可以着手开始我们的系统模块功能,以及界面的设计工作了。

2、软件使用界面

和其他通用表格一个,需要展示一个列表集合,把相关功能集成在其中,包括查询、新建、按模板的导入、导出功能,当然更多的功能,集成在分页控件的(列表)右键菜单中,其中一些字典项目,采用下列列表方式,方便填写或者从中选择。

详细的数据编辑窗体,就是综合各种人员信息的填写和展现了,包括前面提到的基本信息(含肖像管理)、学习情况、职称情况、履历情况、出国情况、家庭情况、个人图片、科研和受奖情况、个人文件资料等功能模块,其中整合了字典管理、附件管理,所以功能使用也很方便,下面就是一个基本资料的主界面情况。

为了更好展现整个人员管理数据内容的情况,我们在看看其中家庭和个人图片的管理界面,其中家庭情况,可以在其中添加、删除、刷新、位置上移、位置下移等操作,个人图片则是集成了附件管理的模块,因此也方便集成进去。

3、报表导出

刚才说到,我们的目的之一,就是需要导出一个符合规格的Word文档,这样方便我们正常的数据交换和打印显示。为了更好的实现固定报表的导出,对于表格的绑定,这里使用了Aspose.Cell的MailMerge功能了,先在固定模板中插入并定义好相关的域引用,如下所示。

然后,插入一些常规的书签(单字段),这样绑定数据就很方便了,最后设计好的Word模板如下所示。至于了Aspose.Cell的MailMerge功能操作,有空再独立的随笔中介绍吧。

4、模块集成使用

由于模块是独立开发,支持集成整合的,因此,把相关的数据库部署到具体的环境上,然后简单调用下面的代码就可以了。

        private void btnAddStaff_Click(objectsender, EventArgs e)
{
FrmEditStaff dlg
= newFrmEditStaff();
dlg.ShowDialog();
}
private void btnStaffList_Click(objectsender, EventArgs e)
{
FrmStaff dlg
= newFrmStaff();
dlg.ShowDialog();
}

以上就是我开发人员管理的一些思路和总结,介于篇幅原因,细节地方以后会逐步介绍一下。整个模块除了能够有效管理人员基本信息外,还比较方便对人员列表数据,如家庭情况,履历情况等列表性的数据合理的管理,还可以通过附件管理(详细功能可以参考一下《
Winform开发框架之通用附件管理模块
》),这样集成,既能达到满足功能需求,又能更好实现松耦合的集成使用,在今后的开发项目中多次运用,达到提高效率,提高代码质量和稳定性的目的。

希望本随笔文章对大家有帮助,有启发。