惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Security Latest
Security Latest
Recorded Future
Recorded Future
人人都是产品经理
人人都是产品经理
S
SegmentFault 最新的问题
Hacker News - Newest:
Hacker News - Newest: "LLM"
C
CXSECURITY Database RSS Feed - CXSecurity.com
博客园 - 三生石上(FineUI控件)
博客园 - 聂微东
P
Privacy & Cybersecurity Law Blog
WordPress大学
WordPress大学
Know Your Adversary
Know Your Adversary
Spread Privacy
Spread Privacy
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
量子位
L
LINUX DO - 热门话题
L
Lohrmann on Cybersecurity
博客园 - Franky
酷 壳 – CoolShell
酷 壳 – CoolShell
T
Tor Project blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
雷峰网
雷峰网
阮一峰的网络日志
阮一峰的网络日志
V
Visual Studio Blog
T
Threatpost
T
Tenable Blog
有赞技术团队
有赞技术团队
大猫的无限游戏
大猫的无限游戏
Engineering at Meta
Engineering at Meta
GbyAI
GbyAI
C
Cisco Blogs
H
Heimdal Security Blog
Attack and Defense Labs
Attack and Defense Labs
A
About on SuperTechFans
Last Week in AI
Last Week in AI
N
News and Events Feed by Topic
T
Threat Research - Cisco Blogs
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
I
Intezer
V
V2EX
Cyberwarzone
Cyberwarzone
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
B
Blog RSS Feed
V
Vulnerabilities – Threatpost
N
Netflix TechBlog - Medium
T
The Blog of Author Tim Ferriss
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
U
Unit 42
PCI Perspectives
PCI Perspectives
P
Privacy International News Feed
D
Docker

博客园 - Insus.NET

User Profile Service 服务未能登录 Visual Studio2026创建Vue项目 安装与配置node.js HTTP Error 403.14 - Forbidden VisualStudio2026回滚上一版本 消息认证码(加强) 网站无法使用插值字符串语法 HMAC(Hash-based Message Authentication Code)认证示例 浏览器自动发送域凭据 企业内小网站兼用Windows验证登录 访问用户控件的函数 onblur事件改为监听处理 将警报消息改为吐司消息 内容有无变化OnBlur即时更新引起的问题与解决 光标离开文框后即刻更新 WebForm实现Web API JavaScript对GridView删除行后并重新给其数据绑定 把CS值传给JS使用 v2 确认信息confirm由C#后端移至javascript前端 无法发布网站Web Site JavaScript判断字符是否为decimal 点击单元格弹出窗口处理数据返回父页 GridView数据控件中实现单选功能 文本框输入完后直接按回车提交数据 GridView对行进行全选或单选 TextBox文本框允许用户输入正或负小数 用户单击文本并复制至剪帖板 Vue3格式化日期时间与插值 SQL Server中验证大小字母和数字 MS SQL Server 数据加密与解密实例 相册由原来Lightbox升级至Vue2瀑布流 从Visual Studio 2022升级至Visual Studio 2026 报表应用图表charts显示数据 钉钉(DingTalk)免登录 Upgrade Outlook Connector 程序中真实应用SignalR Web API路径与IIS站点应用程序名或虚拟目录 在您可以登录前,此副本的 Windows 必须被 Microsoft 激活。您想现在激活它吗 电脑系统由Win10降级Win7折腾还是折腾 System.ComponentModel.Win32Exception: Access is denied
混合式提高用户编辑与操作效率
Insus.NET · 2026-04-29 · via 博客园 - Insus.NET

近段时间修改一个较古老的项目,把涉及到的技术与问题分享于此。
提高用户操作效率,键盘录入数据与鼠标之间操作方面。
原来的操作,用户并没有什么说法,随之需要录入的数据量大,时间与效率自然产生。

下面把改善相关问题,集中成一个小案例,
2026-04-28_16-05-16

