在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
重构关注点
重构的需求领导想省事提出需求,将现有PDA程序修改为支持PC端由电器工程师根据实际的生产流程可配置,PDA程序在读取配置文件后动态生成导航界面。然后导航绑定的是PDA程序Dll里的界面。公司的综合赋码管理系统(CMS)作为服务器端对PDA暴露WCF服务,PDA在执行扫描操作后对CMS发起请求,CMS收到请求后匹配分析PDA的请求,分析完再返回执行结果给PDA。场景就是这样一个场景,PDA扫描条码的操作分:扫描一个码,扫描两个码,扫描多个码三种。领导让写3个界面,其实这正好可以抽象一下,相当于1个界面就能搞定;于是设计了一个扫描操作的基类窗体,把扫描的基本操作写在基类里,使用模板设计模式,扫多少个码的界面继承基类重用大部分扫描的处理逻辑。主要流程是PC端通过反射获取PDA程序的Dll里的窗体类列表,外部定义好PDA要展示的功能清单序列化成为Json格式的文件,PC端使用MTP传输到PDA上,PDA读取该文件来动态生成导航界面。 重构的过程1)定义数据传输对象 using System; using System.Linq; using System.Collections.Generic; using System.Text; namespace ehsure.Core.Data.dto { [Serializable] /// <summary> /// 主界面菜单实体类 /// </summary> public class DesignConfig { /// <summary> /// 界面宽度 /// </summary> public int Width { get; set; } /// <summary> /// 界面高度 /// </summary> public int Height { get; set; } /// <summary> /// 界面下所有控件 /// </summary> public List<ControlsConfig> ControlsList { get; set; } } } using System; using System.Linq; using System.Collections.Generic; using System.Text; namespace ehsure.Core.Data.dto { [Serializable] /// <summary> /// 控件UI配置参数实体 /// </summary> public class ControlsConfig { /// <summary> /// 控件ID /// </summary> public string ID { get; set; } /// <summary> /// 控件显示 /// </summary> public string ShowText { get; set; } /// <summary> /// 控件宽度 /// </summary> public int ControlWidth { get; set; } /// <summary> /// 控件高度 /// </summary> public int ControlHeight { get; set; } /// <summary> /// 控件命令字 /// </summary> public string Command { get; set; } /// <summary> /// 控件要打开的界面 /// </summary> public string ForwardForm { get; set; } /// <summary> /// 位置坐标X /// </summary> public int X { get; set; } /// <summary> /// 位置坐标Y /// </summary> public int Y { get; set; } /// <summary> /// 排序号 /// </summary> public int Sort { get; set; } } } 2)重构解决方案 原来的主程序集是个exe,建立职责是UI层的程序集,建立PDA主入口程序集。 3)编码实现动态导航(使用的自定义控件来代替Button) 自定义控件MenuButton /// <summary> /// 菜单按钮,自定义控件 /// </summary> public class MenuButton : Button { public string ActiveFlag = "※"; public string Command { get; set; } public string ForwardForm { get; set; } protected bool itemActiveFlag = false; /// <summary> /// 是否处于被选中状态 /// </summary> public bool ItemActiveFlag { get { return itemActiveFlag; } set { itemActiveFlag = value; if (value) Text = ActiveFlag + Text; else Text = Text.Replace(ActiveFlag, ""); } } private StringFormat sf; public MenuButton() { BackColor = Color.White; ForeColor = Color.Black; Height = 28; Font = new Font("微软雅黑", 14F, FontStyle.Bold); sf = new StringFormat(); sf.Alignment = StringAlignment.Center; sf.LineAlignment = StringAlignment.Center; } protected override void OnGotFocus(EventArgs e) { BackColor = SystemColors.Highlight; ForeColor = Color.OrangeRed; ItemActiveFlag = true; } protected override void OnLostFocus(EventArgs e) { BackColor = Color.White; ForeColor = Color.Black; ItemActiveFlag = false; } } 导航工作区的父容器就是一个Panel,MenuButton的高度是动态计算的,宽带是匹配父容器。 public partial class ShellMainForm : Form { public ShellMainForm() { InitializeComponent(); this.Load += new EventHandler(ShellMainForm_Load); this.KeyPreview = true; this.KeyDown += new KeyEventHandler(ShellMainForm_KeyDown); } void ShellMainForm_KeyDown(object sender, KeyEventArgs e) { if (e.KeyData == Keys.F1) { using (SystemTimeSetForm frm = new SystemTimeSetForm()) { var result = frm.ShowDialog(); if (result.Equals(DialogResult.OK)) { this.statusBar.Text = DateTime.Now.ToLongDateString() + " " + DateTime.Now.DayOfWeek; } } } if (e.KeyData == Keys.F2) { using (var powerManager = new PowerManagement()) { powerManager.SetSystemPowerState(PowerManagement.SystemPowerStates.Suspend); } } } void ShellMainForm_Load(object sender, EventArgs e) { try { Cursor.Current = Cursors.WaitCursor; MaximizeBox = false; MinimizeBox = false; FormBorderStyle = FormBorderStyle.FixedSingle; this.Text = "CMS智能终端"; this.Width = Screen.PrimaryScreen.WorkingArea.Width; this.Height = Screen.PrimaryScreen.WorkingArea.Height; this.Left = 0; this.Top = 0; statusBar.Text = string.Join(" ", new string[] { "线号:" + DeviceContext.GetInstance().GetGlobalConfig().LineNum , "设备号:" + DeviceContext.GetInstance().GetGlobalConfig().DeviceNum }); TestLogin(); DriveInit(); } finally { Cursor.Current = Cursors.Default; } } /// <summary> /// 初始化系统功能列表 /// </summary> /// <param name="config"></param> void InitSystemNavigateBar(DesignConfig config) { var list = config.ControlsList.OrderByDescending(m => m.Sort); if (list.Any()) { int index = 0; //list.Reverse(); foreach (var navigateItem in list) { var btn = new MenuButton(); btn.TabIndex = index; btn.Dock = DockStyle.Top; btn.TabIndex = workareaPanel.Controls.Count; btn.Text = navigateItem.ShowText; btn.ForwardForm = navigateItem.ForwardForm; btn.Click += new EventHandler(NavigateButton_Click); btn.KeyDown += new KeyEventHandler(NavigateButton_KeyDown); workareaPanel.Controls.Add(btn); index++; } } } /// <summary> /// 响应导航按钮回车事件 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> void NavigateButton_KeyDown(object sender, KeyEventArgs e) { if (e.KeyValue == 13) NavigateButton_Click(sender, null); } /// <summary> /// 响应导航单击事件 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> void NavigateButton_Click(object sender, EventArgs e) { RunModuleForm((MenuButton)sender); } private void menuClose_Click(object sender, EventArgs e) { if (MessageBox.Show("确认要退出系统", "提示", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1) == DialogResult.Yes) { this.Close(); Application.Exit(); } } /// <summary> /// 动态打开模块窗体 /// </summary> /// <param name="mb">MenuButton 被点击的控件 /// mb.ForwardForm要打开的窗体类名:{命名空间+","+类名} /// mb.Command {REPLACE,DELETE...CMS脚本对应的指令名称}</param> protected void RunModuleForm(MenuButton mb) { try { if (string.IsNullOrEmpty(mb.ForwardForm)) { throw new Exception("模块或者窗体没有设定类库信息!"); } string[] tempModulesInfo = mb.ForwardForm.Split(",".ToCharArray()); Assembly asb = Assembly.Load(tempModulesInfo.FirstOrDefault()); string frmTypeName = string.Join(".", tempModulesInfo); var form = asb.GetTypes().Where(p => p.FullName.Equals(frmTypeName)).FirstOrDefault(); if (form != null) { DeviceContext.GetInstance().OperateTypeText = mb.Text.Replace(mb.ActiveFlag, ""); var frm = (System.Windows.Forms.Form)Activator.CreateInstance(form); if (!string.IsNullOrEmpty(mb.Command)) { DeviceContext.GetInstance().CurrentCommand = mb.Command; } frm.ShowDialog(); } else throw new Exception(string.Format("类名:{0}不存在!", frmTypeName)); } catch (Exception ex) { //throw ex; MessageBox.Show("错误描述:" + Environment.NewLine + ex.Message + Environment.NewLine + ex.StackTrace, "错误提示", MessageBoxButtons.OK, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button1); } } /// <summary> /// 获取应用程序界面导航配置 /// </summary> /// <returns></returns> protected DesignConfig GetDesignNavigateConfig() { var list = new List<ControlsConfig>(); System.Action<DesignConfig> addDefaultItemTask = (dc) => { dc.ControlsList.Add(new ControlsConfig { ID = Guid.NewGuid().ToString().Replace("-", ""), Command = "", ForwardForm = "ehsure.Smart.FlowUI,AdminSettingForm", ShowText = "设备配置", ControlHeight = 0, ControlWidth = 0, X = 0, Y = 0, Sort = Int32.MaxValue }); }; var path = new DirectoryInfo(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase)); string fileName = path.FullName + @"\Config\DesignConfig.Config"; if (!File.Exists(fileName)) { DesignConfig dc = new DesignConfig { Height = Screen.PrimaryScreen.WorkingArea.Height, Width = Screen.PrimaryScreen.WorkingArea.Width, ControlsList = list }; addDefaultItemTask.Invoke(dc); string fileContext = JsonConvert.SerializeObject(dc); using (var fs = new FileStream(fileName, FileMode.Create)) { byte[] buffer = Encoding.UTF8.GetBytes(fileContext); fs.Write(buffer, 0, buffer.Length); fs.Flush(); fs.Close(); } return dc; } else { using (var fs = new FileStream(fileName, FileMode.Open, FileAccess.Read)) { byte[] buffer = new byte[fs.Length]; fs.Read(buffer, 0, buffer.Length); string fileContext = Encoding.UTF8.GetString(buffer, 0, buffer.Length); DesignConfig dc = JsonConvert.DeserializeObject<DesignConfig>(fileContext); if (dc != null && !dc.ControlsList.Any(m => m.Sort == Int32.MaxValue)) { addDefaultItemTask.Invoke(dc); } fs.Close(); return dc; } } } /// <summary> /// 测试连接CMS /// </summary> private void TestLogin() { string outMsg; var codeInfo = new TaskDTOCode { Code = "LoginTest" }; CFDataService.SubmitToControl(codeInfo, out outMsg); } /// <summary> /// 加载驱动 /// </summary> private void DriveInit() { string dllName = ScanCache.DllType.DllName; string dllService = ScanCache.DllType.DllService; string[] file = Directory.GetFiles(Path.GetDirectoryName(Assembly.GetCallingAssembly().GetName().CodeBase) + "\\", dllName); if (file.Length == 0) { throw new Exception(ErrorMessage.ScanComponentLoadFailed); } else { Assembly assembly = Assembly.LoadFrom(file[0]); Type typeScanService = assembly.GetType(dllService); ScanCache.BaseScanDirve = (BaseScanDriveService)assembly.CreateInstance(typeScanService.FullName); ScanCache.BaseScanDirve.Init(); } } /// <summary> /// 界面排列布局时触发,初始化界面导航 /// </summary> /// <param name="e"></param> protected override void OnResize(EventArgs e) { base.OnResize(e); workareaPanel.Controls.Clear(); var config = GetDesignNavigateConfig(); if (config != null) { InitSystemNavigateBar(config); } if (workareaPanel.Controls.Count > 0 && workareaPanel.Height < Screen.PrimaryScreen.WorkingArea.Height) { foreach (Control ctrl in workareaPanel.Controls) { ctrl.Height = workareaPanel.Height / workareaPanel.Controls.Count - 2; } workareaPanel.Controls[workareaPanel.Controls.Count - 1].Focus(); } } } 4)UI层业务窗体的实现是本篇随笔的重点
重点考量的地方就是前面所说的要面向抽象设计,封装和继承。 所有业务窗体的基类 /// <summary> /// 窗体基类 /// </summary> public partial class BaseForm : Form { private bool isScaleDown = true; /// <summary> /// 是否屏幕自适应 /// </summary> public bool IsScaleDown { get { return isScaleDown; } set { isScaleDown = value; } } private bool enterToTab = false; /// <summary> /// 是否回车变Tab /// </summary> public bool EnterToTab { get { return enterToTab; } set { enterToTab = value; } } public BaseForm() { InitializeComponent(); KeyPreview = true; SetInterval(); } private ScannerErrorService ScannerErrorService; private ScannerErrorService getScannerErrorService() { return ScannerErrorService ?? (ScannerErrorService = new ScannerErrorService()); } protected virtual void InitMenu() { } protected override void OnActivated(EventArgs e) { CustomizeCursor.CloseCursor(); base.OnActivated(e); } protected void AddMenuItem(MainMenu mainMenu, string text, ehsure.Common.Action func) { MenuItem item = new MenuItem(); item.Text = text; item.Click += delegate(object sender, EventArgs e) { try { CustomizeCursor.ShowWaitCursor(); func(); } catch (Exception ex) { ShowMessageBoxForm(ex); } finally { CustomizeCursor.CloseCursor(); } }; mainMenu.MenuItems.Add(item); } protected void AddMenuItem(MainMenu mainMenu, string mainText, string text, ehsure.Common.Action func) { MenuItem mi = null; Menu.MenuItemCollection collection = mainMenu.MenuItems; for (int i = 0; i < collection.Count; i++) { MenuItem item = collection[i]; if (string.Equals(item.Text, mainText, StringComparison.OrdinalIgnoreCase)) { mi = item; continue; } } if (mi == null) { mi = new MenuItem(); mi.Text = mainText; mainMenu.MenuItems.Add(mi); } MenuItem sub = new MenuItem(); sub.Text = text; sub.Click += delegate(object sender, EventArgs e) { try { CustomizeCursor.ShowWaitCursor(); func(); } catch (Exception ex) { ShowMessageBoxForm(ex); } finally { CustomizeCursor.CloseCursor(); } }; mi.MenuItems.Add(sub); } protected virtual void FireAction(ehsure.Common.Action action) { try { CustomizeCursor.ShowWaitCursor(); action(); } catch (Exception ex) { ShowMessageBoxForm(ex); } finally { CustomizeCursor.CloseCursor(); } } public virtual void FireActionCode(ActionCode action, string code, int inputType) { try { CustomizeCursor.ShowWaitCursor(); action(code, inputType); } catch (Exception ex) { ShowMessageBoxForm(ex); } finally { CustomizeCursor.CloseCursor(); } } private void BaseForm_Load(object sender, EventArgs e) { InitMenu(); } public void ShowMessageBoxForm(Exception ex) { CustomizeCursor.CloseCursor(); try { string msg = null; msg = ex.Message; string type = ex.GetType().FullName; if (type == "System.Net.WebException") { msg = "网络异常!"; } if (type != "System.Exception" || msg == ErrorMessage.ScanComponentLoadFailed) { ScannerErrorReport error = new ScannerErrorReport() { Id = Guid.NewGuid().ToString(), ErrorInfo = msg, ErrorTime = DateTime.Now, CustomerCode = ScanCache.UserContext.CorpId, CustomerName = ScanCache.UserContext.CorpName, Remark = ex.StackTrace }; getScannerErrorService().Insert(error); } MessageBoxForm form = new MessageBoxForm(); form.ShowMessage(msg); form.ShowDialog(); } catch (Exception eex) { MessageBoxForm form = new MessageBoxForm(); form.ShowMessage(eex.Message); form.ShowDialog(); } } private void SetInterval() { var file = Path.GetDirectoryName(Assembly.GetExecutingAssembly().GetName().CodeBase) + "\\App.Config"; XmlDocument doc = new XmlDocument(); if (!File.Exists(file)) { ScanCache.SuccessInterval = 200; ScanCache.ErrorInterval = 3000; return; } doc.Load(file); XmlNodeList nodes = doc.GetElementsByTagName("add"); for (int i = 0; i < nodes.Count; i++) { XmlAttribute att = nodes[i].Attributes["key"]; if (att != null) { if (att.Value == "SuccessInterval") { att = nodes[i].Attributes["value"]; ScanCache.SuccessInterval = Convert.ToInt32(att.Value); } if (att.Value == 全部评论
专题导读
热门推荐
热门话题
阅读排行榜
|
请发表评论