WinForm窗体的创建于传值(1)

一.  前言

自己很少做WinForm方面的开发,最近公司的一个大型项目中,客户要求Client使用WinForm,Server使用java。以前对WinForm的认识并不深入,在这次开发过程中,陆续接触到了一些WinForm方面的技术。其中,今天要跟大家讨论的就是关于WinForm窗体的创建以及传值方面的东西。

 

二.  概述

以前认为WinForm的程序一般都是编译为.exe,程序中的窗体直接new出来,然后根据要求,进行show或者showDialog。但是,在公司目前的这个项目中,所有的模块都编译为.dll(不管是不是窗体),然后通过反射进行创建以及show。不清楚大家在自己的项目中是否这样使用。在此,分享出来与大家一起讨论。如果有更好的实现思路,希望能在回复中进行分享。谢谢!

 

三.  关键点的实现

  1. 原理

    (1)首先,我们需要一个配置文件,将程序中使用到的.dll文件名,窗体名等写入该配置文件。这个配置文件大家可以根据需要使用不同的类型,例如ini,xml,txt等等,在项目中,我们使用的是                  ini,大体结构如

###############################

#   文件名<例如:FuncConfig.ini>

###############################

[FUNCINFO]

#

功能编号=.dll文件名,窗体编号,功能名,功能所属模块编号,权限……

#

#

……

#

例如:

###############################

#   文件名<例如:FuncConfig.ini>

###############################

[FUNCINFO]

#

CMM0100L01=CMM0100,CMM0100F01,人员数据检索,报表管理,00

#

       

        当然,这个文件的格式可以根据程序的需要来更改。这里只是给出一个示例.注意:给出的窗体应该在指定的.dll文件中。

     (2)   程序使用给出的窗体编号去加载对应的.dll,然后在该.dll中将窗体类加载并show出来。

  2.代码实现

    (1)代码的流程如下图所示:

①     实现此功能的关键代码在FuncTransitCtrl类中,该类中,有两个方法,分别为ShowDialog和Show,分别对应两种show出窗体的方式。我们首先来看ShowDialog里面的代码。

  1. 首先,该方法接收两个参数,分别为窗体ID和需要传递给该窗体的参数集合,我们来看下方法定义
     /// <summary>
        /// 以ShowDialog的方式显示指定的窗体
        /// </summary>
        /// <param name="funcID">功能编号</param>
        /// <param name="sendData">要发送的数据</param>
        /// <returns>被show出的窗体</returns>
        public Form ShowDialog(String funcID, SendData sendData)

 

SendData是一个我们自己定义数据结构,专门用于存储要发送给窗体的参数。等会儿,我们将会看到它的代码。现在,我们只需要明白它是做什么的就可以了

  1. 接着,我们来讲解方法里面的每段代码。

     对窗体编号进行非空判断。

       Form form = null;//返回的窗体
            if (String.IsNullOrEmpty(funcID))
            {
                gLogger.ErrorLog("FormTransitCtrl", "没有设定窗体ID。", null);
                MessageBox.Show("指定的功能界面启动失败","错误",MessageBoxButtons.OK,MessageBoxIcon.Hand);
                return form;
            }

    gLogger是用于写日志的一个类型。相信大家在项目里都有自己一套专门处理日志的方法,在这里就不多说了。

     获取与该功能有关的信息。在这里,我们的程序就会去读取配置文件,把跟该窗体编号有关的信息全部读取出来,并保存到一个类中。我们分几步来看代码,首先,定义个用于保存功能相关                   信息的类型:

   /// <summary>
    /// 保存功能信息的类型
    /// </summary>
    public class EihoFuncInfo
    {
        private string _DllFileName;
        private string _EihoFuncID;
        private string _FormID;
        private string _WindowTitle;

        public string DllFileName { get; set; }
        public string EihoFuncID { get; set; }
        public string FormID { get; set; }
        public string WindowTitle { get; set; }

    }

    接着,在FuncTransitCtrl定义一个静态方法,用于读取配置文件,并将获取的信息赋值给EihoFuncInfo的各字段。首先我们来看看如何读取配置文件中的信息(以我提供的配置文件格式为准。)这段代码大家可以简略看过,因为大家的配置文件格式不一定一致:

     /// <summary>
        /// 读取ini文件中的信息
        /// </summary>
        /// <param name="strFileName">ini文件名</param>
        /// <param name="strSection">读取的段落</param>
        /// <param name="strKey">读取的key</param>
        /// <param name="strPath">ini文件路径</param>
        /// <returns>读取到的信息</returns>
        public static String GetIni(String strFileName, String strSection, String strKey, String strPath = "")
        {
            String str2 = strPath;
            //如果没有指明路径,则使用FuncTransitCtrl类中默认的ini路径
            if (String.IsNullOrEmpty(strPath))
            {
                str2 = FuncTransitCtrl.SystemPath + @"ini\";
            }
            String tmpPath = str2 + strFileName;
            if (!File.Exists(tmpPath))
            {
                //写日志
                throw new FileNotFoundException();
            }
            //读取key对应的value
            String lpReturnedString = GetPrivateIniProfileString(strSection, strKey, tmpPath);
            return lpReturnedString;
        }

    GetIni是主要的方法,它位于ConfigUtil类中。在该方法中,调用了一个名为GetPrivateIniProfileString的方法,这个方法会去到配置文件中去匹配段落,找到匹配的段落后,在查找该段落下的key,然后返回对应key的value值,该类型的完整代码如下:

View Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace Demo
{
    /// <summary>
    /// 读取各种配置文件的类型
    /// </summary>
    public class ConfigUtil
    {
        //段落开始和结束的括号
        private const String SECTION_START_STR = "[";
        private const String SECTION_END_STR = "]";
        //注释符号
        private const String COMMENT_STR = ";";
        private const String COMMENT_STR2 = "#";
        //区分key与value的等号
        private const String KEY_AND_VALUE_SEPARATOR = "=";

        /// <summary>
        /// 读取ini文件中的信息
        /// </summary>
        /// <param name="strFileName">ini文件名</param>
        /// <param name="strSection">读取的段落</param>
        /// <param name="strKey">读取的key</param>
        /// <param name="strPath">ini文件路径</param>
        /// <returns>读取到的信息</returns>
        public static String GetIni(String strFileName, String strSection, String strKey, String strPath = "")
        {
            String str2 = strPath;
            //如果没有指明路径,则使用FuncTransitCtrl类中默认的ini路径
            if (String.IsNullOrEmpty(strPath))
            {
                str2 = FuncTransitCtrl.SystemPath + @"ini\";
            }
            String tmpPath = str2 + strFileName;
            if (!File.Exists(tmpPath))
            {
                //写日志
                throw new FileNotFoundException();
            }
            //读取key对应的value
            String lpReturnedString = GetPrivateIniProfileString(strSection, strKey, tmpPath);
            return lpReturnedString;
        }

        /// <summary>
        /// 读取ini配置文件中指定key的值
        /// </summary>
        /// <param name="strSection">要读取的段落</param>
        /// <param name="strKey">读取的key</param>
        /// <param name="strPath">ini文件路径</param>
        /// <returns>key对应的value</returns>
        private static String GetPrivateIniProfileString(String strSection, String strKey, String strPath)
        {
            FileStream fs = new FileStream(strPath, FileMode.Open, FileAccess.Read);
            StreamReader sr = new StreamReader(fs, Encoding.UTF8);
            //是否查找key
            bool isSearchKey = false;
            //
            String strValue = String.Empty;
            try
            {
                String strLine = String.Empty;
                while ((strLine = sr.ReadLine()) != null)
                {
                    String strGetSection = GetSection(strLine);
                    //如果是一个段落
                    if (!String.IsNullOrEmpty(strGetSection))
                    {
                        if (isSearchKey == true)
                        {
                            return strValue;
                        }
                        //如果找到的段落和参数不一致,则继续循环查找
                        if (strGetSection != strSection)
                        {
                            continue;
                        }
                        //找到了一致的,修改该变量,准备查找该段落下的key与value
                        isSearchKey = true;
                    }
                    //不是一个段落,则查找key
                    else
                    {
                        if (!isSearchKey)
                        {
                            continue;
                        }
                        //该行是否为一个注释
                        if (IsComment(strLine))
                        {
                            continue;
                        }
                        //获取区分key,value的符号位置
                        int iPos = GetKeyAndValuePos(strLine);
                        if (iPos < 0)
                        {
                            continue;
                        }
                        //获取key
                        String strGetKey = strLine.Substring(0, iPos).Trim();
                        //如果与参数的不一致,继续循环查找
                        if (strGetKey != strKey)
                        {
                            continue;
                        }
                        //取得value
                        strValue = strLine.Substring(iPos + 1).Trim();
                        break;
                    }
                }
            }
            catch (IOException ex)
            {
                //写日志
                throw new Exception(ex.Message, ex);
            }
            finally
            {
                sr.Close();
                fs.Close();
            }
            return strValue;
        }

        /// <summary>
        /// 获取字符串中,[]之间的内容
        /// </summary>
        /// <param name="strLine"></param>
        /// <returns>[]之间的内容</returns>
        private static String GetSection(String strLine)
        {
            String strTrimLine = strLine.Trim();
            //如果没有段落开始的括号,则不是一个段落,不进行处理
            if (!strTrimLine.StartsWith(SECTION_START_STR))
            {
                return String.Empty;
            }
            int iPos = strTrimLine.IndexOf(SECTION_END_STR);//获得段落结束括号的位置
            return strTrimLine.Substring(1, iPos - 1);//返回[]之间的字符串
        }

        /// <summary>
        /// 判断该行是否为一个注释
        /// </summary>
        /// <param name="strLine"></param>
        /// <returns></returns>
        private static bool IsComment(String strLine)
        {
            String strTrimLine = strLine.Trim();

            return (strTrimLine.StartsWith(COMMENT_STR) || strTrimLine.StartsWith(COMMENT_STR2));
        }

        /// <summary>
        /// 获取区分key与value的符号的位置
        /// </summary>
        /// <param name="strLine">一个包含key=value的字符串</param>
        /// <returns>区分符号的位置</returns>
        private static int GetKeyAndValuePos(String strLine)
        {
            return strLine.IndexOf(KEY_AND_VALUE_SEPARATOR);
        }
    }
}

    得到了配置文件中的信息后,我们就可以提取里面的信息了。代码如下:

     /// <summary>
        /// 获取指定编号的功能的所有信息
        /// </summary>
        /// <param name="funcID">功能编号</param>
        /// <returns>功能信息类型</returns>
        public static EihoFuncInfo GetEihoFuncInfo(String funcID)
        {
            EihoFuncInfo funcInfo = null;
            if (!String.IsNullOrEmpty(funcID))
            {
                try
                {
                    //获取配置的value信息
                    String strFuncInfo = ConfigUtil.GetIni("FuncConfig.ini", "FUNCINFO", funcID,"");
                    funcInfo = new EihoFuncInfo();
                    //分别提取各项信息
                    funcInfo.EihoFuncID = funcID;
                    funcInfo.DllFileName = strFuncInfo.Split(',')[0];
                    funcInfo.FormID = strFuncInfo.Split(',')[1];
                    funcInfo.WindowTitle = strFuncInfo.Split(',')[2];
                }
                catch (Exception ex)
                {
                    //写日志的代码
                    throw ex;
                }
            }
            return funcInfo;
        }

  现在我们获得了必须的信息(窗体编号,所在程序集名称等),那么,接下来,我们就可以使用反射进行处理了。

       //获取功能相关的信息
            EihoFuncInfo funcInfo = GetEihoFuncInfo(funcID);
            if (funcInfo == null)
            {
                gLogger.ErrorLog("FormTransitCtrl", "没有取得与该功能相关的信息。", null);
                MessageBox.Show("指定的功能界面启动失败", "错误", MessageBoxButtons.OK, MessageBoxIcon.Hand);
                return form;
            }
            Assembly assembly = null;
            try
            {
                assembly = Assembly.LoadFrom(funcInfo.DllFileName + ".dll");
            }
            catch (Exception ex)
            {
                gLogger.ErrorLog("FormTransitCtrl", "DLL加载失败,DLL名:" + funcInfo.DllFileName, null);
                MessageBox.Show("指定的功能界面启动失败", "错误", MessageBoxButtons.OK, MessageBoxIcon.Hand);
                return form;
            }

    然后遍历该程序集里面的类型,找到与我们传入的FormID相匹配的,就将该窗体创建出来,代码如下:

       bool flag = false;
            //获得窗体ID
            String formID = "fm" + funcInfo.FormID;
            //遍历程序集中的所有类型
            foreach (Type type in assembly.GetTypes())
            {
                //找到与窗体匹配的
                if (type.Name.ToUpper() == formID.ToUpper())
                {
                    flag = true;
                    try
                    {
                        //调用构造函数创建
                        form = (Form)type.GetConstructor(Type.EmptyTypes).Invoke(null);
                        form.Text = funcInfo.WindowTitle;
                        SetFuncID(form, funcID);
                    }
                    catch(Exception ex)
                    {
                        gLogger.ErrorLog("FormTransitCtrl", "窗体创建失败。DLL名:" + funcInfo.DllFileName, null);
                        MessageBox.Show("指定的功能界面启动失败", "错误", MessageBoxButtons.OK, MessageBoxIcon.Hand);
                        return form;
                    }
                    //处理需要传递的参数
                    if ((sendData != null) && (sendData.GetSendData() != null))
                    {
                        IDictionaryEnumerator enumerator = ((Dictionary<String, String>)sendData.GetSendData()).GetEnumerator();
                        while (enumerator.MoveNext())
                        {
                            KeyValuePair<String, String> current = (KeyValuePair<String, String>)enumerator.Current;
                            String key = current.Key.ToString();
                            String value = current.Value.ToString();
                            ReceiveData.AddParame(key, value, form);

                        }
                    }
                    _form = form;
                    _form.ShowDialog();
                    form = _form;
                }
            }
            //没有找到匹配的窗体类型
            if (!flag)
            {
                gLogger.ErrorLog("FormTransitCtrl", "没有找到匹配的窗体类型。DLL名:" + funcInfo.DllFileName, null);
                MessageBox.Show("指定的功能界面启动失败", "错误", MessageBoxButtons.OK, MessageBoxIcon.Hand);
            }
            return form;

    至此,我们的主体代码应该是完成了,FuncTransitCtrl的完整代码如下:

View Code
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Reflection;

namespace Demo
{
    /// <summary>
    /// 对窗体进行控制的类型
    /// </summary>
    public class FuncTransitCtrl
    {
        /// <summary>
        /// 配置文件路径
        /// </summary>
        public static String _systemPath;

        private static Dictionary<Form, String> funcIDTable = new Dictionary<Form, string>();

        private Logger gLogger = new Logger();
        private Form _form;

        /// <summary>
        /// 获取或者设置配置文件路径
        /// </summary>
        public static String SystemPath
        {
            get
            {
                if (String.IsNullOrEmpty(_systemPath))
                {
                    return (Environment.GetEnvironmentVariable("USERPROFILE") + @"\AppData\Local\EMM\");
                }
                return _systemPath;
            }
            set
            {
                _systemPath = value;
            }
        }

        /// <summary>
        /// 以ShowDialog的方式显示指定的窗体
        /// </summary>
        /// <param name="funcID">功能编号</param>
        /// <param name="sendData">要发送的数据</param>
        /// <returns>被show出的窗体</returns>
        public Form ShowDialog(String funcID, SendData sendData)
        {
            Form form = null;//返回的窗体
            if (String.IsNullOrEmpty(funcID))
            {
                gLogger.ErrorLog("FormTransitCtrl", "没有设定窗体ID。", null);
                MessageBox.Show("指定的功能界面启动失败","错误",MessageBoxButtons.OK,MessageBoxIcon.Hand);
                return form;
            }
            //获取功能相关的信息
            EihoFuncInfo funcInfo = GetEihoFuncInfo(funcID);
            if (funcInfo == null)
            {
                gLogger.ErrorLog("FormTransitCtrl", "没有取得与该功能相关的信息。", null);
                MessageBox.Show("指定的功能界面启动失败", "错误", MessageBoxButtons.OK, MessageBoxIcon.Hand);
                return form;
            }
            Assembly assembly = null;
            try
            {
                assembly = Assembly.LoadFrom(funcInfo.DllFileName + ".dll");
            }
            catch (Exception ex)
            {
                gLogger.ErrorLog("FormTransitCtrl", "DLL加载失败,DLL名:" + funcInfo.DllFileName, null);
                MessageBox.Show("指定的功能界面启动失败", "错误", MessageBoxButtons.OK, MessageBoxIcon.Hand);
                return form;
            }
            bool flag = false;
            //获得窗体ID
            String formID = "fm" + funcInfo.FormID;
            //遍历程序集中的所有类型
            foreach (Type type in assembly.GetTypes())
            {
                //找到与窗体匹配的
                if (type.Name.ToUpper() == formID.ToUpper())
                {
                    flag = true;
                    try
                    {
                        //调用构造函数创建
                        form = (Form)type.GetConstructor(Type.EmptyTypes).Invoke(null);
                        form.Text = funcInfo.WindowTitle;
                        SetFuncID(form, funcID);
                    }
                    catch(Exception ex)
                    {
                        gLogger.ErrorLog("FormTransitCtrl", "窗体创建失败。DLL名:" + funcInfo.DllFileName, null);
                        MessageBox.Show("指定的功能界面启动失败", "错误", MessageBoxButtons.OK, MessageBoxIcon.Hand);
                        return form;
                    }
                    //处理需要传递的参数
                    if ((sendData != null) && (sendData.GetSendData() != null))
                    {
                        IDictionaryEnumerator enumerator = ((Dictionary<String, String>)sendData.GetSendData()).GetEnumerator();
                        while (enumerator.MoveNext())
                        {
                            KeyValuePair<String, String> current = (KeyValuePair<String, String>)enumerator.Current;
                            String key = current.Key.ToString();
                            String value = current.Value.ToString();
                            ReceiveData.AddParame(key, value, form);

                        }
                    }
                    _form = form;
                    _form.ShowDialog();
                    form = _form;
                }
            }
            //没有找到匹配的窗体类型
            if (!flag)
            {
                gLogger.ErrorLog("FormTransitCtrl", "没有找到匹配的窗体类型。DLL名:" + funcInfo.DllFileName, null);
                MessageBox.Show("指定的功能界面启动失败", "错误", MessageBoxButtons.OK, MessageBoxIcon.Hand);
            }
            return form;
        }

        /// <summary>
        /// 获取指定编号的功能的所有信息
        /// </summary>
        /// <param name="funcID">功能编号</param>
        /// <returns>功能信息类型</returns>
        public static EihoFuncInfo GetEihoFuncInfo(String funcID)
        {
            EihoFuncInfo funcInfo = null;
            if (!String.IsNullOrEmpty(funcID))
            {
                try
                {
                    //获取配置的value信息
                    String strFuncInfo = ConfigUtil.GetIni("FuncConfig.ini", "FUNCINFO", funcID,"");
                    funcInfo = new EihoFuncInfo();
                    //分别提取各项信息
                    funcInfo.EihoFuncID = funcID;
                    funcInfo.DllFileName = strFuncInfo.Split(',')[0];
                    funcInfo.FormID = strFuncInfo.Split(',')[1];
                    funcInfo.WindowTitle = strFuncInfo.Split(',')[2];
                }
                catch (Exception ex)
                {
                    //写日志的代码
                    throw ex;
                }
            }
            return funcInfo;
        }

        /// <summary>
        /// 保存窗体对应的功能ID
        /// </summary>
        /// <param name="form"></param>
        /// <param name="strFuncId"></param>
        public static void SetFuncID(Form form, String strFuncId)
        {
            if (funcIDTable.ContainsKey(form))
            {
                funcIDTable.Remove(form);
            }
            funcIDTable.Add(form, strFuncId);
        }
    }
}