项目是WebForm的asp.net。
是显示数据是使用Gridview服务控件。
每行最右边是更新按钮,各行各自更新。
在GridView外,即下面还有一个按钮,“一次性更新”,对所有行的备注进行一次性更新。
还有每行中的备注栏的文本框,用户输入备注信息后,点击文本框外,即更新。
它还可以输入完备注后,按回车亦可更新。

用户按自己习惯来操作。

为了这个示例,先准备一些数据,数据表,添加演示临时数据,创建存储过程,自定义表函数等...

-- =============================================
-- Author:        Insus.NET
-- Create date: 2026-04-28
-- Description:    数据表ZTestData
-- =============================================
CREATE TABLE [dbo].[ZTestData]
(
    [ID] INT NOT NULL IDENTITY(1,1) PRIMARY KEY,
    [IMT_pz] NVARCHAR(30) NOT NULL,
    [IMT_gx] NVARCHAR(30) NOT NULL,
    [BeiZhu] NVARCHAR(30) NULL
)
GO

-- =============================================
-- Author:        Insus.NET
-- Create date: 2026-04-28
-- Description:    数据表添加一些示例临时数据
-- =============================================
INSERT INTO [dbo].[ZTestData]
(
    [IMT_pz],
    [IMT_gx]
)
VALUES
(
    N'f043',
    N'P10'
)
,(
    N'l52',
    N'P10'
)
,(
    N'j71',
    N'P30'
)
,(
    N'v11',
    N'P20'
)
,(
    N'U33',
    N'P20'
)
GO

-- =============================================
-- Author:        Insus.NET
-- Create date: 2026-04-28
-- Description:    存储过程,获取所有数据
-- =============================================
CREATE PROCEDURE [dbo].[usp_ZTestData_SelectAll]
AS
BEGIN
    SELECT [ID],
    [IMT_pz],
    [IMT_gx],
    [BeiZhu]
    FROM [dbo].[ZTestData]
END
GO

-- =============================================
-- Author:        Insus.NET
-- Create date: 2026-04-28
-- Description:    更新存储过程
-- =============================================
CREATE PROCEDURE [dbo].[usp_ZTestData_UpdateBeiZhu]
@ID INT,
@BeiZhu NVARCHAR(30)
AS
BEGIN
    UPDATE [dbo].[ZTestData]
    SET
        [BeiZhu] = @BeiZhu
    WHERE 
        [ID] = @ID
END
GO

-- =============================================
-- Author:        Insus.NET
-- Create date: 2026-04-28
-- Description:    创建一个表函,批量更新,作为存储过程传入的参数
-- =============================================
CREATE TYPE [dbo].[udtt_ZTestData_BZ] AS TABLE(
    [ID] [int] NOT NULL,    
    [BeiZhu] [nvarchar](30) NULL
)
GO

-- =============================================
-- Author:        Insus.NET
-- Create date: 2026-04-28
-- Description:    批理更新存储过程
-- =============================================
CREATE PROCEDURE [dbo].[usp_ZTestData_BatchUpdate]
    @BatchBeiZhu  [dbo].[udtt_ZTestData_BZ] READONLY
AS
BEGIN
    MERGE [dbo].[ZTestData] AS TARGET
    USING @BatchBeiZhu AS SOURCE        
    ON TARGET.[ID] = SOURCE.[ID]
    WHEN MATCHED THEN
        UPDATE SET        
        TARGET.[BeiZhu] = SOURCE.[BeiZhu]
    ;
END
GO

SQL Code

在App_Code目录下,添加2个类,虽然不是ASP.NET MVC项目,修改的项目.NET 2.0时代,那时MVC还没有出来吧。

ZTestData.cs

namespace Insus.NET
{
    public class ZTestData
    {
        public int ID { get; set; }

        public string IMT_pz { get; set; }

        public string IMT_gx { get; set; }

        public string BeiZhu { get; set; }

        public DataTable BatchBeiZhu { get; set; }
    }
}

ZTestData Code


