【.NET Core 跨平台 GUI 开发】第三篇:Gtk# 表格布局与事件处理

除了使用 HBox 和 VBox 进行布局外,还可以使用 Table 对象进行布局。这个有点像 html 的 table,适合方方正正大小差不多的空间集合。本篇将会对 Table 布局进行讲解,利用 Table 做出一个计算器的界面并使其可以响应按钮点击并将点击的按钮内容展示在界面上。在 Windows 系统中效果如下:

源码地址: https://gitee.com/coderbusy/gtk-sharp-demo

1、准备项目

我们需要新建一个 .NET Core 控制台项目,在编写本篇博文时 .NET Core 3.1 LTS 版本已经发布了,所以大可以直接使用 .NET Core 3.1 版本。在引用完 GtkSharp  之后,新建一个名为 CalculatorView.cs 的类文件并使其从 Gtk.Window 中派生:

在 Programe.cs 文件中键入以下代码,用于启动窗体和运行程序:

2、Table 简介

Table 对象的构造函数需要 3 个参数,具体签名如下:

Table table1 = new Table(int rows, int columns, bool homogeneous);

rows、columns 分别指要创建的行和列。homogeneous 为 true 表示所有的表框大小设置为大小最大那个控件,为 false 则各行和列的大小根据各行列中大小最大那个控件决定 。

行和列的布局从0到n,其中 n 是在对 Table 的调用中指定的数字。因此,如果指定 rows = 2,columns = 2,则布局将如下所示:

 0          1          20+----------+----------+|          |          |1+----------+----------+|          |          |2+----------+----------+

注意,坐标系始于左上角。如果想把 Widgt 放入 Table 中,可以使用下面的代码:

table1.Attach (        Widget            child,int               leftAttach,int               rightAttach,int               topAttach,int               bottomAttach);

leftAttach,rightAttach,topAttach,bottomAttach 分别代表表中控件的左右上下边所在位置。这似乎和已有的 WinForm 编程经验不符,在 WinForm 下我们通过指定控件的位置来对其定位,指定其大小来决定其占用的空间。在 Gtk# 的世界中你仍可以将 WinForm 的思想带入,只不过,在指定了坐标 (x,y) 对应 (left,top) 之后,(right,bottom) 对应的值为 (x+width,y+height) 。

3、代码实现

遵循 Gtk# 开发的最佳实践,我们会在窗体关闭后进行应用程序退出操作。因为我们的 CalculatorView 直接从 Gtk.Window 派生,所以我们可以在构造函数中做这件事,同时对窗体进行一些细节上的调整,让他启动时出现在屏幕中央,并且不可以通过鼠标调整大小:

using System;using System.Collections.Generic;using System.Text;using Pango;namespace Gtk.Calculator{class CalculatorView : Window{public CalculatorView() : base("Gtk.Calculator"){//在窗体关闭时,退出应用程序this.DeleteEvent += CalculatorView_DeleteEvent;//设置窗体大小this.SetSizeRequest(300, 500);//设置窗体位置this.SetPosition(WindowPosition.Center);//去掉最大化按钮,不可以调整大小。this.Resizable = false;}private void CalculatorView_DeleteEvent(object o, DeleteEventArgs args){Application.Quit();}}}

计算器需要一个 Label 来显示结果,因为要对这个 Label 进行更新,所以我们将其定义为 CalculatorView 的字段:

private readonly Label _lblDisplay;

添加一个 DefaultMargin 属性,用来表示按钮之间的间距:

/// <summary>/// 获取或设置 按钮之间的间距/// </summary>public int DefaultMargin { get; set; } = 3;

我们需要一个 6 行 4 列的表格来进行布局,创建表格后,将其添加到视图中,然后把显示结果的 Lable 也进行初始化,添加到 Table 的第一行并占满它。该 Label 中的文字在水平方向上居右,在垂直方向上在底部:

//创建一个 6 行 4 列的表格var table = new Table(6, 4, false);this.Add(table);//创建一个标签,用来显示结果
_lblDisplay = new Label { Halign = Align.End, Margin = this.DefaultMargin, Valign = Align.End };
table.Attach(_lblDisplay, 0, 4, 0, 1);

接下来就是填充按钮了。新建一个 CreateButton 方法用于创建按钮,该方法接收一个字符串参数作为按钮上显示的文字,并注册按钮的 Clicked 方法。在按钮被 Click 后,将按钮上的文字附加到 Label 中:

private Button CreateButton(String text){var btn = new Button(text) { Margin = DefaultMargin };btn.Clicked += Btn_Clicked;return btn;}private void Btn_Clicked(object sender, EventArgs e){if (sender is Button btn){this._lblDisplay.Text += btn.Label;}}

下面就是略微烧脑的代码了,补充计算器所需的按钮。一堆参数,写起来还是需要小心翼翼:

//补充计算器的按钮//leftAttach,rightAttach,topAttach,bottomAttach 分别代表表中控件的左右上下边所在位置
table.Attach(CreateButton("AC"), 0, 1, 1, 2);
table.Attach(CreateButton("+/-"), 1, 2, 1, 2);
table.Attach(CreateButton("%"), 2, 3, 1, 2);
table.Attach(CreateButton("+"), 3, 4, 1, 2);table.Attach(CreateButton("7"), 0, 1, 2, 3);
table.Attach(CreateButton("8"), 1, 2, 2, 3);
table.Attach(CreateButton("9"), 2, 3, 2, 3);
table.Attach(CreateButton("*"), 3, 4, 2, 3);table.Attach(CreateButton("4"), 0, 1, 3, 4);
table.Attach(CreateButton("5"), 1, 2, 3, 4);
table.Attach(CreateButton("6"), 2, 3, 3, 4);
table.Attach(CreateButton("-"), 3, 4, 3, 4);table.Attach(CreateButton("1"), 0, 1, 4, 5);
table.Attach(CreateButton("2"), 1, 2, 4, 5);
table.Attach(CreateButton("3"), 2, 3, 4, 5);
table.Attach(CreateButton("+"), 3, 4, 4, 5);table.Attach(CreateButton("0"), 0, 2, 5, 6);
table.Attach(CreateButton("."), 2, 3, 5, 6);
table.Attach(CreateButton("="), 3, 4, 5, 6);

至此,我们的计算器界面就做好了。完整代码可以查看:CalculatorView.cs

关于 Table 的官方介绍,可以参看这里:GtkSharp: Packing with Tables

4、事件处理

与 WinForm 中的 Button 类似,Gtk# 中的 Button 也拥有点击事件,这点上文已经实践过。在 Gtk# 的世界中,所有从 Widget 派生的挂件均含有以下事件:

ButtonPressEventButtonReleaseEventScrollEventMotionNotifyEventDeleteEventDestroyEventExposeEventKeyPressEventKeyReleaseEventEnterNotifyEventLeaveNotifyEventConfigureEventFocusInEventFocusOutEventMapEventUnmapEventPropertyNotifyEventSelectionClearEventSelectionRequestEventSelectionNotifyEventProximityInEventProximityOutEventVisibilityNotifyEventClientEventNoExposeEventWindowStateEvent

但事实上,当我们尝试订阅 Label 的 ButtonPressEvent 时就会发现预期的事件处理器并没有被执行:

using System;namespace Gtk.EventDemo{class Program{static void Main(string[] args){Application.Init();var win = new Window("Gtk.EventDemo");win.SetDefaultSize(250, 100);win.SetPosition(WindowPosition.Center);win.DeleteEvent += (s, e) =>{Application.Quit();};var lbl = new Label("标签内容");lbl.ButtonPressEvent += (s, e) => { Console.WriteLine("标签被点击:" + e); };win.Add(lbl);win.ShowAll();Application.Run();}}}

上述代码在被运行后,即使使用鼠标点击了 Label ,因为 Label 没有关联的窗体所以对应的事件处理器不会被调用。这样的部件有许多,常见的有以下几个:

Gtk.Alignment           Gtk.Arrow          Gtk.BinGtk.Box                 Gtk.Button         Gtk.CheckButtonGtk.Fixed               Gtk.Image          Gtk.LabelGtk.MenuItem            Gtk.Notebook       Gtk.PanedGtk.RadioButton         Gtk.Range          Gtk.ScrolledWindowGtk.Separator           Gtk.Table          Gtk.ToolbarGtk.AspectFrame         Gtk.Frame          Gtk.VBoxGtk.HBox                Gtk.VSeparator     Gtk.HSeparator

即便 Label 控件无法直接响应事件,但是我们仍旧可以通过 EventBox 对象来实现捕获 Label 事件的功能。具体来说,将 EventBox 嵌套在 Label 外即可:

using System;namespace Gtk.EventDemo{class Program{static void Main(string[] args){Application.Init();var win = new Window("Gtk.EventDemo");win.SetDefaultSize(250, 100);win.SetPosition(WindowPosition.Center);win.DeleteEvent += (s, e) =>{Application.Quit();};var lbl = new Label("标签内容");var box = new EventBox();box.Add(lbl);box.ButtonPressEvent += (s, e) => { Console.WriteLine("标签被点击:" + e); };win.Add(box);win.ShowAll();Application.Run();}}}

启动程序后,可以看到点击事件被成功捕获:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/312615.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何构建知识体系

大家好&#xff0c;我是Z哥。不知道你有没有过这样的感觉&#xff0c;那些比你更厉害的人&#xff0c;在一件事中往往可以轻易地从一个「点」延展出一条「线」&#xff0c;甚至一个「面」的知识点。对我们真实感受的冲击是&#xff0c;在大局观上被碾压&#xff0c;相比之下觉得…

蓝桥杯 递增序列

思路&#xff1a; 这道题一开始想复杂了&#xff0c;其实这么小的数据量直接暴力求解即可。 参考代码&#xff1a; #include<bits/stdc.h> using namespace std; typedef long long ll; int m 30, n 50;bool checkL(int x, int y) //判断坐标是否超出边界 {if(x &g…

使用DbContextPool提高EfCore查询性能

长话短说上个月公司上线了一个物联网数据科学项目&#xff0c;我主要负责前端接收设备Event&#xff0c;并提供模型参数下载&#xff08;数据科学团队会优化参数&#xff09;。WebApp部署在Azure&#xff0c;模型参数使用Azure SQL Server存储。最近从灰度测试转向全量部署之后…

蓝桥杯 分巧克力 二分

#include<bits/stdc.h> using namespace std; typedef long long ll; ll n, k; vector<ll> width, height;bool check(ll length) //判断边长为length的正方形都否满足需求 {ll sum 0; //记录能切成的最大方块数 for(int i 0; i < n; i){sum (width[i] / …

[原]调试实战——程序CPU占用率飙升,你知道如何快速定位吗?

前言 如果我们自己的程序的CPU Usage&#xff08;CPU占用率&#xff09;飙升&#xff0c;并且居高不下&#xff0c;很有可能陷入了死循环。你知道怎么快速定位并解决吗&#xff1f;今天跟大家分享几种定位方法&#xff0c;希望对你有所帮助。如何判断是否有死循环&#xff1f; …

蓝桥杯 迷宫

参考代码&#xff1a; #include<bits/stdc.h> using namespace std; typedef long long ll; int e[55][55], vis[55][55]; //e矩阵表示障碍物信息&#xff0c;vis矩阵表示点是否被访问过 int m 30, n 50; struct node{ //定义结构体&#xff0c;用于存放点的信息 i…

ASP.NET Core基于K8S的微服务电商案例实践--学习笔记

摘要一个完整的电商项目微服务的实践过程&#xff0c;从选型、业务设计、架构设计到开发过程管理、以及上线运维的完整过程总结与剖析。讲师介绍产品需求介绍纯线上商城线上线下一体化跨行业跨商业模式从0开始&#xff0c;我们应该采用微服务吗&#xff1f;不适合采用微服务架构…

蓝桥杯 日志统计 尺取

参考代码&#xff1a; #include<bits/stdc.h> using namespace std; typedef long long ll; vector<int> v[100005]; //定义容器用于存放第i篇帖子被点赞的时间 int main() {ios::sync_with_stdio(false); int n, d, k;cin >> n >> d >> k;in…

2019 ASP.NET Core 之微调查报告,新鲜出炉

▼更多精彩推荐&#xff0c;上午11点到达▼在本周三的时候&#xff0c;因为直播没有找到合适内容的缘故&#xff0c;因此在我的公众号内发起了一波问卷调查&#xff0c;地址是&#xff1a;【壹个问卷】NetCore学习的知识点调查&#xff0c;当时就是想着有十来份儿就已经很给面子…

蓝桥杯 子串分值

参考代码&#xff1a; #include<bits/stdc.h> using namespace std;int main() {ios::sync_with_stdio(false); string str;cin >> str;int sum 0;int left, right;char s;int len str.size();for(int i 0; i < len; i) //依次循环每一个字符&#xff0c;…

基于 Blazui 的 Blazor 后台管理模板 BlazAdmin 正式尝鲜

简介BlazAdmin 是一个基于Blazui的后台管理模板&#xff0c;无JS&#xff0c;无TS&#xff0c;非 Silverlight&#xff0c;非 WebForm&#xff0c;一个标签即可使用。  我将在下一篇文章讨论 Blazor 服务器端渲染与客户端渲染的基本原理&#xff0c;对比服务器端渲染与 WebFo…

蓝桥杯 123 二分+打表

参考代码&#xff1a; #include<bits/stdc.h> using namespace std; typedef long long ll; ll temp[1500000], sum[1500000]; //temp数组记录序号和&#xff0c;sum数组记录前缀和 ll cal(ll n) //计算自然数求和 {return (n1)*n/2; }int main() {ios::sync_with_s…

.Net Core使用Ocelot网关(二) -鉴权认证

前言上一章.Net Core使用Ocelot网关(一) -负载,限流,熔断,Header转换 已经简单的介绍了ocelot的使用了,但是网关暴露的接口如果什么人都能访问的话安全性就太低啦。所以我们需要去鉴权和认证。这里我们使用identityServer4给我们的网关来鉴权认证。创建Identity服务我们创建一个…

蓝桥杯 k倍区间 前缀和

参考代码&#xff1a; #include<bits/stdc.h> using namespace std; typedef long long ll; int array[100005], t[100005]; //array记录每个元素值&#xff0c;t记录取余k&#xff0c;各余数对应前缀和的数量 int main() {ios::sync_with_stdio(false); ll sum 0, c…

Kubernetes 的2020年“野望”

Kubernetes是一个用于部署容器化应用程序的开源容器编排系统&#xff0c;由Alphabet的GOOGL Google部门设计&#xff0c;其发展势头强劲&#xff0c;这得益于全球企业以指数级的速度生成数据之大势。Kubernetes支持在单个OS上无缝部署多个应用程序并实现诸如监视、调度、扩展这…

微服务的时间和成本去哪儿了

2019 中国.NET 开发者峰会目前在国内的.NET社区还是很有影响力的&#xff0c;宣传的内容也都是比较新潮和前言的技术栈。有一个不争的现实是基本上主题都是关于.NET Core的&#xff0c;以及基于该主题之上的延展。比如ML.NET相关的机器学习&#xff1b;基于.NET Core的微服务实…

自定义滚动条(Custom ScrollBar)

时间如流水&#xff0c;只能流去不流回&#xff01; 点赞再看&#xff0c;养成习惯&#xff0c;这是您给我创作的动力&#xff01; 本文 Dotnet9 https://dotnet9.com 已收录&#xff0c;站长乐于分享dotnet相关技术&#xff0c;比如Winform、WPF、ASP.NET Core等&#xff0c;亦…

用ASP.NET Core构建可检测的高可用服务--学习笔记

摘要随着现代化微服务架构的发展&#xff0c;系统故障的定位与快速恢复面临着诸多挑战&#xff0c;构建可检测的服务&#xff0c;帮助线上保障团队时刻掌控应用的运行状况越来越重要。本次分享会讲解如何让 ASP .NET Core 应用与现代化云基础设施完美融合&#xff0c;提升服务的…

[功能发布]Excel催化剂2周年巨献-网页数据采集功能发布,满足90%合理场景使用...

转眼间&#xff0c;Excel催化剂推出已经两周年&#xff0c;在此之际&#xff0c;献上数据时代最刚需的网页采集功能&#xff0c;无需苦苦寻觅各种工具&#xff0c;借助Excel催化剂过往数据处理、清洗功能&#xff0c;加上此轮的网页采集功能&#xff0c;一点不输于市面上的各种…

.NET Core 微服务学习与实践系列文章目录索引(2019版)

Photo &#xff1a;.NET Core文 | Edison Zhou2018年&#xff0c;我开始学习和实践.NET Core&#xff0c;并开始了微服务的学习&#xff0c;以及通过各种开源组件搭建服务治理技术方案&#xff0c;并在学习过程中总结了一个.NET Core微服务学习与实践系列文章&#xff0c;涵盖了…