this.hide(),this.close(),this.dispose(),以及this.Hide()与this.Visible、Application.Exit与this.Close()区别

本文深入探讨了WinForms窗体中的this.Hide(), this.Close(), this.Dispose()等方法的区别及应用场景,同时对比了Application.Exit与this.Close()的不同之处,帮助读者理解如何合理选择并使用这些方法。
首先,假设你所说的 this 是指一个 WinForm 窗体。

this.Hide()隐藏窗体后还可用 this.Show()重新显示其。而且 Hide 一旦执行不能被中止。

this.Close() 若关闭成功,则 this 也就变为垃圾内存,等待垃圾内存自动回收器(GC)回收。但 Close 操作是可以被终止的,通过 Closing 事件检查是否真的可以退出,如果不行,请设置 CanceEventArgs 的 e.Cancel = true;后即可中止 Close 操作,这样你的 WinForm 依然保持。

(注意对于 WinForm 是如此,但对于像 FileStream 这样的类,Close 就是实现 Dispose 功效的方法哦,是风马牛不相及的一回事)。^Q^

this.Dispose() 用来(提前)释放非托管资源,注意,仅是非托管资源。调用此操作时,你必须确保 Dispose 方法中释放的非托管资源确实不再有用了,以后的代码执行不会再访问已被释放的非托管资源,否则将引发异常(ObjectDisposedException)。

!!!(以下讨论,不限于 WinForms)

为什么,.NET 要提供此 Dispose 操作呢?这与 GC 的垃圾内存回收算法有关。

譬如说,在一个数据访问的方法中,建立一个到数据库的连接 connection1 以访问数据,此时.NET 就使用了一个非托管资源,访问结束后,在退出数据访问方法前,你可以不调用 connection1.Close() 方法。等到内存堆的第 0 代内存满了后,GC 回收 connection1 的内存时,会自动释放绑定的非托管资源。

然而,离开数据访问方法到第 0 代内存堆满时,这期间的时间是不可预计的。而如果 connection1 已位于第 1 代或第 2 代的内存堆上时,而且运行机器上的内存相当富裕,被回收的时间可能就是“遥遥无期”了。因为第 1 代的回收周期远远大于第 0 代的回收周期,第 2 代的回收周期又远远大于第 1 代的回收周期。^Q^

如果每次非托管资源都要等到 GC 回收时才释放,那么应用程序长时间的占用着非托管资源,会严重影响性能的。本例中,数据库连接就是一个好例子,如果数据库服务器限制最大连接数为 10, 在一个数据访问的高峰期,如果在离开数据访问方法不及时调用 connetion1.Close(),那么性能就会差得很离谱了。

接口 IDispozable 的 Dispose 方法就是用来让程序员显式地控制非托管资源的释放操作。

对于 FCL 类,有的只提供了 Dispose 方法,有的只提供 Close 方法,有的两者都提供。要仔细阅读文档加以区别,有时是一回事,有时却不是一回事。这种类通常会占用非托管资源,而且都实现 IDispozable 接口。

举以下二个例子分析:
<1>
譬如,FileStream 并没有显式提供 Dispose 方法,而只提供了 Close 方法,此 Close 方法实现了通常意义上的 Dispose 功能。
然而,FileStream (显式)实现了 IDisposable 接口,能够通过 ((IDisposable)fileStream).Dispose() 来达到与 fileStream.Close() 相同的功效。只是不能fileStream.Dispose() 罢了。

<2>
譬如,SQLConnection 同时提供了 Dispose 和 Close 方法,它们都可用来释放非托管资源,但两者功效并不完全相同。Dispose 仅仅释放非托管的数据库连接资源。

而 Close 方法可以回滚任何挂起的事务,然后,它将连接释放到连接池,或者在连接池被禁用的情况下释放非托管的 DB 连接资源。所以,微软一般建议程序员尽量使用 SQLConnection 的 Close 方法。



首先明确this.Hide()和this.Visible作用完全一样,都是隐藏当前窗体,使其不可见;Application.Exit与this.Close()虽然都有关闭当前应用的作用,但作用不同。

(1)this.Hide()与this.Visible