    这是我们加载并show出窗体的代码,那么,传递的参数是怎么处理的呢?这里牵涉到两个类,分别为SendData和ReceiveData,看名字大家就知道是做什么的啦!首先来看看SendData的代码,如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Demo
{
    public class SendData
    {
        private Dictionary<String, String> _datas;

        public SendData()
        {
            _datas = new Dictionary<String, String>();
        }

        public Object GetSendData()
        {
            return _datas;
        }

        public void AddData(String key, String value)
        {
            if (_datas.ContainsKey(key))
            {
                _datas.Remove(key);
            }
            _datas.Add(key, value);
        }

        public void RemoveData(String key)
        {
            _datas.Remove(key);
        }
    }
}

    接下来,我们来看看ReceiveData类的代码。它与SendData有些不一样,虽然都是Key/Value集合,但是有那么多窗体,程序怎么知道哪些参数属于哪些窗体呢。所以,它内部使用一个嵌套的Key/Value集合来保存参数。key是某窗体的句柄值,而value则是属于该窗体的又一个Key/Value集合,我们来看代码,如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace Demo
{
    /// <summary>
    /// 用于保存窗体之间接收参数的类型
    /// </summary>
    public class ReceiveData
    {
        //用于保存接收参数的集合
        private static Dictionary<long, Dictionary<String, String>> _parameTable;

        static ReceiveData()
        {
            _parameTable = new Dictionary<long, Dictionary<String, String>>();
        }

        /// <summary>
        /// 添加参数到集合
        /// </summary>
        /// <param name="key">参数key</param>
        /// <param name="value">参数value</param>
        /// <param name="oForm">参数所属窗体</param>
        public static void AddParame(String key, String value, Form oForm)
        {
            IntPtr handle = oForm.Handle;
            Dictionary<String, String> parames = null;
            if (_parameTable.ContainsKey((long)handle))
            {
                parames = _parameTable[(long)handle];
            }
            else
            {
                parames = new Dictionary<String, String>();
                _parameTable.Add((long)handle, parames);
            }

            if (parames.ContainsKey(key))
            {
                parames.Remove(key);
            }
            parames.Add(key, value);
        }

        /// <summary>
        /// 获取指定key的value
        /// </summary>
        /// <param name="key">参数key</param>
        /// <param name="oForm">参数所属窗体</param>
        /// <returns>参数value</returns>
        public static String GetParame(String key, Form oForm)
        {
            IntPtr handle = oForm.Handle;
            if (_parameTable.ContainsKey((long)handle))
            {
                Dictionary<String, String> parames = _parameTable[(long)handle];
                if (parames.ContainsKey(key))
                {
                    return parames[key];
                }
            }
            return "";
        }
    }
}

