.NET快速开发实践之应用IExtenderProvider实现控件焦点跳转

简介: 我在前面的.NET快速开发实践中的IExtenderProvider扩展组件和其他两篇Post介绍了IExtenderProvider扩展编程模式和应用IExtenderProvider实现实体与对象的做法与例子,下面我为介绍一下在管理信息系统开发中一个常用的功能,控件的输入焦点跳转,我们知道,系统为我们提供了Tab和Shift+Tab切换输入焦点的功能,但是这里面有一个问题,即最终操作者最不习惯于使用Tab进行切换输入焦点,因为都已经习惯于无鼠标操作,常使用回车,上下方向键进行操作以实现焦点切换。

         我在前面的.NET快速开发实践中的IExtenderProvider扩展组件和其他两篇Post介绍了IExtenderProvider扩展编程模式和应用IExtenderProvider实现实体与对象的做法与例子,下面我为介绍一下在管理信息系统开发中一个常用的功能,控件的输入焦点跳转,我们知道,系统为我们提供了Tab和Shift+Tab切换输入焦点的功能,但是这里面有一个问题,即最终操作者最不习惯于使用Tab进行切换输入焦点,因为都已经习惯于无鼠标操作,常使用回车,上下方向键进行操作以实现焦点切换。

         那么我们如何实现这样的功能,最普通的方法是处理输入控件的KeyDown事件,判读按键是否满足跳转:例如

        private void tbName_KeyDown(object sender, KeyEventArgs e)
        {
            if(e.KeyCode == Keys.Enter) //回车向下一焦点跳转
			{
                this.tbInputCode1.Focus();
			}
            else if(e.KeyCode == Keys.Up) //向上键向上一焦点跳转
			{
                this.tbCode.Focus();
        }

         这样的做法,我们就必须重写所有输入控件的KeyDown事件,效率低下,也影响代码的美观,我们可以通过应用应用IExtenderProvider实现另一种方式,即把这样的功能封装在一个名字叫ControlFocus的组件中:

    [ToolboxItem(true)]
    [Description("控制焦点跳转组件")]
	[ProvideProperty( "NextControl", typeof(Control)) ]
    [ProvideProperty("PreviousControl", typeof(Control))]
	public class ControlFocus:Component, IExtenderProvider
	{
        Dictionary<Control, Control> _NextFocus = new Dictionary<Control, Control>();
        Dictionary<Control, Control> _PreviousFocus = new Dictionary<Control, Control>();

        private Keys [] nextKeys;
        private Keys[] previousKeys;

		public ControlFocus()
		{
			
		}
        
        public ControlFocus(System.ComponentModel.IContainer container)
        {
            container.Add(this);
        }

        [Category("焦点跳转")]
        [Description("获取/设置向前跳转按键集合")]
        public Keys[] NextKeys
        {
            get
            {
                return this.nextKeys;
            }
            set
            {
                this.nextKeys = value;
            }
        }

        [Category("焦点跳转")]
        [Description("获取/设置向后跳转按键集合")]
        public Keys[] PreviousKeys
        {
            get
            {
                return this.previousKeys;
            }
            set
            {
                this.previousKeys = value;
            }
        }

        [Category("焦点跳转")]
        [Description("获取/设置向前跳转控件")]
        public System.Windows.Forms.Control GetNextControl(Control control)
        {
            if (_NextFocus.ContainsKey(control))
            {
                return _NextFocus[control];
            }
            return null;
        }

        public void SetNextControl(Control control, Control nextControl)
		{
            if (_NextFocus.ContainsKey(control) != true)
			{
				_NextFocus.Add(control,nextControl);
                control.KeyDown += new KeyEventHandler(Control_KeyDown);
			}
			else
			{
				_NextFocus[control] = nextControl;
			}
		}

        [Category("焦点跳转")]
        [Description("获取/设置向后跳转控件")]
        public Control GetPreviousControl(Control control)
        {
            if (_PreviousFocus.ContainsKey(control))
            {
                return (System.Windows.Forms.Control)_PreviousFocus[control];
            }
            return null;
        }

        public void SetPreviousControl(Control control, Control previousControl)
		{
            if (_PreviousFocus.ContainsKey(control) != true)
			{
				_PreviousFocus.Add(control,previousControl);
                control.KeyDown += new KeyEventHandler(Control_KeyDown);
			}
			else
			{
				_PreviousFocus[control] = previousControl;
			}
		}        

		private void Control_KeyDown(object sender, KeyEventArgs e)
		{
			if(NextKeysContains(e.KeyCode))
			{
                          Control nextControl = this.GetNextControl((Control)sender);
				if(nextControl != null && nextControl.CanFocus)
					nextControl.Focus();
			}
            else if (PreviousKeysContains(e.KeyCode))
			{
                           Control previousControl = this.GetPreviousControl((Control)sender);
				if(previousControl != null && previousControl.CanFocus)
					previousControl.Focus();
			}            
		}

        internal bool NextKeysContains(Keys k)
        {
            if (this.nextKeys == null)
                return false;

            for (int i = 0; i < this.nextKeys.Length; i++)
            {
                if (this.nextKeys[i] == k)
                    return true;
            }

            return false;
        }

        internal bool PreviousKeysContains(Keys k)
        {
            if (this.previousKeys == null)
                return false;

            for (int i = 0; i < this.previousKeys.Length; i++)
            {
                if (this.previousKeys[i] == k)
                    return true;
            }

            return false;
        }

        #region IExtenderProvider 成员

        bool IExtenderProvider.CanExtend(object component)
        {
            if (component is Control && !(component is Form))
            {
                return true;
            }

            return false;
        }

        #endregion
    }

         下面我们来看看如何应用ControlFocus,拖出一个ControlFocus设置跳转按键值,可以通过属性窗口,也可以通过代码:

        public DiagnosisEditor()
        {
            InitializeComponent();
            
            this.controlFocus1.NextKeys = new Keys[] {Keys.Enter,Keys.PageDown};
            this.controlFocus1.PreviousKeys = new Keys [] {Keys.PageUp};
        }

         下面设置输入控件的焦点跳转顺序,需要为每个控件设置下一个焦点控件和前一焦点控件:

控件跳转

         我做了一个简单的demo,请下载Exam.DataUIMapper.rar

QQ群:15118502

链接:AgileEAS.NET应用开发平台介绍

AgileEAS.NET之敏捷并行开发方法

敏捷软件工程实验室

目录
相关文章
|
4天前
|
云安全 人工智能 运维
阿里云SecOps Agent,全新安全跨产品执行体验
自然语言驱动 云安全中心/WAF/CFW/ 等多款安全产品联动
1595 2
|
1天前
|
人工智能 定位技术 SEO
我学 GEO 第 15 天:终于知道AI GEO该如何做?
我是暴走的莉莉酱,边旅行边研究AI GEO的数字游民。专注普通人如何提升“AI可见度”——让AI在回答用户问题时准确识别、理解并推荐你。不讲玄学,只做可测、可调、可持续的GEO实践。
349 122
|
4天前
|
机器学习/深度学习 人工智能 调度
🐴 HappyHorse 1.1 现已上线阿里云百炼!快来查收模型使用指南,现在调用享 6 折~
HappyHorse 1.1 是新一代视频生成大模型,全面升级动态表现力、角色一致性、指令遵循、视觉质感与音画协同能力。支持I2V/T2V/R2V三类生成,适配短剧、电商广告、品牌营销等场景,提供高质、流畅、可控的AI视频生产力。
583 4
🐴 HappyHorse 1.1 现已上线阿里云百炼!快来查收模型使用指南,现在调用享 6 折~
|
14天前
|
缓存 测试技术 API
Qwen 3.7 Plus 与 Max 实测:性价比与多模态能力差异解析(2026)
2026 年 6 月 1 日,阿里悄无声息地发布了 Qwen 3.7 Plus,距 Qwen 3.7 Max 上线刚好 11 天。同样的 1M 上下文,同样的 35 小时自治上限。但价格才是头条:Plus 是 0.40/M输入,Max是 2.50/M——便宜约 6 倍——并且还能看图、看视频。Vision Arena 上 Plus 已经排到 #16。所以这周真正值得讨论的问题不是”要不要为视觉能力买单”,而是”Max 凭什么用 6 倍价格换来 2 个百分点的 benchmark 领先”。
|
15天前
|
JavaScript 定位技术 API
CodeGraph 爆火:编程 Agent 需要的不是更多上下文,而是一张提前画好的代码地图
CodeGraph 是一款爆火的本地代码智能工具,通过 tree-sitter 解析 AST 构建结构化知识图谱(存于 SQLite),为编程 Agent 提前生成“代码地图”。它显著降低 Agent 在中大型项目中的探索成本——实测工具调用减少71%、Token 降57%、速度提升46%,支持19+语言及主流框架路由识别,完全离线、无需 API Key。
915 11
CodeGraph 爆火:编程 Agent 需要的不是更多上下文,而是一张提前画好的代码地图
|
8天前
|
缓存 人工智能 运维
GLM 5.2自托管全流程实战:硬件选型、vLLM/SGLang部署与成本盈亏测算
2026年智谱发布GLM 5.2超大混合专家模型,区别于以往仅开放API的闭源大模型,该模型权重以MIT开源协议对外发布,企业与开发者可完整下载、本地审计、私有化部署,实现数据不出环境、自定义微调、自主调度推理资源。GLM 5.2拥有753B总参数,原生支持百万级上下文窗口,在代码生成、长文档推理、数学逻辑等多项基准测试中对标国际顶尖商用模型,是首款可完整自托管的前沿代码向大模型。
667 0
|
3天前
|
消息中间件 人工智能 Kafka
AI 时代,实时入湖正在告别 ETL:从 Kafka 到 Iceberg 的架构减法
本文围绕“零 ETL”这一趋势,讨论流数据入湖为什么需要做架构减法,并结合 Kafka × Table Bucket 的实践,分析一种将通用入湖能力前移到消息与表存储链路中的方案,如何在降低复杂度的同时,兼顾实时性、一致性、Schema 演进、CDC 语义与开放生态兼容。
193 121
|
3天前
|
人工智能 监控 前端开发
Electron 监控:让桌面 Agent 监控触手可及
一行代码实现Electron桌面端全景监控,自动还原崩溃现场、预警内存泄漏、全链路追踪、 SSE流式响应与交互埋点,让 AI 助手运行状态清晰可见,助力快速恢复稳定与流畅。
183 125
|
11天前
|
人工智能 自然语言处理 算法
阿里云百炼Qwen 3.7 Plus与Max实测全解:性价比与多模态能力、成本深度对比
2026年,阿里云百炼平台推出的Qwen 3.7系列成为企业与开发者落地AI应用的核心选择,其中Qwen 3.7 Max与Plus作为两大旗舰版本,定位差异显著:Max是纯文本推理旗舰,专注高强度智能体与复杂逻辑任务;Plus则是多模态全能版,在保留强大文本能力的同时,补齐图像、视频理解能力,且价格大幅降低。本文基于2026年最新实测数据,从核心参数、文本能力、多模态能力、智能体表现、性价比与场景选型六大维度,全面解析两款模型的差异,为用户提供精准选型参考。
541 0