ZTestDataEntity.cs

namespace Insus.NET
{
    /// <summary>
    /// ZTestDataEntity 的摘要说明
    /// </summary>
    
    public class ZTestDataEntity
    {
        BusinessBase objBusinessBase = new BusinessBase();

        public DataTable Get_DataTable_GetData()
        {
            return objBusinessBase.GetDataToDataSet("usp_ZTestData_SelectAll").Tables[0];
        }

        public void UpdateBeiZhu(ZTestData ztd)
        {
            Parameter[] parameter = {
                                        new Parameter("@ID",SqlDbType.Int,4,ztd.ID),
                                        new Parameter("@BeiZhu",SqlDbType.NVarChar,-1,ztd.BeiZhu)
                                    };
            objBusinessBase.ExecuteProcedure("usp_ZTestData_UpdateBeiZhu", parameter);
        }

        public void BatchUpdateBeiZhu(ZTestData ztd)
        {
            Parameter[] parameter = {
                                        new Parameter("@BatchBeiZhu",SqlDbType.Structured,-1,ztd.BatchBeiZhu)
                                    };
            objBusinessBase.ExecuteProcedure("usp_ZTestData_BatchUpdate", parameter);
        }
    }
}

ZTestDataEntity Code

 后端实现的功能,每行各自更新功能,和一次性更新功能。

public partial class _Default : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
        {
            Data_Binding();
        }

    }

    private void Data_Binding()
    {
        ZTestDataEntity ztde = new ZTestDataEntity();
        this.GridViewZTestData.DataSource = ztde.Get_DataTable_GetData();
        this.GridViewZTestData.DataBind();
    }

    protected void GridViewZTestData_RowCreated(object sender, GridViewRowEventArgs e)
    {
        if (e.Row.RowType != DataControlRowType.DataRow) return;       

        Button btnUpdate = (Button)e.Row.FindControl("ButtonUpdate");
        if (btnUpdate != null)
        {
            btnUpdate.Click += BtnUpdate_Click;
        }
    }

    private void BtnUpdate_Click(object sender, EventArgs e)
    {
        Button button = (Button)sender;
        GridViewRow gvr = (GridViewRow)button.Parent.Parent;

        var textboxBeiZhu = (TextBox)gvr.FindControl("TextBoxBeiZhu");

        try
        {
            string keyVal = GridViewZTestData.DataKeys[gvr.RowIndex].Value.ToString();

            ZTestData ztd = new ZTestData();
            ztd.ID = Convert.ToInt32(keyVal);
            ztd.BeiZhu = textboxBeiZhu.Text.Trim();

            ZTestDataEntity ztde = new ZTestDataEntity();
            ztde.UpdateBeiZhu(ztd);
            //JsAlert("保存成功。");

        }
        catch (Exception ex)
        {
            //JsAlert(ex.Message);
            return;
        }
        finally
        {
            Data_Binding();
        }
    }

    protected void ButtonOneTimeUpdate_Click(object sender, EventArgs e)
    {
        DataTable _dt_bz = new DataTable();
        _dt_bz.Columns.Add("ID", typeof(int));
        _dt_bz.Columns.Add("BeiZhu", typeof(string));

        foreach (GridViewRow gvr in this.GridViewZTestData.Rows)
        {
            int keyID = Convert.ToInt32(this.GridViewZTestData.DataKeys[gvr.RowIndex].Value);
            string bz = ((TextBox)gvr.FindControl("TextBoxBeiZhu")).Text.Trim();
            _dt_bz.Rows.Add(keyID, bz);
        }

        try
        {
            ZTestData ztd = new ZTestData();
            ztd.BatchBeiZhu = _dt_bz;
            ZTestDataEntity ztde = new ZTestDataEntity();
            ztde.BatchUpdateBeiZhu(ztd);
            //JsAlert("数据已经保存。");
        }
        catch (Exception ex)
        {
            //JsAlert(ex.Message);
        }

    }
}

View Code