    代码非常简单,这里就不作过多的解释了。然后我们在再次回到FuncTransitCtrl类的ShowDialog方法中,在反射创建出窗体和show出窗体之间,加入传参数的代码,如下:

            //处理需要传递的参数
                    if ((sendData != null) && (sendData.GetSendData() != null))
                    {
                        IDictionaryEnumerator enumerator = ((Dictionary<String, String>)sendData.GetSendData()).GetEnumerator();
                        while (enumerator.MoveNext())
                        {
                            KeyValuePair<String, String> current = (KeyValuePair<String, String>)enumerator.Current;
                            String key = current.Key.ToString();
                            String value = current.Value.ToString();
                            ReceiveData.AddParame(key, value, form);

                        }
                    }

至此,主体代码就完成了。接下来是程序的配置。

(1)  首先,整个项目应该有个入口的.exe,例如入口为A.exe,另外有B.dll,C.dll等等,在这些.dll里面有很多窗体。在任何一个.dll或者.exe里面,我们都可以使用ShowDiglog来反射调用指定的窗体。

(2)  其次,配置文件的路径。默认情况下,我是使用Environment.GetEnvironmentVariable(“USERPROFILE”)来获取C:\Users\[当前用户]这个目录,将我们的配置文件放在这个目录下。回忆一下FuncTransitCtrl,里面有个SystemPath的属性,用于获取和设置配置文件路径,代码如下:

     /// <summary>
        /// 获取或者设置配置文件路径
        /// </summary>
        public static String SystemPath
        {
            get
            {
                if (String.IsNullOrEmpty(_systemPath))
                {
                    return (Environment.GetEnvironmentVariable("USERPROFILE") + @"\AppData\Local\EMM\");
                }
                return _systemPath;
            }
            set
            {
                _systemPath = value;
            }
        }

最后,完整的代码下载在这里。在使用之前仍然再在这里啰嗦几句。

(1)              FuncConfig.ini配置文件放到C:\Users\ja\AppData\Local\EMM\ini目录下,当让,你也可以修改程序的SystemPath,指向你自己的目录

(2)              Demo,CMM0100,MainDemo编译后生成的程序集统一放在了bin目录下,而不是他们各自的bin目录。

(3)              注意窗体和程序集的命名应和配置文件中的保持一致

(4)              最后,示例中一些无关主体功能实现的代码(例如日志之类的),给出的是空实现。

结语

              不知道大家的项目中是否也是这样处理,我想,使用这种方式加载窗体,好处是显而易见的。不同的模块由不同的人开发,编译打包为dll,不同的.dll之间的窗体调用通过反射封装。整个项目走一个.exe入口启动。

              好了,这一集中,分享的是ShowDialog的方式,下一集中,跟大家分享Show的方式。谢谢!

 

posted on 2013-01-17 16:39  雪燃  阅读(342)  评论(0)    收藏  举报

导航