.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之敏捷并行开发方法

敏捷软件工程实验室

目录
相关文章
|
23小时前
|
人工智能 JSON 供应链
畅用7个月无影 JVS Claw |手把手教你把JVS改造成「科研与产业地理情报可视化大师」
LucianaiB分享零成本畅用JVS Claw教程(学生认证享7个月使用权),并开源GeoMind项目——将JVS改造为科研与产业地理情报可视化AI助手,支持飞书文档解析、地理编码与腾讯地图可视化,助力产业关系图谱构建。
23254 1
畅用7个月无影 JVS Claw |手把手教你把JVS改造成「科研与产业地理情报可视化大师」
|
2天前
|
人工智能 API 开发工具
Claude Code国内安装:2026最新保姆教程(附cc-switch配置)
Claude Code是我目前最推荐的AI编程工具,没有之一。 它可能不是最简单的,但绝对是上限最高的。一旦跑通安装、接上模型、定好规范,你会发现很多原本需要几小时的工作,现在几分钟就能搞定。 这套方案的核心优势就三个字:可控性。你不用依赖任何不稳定服务,所有组件都在自己手里。模型效果不好?换一个。框架更新了?自己决定升不升。 这才是AI时代开发者该有的姿势——不是被动等喂饭,而是主动搭建自己的生产力基础设施。 希望这篇保姆教程,能帮你顺利上车。做出你自己的作品。
Claude Code国内安装:2026最新保姆教程(附cc-switch配置)
|
10天前
|
缓存 人工智能 自然语言处理
我对比了8个Claude API中转站,踩了不少坑,总结给你
本文是个人开发者耗时1周实测的8大Claude中转平台横向评测,聚焦Claude Code真实体验:以加权均价(¥/M token)、内部汇率、缓存支持、模型真实性及稳定性为核心指标。
3977 23
|
4天前
|
人工智能 缓存 BI
Claude Code + DeepSeek V4-Pro 真实评测:除了贵,没别的毛病
JeecgBoot AI专题研究 把 Claude Code 接入 DeepSeek V4Pro,跑完 Skills —— OA 审批、大屏、报表、部署 5 大实战场景后的真实体验 ![](https://oscimg.oschina.net/oscnet/up608d34aeb6bafc47f
2230 4
Claude Code + DeepSeek V4-Pro 真实评测:除了贵,没别的毛病
|
5天前
|
人工智能 JSON BI
DeepSeek V4 来了!超越 Claude Sonnet 4.5,赶紧对接 Claude Code 体验一把
JeecgBoot AI专题研究 把 Claude Code 接入 DeepSeek V4Pro 的真实体验与避坑记录 本文记录我将 Claude Code 对接 DeepSeek 最新模型(V4Pro)后的真实体验,测试了 Skills 自动化查询和积木报表 AI 建表两个场景——有惊喜,也踩
2636 8
|
22天前
|
人工智能 自然语言处理 安全
Claude Code 全攻略:命令大全 + 实战工作流(建议收藏)
本文介绍了Claude Code终端AI助手的使用指南,主要内容包括:1)常用命令如版本查看、项目启动和更新;2)三种工作模式切换及界面说明;3)核心功能指令速查表,包含初始化、压缩对话、清除历史等操作;4)详细解析了/init、/help、/clear、/compact、/memory等关键命令的使用场景和语法。文章通过丰富的界面截图和场景示例,帮助开发者快速掌握如何通过命令行和交互界面高效使用Claude Code进行项目开发,特别强调了CLAUDE.md文件作为项目知识库的核心作用。
19319 61
Claude Code 全攻略:命令大全 + 实战工作流(建议收藏)
|
2天前
|
SQL 人工智能 弹性计算
阿里云发布 Agentic NDR,威胁检测与响应进入智能体时代
欢迎前往阿里云云防火墙控制台体验!
1173 2