本随笔着重是GridView每行的备注文本框,让其实现OnBlur和用户输入完后按回车提交更新。
打开上面

protected void GridViewZTestData_RowCreated(object sender, GridViewRowEventArgs e)

为文本框添加2个属性,一个OnBlur和数据属性,
2026-04-29_08-23-26

 
前端js,写一个方法,传入更新参数,去呼叫Web API,
2026-04-29_10-37-58

let isUpdating = false; //更新请求
let skipBlurOnce = false; //区分是否为blur事件

function updateBeizhuByTextBox(txtboxElement) {
    return new Promise((resolve, reject) => {
        if (isUpdating) return reject("已有请求进行中");

        var id = txtboxElement.id;
        var baseId = id.substring(0, id.lastIndexOf("_") + 1);
        var hiddenKey = document.getElementById(baseId + "HiddenFieldID");

        isUpdating = true;
        let url = '/api/MyTest/UpdateBeiZhu';
        var postData = {
            ID: hiddenKey.value,
            BeiZhu: txtboxElement.value.trim()
        };

        axios.post(url, JSON.stringify(postData), {
            headers: { 'Content-Type': 'application/json; charset=utf-8' }
        })
            .then(response => {
                if (!response.data.Status) {
                    alert(response.data.Message);
                    reject(response.data.Message);
                } else {
                    alert(response.data.Message);
                    resolve(response.data);
                }
            })
            .catch(error => {
                console.error(error);
                reject(error);
            })
            .finally(() => {
                isUpdating = false;
            });
    });
}

View Code

 OnBlur前端事件,
2026-04-29_10-45-54

function UpdateBeiZhu(event) {
    if (skipBlurOnce) {
        skipBlurOnce = false;
        return;
    }

    updateBeizhuByTextBox(event.target)
        .catch(console.warn);   
}

View Code

最后是用户按回车更新前端事件,
2026-04-29_10-49-37

document.addEventListener('keydown', function (e) {
    if (e.key === 'Enter' || e.keyCode === 13) {
        var activeEl = document.activeElement;
        if (activeEl && activeEl.getAttribute('data-bz') === 'gv-txtbox-beizhu') {
            e.preventDefault();
            skipBlurOnce = true;  // 阻止随后的 blur 触发  
            activeEl.blur();      //让按完回车后文框的focus焦点去除。
            updateBeizhuByTextBox(activeEl)
                .catch(console.warn)
                .finally(() => {
                    // 延迟重置标志,确保 blur 已被忽略
                    setTimeout(() => { skipBlurOnce = false; }, 300);
                });
        } else {
            // 如果其他元素按回车不想提交表单,可以阻止默认
            // e.preventDefault();  // 根据需求决定是否全局阻止
        }
    }
}); 

View Code


最后截屏图,还可以与下这篇对比,单独文框与GridView行内的文框按回车实现的方法。
文本框输入完后直接按回车提交数据https://www.cnblogs.com/insus/p/19811442

另外,你会看到下面代码:

 var id = txtboxElement.id;
 var baseId = id.substring(0, id.lastIndexOf("_") + 1);
 var hiddenKey = document.getElementById(baseId + "HiddenFieldID");

日后你可以参考这个方法来获取GridView行中各列服务控件。
下面是示例运行时,按F12,在查看器展开代码可见,
2026-04-29_11-13-41

TextBoxBeiZhu这是服务控件的ID,生成网页时,它的id会添加前缀高亮部分。GridView的ID + _Ctl +行索引_ + 列服务控件ID
var id = txtboxElement.id; 
获取本身的ID。
var baseId = id.substring(0, id.lastIndexOf("_") + 1);
然后使用substring()进行切割得到ID前缀(高亮部分),
var hiddenKey = document.getElementById(baseId + "HiddenFieldID");
最后与服务控件ID拼接。


整篇关键功能随笔至此。看不明或者参考使用有啥问题,可以跟Insus.NET反馈或分享。