对于.net平台窗体开发,当由代码 Form f=new Form();f.show()创建窗体时,需要隐藏当前窗体,我们第一想到的就是this.Close();然而这样是关闭当前进程,相当于Application.Exit();正确的处理方法是f.Hide()或f.Visible=false;前者的作用和后者没任何差别,只是hide函数通过函数体修改Visible属性而已。

public void Hide()

{

this.Visible=false;

}

(2)Application.Exit与this.Close()

Application.Exit   方法:通知所有消息泵必须终止,并且在处理了消息以后关闭所有应用程序窗口。 

Form.Close   方法:关闭窗体。

如果该窗体是应用程序的主启动窗体,则調用窗体的Close()方法,跟調用Application.Exit的效果差不多。

如果该窗体非主启动窗体,则只是关闭该窗体而不关闭整个应用程序。 

同时要注意它们在多线程应用程序中的用法,注意线程的关闭工作,此处不详细展开。


public class 自动补全管理器 { private List<string> lst候选词列表; private bool bl是否启用功能 = false; private ToolStripDropDown 下拉菜单; private System.Windows.Forms.ListBox lb候选词列表框; private ToolStripControlHost 控件宿主; private Word.Application appWord应用; private string str当前输入缓存 = ""; public 自动补全管理器() { fun初始化提示窗体(); } public void fun初始化(Word.Application wordApp, List<string> lst外部候选词列表 = null) { appWord应用 = wordApp; // 设置候选词列表 if (lst外部候选词列表 != null) { lst候选词列表 = lst外部候选词列表; } else { lst候选词列表 = new List<string> { "第一芯片", "第一结构件", "第二芯片", "第二结构件", "第三芯片" }; } // 注册Word事件 appWord应用.WindowSelectionChange += fun处理选择变化; appWord应用.DocumentChange += () => fun隐藏提示窗体(); } private void fun初始化提示窗体() { 下拉菜单 = new ToolStripDropDown(); 下拉菜单.AutoSize = false; 下拉菜单.Size = new System.Drawing.Size(250, 120); 下拉菜单.BackColor = System.Drawing.Color.White; 下拉菜单.Padding = new Padding(0); 下拉菜单.Margin = new Padding(0); // 设置下拉菜单的渲染样式 下拉菜单.Renderer = new ToolStripProfessionalRenderer(new 自定义颜色表()); lb候选词列表框 = new System.Windows.Forms.ListBox(); lb候选词列表框.Size = new System.Drawing.Size(250, 120); lb候选词列表框.Dock = DockStyle.Fill; lb候选词列表框.BorderStyle = System.Windows.Forms.BorderStyle.None; lb候选词列表框.Font = new System.Drawing.Font("微软雅黑", 10); lb候选词列表框.ItemHeight = 22; lb候选词列表框.BackColor = System.Drawing.Color.White; lb候选词列表框.ForeColor = System.Drawing.Color.Black; // 绑定ListBox事件 lb候选词列表框.KeyDown += fun处理列表框按键; lb候选词列表框.DoubleClick += fun处理列表框双击; lb候选词列表框.PreviewKeyDown += fun处理列表框预览按键; 控件宿主 = new ToolStripControlHost(lb候选词列表框); 控件宿主.AutoSize = false; 控件宿主.Size = new System.Drawing.Size(250, 120); 控件宿主.Margin = new Padding(0); 控件宿主.Padding = new Padding(0); 下拉菜单.Items.Add(控件宿主); } // 自定义颜色表以保持白色背景 private class 自定义颜色表 : ProfessionalColorTable { public override System.Drawing.Color ToolStripDropDownBackground { get { return System.Drawing.Color.White; } } } private void fun处理选择变化(Word.Selection sel当前选择) { if (!bl是否启用功能) return; try { // 获取当前光标前的完整单词 string str当前单词 = fun获取光标前单词(sel当前选择); System.Diagnostics.Debug.WriteLine($"处理选择变化 - 当前单词: '{str当前单词}', 缓存: '{str当前输入缓存}'"); if (!string.IsNullOrEmpty(str当前单词)) { // 只有当新单词缓存不同时才更新 if (str当前单词 != str当前输入缓存) { str当前输入缓存 = str当前单词; System.Drawing.Point pt光标位置 = Control.MousePosition; var lst匹配的词语列表 = lst候选词列表 .Where(str词语 => str词语.StartsWith(str当前输入缓存, StringComparison.OrdinalIgnoreCase)) .ToList(); System.Diagnostics.Debug.WriteLine($"找到 {lst匹配的词语列表.Count} 个匹配项"); if (lst匹配的词语列表.Count > 0) { fun显示提示窗体(lst匹配的词语列表, pt光标位置); } else { fun隐藏提示窗体(); // 如果没有匹配项,清空缓存重新开始 str当前输入缓存 = ""; } } } else { // 如果获取不到单词,检查是否是删除操作 if (!string.IsNullOrEmpty(str当前输入缓存)) { // 可能是用户删除了内容,清空缓存 str当前输入缓存 = ""; System.Diagnostics.Debug.WriteLine("检测到删除操作,清空缓存"); } fun隐藏提示窗体(); } } catch (Exception ex异常) { System.Diagnostics.Debug.WriteLine($"处理选择变化时出错: {ex异常.Message}"); } } private string fun获取光标前单词(Word.Selection sel选择) { if (sel选择 == null) return ""; try { int 光标位置 = sel选择.Start; if (光标位置 == 0) return ""; // 从光标位置向前查找,直到遇到空格或段落开头 int 词开始位置 = 光标位置; while (词开始位置 > 0) { Word.Range 前一个字符范围 = sel选择.Document.Range(词开始位置 - 1, 词开始位置); string 前一个字符 = 前一个字符范围.Text; // 如果前一个字符为null,说明到达文档边界 if (前一个字符 == null) { break; } // 遇到空格、标点或换行就停止 if (string.IsNullOrWhiteSpace(前一个字符) || char.IsPunctuation(前一个字符[0])) { break; } 词开始位置--; } // 提取单词 Word.Range 当前词范围 = sel选择.Document.Range(词开始位置, 光标位置); string 当前词 = 当前词范围.Text; // 先不Trim // 多重空值检查 if (string.IsNullOrEmpty(当前词)) { return ""; } 当前词 = 当前词.Trim(); return 当前词.Length <= 20 ? 当前词 : ""; } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"获取光标前单词出错: {ex.Message}"); return ""; } } private void fun显示提示窗体(List<string> lst词语列表, System.Drawing.Point pt光标位置) { System.Diagnostics.Debug.WriteLine($"显示提示窗体,匹配词: {string.Join(", ", lst词语列表)}"); // 强制关闭 fun强制关闭提示窗体(); // 更新内容 lb候选词列表框.Items.Clear(); foreach (var str词语 in lst词语列表) { lb候选词列表框.Items.Add(str词语); } // 动态调整高度 int 显示行数 = Math.Min(lst词语列表.Count, 5); int 新高度 = (lb候选词列表框.ItemHeight * 显示行数) + 4; 下拉菜单.Height = 新高度; 控件宿主.Height = 新高度; if (lb候选词列表框.Items.Count > 0) { lb候选词列表框.SelectedIndex = 0; } // 显示下拉菜单 下拉菜单.Show(pt光标位置); } private void fun强制关闭提示窗体() { try { if (下拉菜单 != null) { // 尝试多种关闭方式 下拉菜单.Close(); 下拉菜单.Hide(); // 强制发送关闭消息 if (下拉菜单.IsHandleCreated) { SendMessage(下拉菜单.Handle, WM_CLOSE, IntPtr.Zero, IntPtr.Zero); } System.Windows.Forms.Application.DoEvents(); System.Threading.Thread.Sleep(20); } } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"强制关闭菜单时出错: {ex.Message}"); } } // Windows API [System.Runtime.InteropServices.DllImport("user32.dll")] private static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam); private const uint WM_CLOSE = 0x0010; private void fun隐藏提示窗体() { if (下拉菜单 != null && 下拉菜单.Visible) { 下拉菜单.Close(); } } private void fun处理列表框按键(object sender, KeyEventArgs e事件参数) { switch (e事件参数.KeyCode) { case Keys.Enter: case Keys.Space: fun确认选择当前项(); e事件参数.Handled = true; e事件参数.SuppressKeyPress = true; break; case Keys.Escape: fun隐藏提示窗体(); e事件参数.Handled = true; e事件参数.SuppressKeyPress = true; break; case Keys.Tab: fun循环选择下一项(); e事件参数.Handled = true; e事件参数.SuppressKeyPress = true; break; } } private void fun处理列表框预览按键(object sender, PreviewKeyDownEventArgs e事件参数) { if (e事件参数.KeyCode == Keys.Tab) { e事件参数.IsInputKey = true; } } private void fun处理列表框双击(object sender, EventArgs e) { fun确认选择当前项(); } private void fun确认选择当前项() { if (lb候选词列表框.SelectedItem != null) { string str选中的词语 = lb候选词列表框.SelectedItem.ToString(); fun处理选中的词语(str选中的词语); } } private void fun循环选择下一项() { if (lb候选词列表框.Items.Count == 0) return; int n下一项索引 = (lb候选词列表框.SelectedIndex + 1) % lb候选词列表框.Items.Count; lb候选词列表框.SelectedIndex = n下一项索引; } private void fun处理选中的词语(string str选中的词语) { try { if (appWord应用.Selection != null) { Word.Selection sel当前选择 = appWord应用.Selection; int 原始光标位置 = sel当前选择.Start; System.Diagnostics.Debug.WriteLine($"处理选中词语: '{str选中的词语}', 缓存: '{str当前输入缓存}', 光标位置: {原始光标位置}"); // 删除已输入的文本 if (!string.IsNullOrEmpty(str当前输入缓存)) { int 词开始位置 = 原始光标位置 - str当前输入缓存.Length; if (词开始位置 >= 0 && 词开始位置 < 原始光标位置) { Word.Range 删除范围 = sel当前选择.Document.Range(词开始位置, 原始光标位置); string 删除前文本 = 删除范围.Text; System.Diagnostics.Debug.WriteLine($"删除范围: {词开始位置}-{原始光标位置}, 文本: '{删除前文本}'"); 删除范围.Text = ""; } } // 插入选中的词语 sel当前选择.Text = str选中的词语; // 移动光标到末尾 sel当前选择.Collapse(Word.WdCollapseDirection.wdCollapseEnd); // 清空缓存并隐藏 str当前输入缓存 = ""; fun隐藏提示窗体(); System.Diagnostics.Debug.WriteLine("插入完成,清空缓存"); } } catch (Exception ex异常) { System.Diagnostics.Debug.WriteLine($"插入选中词语时出错: {ex异常.Message}"); } } // 以下公共方法保持不变 public void fun设置功能启用状态(bool bl启用状态) { bl是否启用功能 = bl启用状态; if (!bl启用状态) { fun隐藏提示窗体(); } } public bool fun获取功能启用状态() { return bl是否启用功能; } public void fun设置候选词列表(List<string> lst新的候选词列表) { lst候选词列表 = lst新的候选词列表 ?? new List<string>(); } public void fun添加候选词(string str新词语) { if (!lst候选词列表.Contains(str新词语)) { lst候选词列表.Add(str新词语); } } public void fun刷新候选词列表(Func<List<string>> func获取候选词函数) { if (func获取候选词函数 != null) { lst候选词列表 = func获取候选词函数() ?? new List<string>(); } } public void fun合并候选词列表(List<string> lst额外候选词列表) { if (lst额外候选词列表 != null) { foreach (var str词语 in lst额外候选词列表) { if (!lst候选词列表.Contains(str词语)) { lst候选词列表.Add(str词语); } } } } public List<string> fun获取所有候选词() { return new List<string>(lst候选词列表); } public int fun获取候选词数量() { return lst候选词列表.Count; } public void fun清空输入缓存() { str当前输入缓存 = ""; fun隐藏提示窗体(); } } ====上述代码中,第一次输入“第”后会显示候选列表,再输入“二”时,第一次显示的候选列表不会消失,第二次触发要显示的列表就会在第一次显示的列表的后面,原因是什么?怎么才能在第二次显示候选列表前让第一次显示的候选列表消息?
09-30
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值