• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    公众号

C#WinCE程序(.NETCompactFramework3.5)项目重构面向抽象设计

原作者: [db:作者] 来自: [db:来源] 收藏 邀请

重构关注点

  • 遵循开闭原则
  • 面向抽象设计
  • 实现设备程序端可动态配置

重构的需求

领导想省事提出需求,将现有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; }
    }
}
DesignConfig
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; }
    }
}
ControlsConfig

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;
        }
    }
MenuButton

 导航工作区的父容器就是一个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 ==  
                       
                    
                    

鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
上一篇:
C#对接金蝶接口(K3)发布时间:2022-07-13
下一篇:
c#:画圆(整理)发布时间:2022-07-13
热门推荐
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap