├── LBH.SerialPortTools
├── 1.png
├── 2.png
├── logo.png
├── win11.png
├── sp.css
├── ComConfig.cs
├── Program.cs
├── LBH.SerialPortTools.csproj
├── MessageBox.cs
├── Utils.cs
└── MainWindow.cs
├── .idea
└── .idea.LBH.SerialPortTools
│ └── .idea
│ ├── indexLayout.xml
│ └── .gitignore
├── README.md
├── LBH.SerialPortTools.sln
├── .gitattributes
└── .gitignore
/LBH.SerialPortTools/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geniuskai/serialporttools/HEAD/LBH.SerialPortTools/1.png
--------------------------------------------------------------------------------
/LBH.SerialPortTools/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geniuskai/serialporttools/HEAD/LBH.SerialPortTools/2.png
--------------------------------------------------------------------------------
/LBH.SerialPortTools/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geniuskai/serialporttools/HEAD/LBH.SerialPortTools/logo.png
--------------------------------------------------------------------------------
/LBH.SerialPortTools/win11.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geniuskai/serialporttools/HEAD/LBH.SerialPortTools/win11.png
--------------------------------------------------------------------------------
/LBH.SerialPortTools/sp.css:
--------------------------------------------------------------------------------
1 | #textResult text {
2 | background-color: forestgreen;
3 | color:white;
4 | }
5 | .btnRed{
6 | color:red;
7 | }
--------------------------------------------------------------------------------
/.idea/.idea.LBH.SerialPortTools/.idea/indexLayout.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/.idea.LBH.SerialPortTools/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 | # Rider ignored files
5 | /contentModel.xml
6 | /.idea.LBH.SerialPortTools.iml
7 | /modules.xml
8 | /projectSettingsUpdater.xml
9 | # Editor-based HTTP Client requests
10 | /httpRequests/
11 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Linux下的图形界面串口调试工具,基于.net6+gtksharp
2 | ### 跨平台 支持window及Linux操作系统
3 |
4 |
5 | 测试平台
6 |
7 | 1、win11
8 | 2、ubuntu20.4
9 | 
10 |
11 | ### Ubuntu20.4 deb安装包
12 | #### 软件安装使用命令: sudo dpkg -i serialporttools_1.0.00_amd64.deb
13 | #### 软件卸载使用命令: sudo dpkg -P serialporttools
14 | #### 查看软件是否安装以及版本号使用命令: dpkg-query --show serialporttools
15 |
16 |
--------------------------------------------------------------------------------
/LBH.SerialPortTools/ComConfig.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace LBH.SerialPortTools
8 | {
9 | internal class ComConfig
10 | {
11 | public string PortName { get; set; }
12 | public string BaudRate { get; set; }
13 | public string StopBit { get; set; }
14 | public string DataBit { get; set; }
15 | public string Parity { get; set; }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/LBH.SerialPortTools/Program.cs:
--------------------------------------------------------------------------------
1 | using Gtk;
2 | using LBH.SerialPortTools;
3 |
4 | class Program
5 | {
6 | public static Application App;
7 | public static MainWindow mainWindow;
8 | [STAThread]
9 | public static void Main(string[] args)
10 | {
11 | Application.Init();
12 |
13 | App = new Application("LBH.SerialPortTools", GLib.ApplicationFlags.None);
14 | App.Register(GLib.Cancellable.Current);
15 |
16 | Gtk.CssProvider provider = new Gtk.CssProvider();
17 | provider.LoadFromPath("sp.css");
18 | Gtk.StyleContext.AddProviderForScreen(Gdk.Screen.Default, provider, 800);
19 |
20 | mainWindow = new MainWindow();
21 | mainWindow.SetIconFromFile("logo.png");
22 | App.AddWindow(mainWindow);
23 |
24 | Application.Run();
25 | }
26 | }
--------------------------------------------------------------------------------
/LBH.SerialPortTools/LBH.SerialPortTools.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net6.0
6 | enable
7 | enable
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | PreserveNewest
18 |
19 |
20 | PreserveNewest
21 |
22 |
23 | Always
24 |
25 |
26 | PreserveNewest
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/LBH.SerialPortTools.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.1.32414.318
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LBH.SerialPortTools", "LBH.SerialPortTools\LBH.SerialPortTools.csproj", "{E45090E9-642E-4C1A-A341-559F86DC0C5B}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|Any CPU = Debug|Any CPU
11 | Release|Any CPU = Release|Any CPU
12 | EndGlobalSection
13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
14 | {E45090E9-642E-4C1A-A341-559F86DC0C5B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15 | {E45090E9-642E-4C1A-A341-559F86DC0C5B}.Debug|Any CPU.Build.0 = Debug|Any CPU
16 | {E45090E9-642E-4C1A-A341-559F86DC0C5B}.Release|Any CPU.ActiveCfg = Release|Any CPU
17 | {E45090E9-642E-4C1A-A341-559F86DC0C5B}.Release|Any CPU.Build.0 = Release|Any CPU
18 | EndGlobalSection
19 | GlobalSection(SolutionProperties) = preSolution
20 | HideSolutionNode = FALSE
21 | EndGlobalSection
22 | GlobalSection(ExtensibilityGlobals) = postSolution
23 | SolutionGuid = {EEAB9E15-0315-4C22-A2AA-C9BAD2C7AE98}
24 | EndGlobalSection
25 | EndGlobal
26 |
--------------------------------------------------------------------------------
/LBH.SerialPortTools/MessageBox.cs:
--------------------------------------------------------------------------------
1 | using Gtk;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace LBH.SerialPortTools
9 | {
10 | ///
11 | /// The MessageBox - Class tries to get the feel - alike of the good old MessageBox - Class of the .NET Framework
12 | ///
13 | public static class MessageBox
14 | {
15 | ///
16 | /// Show the specified text, caption, _Buttons and _Type.
17 | ///
18 | ///
19 | /// Text.
20 | ///
21 | ///
22 | /// Caption.
23 | ///
24 | ///
25 | /// The Buttons of the MessageDialog
26 | ///
27 | ///
28 | /// The Type of the MessageDialog.
29 | ///
30 | public static ResponseType Show(string text, string caption, ButtonsType _Buttons = ButtonsType.Ok, MessageType _Type = MessageType.Info, Gtk.Window parent = null)
31 | {
32 | if (text.Contains("not set"))
33 | {
34 | return ResponseType.None;
35 | }
36 | else
37 | {
38 | MessageDialog md = new MessageDialog(parent, DialogFlags.Modal, _Type, _Buttons, text);
39 | //if (parent != null)
40 | // md.ParentWindow = parent;
41 | md.Title = caption;
42 | ResponseType result = (ResponseType)md.Run();
43 | md.Destroy();
44 | return result;
45 | }
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Set default behavior to automatically normalize line endings.
3 | ###############################################################################
4 | * text=auto
5 |
6 | ###############################################################################
7 | # Set default behavior for command prompt diff.
8 | #
9 | # This is need for earlier builds of msysgit that does not have it on by
10 | # default for csharp files.
11 | # Note: This is only used by command line
12 | ###############################################################################
13 | #*.cs diff=csharp
14 |
15 | ###############################################################################
16 | # Set the merge driver for project and solution files
17 | #
18 | # Merging from the command prompt will add diff markers to the files if there
19 | # are conflicts (Merging from VS is not affected by the settings below, in VS
20 | # the diff markers are never inserted). Diff markers may cause the following
21 | # file extensions to fail to load in VS. An alternative would be to treat
22 | # these files as binary and thus will always conflict and require user
23 | # intervention with every merge. To do so, just uncomment the entries below
24 | ###############################################################################
25 | #*.sln merge=binary
26 | #*.csproj merge=binary
27 | #*.vbproj merge=binary
28 | #*.vcxproj merge=binary
29 | #*.vcproj merge=binary
30 | #*.dbproj merge=binary
31 | #*.fsproj merge=binary
32 | #*.lsproj merge=binary
33 | #*.wixproj merge=binary
34 | #*.modelproj merge=binary
35 | #*.sqlproj merge=binary
36 | #*.wwaproj merge=binary
37 |
38 | ###############################################################################
39 | # behavior for image files
40 | #
41 | # image files are treated as binary by default.
42 | ###############################################################################
43 | #*.jpg binary
44 | #*.png binary
45 | #*.gif binary
46 |
47 | ###############################################################################
48 | # diff behavior for common document formats
49 | #
50 | # Convert binary document formats to text before diffing them. This feature
51 | # is only available from the command line. Turn it on by uncommenting the
52 | # entries below.
53 | ###############################################################################
54 | #*.doc diff=astextplain
55 | #*.DOC diff=astextplain
56 | #*.docx diff=astextplain
57 | #*.DOCX diff=astextplain
58 | #*.dot diff=astextplain
59 | #*.DOT diff=astextplain
60 | #*.pdf diff=astextplain
61 | #*.PDF diff=astextplain
62 | #*.rtf diff=astextplain
63 | #*.RTF diff=astextplain
64 |
--------------------------------------------------------------------------------
/LBH.SerialPortTools/Utils.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO.Ports;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace LBH.SerialPortTools
9 | {
10 | static class Utils
11 | {
12 | //支持的波特率、停止位,数据位,奇偶校验位
13 | public static string[] baudRateList = { "1382400", "460800", "230400", "115200", "38400", "9600" };
14 | public static string[] stopBitList = { "One", "None", "OnePointFive", "Two" };
15 | public static string[] dataBitList = { "8", "7", "6", " 5" };
16 | public static string[] parityList = { "None", "Odd", "Even", "Mark", "Space" };
17 |
18 | public static string convertToHexString(string str)
19 | {
20 | string hexString = "";
21 | char[] strChars = str.ToCharArray();
22 | foreach (char c in strChars)
23 | {
24 | hexString += Convert.ToByte(c).ToString("X2") + " ";
25 | }
26 | if (hexString.EndsWith(" "))
27 | {
28 | hexString = hexString.Substring(0, hexString.LastIndexOf(" "));
29 | }
30 | return hexString;
31 | }
32 |
33 | public static string convertHexStringToCommonString(string hexString)
34 | {
35 | if (hexString.Length == 0)
36 | {
37 | return "";
38 | }
39 | string commonString = "";
40 |
41 | if (hexString.EndsWith(" "))
42 | {
43 | hexString = hexString.Substring(0, hexString.LastIndexOf(" "));
44 | }
45 | //过滤掉非hex形式的字符
46 | for (int i = 0; i < hexString.ToCharArray().Length; i++)
47 | {
48 | char s = hexString[i];
49 | if ((s >= '0' && s <= '9') || (s >= 'A' && s <= 'F'))
50 | {
51 | hexString = hexString.Substring(i);
52 | break;
53 | }
54 | }
55 | String[] hexBytes = hexString.Split(' ');
56 | foreach (string hex in hexBytes)
57 | {
58 | int value = Convert.ToInt32(hex, 16);
59 | commonString += Convert.ToChar(value);
60 | }
61 | return commonString;
62 | }
63 |
64 | public static byte[] convertHexStringToBytes(string hexString)
65 | {
66 | try
67 | {
68 | String[] hexBytes = hexString.Split(' ');
69 | byte[] bytes = new byte[hexBytes.Length];
70 | for (int i = 0; i < bytes.Length; i++)
71 | {
72 | int value = Convert.ToInt32(hexBytes[i], 16);
73 | bytes[i] = Convert.ToByte(value);
74 | }
75 | return bytes;
76 | }
77 | catch (Exception e3)
78 | {
79 | //MessageBox.Show("16进制的格式不对,请重试");
80 | return null;
81 | }
82 | }
83 |
84 |
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 | ##
4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
5 |
6 | # User-specific files
7 | *.rsuser
8 | *.suo
9 | *.user
10 | *.userosscache
11 | *.sln.docstates
12 |
13 | # User-specific files (MonoDevelop/Xamarin Studio)
14 | *.userprefs
15 |
16 | # Mono auto generated files
17 | mono_crash.*
18 |
19 | # Build results
20 | [Dd]ebug/
21 | [Dd]ebugPublic/
22 | [Rr]elease/
23 | [Rr]eleases/
24 | x64/
25 | x86/
26 | [Ww][Ii][Nn]32/
27 | [Aa][Rr][Mm]/
28 | [Aa][Rr][Mm]64/
29 | bld/
30 | [Bb]in/
31 | [Oo]bj/
32 | [Oo]ut/
33 | [Ll]og/
34 | [Ll]ogs/
35 |
36 | # Visual Studio 2015/2017 cache/options directory
37 | .vs/
38 | # Uncomment if you have tasks that create the project's static files in wwwroot
39 | #wwwroot/
40 |
41 | # Visual Studio 2017 auto generated files
42 | Generated\ Files/
43 |
44 | # MSTest test Results
45 | [Tt]est[Rr]esult*/
46 | [Bb]uild[Ll]og.*
47 |
48 | # NUnit
49 | *.VisualState.xml
50 | TestResult.xml
51 | nunit-*.xml
52 |
53 | # Build Results of an ATL Project
54 | [Dd]ebugPS/
55 | [Rr]eleasePS/
56 | dlldata.c
57 |
58 | # Benchmark Results
59 | BenchmarkDotNet.Artifacts/
60 |
61 | # .NET Core
62 | project.lock.json
63 | project.fragment.lock.json
64 | artifacts/
65 |
66 | # ASP.NET Scaffolding
67 | ScaffoldingReadMe.txt
68 |
69 | # StyleCop
70 | StyleCopReport.xml
71 |
72 | # Files built by Visual Studio
73 | *_i.c
74 | *_p.c
75 | *_h.h
76 | *.ilk
77 | *.meta
78 | *.obj
79 | *.iobj
80 | *.pch
81 | *.pdb
82 | *.ipdb
83 | *.pgc
84 | *.pgd
85 | *.rsp
86 | *.sbr
87 | *.tlb
88 | *.tli
89 | *.tlh
90 | *.tmp
91 | *.tmp_proj
92 | *_wpftmp.csproj
93 | *.log
94 | *.vspscc
95 | *.vssscc
96 | .builds
97 | *.pidb
98 | *.svclog
99 | *.scc
100 |
101 | # Chutzpah Test files
102 | _Chutzpah*
103 |
104 | # Visual C++ cache files
105 | ipch/
106 | *.aps
107 | *.ncb
108 | *.opendb
109 | *.opensdf
110 | *.sdf
111 | *.cachefile
112 | *.VC.db
113 | *.VC.VC.opendb
114 |
115 | # Visual Studio profiler
116 | *.psess
117 | *.vsp
118 | *.vspx
119 | *.sap
120 |
121 | # Visual Studio Trace Files
122 | *.e2e
123 |
124 | # TFS 2012 Local Workspace
125 | $tf/
126 |
127 | # Guidance Automation Toolkit
128 | *.gpState
129 |
130 | # ReSharper is a .NET coding add-in
131 | _ReSharper*/
132 | *.[Rr]e[Ss]harper
133 | *.DotSettings.user
134 |
135 | # TeamCity is a build add-in
136 | _TeamCity*
137 |
138 | # DotCover is a Code Coverage Tool
139 | *.dotCover
140 |
141 | # AxoCover is a Code Coverage Tool
142 | .axoCover/*
143 | !.axoCover/settings.json
144 |
145 | # Coverlet is a free, cross platform Code Coverage Tool
146 | coverage*.json
147 | coverage*.xml
148 | coverage*.info
149 |
150 | # Visual Studio code coverage results
151 | *.coverage
152 | *.coveragexml
153 |
154 | # NCrunch
155 | _NCrunch_*
156 | .*crunch*.local.xml
157 | nCrunchTemp_*
158 |
159 | # MightyMoose
160 | *.mm.*
161 | AutoTest.Net/
162 |
163 | # Web workbench (sass)
164 | .sass-cache/
165 |
166 | # Installshield output folder
167 | [Ee]xpress/
168 |
169 | # DocProject is a documentation generator add-in
170 | DocProject/buildhelp/
171 | DocProject/Help/*.HxT
172 | DocProject/Help/*.HxC
173 | DocProject/Help/*.hhc
174 | DocProject/Help/*.hhk
175 | DocProject/Help/*.hhp
176 | DocProject/Help/Html2
177 | DocProject/Help/html
178 |
179 | # Click-Once directory
180 | publish/
181 |
182 | # Publish Web Output
183 | *.[Pp]ublish.xml
184 | *.azurePubxml
185 | # Note: Comment the next line if you want to checkin your web deploy settings,
186 | # but database connection strings (with potential passwords) will be unencrypted
187 | *.pubxml
188 | *.publishproj
189 |
190 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
191 | # checkin your Azure Web App publish settings, but sensitive information contained
192 | # in these scripts will be unencrypted
193 | PublishScripts/
194 |
195 | # NuGet Packages
196 | *.nupkg
197 | # NuGet Symbol Packages
198 | *.snupkg
199 | # The packages folder can be ignored because of Package Restore
200 | **/[Pp]ackages/*
201 | # except build/, which is used as an MSBuild target.
202 | !**/[Pp]ackages/build/
203 | # Uncomment if necessary however generally it will be regenerated when needed
204 | #!**/[Pp]ackages/repositories.config
205 | # NuGet v3's project.json files produces more ignorable files
206 | *.nuget.props
207 | *.nuget.targets
208 |
209 | # Microsoft Azure Build Output
210 | csx/
211 | *.build.csdef
212 |
213 | # Microsoft Azure Emulator
214 | ecf/
215 | rcf/
216 |
217 | # Windows Store app package directories and files
218 | AppPackages/
219 | BundleArtifacts/
220 | Package.StoreAssociation.xml
221 | _pkginfo.txt
222 | *.appx
223 | *.appxbundle
224 | *.appxupload
225 |
226 | # Visual Studio cache files
227 | # files ending in .cache can be ignored
228 | *.[Cc]ache
229 | # but keep track of directories ending in .cache
230 | !?*.[Cc]ache/
231 |
232 | # Others
233 | ClientBin/
234 | ~$*
235 | *~
236 | *.dbmdl
237 | *.dbproj.schemaview
238 | *.jfm
239 | *.pfx
240 | *.publishsettings
241 | orleans.codegen.cs
242 |
243 | # Including strong name files can present a security risk
244 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
245 | #*.snk
246 |
247 | # Since there are multiple workflows, uncomment next line to ignore bower_components
248 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
249 | #bower_components/
250 |
251 | # RIA/Silverlight projects
252 | Generated_Code/
253 |
254 | # Backup & report files from converting an old project file
255 | # to a newer Visual Studio version. Backup files are not needed,
256 | # because we have git ;-)
257 | _UpgradeReport_Files/
258 | Backup*/
259 | UpgradeLog*.XML
260 | UpgradeLog*.htm
261 | ServiceFabricBackup/
262 | *.rptproj.bak
263 |
264 | # SQL Server files
265 | *.mdf
266 | *.ldf
267 | *.ndf
268 |
269 | # Business Intelligence projects
270 | *.rdl.data
271 | *.bim.layout
272 | *.bim_*.settings
273 | *.rptproj.rsuser
274 | *- [Bb]ackup.rdl
275 | *- [Bb]ackup ([0-9]).rdl
276 | *- [Bb]ackup ([0-9][0-9]).rdl
277 |
278 | # Microsoft Fakes
279 | FakesAssemblies/
280 |
281 | # GhostDoc plugin setting file
282 | *.GhostDoc.xml
283 |
284 | # Node.js Tools for Visual Studio
285 | .ntvs_analysis.dat
286 | node_modules/
287 |
288 | # Visual Studio 6 build log
289 | *.plg
290 |
291 | # Visual Studio 6 workspace options file
292 | *.opt
293 |
294 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
295 | *.vbw
296 |
297 | # Visual Studio LightSwitch build output
298 | **/*.HTMLClient/GeneratedArtifacts
299 | **/*.DesktopClient/GeneratedArtifacts
300 | **/*.DesktopClient/ModelManifest.xml
301 | **/*.Server/GeneratedArtifacts
302 | **/*.Server/ModelManifest.xml
303 | _Pvt_Extensions
304 |
305 | # Paket dependency manager
306 | .paket/paket.exe
307 | paket-files/
308 |
309 | # FAKE - F# Make
310 | .fake/
311 |
312 | # CodeRush personal settings
313 | .cr/personal
314 |
315 | # Python Tools for Visual Studio (PTVS)
316 | __pycache__/
317 | *.pyc
318 |
319 | # Cake - Uncomment if you are using it
320 | # tools/**
321 | # !tools/packages.config
322 |
323 | # Tabs Studio
324 | *.tss
325 |
326 | # Telerik's JustMock configuration file
327 | *.jmconfig
328 |
329 | # BizTalk build output
330 | *.btp.cs
331 | *.btm.cs
332 | *.odx.cs
333 | *.xsd.cs
334 |
335 | # OpenCover UI analysis results
336 | OpenCover/
337 |
338 | # Azure Stream Analytics local run output
339 | ASALocalRun/
340 |
341 | # MSBuild Binary and Structured Log
342 | *.binlog
343 |
344 | # NVidia Nsight GPU debugger configuration file
345 | *.nvuser
346 |
347 | # MFractors (Xamarin productivity tool) working folder
348 | .mfractor/
349 |
350 | # Local History for Visual Studio
351 | .localhistory/
352 |
353 | # BeatPulse healthcheck temp database
354 | healthchecksdb
355 |
356 | # Backup folder for Package Reference Convert tool in Visual Studio 2017
357 | MigrationBackup/
358 |
359 | # Ionide (cross platform F# VS Code tools) working folder
360 | .ionide/
361 |
362 | # Fody - auto-generated XML schema
363 | FodyWeavers.xsd
--------------------------------------------------------------------------------
/LBH.SerialPortTools/MainWindow.cs:
--------------------------------------------------------------------------------
1 | using Gtk;
2 | using System.IO.Ports;
3 | using System.Text;
4 | using System.Text.Json;
5 | using System.Text.Json.Serialization;
6 |
7 | namespace LBH.SerialPortTools
8 | {
9 | public class MainWindow:Window
10 | {
11 | //操作面板
12 | private VBox layout = new VBox();
13 | private HBox mainBox=new HBox();
14 | private SerialPort mSerialPort;
15 | private Thread receiveThread;
16 | private Statusbar statusbar;
17 | private int sendByteCount,recevieByteCount=0;
18 | private string cacheFile=System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory,"sp.cache");
19 |
20 | JsonSerializerOptions options = new JsonSerializerOptions {
21 | ReferenceHandler= ReferenceHandler.IgnoreCycles
22 | };
23 | public MainWindow():base(WindowType.Toplevel)
24 | {
25 | this.Build();
26 | InitHistoryConfig();
27 | }
28 |
29 |
30 | #region 状态栏
31 | void UpdateStatus()
32 | {
33 | string comState = "COM Closed";
34 | if (mSerialPort != null && mSerialPort.IsOpen)
35 | {
36 | comState = $"{mSerialPort.PortName} Opened";
37 | }
38 | string status = string.Format(" STATUS: : {0} S: {1} R:{2}", comState, sendByteCount, recevieByteCount);
39 | statusbar.Pop (0);
40 | statusbar.Push (0, status);
41 | }
42 | #endregion
43 |
44 | #region 变量声明
45 | //串口设置
46 | private ComboBox comboCom;
47 | //波特率
48 | private ComboBox comboRate;
49 | //停止位
50 | private ComboBox comboStop;
51 | //数据位
52 | private ComboBox comboData;
53 | //奇偶校验
54 | private ComboBox comboJo;
55 | //16进制显示
56 | private CheckButton ck16;
57 | //RTX设置
58 | private CheckButton ckRtx;
59 | //DTR设置
60 | private CheckButton ckDtr;
61 | //16进制发送
62 | private CheckButton ckS16;
63 | //新行发送
64 | private CheckButton ckNewline;
65 | //定时发送
66 | private CheckButton ckTime;
67 | //定时毫秒
68 | private Entry txtTime;
69 | //打开串口按钮
70 | private Button btnSp;
71 | //清除接受按钮
72 | private Button btnClear;
73 | //数据接收区
74 | private TextView textResult;
75 | //数据发送区域
76 | private TextView textSend;
77 | //发送数据
78 | private Button btnSend;
79 | //清除数据
80 | private Button btnSendClear;
81 | //定时器ID
82 | private uint timerSendId = 0;
83 |
84 | private Gdk.Pixbuf img;
85 | private Gtk.Image imgRender;
86 | #endregion
87 |
88 | #region 界面布局
89 | private void Build()
90 | {
91 | this.WindowPosition = WindowPosition.Center;
92 | this.SetDefaultSize(600, 600);
93 | this.Title = "串口调试助手v1.0.00";
94 | this.Resizable = false;
95 | this.DeleteEvent += MainWindow_DeleteEvent;
96 | this.Add(layout);
97 |
98 | //统计数据
99 | statusbar = new Statusbar();
100 | statusbar.Visible = true;
101 | UpdateStatus();
102 |
103 | layout.PackStart(mainBox, false, false, 0);
104 | layout.PackEnd(statusbar, false, false, 0);
105 | this.BuildLayout();
106 | this.ShowAll();
107 | }
108 | private void BuildLayout()
109 | {
110 | #region 两列布局
111 | VBox leftBox = new VBox();
112 | leftBox.Margin = 5;
113 | leftBox.SetSizeRequest(200, 400);
114 | this.mainBox.Add(leftBox);
115 |
116 | VBox rightBox = new VBox();
117 | rightBox.SetSizeRequest(400, 600);
118 | rightBox.Margin = 5;
119 | this.mainBox.Add(rightBox);
120 | #endregion
121 |
122 | #region 串口操作
123 | Frame frame1 = new Frame("串口设置");
124 | ((Gtk.Label)frame1.LabelWidget).UseMarkup = true;
125 | leftBox.PackStart(frame1, true, true, 5);
126 |
127 | VBox vfram = new VBox();
128 | frame1.Add(vfram);
129 | //串口设置
130 | HBox hCom = new HBox();
131 | var lblCom = new Label { Text = "串口号:" };
132 | lblCom.UseMarkup = true;
133 | lblCom.Halign = Align.Start;
134 | comboCom = new ComboBox();
135 |
136 | comboCom.SetSizeRequest(150, 30);
137 | hCom.PackStart(lblCom, true, true, 5);
138 | hCom.PackStart(comboCom, true, true, 5);
139 |
140 | vfram.PackStart(hCom, false, false, 2);
141 | //波特率
142 | HBox hRate = new HBox();
143 | var lblRate = new Label { Text = "波特率:" };
144 | lblRate.Halign = Align.Start;
145 | comboRate = new ComboBox();
146 | comboRate.SetSizeRequest(150, 30);
147 | hRate.PackStart(lblRate, true, true, 5);
148 | hRate.PackStart(comboRate, true, true, 5);
149 |
150 | vfram.PackStart(hRate, false, false, 2);
151 |
152 |
153 | //停止位
154 | HBox hStop = new HBox();
155 | var lblStop = new Label { Text = "停止位:" };
156 | lblStop.Halign = Align.Start;
157 | comboStop = new ComboBox();
158 | comboStop.SetSizeRequest(150, 30);
159 | hStop.PackStart(lblStop, true, true, 5);
160 | hStop.PackStart(comboStop, true, true, 5);
161 |
162 | vfram.PackStart(hStop, false, false, 2);
163 |
164 |
165 | //数据位
166 | HBox hData = new HBox();
167 | var lblData = new Label { Text = "数据位:" };
168 | lblData.Halign = Align.Start;
169 | comboData = new ComboBox();
170 | comboData.SetSizeRequest(150, 30);
171 | hData.PackStart(lblData, true, true, 5);
172 | hData.PackStart(comboData, true, true, 5);
173 |
174 | vfram.PackStart(hData, false, false, 2);
175 |
176 |
177 | //奇偶校验
178 | HBox hJo = new HBox();
179 | var lblJo = new Label { Text = "校验位:" };
180 | lblJo.Halign = Align.Start;
181 | comboJo = new ComboBox();
182 | comboJo.SetSizeRequest(150, 30);
183 | hJo.PackStart(lblJo, true, true, 5);
184 | hJo.PackStart(comboJo, true, true, 5);
185 |
186 | vfram.PackStart(hJo, false, false, 2);
187 |
188 | //打开/关闭串口
189 | HBox co = new HBox();
190 | btnSp = new Button { Label = "打开串口" };
191 | btnSp.Name = "btnSp";
192 | btnSp.AlwaysShowImage = true;
193 | btnSp.Image = Image.NewFromIconName("window-new", IconSize.Button);
194 | btnSp.Clicked += BtnSp_Clicked;
195 | btnSp.SetSizeRequest(110, 30);
196 | co.PackStart(btnSp, true, true, 5);
197 |
198 | vfram.PackStart(co, false, false, 2);
199 | //开关表示图
200 | HBox imgSwitch = new HBox();
201 | img = new Gdk.Pixbuf("2.png",30,30);
202 | imgRender =new Image(img);
203 | imgRender.Margin = 5;
204 | imgSwitch.PackStart(imgRender, true, true, 5);
205 |
206 | vfram.PackStart(imgSwitch, false, false, 2);
207 | #endregion
208 |
209 | #region 接受设置
210 | Frame frame2 = new Frame("接收设置");
211 | ((Gtk.Label)frame2.LabelWidget).UseMarkup = true;
212 | leftBox.PackStart(frame2, true, true, 5);
213 |
214 | VBox f2vbox=new VBox();
215 | f2vbox.Margin = 5;
216 |
217 | btnClear = new Button { Label = "清空接收区" };
218 | btnClear.Clicked += BtnClear_Clicked;
219 | f2vbox.PackStart(btnClear, false, false, 5);
220 |
221 | ck16 = new CheckButton { Label = "16进制显示" };
222 | f2vbox.PackStart(ck16, false, false, 5);
223 | ck16.Toggled += new EventHandler(ck16Toggled);
224 |
225 | ckRtx = new CheckButton { Label = "RTX设置" };
226 | f2vbox.PackStart(ckRtx, false, false, 5);
227 | ckRtx.Toggled += new EventHandler(ckRtxToggled);
228 |
229 |
230 | ckDtr = new CheckButton { Label = "DTR设置" };
231 | f2vbox.PackStart(ckDtr, false, false, 5);
232 | ckDtr.Toggled += new EventHandler(ckDtrToggled);
233 |
234 | frame2.Add(f2vbox);
235 | #endregion
236 |
237 | #region 发送设置
238 | Frame frame3 = new Frame("发送设置");
239 | ((Gtk.Label)frame3.LabelWidget).UseMarkup = true;
240 | leftBox.PackStart(frame3, true, true, 5);
241 |
242 | VBox f3vbox = new VBox();
243 | f3vbox.Margin = 5;
244 |
245 | ckS16 = new CheckButton { Label = "16进制发送" };
246 | f3vbox.PackStart(ckS16, false, false, 5);
247 |
248 | ckNewline = new CheckButton { Label = "发送新行" };
249 | f3vbox.PackStart(ckNewline, false, false, 5);
250 |
251 | HBox timeBox = new HBox();
252 | ckTime = new CheckButton { Label = "定时发送" };
253 | timeBox.PackStart(ckTime, false, false,0);
254 | ckTime.Toggled += new EventHandler(ckTimeToggled);
255 |
256 | txtTime =new Entry();
257 | txtTime.SetSizeRequest(100, 20);
258 | txtTime.Buffer.Text = "1000";
259 | timeBox.PackStart(txtTime, false, false, 5);
260 |
261 | Label lblTime=new Label { Text="ms"};
262 | timeBox.PackStart(lblTime, false, false, 5);
263 | f3vbox.PackStart(timeBox, false, false, 5);
264 |
265 | frame3.Add(f3vbox);
266 |
267 | #endregion
268 |
269 | #region 数据接收区
270 | Frame frame4 = new Frame("数据显示");
271 | ((Gtk.Label)frame4.LabelWidget).UseMarkup = true;
272 | rightBox.PackStart(frame4, true, true, 5);
273 | var scrollResult = new ScrolledWindow();
274 | scrollResult.SetSizeRequest(360, 500);
275 | //数据接收区
276 | textResult =new TextView();
277 | textResult.Name = "textResult";
278 | textResult.Margin = 2;
279 | textResult.Editable = false;
280 | textResult.WrapMode = WrapMode.WordChar;
281 | textResult.SizeAllocated += TextView_SizeAllocated;
282 | scrollResult.Child = textResult;
283 | frame4.Add(scrollResult);
284 | #endregion
285 |
286 | #region 数据发送区
287 | Frame frame5 = new Frame("数据发送");
288 | ((Gtk.Label)frame5.LabelWidget).UseMarkup = true;
289 | rightBox.PackStart(frame5, true, true, 5);
290 |
291 | var scrollSend = new ScrolledWindow();
292 | scrollSend.SetSizeRequest(360, 100);
293 | textSend = new TextView();
294 | textSend.Margin = 2;
295 | textSend.WrapMode = WrapMode.WordChar;
296 | scrollSend.Child = textSend;
297 | frame5.Add(scrollSend);
298 |
299 | HBox hSendOpt=new HBox();
300 | hSendOpt.Margin = 2;
301 | btnSend = new Button { Label = "发送数据" };
302 | btnSend.Clicked += BtnSend_Clicked;
303 | btnSendClear = new Button { Label = "清除发送" };
304 | btnSendClear.Clicked += BtnSendClear_Clicked;
305 | btnSend.SetSizeRequest(100, 30);
306 | btnSendClear.SetSizeRequest(100, 30);
307 | hSendOpt.PackStart(btnSend, false, false, 5);
308 | hSendOpt.PackStart(btnSendClear, false, false, 5);
309 | rightBox.PackStart(hSendOpt, false, false, 5);
310 |
311 | #endregion
312 | }
313 | #endregion
314 |
315 | #region 加载上次配置
316 | private void InitHistoryConfig()
317 | {
318 | var config = LoadCache();
319 | FillCombo(config,comboCom, "com", SerialPort.GetPortNames());
320 | FillCombo(config, comboRate, "rate", Utils.baudRateList);
321 | FillCombo(config, comboStop, "stop", Utils.stopBitList);
322 | FillCombo(config, comboData, "data", Utils.dataBitList);
323 | FillCombo(config, comboJo, "pty", Utils.parityList);
324 | }
325 | private void InitCache(ComConfig config)
326 | {
327 | try
328 | {
329 | if (File.Exists(cacheFile)) File.Delete(cacheFile);
330 | var cacheString = JsonSerializer.Serialize(config, options);
331 | byte[] bs = Encoding.Default.GetBytes(cacheString);
332 | using (Stream fileStream = new FileStream(cacheFile, FileMode.OpenOrCreate))
333 | {
334 | fileStream.Write(bs, 0, bs.Length);
335 | }
336 | }
337 | catch (Exception)
338 | {
339 |
340 | }
341 | }
342 | private ComConfig LoadCache()
343 | {
344 | ComConfig config = null;
345 | if (File.Exists(cacheFile))
346 | {
347 | try
348 | {
349 | using (Stream fileStream = new FileStream(cacheFile, FileMode.Open))
350 | {
351 | var bs = new byte[fileStream.Length];
352 | fileStream.Read(bs, 0, bs.Length);
353 | var tmp = Encoding.Default.GetString(bs);
354 | config = JsonSerializer.Deserialize(tmp);
355 | }
356 | }
357 | catch (Exception)
358 | {
359 |
360 | }
361 | }
362 | return config;
363 | }
364 | private void FillCombo(ComConfig config, Gtk.ComboBox cb, string type, string[] ctextList)
365 | {
366 | cb.Clear();
367 | CellRendererText cell = new CellRendererText();
368 | cb.PackStart(cell, false);
369 | cb.AddAttribute(cell, "text", 0);
370 | ListStore store = new ListStore(typeof(string));
371 | cb.Model = store;
372 |
373 | int index = 0;
374 | for (int i = 0; i < ctextList.Length; i++)
375 | {
376 | store.AppendValues(ctextList[i]);
377 | if (config != null && type == "com" && ctextList[i] == config.PortName)
378 | {
379 | index = i;
380 | }
381 | if (config != null && type == "rate" && ctextList[i] == config.BaudRate)
382 | {
383 | index = i;
384 | }
385 | if (config != null && type == "data" && ctextList[i] == config.DataBit)
386 | {
387 | index = i;
388 | }
389 | if (config != null && type == "stop" && ctextList[i] == config.StopBit)
390 | {
391 | index = i;
392 | }
393 | if (config != null && type == "pty" && ctextList[i] == config.Parity)
394 | {
395 | index = i;
396 | }
397 | }
398 | cb.Active = index;
399 | }
400 |
401 | #endregion
402 |
403 | #region 事件处理
404 |
405 | private void MainWindow_DeleteEvent(object o, DeleteEventArgs args)
406 | {
407 | Application.Quit();
408 | }
409 |
410 | private void TextView_SizeAllocated(object o, SizeAllocatedArgs args)
411 | {
412 | textResult.ScrollToIter(textResult.Buffer.EndIter, 0, false, 0, 0);
413 | }
414 |
415 | private void ckTimeToggled(object? sender, EventArgs e)
416 | {
417 | if (ckTime.Active)
418 | {
419 | if (mSerialPort == null || !mSerialPort.IsOpen)
420 | {
421 | MessageBox.Show("尚未打开串口,请打开后再发送", "提示",parent:this);
422 | ckTime.Active = false;
423 | return;
424 | }
425 |
426 | if (!uint.TryParse(txtTime.Buffer.Text, out uint sp))
427 | {
428 | MessageBox.Show("周期格式不正确", "错误", parent: this);
429 | ckTime.Active = false;
430 | return;
431 | }
432 | txtTime.SetProperty("editable",new GLib.Value(false));
433 |
434 | timerSendId = GLib.Timeout.Add(sp, () =>
435 | {
436 | sendData();
437 | return true;
438 | });
439 | }
440 | else
441 | {
442 | if (timerSendId == 0)
443 | return;
444 | txtTime.SetProperty("editable", new GLib.Value(true));
445 | GLib.Timeout.Remove(timerSendId);
446 | }
447 | }
448 |
449 | private void BtnSendClear_Clicked(object? sender, EventArgs e)
450 | {
451 | textSend.Buffer.Text = "";
452 | sendByteCount = 0;
453 | UpdateStatus();
454 | }
455 |
456 | private void BtnSend_Clicked(object? sender, EventArgs e)
457 | {
458 | sendData();
459 | }
460 |
461 | private void sendData()
462 | {
463 | if (mSerialPort != null && mSerialPort.IsOpen)
464 | {
465 | if ((ckNewline.Active) && (ckS16.Active))
466 | {
467 | //发送16进制新行
468 | byte[] bytes = Utils.convertHexStringToBytes(textSend.Buffer.Text);
469 | if (bytes != null)
470 | {
471 | mSerialPort.Write(bytes, 0, bytes.Length);
472 | byte[] newLine = { 0xd, 0xa };
473 | mSerialPort.Write(newLine, 0, newLine.Length);
474 | sendByteCount += (bytes.Length + newLine.Length);
475 | }
476 | }
477 | else if ((ckNewline.Active) && (!ckS16.Active))
478 | {
479 | //发送普通字符串新行
480 | mSerialPort.Write(textSend.Buffer.Text + "\r\n");
481 | sendByteCount += (textSend.Buffer.Text.Length + 2);
482 | }
483 | else if ((!ckNewline.Active) && (!ckS16.Active))
484 | {
485 | //发送普通字符串
486 | mSerialPort.Write(textSend.Buffer.Text);
487 | sendByteCount += textSend.Buffer.Text.Length;
488 | }
489 | else if ((!ckNewline.Active) && (ckS16.Active))
490 | {
491 | //发送16进制
492 | byte[] bytes = Utils.convertHexStringToBytes(textSend.Buffer.Text);
493 | if (bytes != null)
494 | {
495 | mSerialPort.Write(bytes, 0, bytes.Length);
496 | sendByteCount += bytes.Length;
497 | }
498 |
499 | }
500 | else
501 | {
502 | MessageBox.Show("不支持的操作!","错误", parent: this);
503 | }
504 | UpdateStatus();
505 | }
506 | else
507 | {
508 | MessageBox.Show("尚未打开串口,请打开后再发送","提示", parent: this);
509 | }
510 | }
511 |
512 | private void BtnClear_Clicked(object? sender, EventArgs e)
513 | {
514 | this.textResult.Buffer.Clear();
515 | recevieByteCount = 0;
516 | UpdateStatus();
517 | }
518 |
519 | private void BtnSp_Clicked(object? sender, EventArgs e)
520 | {
521 | if (mSerialPort != null)
522 | {
523 | if (mSerialPort.IsOpen)
524 | {
525 | try
526 | {
527 | mSerialPort.Close();
528 | btnSp.Label = "打开串口";
529 | //定时发送取消勾选
530 | ckTime.Active = false;
531 | //更改按钮样式
532 | btnSp.StyleContext.RemoveClass("btnRed");
533 | btnSp.Image = Image.NewFromIconName("window-new", IconSize.Button);
534 | img = new Gdk.Pixbuf("2.png", 30, 30);
535 | this.imgRender.Pixbuf = img;
536 | //改为可用状态
537 | ChangeComConfigStatus(true);
538 | }
539 | catch (Exception)
540 | {
541 | MessageBox.Show("串口关闭失败", "错误", parent: this);
542 | }
543 | }
544 | else
545 | {
546 | OpenSerialPort();
547 | }
548 | }
549 | else
550 | {
551 | OpenSerialPort();
552 | }
553 | UpdateStatus();
554 | }
555 |
556 | private void OpenSerialPort()
557 | {
558 | if (comboCom.Active == -1)
559 | {
560 | MessageBox.Show("请先选择串口", "提示", parent: this); return;
561 | }
562 | if (comboRate.Active == -1)
563 | {
564 | MessageBox.Show("请设置波特率", "提示", parent: this); return;
565 | }
566 | if (comboData.Active == -1)
567 | {
568 | MessageBox.Show("请设置数据位", "提示", parent: this); return;
569 | }
570 | if (comboJo.Active == -1)
571 | {
572 | MessageBox.Show("请设置奇偶校验位", "提示", parent: this); return;
573 | }
574 | if (comboStop.Active == -1)
575 | {
576 | MessageBox.Show("请设置停止位", "提示", parent: this); return;
577 | }
578 | string com = GetComboxText(comboCom);
579 | int rate = int.Parse(GetComboxText(comboRate));
580 | int dataBits = int.Parse(GetComboxText(comboData));
581 | Parity parity = (Parity)Enum.Parse(typeof(Parity), GetComboxText(comboJo));
582 | StopBits stopBits = (StopBits)Enum.Parse(typeof(StopBits), GetComboxText(comboStop), true);
583 |
584 | var config = new ComConfig
585 | {
586 | PortName = com,
587 | BaudRate = GetComboxText(comboRate),
588 | DataBit = GetComboxText(comboData),
589 | Parity = GetComboxText(comboJo),
590 | StopBit = GetComboxText(comboStop)
591 | };
592 |
593 | InitCache(config);
594 |
595 | if (stopBits != StopBits.None)
596 | {
597 | mSerialPort = new SerialPort(com, rate, parity, dataBits, stopBits);
598 | }
599 | else
600 | {
601 | mSerialPort = new SerialPort(com, rate, parity, dataBits);
602 | }
603 |
604 | if (!mSerialPort.IsOpen)
605 | {
606 | try
607 | {
608 | mSerialPort.Open();
609 | btnSp.Label = "关闭串口";
610 | //更改按钮样式
611 | btnSp.StyleContext.AddClass("btnRed");
612 | btnSp.Image = Image.NewFromIconName("window-close", IconSize.Button);
613 | img = new Gdk.Pixbuf("1.png", 30, 30);
614 | this.imgRender.Pixbuf = img;
615 | ChangeComConfigStatus(false);
616 | }
617 | catch (Exception ex)
618 | {
619 | MessageBox.Show($"打开串口失败:{ex.ToString()}", "提示", parent: this);
620 | return;
621 | }
622 |
623 | }
624 | mSerialPort.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
625 | }
626 |
627 | private void ChangeComConfigStatus(bool state)
628 | {
629 | comboCom.Sensitive = state;
630 | comboData.Sensitive = state;
631 | comboJo.Sensitive = state;
632 | comboRate.Sensitive = state;
633 | comboStop.Sensitive = state;
634 | }
635 |
636 | private void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
637 | {
638 | SerialPort sp = (SerialPort)sender;
639 | string indata = sp.ReadExisting();
640 | receiveThread = new Thread(new ParameterizedThreadStart(displayReceiveData));
641 | receiveThread.Start(indata);
642 | }
643 |
644 | private void displayReceiveData(object? obj)
645 | {
646 | if (ck16.Active)
647 | {
648 | AppendText(textResult, Utils.convertToHexString((string)obj));
649 | recevieByteCount = (textResult.Buffer.Text.Length + 1) / 3;
650 | }
651 | else
652 | {
653 | AppendText(textResult, (string)obj);
654 | recevieByteCount = textResult.Buffer.Text.Length;
655 | }
656 | UpdateStatus();
657 | }
658 |
659 | #endregion
660 |
661 | #region 接收设置
662 | private void ckDtrToggled(object? sender, EventArgs e)
663 | {
664 | if (mSerialPort != null)
665 | {
666 | if (ckDtr.Active)
667 | {
668 | mSerialPort.DtrEnable = true;
669 | }
670 | else
671 | {
672 | mSerialPort.DtrEnable = false;
673 | }
674 | }
675 | }
676 |
677 | private void ckRtxToggled(object? sender, EventArgs e)
678 | {
679 | if (mSerialPort != null)
680 | {
681 | if (ckRtx.Active)
682 | {
683 | mSerialPort.RtsEnable = true;
684 | }
685 | else
686 | {
687 | mSerialPort.RtsEnable = false;
688 | }
689 | }
690 | }
691 |
692 | private void ck16Toggled(object? sender, EventArgs e)
693 | {
694 | if (ck16.Active)
695 | {
696 | textResult.Buffer.Text = Utils.convertToHexString(textResult.Buffer.Text);
697 | }
698 | else
699 | {
700 | textResult.Buffer.Text = Utils.convertHexStringToCommonString(textResult.Buffer.Text);
701 | }
702 | }
703 | #endregion
704 |
705 | #region 辅助方法
706 | private string GetComboxText(ComboBox comboBox)
707 | {
708 | TreeIter tree;
709 | comboBox.GetActiveIter(out tree);
710 | String selectedText = (String)comboBox.Model.GetValue(tree, 0);
711 | return selectedText;
712 | }
713 | private void AppendText(TextView textView, string text)
714 | {
715 | var buffer = textView.Buffer;
716 | TextIter insertIter = buffer.EndIter;
717 | buffer.Insert(ref insertIter, text);
718 | }
719 | #endregion
720 | }
721 | }
722 |
--------------------------------------------------------------------------------