aboutsummaryrefslogtreecommitdiffstats
path: root/Software/Visual_Studio
diff options
context:
space:
mode:
authorRoy Ben Shabat <Roy.mail.net@gmail.com>2020-08-12 02:12:46 +0300
committerRoy Ben Shabat <Roy.mail.net@gmail.com>2020-08-12 02:12:46 +0300
commitbd72c7efe687dfaca6d4fd3c0fc2b5a39d57df55 (patch)
tree0d238b22049d445b40a25ca95f5fef2a1c725bbd /Software/Visual_Studio
parent3cfc4ee06b801e581107f24e691451cd291b6a70 (diff)
downloadTango-bd72c7efe687dfaca6d4fd3c0fc2b5a39d57df55.tar.gz
Tango-bd72c7efe687dfaca6d4fd3c0fc2b5a39d57df55.zip
More work on proc_doc.
Fixed app crash on code editor selection.
Diffstat (limited to 'Software/Visual_Studio')
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures.Documentation/Content/Welcome.aml146
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures.Documentation/Help/proc-doc.chmbin165989 -> 204761 bytes
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures.Documentation/Tango.FSE.Procedures.Documentation.shfbproj25
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/ArrayParsingStyle.cs10
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Examples/AddResult/Program.cs91
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Examples/Connection/Program.cs38
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Examples/Csv/Program.cs63
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Examples/Diagnostics/Program.cs44
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Examples/RequestUserInputFor/Program.cs48
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Examples/Sql/Program.cs78
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/IProcedureContext.cs123
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/SqlResult.cs19
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Tango.FSE.Procedures.csproj11
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Themes/Generic.xaml6
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/UserInput.cs11
-rw-r--r--Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Views/ProcedureDesignerView.xaml.cs1
-rw-r--r--Software/Visual_Studio/FSE/Tango.FSE.Common/Diagnostics/DiagnosticsPackage.cs35
-rw-r--r--Software/Visual_Studio/FSE/Tango.FSE.Common/SQL/IRemoteSqlProvider.cs22
-rw-r--r--Software/Visual_Studio/FSE/Tango.FSE.Common/SQL/RemoteSqlCommand.cs25
-rw-r--r--Software/Visual_Studio/FSE/Tango.FSE.Common/SQL/RemoteSqlCommandMode.cs10
-rw-r--r--Software/Visual_Studio/FSE/Tango.FSE.Common/SQL/RemoteSqlCommandResult.cs50
-rw-r--r--Software/Visual_Studio/FSE/Tango.FSE.UI/SQL/DefaultRemoteSqlProvider.cs2
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Shared/SQL/RemoteSqlColumn.cs28
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Shared/SQL/RemoteSqlColumnCollection.cs31
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Shared/SQL/RemoteSqlDataSet.cs35
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Shared/SQL/RemoteSqlRow.cs55
-rw-r--r--Software/Visual_Studio/PPC/Tango.PPC.Shared/Tango.PPC.Shared.csproj4
-rw-r--r--Software/Visual_Studio/Scripting/Tango.Scripting.Editors/TextEditor.cs2343
28 files changed, 2140 insertions, 1214 deletions
diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures.Documentation/Content/Welcome.aml b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures.Documentation/Content/Welcome.aml
index 29e0c47c7..eb51fa37f 100644
--- a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures.Documentation/Content/Welcome.aml
+++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures.Documentation/Content/Welcome.aml
@@ -1,59 +1,111 @@
<?xml version="1.0" encoding="utf-8"?>
<topic id="0f61f11a-7ea4-4d45-b34a-1f31a2c77a5a" revisionNumber="1">
<developerConceptualDocument xmlns="http://ddue.schemas.microsoft.com/authoring/2003/5" xmlns:xlink="http://www.w3.org/1999/xlink">
- <introduction>
- <para>
-This is a sample conceptual topic. You can use this as a starting point for adding more conceptual
-content to your help project.
-<codeEntityReference>T:Tango.FSE.Procedures.IProcedureContext</codeEntityReference>
-Success!
- </para>
- </introduction>
+
+ <section>
+ <title>Welcome</title>
+ <content>
+ <para>Welcome to the Tango FSE Procedures API documentation.</para>
+ <para>This document contains a detailed description of each part of the API with some examples.</para>
- <section>
- <title>Getting Started</title>
- <content>
- <para>To get started, add a documentation source to the project (a Visual Studio solution, project, or
-assembly and XML comments file). See the <legacyBold>Getting Started</legacyBold> topics in the Sandcastle Help
-File Builder's help file for more information. The following default items are included in this project:</para>
+ <para>
+ The best way to start exploring the main capabilities of a procedure is the <codeEntityReference>T:Tango.FSE.Procedures.IProcedureContext</codeEntityReference> interface.
+ </para>
+ </content>
+ </section>
- <list class="bullet">
- <listItem>
- <para><localUri>ContentLayout.content</localUri> - Use the content layout file to manage the
-conceptual content in the project and define its layout in the table of contents.</para>
- </listItem>
+ <section>
+ <title>What is a Procedure ?</title>
+ <content>
+ <list class="bullet">
+ <listItem>
+ <para>
+ A procedure is a sequence of a pre-programmed actions/commands.
+ </para>
+ </listItem>
- <listItem>
- <para>The <localUri>.\media</localUri> folder - Place images in this folder that you will reference
-from conceptual content using <codeInline>medialLink</codeInline> or <codeInline>mediaLinkInline</codeInline>
-elements. If you will not have any images in the file, you may remove this folder.</para>
- </listItem>
+ <listItem>
+ <para>
+ A procedure can have one or more user input parameters.
+ </para>
+ </listItem>
- <listItem>
- <para>The <localUri>.\icons</localUri> folder - This contains a default logo for the help file. You
-may replace it or remove it and the folder if not wanted. If removed or if you change the file name, update
-the <ui>Transform Args</ui> project properties page by removing or changing the filename in the
-<codeInline>logoFile</codeInline> transform argument. Note that unlike images referenced from conceptual topics,
-the logo file should have its <legacyBold>BuildAction</legacyBold> property set to <codeInline>Content</codeInline>.</para>
- </listItem>
+ <listItem>
+ <para>
+ A procedure can have one or more output values.
+ </para>
+ </listItem>
+ </list>
- <listItem>
- <para>The <localUri>.\Content</localUri> folder - Use this to store your conceptual topics. You may
-name the files and organize them however you like. One suggestion is to lay the files out on disk as you have
-them in the content layout file as shown in this project but the choice is yours. Files can be added via the
-Solution Explorer or from within the content layout file editor. Files must appear in the content layout file
-in order to be compiled into the help file.</para>
- </listItem>
- </list>
+ <para>
+ Procedures are not legacy “stubs”. They are a completely different creature.
+ <lineBreak/>
+ Procedures are a way for providing immediate and dynamic response for almost any scenario or issue that is being raised by customers/technicians.
+ <lineBreak/>
+ Procedure programmers can leverage an intuitive and extensive API and accomplish almost any requirement.
+ <lineBreak/>
+ Programming and managing Procedures is done through the “Procedure Designer” module.
+ </para>
+ </content>
+ </section>
- <para>See the <legacyBold>Conceptual Content</legacyBold> topics in the Sandcastle Help File Builder's
-help file for more information. See the <legacyBold> Sandcastle MAML Guide</legacyBold> for details on Microsoft
-Assistance Markup Language (MAML) which is used to create these topics.</para>
- </content>
- </section>
+ <section>
+ <title>The Procedure Designer</title>
+ <content>
+ <para>
+ The procedure designer module can be seen as a fully fledged IDE (Integrated Development Environment), designed to provide the best development experience for the “procedure programmer”.
+ </para>
+ <para>
+ The procedure designer is basically a project editor. In the technical sense, a “Procedure” is basically a project.
+ </para>
+ <para>
+ A project can be composed of one or more script files that are automatically linked together.
+ </para>
+ <para>
+ A project, is represented as a single file (.pproj) and is designed to encapsulate all the files and information about the project.
+ </para>
+ <para>
+ A project can have multiple assembly references and make use of them. An assembly reference can be any DLL file from the entire .NET framework, or from Twine’s libraries.
+ </para>
+ <para>
+ A “Procedure” in the “Procedures Module” is actually a single published procedure project.
+ <lineBreak/>
+ A published procedure is a project that is saved on Twine’s global database (per environment).
+ <lineBreak/>
+ Each published project has a name, description and version, while preserving the history of the last 10 versions.
+ <lineBreak/>
+ When the user navigates to the Procedures Module, Tango FSE will retrieves all published projects from the server.
+ </para>
+ <para>
+ Accessing the procedure designer module requires the “Run Procedure Designer” permission.
+ <lineBreak/>
+ Publishing a procedure project requires the “Publish Procedure Projects” permission.
+ </para>
- <relatedTopics>
- <codeEntityReference>T:Tango.FSE.Procedures.IProcedureContext</codeEntityReference>
- </relatedTopics>
+ <legacyBold>
+ <legacyUnderline>
+ Summery:
+ </legacyUnderline>
+ </legacyBold>
+
+ <list class="bullet">
+ <listItem>
+ <para>
+ Published procedure projects are intended for all users and can be executed using the Procedures Module.
+ </para>
+ </listItem>
+
+ <listItem>
+ <para>
+ Procedure Project files are intended for Twine’s programmers and technicians and can be executed/edited using the Procedure Designer.
+ </para>
+ </listItem>
+ </list>
+
+ <para>
+ We can say that once a procedure project that is being designed for end-users, is well-tested and optimized, it can be published and be available as a “Procedure” for end-users.
+ </para>
+ </content>
+ </section>
</developerConceptualDocument>
</topic>
diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures.Documentation/Help/proc-doc.chm b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures.Documentation/Help/proc-doc.chm
index 24e7db2e2..3ac22ce67 100644
--- a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures.Documentation/Help/proc-doc.chm
+++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures.Documentation/Help/proc-doc.chm
Binary files differ
diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures.Documentation/Tango.FSE.Procedures.Documentation.shfbproj b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures.Documentation/Tango.FSE.Procedures.Documentation.shfbproj
index 78ae89b52..16251df8f 100644
--- a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures.Documentation/Tango.FSE.Procedures.Documentation.shfbproj
+++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures.Documentation/Tango.FSE.Procedures.Documentation.shfbproj
@@ -33,6 +33,7 @@
<DocumentationSources>
<DocumentationSource sourceFile="..\Tango.FSE.Procedures\Tango.FSE.Procedures.csproj" />
<DocumentationSource sourceFile="..\..\Tango.FSE.Common\Tango.FSE.Common.csproj" />
+ <DocumentationSource sourceFile="..\..\..\PPC\Tango.PPC.Shared\Tango.PPC.Shared.csproj" />
</DocumentationSources>
<HelpFileFormat>HtmlHelp1</HelpFileFormat>
<SyntaxFilters>C#</SyntaxFilters>
@@ -102,7 +103,6 @@
<Filter entryType="Namespace" fullName="Tango.FSE.Common.WindowsManager" isExposed="False" />
<Filter entryType="Namespace" fullName="Tango.FSE.Procedures" isExposed="False">
<Filter entryType="Enumeration" fullName="Tango.FSE.Procedures.ArrayParsingStyle" filterName="ArrayParsingStyle" isExposed="True" />
- <Filter entryType="Class" fullName="Tango.FSE.Procedures.DialogController" filterName="DialogController" isExposed="True" />
<Filter entryType="Interface" fullName="Tango.FSE.Procedures.IDialogController" filterName="IDialogController" isExposed="True" />
<Filter entryType="Interface" fullName="Tango.FSE.Procedures.IProcedureContext" filterName="IProcedureContext" isExposed="True">
<Filter entryType="Method" fullName="Tango.FSE.Procedures.IProcedureContext.BreakPoint" filterName="BreakPoint" isExposed="False" />
@@ -110,13 +110,13 @@
<Filter entryType="Event" fullName="Tango.FSE.Procedures.IProcedureContext.BreakPointRequest" filterName="BreakPointRequest" isExposed="False" />
<Filter entryType="Event" fullName="Tango.FSE.Procedures.IProcedureContext.Progress" filterName="Progress" isExposed="False" />
</Filter>
- <Filter entryType="Class" fullName="Tango.FSE.Procedures.ProcedureFailedException" filterName="ProcedureFailedException" isExposed="True" />
<Filter entryType="Class" fullName="Tango.FSE.Procedures.Result" filterName="Result" isExposed="True">
<Filter entryType="Property" fullName="Tango.FSE.Procedures.Result.IsBitmap" filterName="IsBitmap" isExposed="False" />
<Filter entryType="Property" fullName="Tango.FSE.Procedures.Result.IsGraph" filterName="IsGraph" isExposed="False" />
<Filter entryType="Property" fullName="Tango.FSE.Procedures.Result.IsValueArray" filterName="IsValueArray" isExposed="False" />
</Filter>
<Filter entryType="Enumeration" fullName="Tango.FSE.Procedures.ResultType" filterName="ResultType" isExposed="True" />
+ <Filter entryType="Class" fullName="Tango.FSE.Procedures.UserInput" filterName="UserInput" isExposed="True" />
</Filter>
<Filter entryType="Namespace" fullName="Tango.FSE.Procedures.Contracts" isExposed="False" />
<Filter entryType="Namespace" fullName="Tango.FSE.Procedures.Controls" isExposed="False" />
@@ -124,20 +124,37 @@
<Filter entryType="Namespace" fullName="Tango.FSE.Procedures.CSV" isExposed="False" />
<Filter entryType="Namespace" fullName="Tango.FSE.Procedures.Designer" isExposed="False" />
<Filter entryType="Namespace" fullName="Tango.FSE.Procedures.Dialogs" isExposed="False" />
-<Filter entryType="Namespace" fullName="Tango.FSE.Procedures.Examples.Another" isExposed="False" />
+<Filter entryType="Namespace" fullName="Tango.FSE.Procedures.Examples.AddResult" isExposed="False" />
+<Filter entryType="Namespace" fullName="Tango.FSE.Procedures.Examples.Connection" isExposed="False" />
+<Filter entryType="Namespace" fullName="Tango.FSE.Procedures.Examples.Csv" isExposed="False" />
+<Filter entryType="Namespace" fullName="Tango.FSE.Procedures.Examples.Diagnostics" isExposed="False" />
+<Filter entryType="Namespace" fullName="Tango.FSE.Procedures.Examples.RequestUserInputFor" isExposed="False" />
<Filter entryType="Namespace" fullName="Tango.FSE.Procedures.Examples.Send" isExposed="False" />
+<Filter entryType="Namespace" fullName="Tango.FSE.Procedures.Examples.SendContinuous" isExposed="False" />
+<Filter entryType="Namespace" fullName="Tango.FSE.Procedures.Examples.Sql" isExposed="False" />
<Filter entryType="Namespace" fullName="Tango.FSE.Procedures.Helpers" isExposed="False" />
<Filter entryType="Namespace" fullName="Tango.FSE.Procedures.Messages" isExposed="False" />
<Filter entryType="Namespace" fullName="Tango.FSE.Procedures.ViewModels" isExposed="False" />
<Filter entryType="Namespace" fullName="Tango.FSE.Procedures.Views" isExposed="False" />
<Filter entryType="Namespace" fullName="Tango.FSE.Procedures.Windows" isExposed="False" />
+<Filter entryType="Namespace" fullName="Tango.PPC.Shared.Information" isExposed="False" />
+<Filter entryType="Namespace" fullName="Tango.PPC.Shared.Jobs" isExposed="False" />
+<Filter entryType="Namespace" fullName="Tango.PPC.Shared.Logs" isExposed="False" />
+<Filter entryType="Namespace" fullName="Tango.PPC.Shared.Performance" isExposed="False" />
+<Filter entryType="Namespace" fullName="Tango.PPC.Shared.RemoteUpgrade" isExposed="False" />
+<Filter entryType="Namespace" fullName="Tango.PPC.Shared.SQL" isExposed="True">
+ <Filter entryType="Class" fullName="Tango.PPC.Shared.SQL.ExecuteSqlRequest" filterName="ExecuteSqlRequest" isExposed="False" />
+ <Filter entryType="Class" fullName="Tango.PPC.Shared.SQL.ExecuteSqlResponse" filterName="ExecuteSqlResponse" isExposed="False" />
+</Filter>
+<Filter entryType="Namespace" fullName="Tango.PPC.Shared.Updates" isExposed="False" />
<Filter entryType="Namespace" fullName="" isExposed="False" /></ApiFilter>
<ProjectSummary>This is the global summery for the help file.</ProjectSummary>
<NamespaceSummaries>
<NamespaceSummaryItem name="(global)" isDocumented="True">This is the global summery for the help file.</NamespaceSummaryItem>
<NamespaceSummaryItem name="Tango.FSE.Procedures" isDocumented="True">Contains a collection of interfaces and classes for the main procedures API.</NamespaceSummaryItem>
</NamespaceSummaries>
- <ComponentConfigurations />
+ <ComponentConfigurations>
+ </ComponentConfigurations>
<MissingTags>Summary, Parameter, Returns, AutoDocumentCtors, TypeParameter, AutoDocumentDispose</MissingTags>
</PropertyGroup>
<!-- There are no properties for these groups. AnyCPU needs to appear in order for Visual Studio to perform
diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/ArrayParsingStyle.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/ArrayParsingStyle.cs
index c829a5b21..e731b7672 100644
--- a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/ArrayParsingStyle.cs
+++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/ArrayParsingStyle.cs
@@ -6,9 +6,19 @@ using System.Threading.Tasks;
namespace Tango.FSE.Procedures
{
+ /// <summary>
+ /// Represents an array parsing style.
+ /// </summary>
public enum ArrayParsingStyle
{
+ /// <summary>
+ /// Formats the array separated by commas. (e.g 1,2,3,4...)
+ /// </summary>
Comma,
+
+ /// <summary>
+ /// Each item enclosed in square brackets. (e.g [1] [2] [3]...)
+ /// </summary>
SquareBrackets,
}
}
diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Examples/AddResult/Program.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Examples/AddResult/Program.cs
new file mode 100644
index 000000000..750dd436d
--- /dev/null
+++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Examples/AddResult/Program.cs
@@ -0,0 +1,91 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.ComponentModel;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Drawing;
+using Google.Protobuf;
+using Tango.BL.Entities;
+using Tango.BL.Enumerations;
+using Tango.PMR.Stubs;
+using Tango.PMR.Diagnostics;
+using Tango.FSE.Common.Connection;
+using Tango.FSE.Common.Diagnostics;
+using Tango.FSE.Procedures;
+
+namespace Tango.FSE.Procedures.Examples.AddResult
+{
+ #region Example
+ public class Program
+ {
+ public class Person
+ {
+ public int Age;
+
+ //Here we use the Description attribute in order to set a custom name
+ //for this field at the results grid and csv file.
+ [Description("First Name")]
+ public String Name;
+ }
+
+ public void OnExecute(IProcedureContext context)
+ {
+ //Add scalar result with the value 10.
+ context.AddResult(ResultType.Passed, "My Scalar Result", 10);
+
+ //Initialize a list of persons.
+ List<Person> persons = new List<Person>();
+
+ for (int i = 0; i < 100; i++)
+ {
+ Person p = new Person();
+ p.Age = i;
+ p.Name = "Name " + i;
+ persons.Add(p);
+ }
+
+ //Add the persons list as a result.
+ //This will allow the user to display a grid with all the persons in the list.
+ //The list can also be exported to CSV.
+ context.AddResult(ResultType.Passed, "Persons", persons);
+
+ //Here is how we can get only the ages from the persons list and cast them as double values.
+ List<double> ages = persons.Select(x => x.Age).Cast<double>().ToList();
+
+ //Adding the ages list as a result..
+ context.AddResult(ResultType.Passed, "Ages", ages);
+
+ //Plotting the ages list as a graph result..
+ context.AddGraphResult(ResultType.Passed, "Ages Graph", ages);
+
+
+
+ //And here is how we can draw a completely customized result by drawing a bitmap.
+ Bitmap bitmap = context.CreateBitmap(400, 200);
+ Graphics g = context.GetDrawingContext(bitmap);
+
+ //Draw a simple rectangle border.
+ g.DrawRectangle(Pens.Gray, 0, 0, 399, 199);
+
+ //Draw a diagonal red line across the rectangle.
+ g.DrawLine(Pens.Red, 0, 0, 400, 200);
+
+ //Draw a string at the center of the rect.
+ Font font = new Font("Tahoma", 24, FontStyle.Bold, GraphicsUnit.Pixel);
+ RectangleF layout = new RectangleF(0, 0, 400, 200);
+ StringFormat format = new StringFormat();
+ format.Alignment = StringAlignment.Center;
+ format.LineAlignment = StringAlignment.Center;
+ g.DrawString("Bitmap Result", font, Brushes.YellowGreen, layout, format);
+
+ //Dispose the drawing context after you done drawing.
+ g.Dispose();
+
+ context.AddBitmapResult(ResultType.Passed, "Bitmap Result", bitmap);
+ }
+ }
+ #endregion
+}
diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Examples/Connection/Program.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Examples/Connection/Program.cs
new file mode 100644
index 000000000..63c8cc5f7
--- /dev/null
+++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Examples/Connection/Program.cs
@@ -0,0 +1,38 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.ComponentModel;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Drawing;
+using Google.Protobuf;
+using Tango.BL.Entities;
+using Tango.BL.Enumerations;
+using Tango.PMR.Stubs;
+using Tango.PMR.Diagnostics;
+using Tango.FSE.Common.Connection;
+using Tango.FSE.Common.Diagnostics;
+using Tango.FSE.Procedures;
+
+namespace Tango.FSE.Procedures.Examples.Connection
+{
+ #region Example
+ public class Program
+ {
+ public void OnExecute(IProcedureContext context)
+ {
+
+ if (context.IsConnected) //Check if there is an active machine connection.
+ {
+ if (context.ConnectionType == MachineConnectionTypes.Wifi) //Check enumeration of the active connection type.
+ {
+ context.WriteLine("Machine " + context.ConnectedMachine.SerialNumber + " is connected via WiFi.");
+ }
+ }
+
+ }
+ }
+ #endregion
+}
diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Examples/Csv/Program.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Examples/Csv/Program.cs
new file mode 100644
index 000000000..70526436b
--- /dev/null
+++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Examples/Csv/Program.cs
@@ -0,0 +1,63 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.ComponentModel;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Drawing;
+using Google.Protobuf;
+using Tango.BL.Entities;
+using Tango.BL.Enumerations;
+using Tango.PMR.Stubs;
+using Tango.PMR.Diagnostics;
+using Tango.FSE.Common.Connection;
+using Tango.FSE.Common.Diagnostics;
+using Tango.FSE.Procedures;
+
+namespace Tango.FSE.Procedures.Examples.Csv
+{
+ #region Example
+ //CSV Model must contain properties, not plain fields.
+ public class Person
+ {
+ public String Name { get; set; }
+ public int Age { get; set; }
+ }
+
+ public class Program
+ {
+ //This example demonstrates CSV file writing and reading.
+ //The csv reading method also accepts a byte array so you can also read a CSV file from resource.
+ public void OnExecute(IProcedureContext context)
+ {
+ //Initialize a list of person model (see Person.csx).
+ List<Person> persons = new List<Person>();
+
+ for (int i = 0; i < 100; i++)
+ {
+ Person p = new Person();
+ p.Name = "Person " + i;
+ p.Age = i;
+ persons.Add(p);
+ }
+
+ //Request the user to choose the csv file location.
+ String csvFile = context.RequestFileSave("Select CSV File Location", "*.csv", "persons.csv");
+
+ if (csvFile != null) //Check if user selected a file and pressed 'OK'.
+ {
+ //Write the persons list to the selected csv file.
+ context.WriteCsv<Person>(csvFile, persons);
+
+ //Test csv file for reading...
+ persons = context.ReadCsv<Person>(csvFile);
+
+ //Write the reading result to the output window.
+ context.WriteLine(persons);
+ }
+ }
+ }
+ #endregion
+}
diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Examples/Diagnostics/Program.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Examples/Diagnostics/Program.cs
new file mode 100644
index 000000000..c783abaa1
--- /dev/null
+++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Examples/Diagnostics/Program.cs
@@ -0,0 +1,44 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.ComponentModel;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Drawing;
+using Google.Protobuf;
+using Tango.BL.Entities;
+using Tango.BL.Enumerations;
+using Tango.PMR.Stubs;
+using Tango.PMR.Diagnostics;
+using Tango.FSE.Common.Connection;
+using Tango.FSE.Common.Diagnostics;
+using Tango.FSE.Procedures;
+
+namespace Tango.FSE.Procedures.Examples.Diagnostics
+{
+ #region Example
+ public class Program
+ {
+ public void OnExecute(IProcedureContext context)
+ {
+ //Initialize a new list of dancer angle values.
+ List<double> dancerValues = new List<double>();
+
+ //Collect 100 samples of the middle dancer angle.
+ for (int i = 0; i < 100; i++)
+ {
+ //Get the current diagnostics package. (set 'true' to block the execution until a fresh diagnostics frame arrives)
+ DiagnosticsPackage package = context.GetDiagnosticsPackage(true);
+
+ //Add the last value in the array to the list.
+ dancerValues.Add(package.GetMonitorLastValue(TechMonitors.Dancer2Angle));
+ }
+
+ //Plot the dancer samples to a graph result.
+ context.AddGraphResult(ResultType.Passed, "Dancer Angle", dancerValues);
+ }
+ }
+ #endregion
+}
diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Examples/RequestUserInputFor/Program.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Examples/RequestUserInputFor/Program.cs
new file mode 100644
index 000000000..3ea277bf8
--- /dev/null
+++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Examples/RequestUserInputFor/Program.cs
@@ -0,0 +1,48 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.ComponentModel;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Drawing;
+using Google.Protobuf;
+using Tango.BL.Entities;
+using Tango.BL.Enumerations;
+using Tango.PMR.Stubs;
+using Tango.PMR.Diagnostics;
+using Tango.FSE.Common.Connection;
+using Tango.FSE.Common.Diagnostics;
+using Tango.FSE.Procedures;
+
+namespace Tango.FSE.Procedures.Examples.RequestUserInputFor
+{
+ #region Example
+ public enum PersonType
+ {
+ Student = 0,
+ Employee = 1,
+ Guest = 2
+ }
+
+ public class Person
+ {
+ public int Age;
+ public String name;
+ public bool Activated;
+ public PersonType Type = PersonType.Employee;
+ }
+
+ public class Program
+ {
+ public void OnExecute(IProcedureContext context)
+ {
+
+ Person p = context.RequestUserInputFor<Person>("Input Demo", "Please fill in the form");
+ context.WriteLine(p);
+
+ }
+ }
+ #endregion
+}
diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Examples/Sql/Program.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Examples/Sql/Program.cs
new file mode 100644
index 000000000..33072a2dd
--- /dev/null
+++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Examples/Sql/Program.cs
@@ -0,0 +1,78 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.ComponentModel;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Drawing;
+using Google.Protobuf;
+using Tango.BL.Entities;
+using Tango.BL.Enumerations;
+using Tango.PMR.Stubs;
+using Tango.PMR.Diagnostics;
+using Tango.FSE.Common.Connection;
+using Tango.FSE.Common.Diagnostics;
+using Tango.FSE.Procedures;
+
+namespace Tango.FSE.Procedures.Examples.Sql
+{
+ #region Example
+ //Additional name spaces that are required for working with remote SQL.
+ using Tango.FSE.Common.SQL;
+ using Tango.PPC.Shared.SQL;
+
+ public class Program
+ {
+ public void OnExecute(IProcedureContext context)
+ {
+
+ //Get the remote SQL provider from the Tango IOC container.
+ IRemoteSqlProvider sql = context.GetService<IRemoteSqlProvider>();
+
+ //Create the SQL command and direct it to affect both the local machine's database and the global Twine's database.
+ RemoteSqlCommand command = new RemoteSqlCommand();
+ command.SQL = "UPDATE MACHINES SET IS_DEMO = 0 WHERE SERIAL_NUMBER = '" + context.ConnectedMachine.SerialNumber + "'";
+ command.Mode = RemoteSqlCommandMode.Both;
+
+ //Execute the command...
+ RemoteSqlCommandResult result = sql.ExecuteSqlCommand(command);
+
+ context.WriteLine("Command executed.");
+ context.WriteLine("Local Affected Rows " + result.LocalAffectedRecords);
+ context.WriteLine("Global Affected Rows " + result.GlobalAffectedRecords);
+
+ context.WriteLine("");
+
+ //Query the machine's database for all the jobs names and length.
+ command.SQL = "SELECT NAME,LAST_RUN FROM JOBS";
+ command.Mode = RemoteSqlCommandMode.Local;
+
+ result = sql.ExecuteSqlCommand(command);
+
+ //Check for errors.
+ if (!result.HasLocalError)
+ {
+ //Write the formatted results set to the console.
+ context.WriteLine(result.LocalDatSet.ToTableString());
+ context.WriteLine("");
+
+ //Iterate over the data set rows and get values.
+ foreach (RemoteSqlRow row in result.LocalDatSet.Rows)
+ {
+ String name = row.Get<String>("NAME");
+ String lastRun = row.Get<String>("LAST_RUN");
+
+ context.WriteLine(String.Format("NAME {0}, LAST_RUN {1}", name, lastRun));
+ }
+ }
+ else
+ {
+ //Write the error to console.
+ context.WriteLine(result.LocalError);
+ }
+ }
+ }
+ #endregion
+}
diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/IProcedureContext.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/IProcedureContext.cs
index 5081e8315..aa5bc4b5d 100644
--- a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/IProcedureContext.cs
+++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/IProcedureContext.cs
@@ -45,11 +45,20 @@ namespace Tango.FSE.Procedures
/// <summary>
/// Adds a new procedure result.
+ /// The value object can be a primitive, a collection of primitives or a collection of complex types.
/// </summary>
/// <param name="type">Result type.</param>
/// <param name="name">Result title.</param>
/// <param name="value">Result value.</param>
/// <returns>The result instance.</returns>
+ /// <example>
+ /// <para>
+ /// <i>
+ /// The following example demonstrates how to add various procedure results.
+ /// </i>
+ /// </para>
+ /// <code lang="C#" source="../Tango.FSE.Procedures/Examples/AddResult/Program.cs" title="Add results" region="Example" />
+ /// </example>
Result AddResult(ResultType type, String name, object value);
/// <summary>
@@ -60,6 +69,14 @@ namespace Tango.FSE.Procedures
/// <param name="name">Result title.</param>
/// <param name="values">Graph Y axis values.</param>
/// <returns>The result instance.</returns>
+ /// <example>
+ /// <para>
+ /// <i>
+ /// The following example demonstrates how to add various procedure results.
+ /// </i>
+ /// </para>
+ /// <code lang="C#" source="../Tango.FSE.Procedures/Examples/AddResult/Program.cs" title="Add results" region="Example" />
+ /// </example>
Result AddGraphResult(ResultType type, String name, IEnumerable<double> values);
/// <summary>
@@ -71,6 +88,14 @@ namespace Tango.FSE.Procedures
/// <param name="name">Result title.</param>
/// <param name="bitmap">Result bitmap.</param>
/// <returns>The result instance.</returns>
+ /// <example>
+ /// <para>
+ /// <i>
+ /// The following example demonstrates how to add various procedure results.
+ /// </i>
+ /// </para>
+ /// <code lang="C#" source="../Tango.FSE.Procedures/Examples/AddResult/Program.cs" title="Add results" region="Example" />
+ /// </example>
Result AddBitmapResult(ResultType type, String name, Bitmap bitmap);
/// <summary>
@@ -251,6 +276,14 @@ namespace Tango.FSE.Procedures
/// <param name="callback">Specify a callback method to handle the incoming response messages.</param>
/// <param name="timeout">First and continuous request timeout in seconds. (leave empty to get the default timeout)</param>
/// <param name="args">Request arguments separated by commas.</param>
+ /// <example>
+ /// <para>
+ /// <i>
+ /// The following example demonstrates how to send a continuous request to the machine and get a continuous response using the various different SendContinuous method overrides.
+ /// </i>
+ /// </para>
+ /// <code lang="C#" source="../Tango.FSE.Procedures/Examples/SendContinuous/Program.cs" title="Send a continuous request" region="Example" />
+ /// </example>
void SendContinuous<T>(String messageName, Action<T> callback, TimeSpan? timeout, params Object[] args) where T : class, IMessage;
/// <summary>
@@ -263,6 +296,14 @@ namespace Tango.FSE.Procedures
/// <param name="messageName">Full or shortened name of the message (e.g "CalculateRequest" or "calculate").</param>
/// <param name="callback">Specify a callback method to handle the incoming response messages.</param>
/// <param name="args">Request arguments separated by commas.</param>
+ /// <example>
+ /// <para>
+ /// <i>
+ /// The following example demonstrates how to send a continuous request to the machine and get a continuous response using the various different SendContinuous method overrides.
+ /// </i>
+ /// </para>
+ /// <code lang="C#" source="../Tango.FSE.Procedures/Examples/SendContinuous/Program.cs" title="Send a continuous request" region="Example" />
+ /// </example>
void SendContinuous<T>(String messageName, Action<T> callback, params Object[] args) where T : class, IMessage;
/// <summary>
@@ -401,6 +442,15 @@ namespace Tango.FSE.Procedures
/// <param name="title">The request title.</param>
/// <param name="message">The request message.</param>
/// <returns>Request result of type T.</returns>
+ /// <example>
+ /// <para>
+ /// <i>
+ /// The following example demonstrates how to create a model and request the user to fill or modify that model.
+ /// The model can contain primitives, arrays (comma separated), enums and booleans.
+ /// </i>
+ /// </para>
+ /// <code lang="C#" source="../Tango.FSE.Procedures/Examples/RequestUserInputFor/Program.cs" title="Request user input" region="Example" />
+ /// </example>
T RequestUserInputFor<T>(String title, String message);
/// <summary>
@@ -411,6 +461,15 @@ namespace Tango.FSE.Procedures
/// <param name="title">The request title.</param>
/// <param name="message">The request message.</param>
/// <returns>Request result of type T.</returns>
+ /// <example>
+ /// <para>
+ /// <i>
+ /// The following example demonstrates how to create a model and request the user to fill or modify that model.
+ /// The model can contain primitives, arrays (comma separated), enums and booleans.
+ /// </i>
+ /// </para>
+ /// <code lang="C#" source="../Tango.FSE.Procedures/Examples/RequestUserInputFor/Program.cs" title="Request user input" region="Example" />
+ /// </example>
T RequestUserInputFor<T>(T model, String title, String message);
/// <summary>
@@ -440,12 +499,21 @@ namespace Tango.FSE.Procedures
void UpdateProgress(String message, bool isIndeterminate = true, double value = 0, double maximum = 100);
/// <summary>
- /// Gets the latest diagnostics package.
+ /// Gets the current diagnostics package.
/// </summary>
- /// <param name="waitForNext">Blocks the execution until a fresh package is available.
+ /// <param name="waitForNext">Blocks the execution until a fresh diagnostics frame is available.
/// This will guarantee that you will get a distinct package each time.
/// </param>
/// <returns>The diagnostics package.</returns>
+ /// <example>
+ /// <para>
+ /// <i>
+ /// The following example demonstrates how to collect 100 middle dancer samples and plot them as a graph result.
+ /// </i>
+ /// </para>
+ /// <code lang="C#" source="../Tango.FSE.Procedures/Examples/Diagnostics/Program.cs" title="Collect dancer samples" region="Example" />
+ /// </example>
+ /// <seealso cref="AddGraphResult(ResultType, string, IEnumerable{double})"/>
DiagnosticsPackage GetDiagnosticsPackage(bool waitForNext = false);
/// <summary>
@@ -480,16 +548,40 @@ namespace Tango.FSE.Procedures
/// Gets the type of the current machine connection.
/// It is recommended to use <see cref="IsConnected"/> before, to ensure active machine connection.
/// </summary>
+ /// <example>
+ /// <para>
+ /// <i>
+ /// The following example demonstrates how to check for valid machine connection, check the connection type and get the connected machine's serial number.
+ /// </i>
+ /// </para>
+ /// <code lang="C#" source="../Tango.FSE.Procedures/Examples/Connection/Program.cs" title="Validate connection" region="Example" />
+ /// </example>
MachineConnectionTypes ConnectionType { get; }
/// <summary>
/// Gets or sets a value indicating whether a machine is currently connected.
/// </summary>
+ /// <example>
+ /// <para>
+ /// <i>
+ /// The following example demonstrates how to check for valid machine connection, check the connection type and get the connected machine's serial number.
+ /// </i>
+ /// </para>
+ /// <code lang="C#" source="../Tango.FSE.Procedures/Examples/Connection/Program.cs" title="Validate connection" region="Example" />
+ /// </example>
bool IsConnected { get; }
/// <summary>
/// Gets the currently connected machine entity.
/// </summary>
+ /// <example>
+ /// <para>
+ /// <i>
+ /// The following example demonstrates how to check for valid machine connection, check the connection type and get the connected machine's serial number.
+ /// </i>
+ /// </para>
+ /// <code lang="C#" source="../Tango.FSE.Procedures/Examples/Connection/Program.cs" title="Validate connection" region="Example" />
+ /// </example>
Machine ConnectedMachine { get; }
/// <summary>
@@ -549,6 +641,15 @@ namespace Tango.FSE.Procedures
/// <typeparam name="T">CSV model type.</typeparam>
/// <param name="file">The file path.</param>
/// <returns>A list containing all the CSV rows as models of type T.</returns>
+ /// <example>
+ /// <para>
+ /// <i>
+ /// The following example demonstrates CSV file writing and reading.
+ /// The csv reading method also accepts a byte array so you can also read a CSV file from a procedure resource.
+ /// </i>
+ /// </para>
+ /// <code lang="C#" source="../Tango.FSE.Procedures/Examples/Csv/Program.cs" title="Read/Write CSV" region="Example" />
+ /// </example>
List<T> ReadCsv<T>(String file) where T : class, new();
/// <summary>
@@ -558,6 +659,15 @@ namespace Tango.FSE.Procedures
/// <typeparam name="T">CSV model type.</typeparam>
/// <param name="data">The byte array.</param>
/// <returns>A list containing all the CSV rows as models of type T.</returns>
+ /// <example>
+ /// <para>
+ /// <i>
+ /// The following example demonstrates CSV file writing and reading.
+ /// The csv reading method also accepts a byte array so you can also read a CSV file from a procedure resource.
+ /// </i>
+ /// </para>
+ /// <code lang="C#" source="../Tango.FSE.Procedures/Examples/Csv/Program.cs" title="Read/Write CSV" region="Example" />
+ /// </example>
List<T> ReadCsv<T>(byte[] data) where T : class, new();
/// <summary>
@@ -567,6 +677,15 @@ namespace Tango.FSE.Procedures
/// <typeparam name="T">CSV model type.</typeparam>
/// <param name="file">The output file path.</param>
/// <param name="items">The items to write.</param>
+ /// <example>
+ /// <para>
+ /// <i>
+ /// The following example demonstrates CSV file writing and reading.
+ /// The csv reading method also accepts a byte array so you can also read a CSV file from a procedure resource.
+ /// </i>
+ /// </para>
+ /// <code lang="C#" source="../Tango.FSE.Procedures/Examples/Csv/Program.cs" title="Read/Write CSV" region="Example" />
+ /// </example>
void WriteCsv<T>(String file, List<T> items);
/// <summary>
diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/SqlResult.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/SqlResult.cs
deleted file mode 100644
index caf4cae32..000000000
--- a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/SqlResult.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace Tango.FSE.Procedures
-{
- public class SqlResult
- {
- public int AffectedRecords { get; set; }
- public List<Dictionary<String, Object>> Rows { get; set; }
-
- public SqlResult()
- {
- Rows = new List<Dictionary<string, object>>();
- }
- }
-}
diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Tango.FSE.Procedures.csproj b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Tango.FSE.Procedures.csproj
index 266fc55aa..b25990012 100644
--- a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Tango.FSE.Procedures.csproj
+++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Tango.FSE.Procedures.csproj
@@ -150,8 +150,14 @@
<DependentUpon>UserInputDialogView.xaml</DependentUpon>
</Compile>
<Compile Include="Dialogs\UserInputDialogViewVM.cs" />
+ <Compile Include="Examples\AddResult\Program.cs" />
+ <Compile Include="Examples\Connection\Program.cs" />
+ <Compile Include="Examples\Csv\Program.cs" />
+ <Compile Include="Examples\Diagnostics\Program.cs" />
+ <Compile Include="Examples\RequestUserInputFor\Program.cs" />
<Compile Include="Examples\SendContinuous\Program.cs" />
<Compile Include="Examples\Send\Program.cs" />
+ <Compile Include="Examples\Sql\Program.cs" />
<Compile Include="Helpers\ProcedureExceptionHelper.cs" />
<Compile Include="IDialogController.cs" />
<Compile Include="IProcedureContext.cs" />
@@ -171,7 +177,6 @@
<Compile Include="ProcedureFailedException.cs" />
<Compile Include="ProcedureInput.cs" />
<Compile Include="ProcedureProject.cs" />
- <Compile Include="SqlResult.cs" />
<Compile Include="UserInput.cs" />
<Compile Include="ViewModelLocator.cs" />
<Compile Include="ProceduresModule.cs" />
@@ -233,6 +238,10 @@
<EmbeddedResource Include="Resources\procedure_dialog_template.zip" />
</ItemGroup>
<ItemGroup>
+ <ProjectReference Include="..\..\..\PPC\Tango.PPC.Shared\Tango.PPC.Shared.csproj">
+ <Project>{208c8bd8-72c6-4e3c-acaa-351091a2acc7}</Project>
+ <Name>Tango.PPC.Shared</Name>
+ </ProjectReference>
<ProjectReference Include="..\..\..\Scripting\Tango.Scripting.Basic\Tango.Scripting.Basic.csproj">
<Project>{2b29a699-1d65-463a-8250-a2ce81d019c9}</Project>
<Name>Tango.Scripting.Basic</Name>
diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Themes/Generic.xaml b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Themes/Generic.xaml
index 661eb5405..46b4058e8 100644
--- a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Themes/Generic.xaml
+++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Themes/Generic.xaml
@@ -647,7 +647,7 @@
<material:PackIcon Kind="Cube" VerticalAlignment="Center" Width="12" Height="12" Foreground="{StaticResource FSE_PrimaryForegroundBrush}" />
<DockPanel Margin="2 0 0 0">
<Border BorderThickness="0.5" BorderBrush="{StaticResource FSE_BorderBrush}" Padding="2">
- <TextBlock Margin="0 0 0 0" Width="100" Text="{Binding Name}" ToolTip="{Binding Name}"></TextBlock>
+ <TextBlock Margin="0 0 0 0" Width="100" Text="{Binding Name}" ToolTip="{Binding Name}" TextWrapping="NoWrap" TextTrimming="CharacterEllipsis"></TextBlock>
</Border>
<DockPanel>
<TextBlock DockPanel.Dock="Right" Margin="10 -1 10 0" Foreground="{StaticResource FSE_GrayBrush}" VerticalAlignment="Center">
@@ -655,10 +655,10 @@
<Run Text="{Binding Type}"></Run>
<Run>]</Run>
</TextBlock>
- <Border Margin="0 0 0 0" BorderThickness="0.5" BorderBrush="{StaticResource FSE_BorderBrush}" Padding="2">
+ <Border Height="20" Margin="0 0 0 0" BorderThickness="0.5" BorderBrush="{StaticResource FSE_BorderBrush}" Padding="2">
<DockPanel ToolTip="{Binding DisplayValue}">
<material:PackIcon Kind="Search" Width="12" Height="12" VerticalAlignment="Center" />
- <TextBox IsEnabled="{Binding IsEditable}" MinWidth="100" MaxWidth="100" Margin="2 0 0 0" Text="{Binding DisplayValue,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}">
+ <TextBox IsEnabled="{Binding IsEditable}" TextWrapping="NoWrap" MinWidth="100" MaxWidth="100" Margin="2 0 0 0" Text="{Binding DisplayValue,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}">
<TextBox.Style>
<Style TargetType="TextBox" BasedOn="{StaticResource FSE_Debug_TextBoxStyle}">
<Setter Property="FontWeight" Value="Normal"></Setter>
diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/UserInput.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/UserInput.cs
index f5d3472d8..043b5bbae 100644
--- a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/UserInput.cs
+++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/UserInput.cs
@@ -6,10 +6,21 @@ using System.Threading.Tasks;
namespace Tango.FSE.Procedures
{
+ /// <summary>
+ /// Can be used to decorate a property or field in order to set their custom name when using <see cref="IProcedureContext.RequestUserInputFor{T}(string, string)"/>
+ /// </summary>
+ /// <seealso cref="System.Attribute" />
public class UserInput : Attribute
{
+ /// <summary>
+ /// Gets or sets the field/property custom name.
+ /// </summary>
public String Name { get; set; }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="UserInput"/> class.
+ /// </summary>
+ /// <param name="name">The field name.</param>
public UserInput(String name)
{
Name = name;
diff --git a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Views/ProcedureDesignerView.xaml.cs b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Views/ProcedureDesignerView.xaml.cs
index e6857a87b..301c3a25a 100644
--- a/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Views/ProcedureDesignerView.xaml.cs
+++ b/Software/Visual_Studio/FSE/Modules/Tango.FSE.Procedures/Views/ProcedureDesignerView.xaml.cs
@@ -224,6 +224,7 @@ namespace Tango.FSE.Procedures.Views
public void ResetBreakPointRequest()
{
GetCurrentEditor().ResetBreakPointLine();
+ runTimeBreakPointCanvas.Visibility = Visibility.Hidden;
}
private void ScriptEditor_BreakPointSymbolPressed(object sender, BreakPointSymbolPressedEventArgs e)
diff --git a/Software/Visual_Studio/FSE/Tango.FSE.Common/Diagnostics/DiagnosticsPackage.cs b/Software/Visual_Studio/FSE/Tango.FSE.Common/Diagnostics/DiagnosticsPackage.cs
index d546a8c17..5413dd8ec 100644
--- a/Software/Visual_Studio/FSE/Tango.FSE.Common/Diagnostics/DiagnosticsPackage.cs
+++ b/Software/Visual_Studio/FSE/Tango.FSE.Common/Diagnostics/DiagnosticsPackage.cs
@@ -11,12 +11,15 @@ using Tango.PMR.Diagnostics;
namespace Tango.FSE.Common.Diagnostics
{
+ /// <summary>
+ /// Represents a machine diagnostics frame package.
+ /// </summary>
public class DiagnosticsPackage
{
private static Dictionary<String, PropertyInfo> _monitorsProperties;
/// <summary>
- /// Gets or sets the frame.
+ /// Gets or sets the diagnostics frame.
/// </summary>
public DiagnosticsFrame Frame { get; set; }
@@ -48,21 +51,20 @@ namespace Tango.FSE.Common.Diagnostics
}
/// <summary>
- /// Gets the monitor value by the specified monitor using reflections.
+ /// Gets the monitor value by the specified <see cref="TechMonitors"/> enumeration.
/// </summary>
- /// <param name="monitor">The monitor.</param>
- /// <returns></returns>
+ /// <param name="monitor">The monitor enumeration.</param>
+ /// <returns>The monitor object.</returns>
public object GetMonitorObject(TechMonitors monitor)
{
return MonitorsProperties[monitor.ToString()].GetValue(Frame.Data.Monitors);
}
/// <summary>
- /// Gets the last data point from a protobuf repeated field.
+ /// Gets the last data point of a repeated field monitor by the specified <see cref="TechMonitors"/> enumeration.
/// </summary>
- /// <param name="monitor">The monitor.</param>
- /// <param name="value">The value.</param>
- /// <returns></returns>
+ /// <param name="monitor">The monitor enumeration.</param>
+ /// <returns>Last value in the array.</returns>
public double GetMonitorLastValue(TechMonitors monitor)
{
var value = GetMonitorObject(monitor);
@@ -71,11 +73,10 @@ namespace Tango.FSE.Common.Diagnostics
}
/// <summary>
- /// Gets the data array from a protobuf repeated field.
+ /// Gets the data array from a protobuf repeated monitor by the specified <see cref="TechMonitors"/> enumeration.
/// </summary>
- /// <param name="monitor">The monitor.</param>
- /// <param name="value">The value.</param>
- /// <returns></returns>
+ /// <param name="monitor">The monitor enumeration.</param>
+ /// <returns>List of double values representing the monitor data points.</returns>
public List<double> GetMonitorArray(TechMonitors monitor)
{
var value = GetMonitorObject(monitor);
@@ -85,8 +86,8 @@ namespace Tango.FSE.Common.Diagnostics
/// <summary>
/// Gets the data matrix from a protobuf repeated field of <see cref="DoubleArray"/>.
/// </summary>
- /// <param name="monitor">The monitor.</param>
- /// <returns></returns>
+ /// <param name="monitor">The monitor enumeration.</param>
+ /// <returns>A matrix of double values.</returns>
public List<List<double>> GetMonitorMatrix(TechMonitors monitor)
{
var value = GetMonitorObject(monitor);
@@ -98,7 +99,7 @@ namespace Tango.FSE.Common.Diagnostics
/// Gets the state of the digital interface.
/// </summary>
/// <param name="interface">The interface.</param>
- /// <returns></returns>
+ /// <returns>Digital interface state object.</returns>
public DigitalInterfaceState GetDigitalInterfaceState(TechIos @interface)
{
return Frame.Data.DigitalInterfaceStates.SingleOrDefault(x => x.InterfaceIO == (InterfaceIOs)@interface);
@@ -108,7 +109,7 @@ namespace Tango.FSE.Common.Diagnostics
/// Gets the state of the specified valve.
/// </summary>
/// <param name="valve">The valve.</param>
- /// <returns></returns>
+ /// <returns>Valve state object.</returns>
public ValveState GetValveState(TechValves valve)
{
return Frame.Data.ValvesStates.SingleOrDefault(x => x.ValveType == (ValveType)valve);
@@ -118,7 +119,7 @@ namespace Tango.FSE.Common.Diagnostics
/// Gets the state of the specified heater.
/// </summary>
/// <param name="heater">The heater.</param>
- /// <returns></returns>
+ /// <returns>Heater state object.</returns>
public HeaterState GetHeaterState(TechHeaters heater)
{
return Frame.Data.HeatersStates.SingleOrDefault(x => x.HeaterType == (HeaterType)heater);
diff --git a/Software/Visual_Studio/FSE/Tango.FSE.Common/SQL/IRemoteSqlProvider.cs b/Software/Visual_Studio/FSE/Tango.FSE.Common/SQL/IRemoteSqlProvider.cs
index d3b6660bf..e41d864c3 100644
--- a/Software/Visual_Studio/FSE/Tango.FSE.Common/SQL/IRemoteSqlProvider.cs
+++ b/Software/Visual_Studio/FSE/Tango.FSE.Common/SQL/IRemoteSqlProvider.cs
@@ -7,10 +7,32 @@ using Tango.PPC.Shared.SQL;
namespace Tango.FSE.Common.SQL
{
+ /// <summary>
+ /// Represents a remote SQL provider.
+ /// Can be used to execute commands against the remote machine's database and the global twine's database.
+ /// </summary>
+ /// <example>
+ /// <para>
+ /// <i>
+ /// The following example demonstrates how to set the connected machine's demo state and query for the connected machine's jobs.
+ /// </i>
+ /// </para>
+ /// <code lang="C#" source="../Tango.FSE.Procedures/Examples/Sql/Program.cs" title="Remote SQL" region="Example" />
+ /// </example>
public interface IRemoteSqlProvider
{
+ /// <summary>
+ /// Executes the SQL command asynchronously.
+ /// </summary>
+ /// <param name="command">The command.</param>
+ /// <returns>Remote command result.</returns>
Task<RemoteSqlCommandResult> ExecuteSqlCommandAsync(RemoteSqlCommand command);
+ /// <summary>
+ /// Executes the SQL command.
+ /// </summary>
+ /// <param name="command">The command.</param>
+ /// <returns>Remote command result.</returns>
RemoteSqlCommandResult ExecuteSqlCommand(RemoteSqlCommand command);
}
}
diff --git a/Software/Visual_Studio/FSE/Tango.FSE.Common/SQL/RemoteSqlCommand.cs b/Software/Visual_Studio/FSE/Tango.FSE.Common/SQL/RemoteSqlCommand.cs
index 95f94ad08..99d0f1878 100644
--- a/Software/Visual_Studio/FSE/Tango.FSE.Common/SQL/RemoteSqlCommand.cs
+++ b/Software/Visual_Studio/FSE/Tango.FSE.Common/SQL/RemoteSqlCommand.cs
@@ -6,12 +6,37 @@ using System.Threading.Tasks;
namespace Tango.FSE.Common.SQL
{
+ /// <summary>
+ /// Represents a SQL command that can be executed by a <see cref="IRemoteSqlProvider"/>.
+ /// </summary>
+ /// <example>
+ /// <para>
+ /// <i>
+ /// The following example demonstrates how to set the connected machine's demo state and query for the connected machine's jobs.
+ /// </i>
+ /// </para>
+ /// <code lang="C#" source="../Tango.FSE.Procedures/Examples/Sql/Program.cs" title="Remote SQL" region="Example" />
+ /// </example>
public class RemoteSqlCommand
{
+ /// <summary>
+ /// Gets or sets the command execution mode.
+ /// </summary>
public RemoteSqlCommandMode Mode { get; set; }
+
+ /// <summary>
+ /// Gets or sets the SQL query.
+ /// </summary>
public String SQL { get; set; }
+
+ /// <summary>
+ /// Gets or sets the command timeout.
+ /// </summary>
public int Timeout { get; set; }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="RemoteSqlCommand"/> class.
+ /// </summary>
public RemoteSqlCommand()
{
Timeout = 30;
diff --git a/Software/Visual_Studio/FSE/Tango.FSE.Common/SQL/RemoteSqlCommandMode.cs b/Software/Visual_Studio/FSE/Tango.FSE.Common/SQL/RemoteSqlCommandMode.cs
index 1b05e8e86..702f35e8a 100644
--- a/Software/Visual_Studio/FSE/Tango.FSE.Common/SQL/RemoteSqlCommandMode.cs
+++ b/Software/Visual_Studio/FSE/Tango.FSE.Common/SQL/RemoteSqlCommandMode.cs
@@ -7,8 +7,16 @@ using System.Threading.Tasks;
namespace Tango.FSE.Common.SQL
{
/// <summary>
- /// Represents an SQL command mode.
+ /// Represents an SQL command execution mode.
/// </summary>
+ /// <example>
+ /// <para>
+ /// <i>
+ /// The following example demonstrates how to set the connected machine's demo state and query for the connected machine's jobs.
+ /// </i>
+ /// </para>
+ /// <code lang="C#" source="../Tango.FSE.Procedures/Examples/Sql/Program.cs" title="Remote SQL" region="Example" />
+ /// </example>
public enum RemoteSqlCommandMode
{
/// <summary>
diff --git a/Software/Visual_Studio/FSE/Tango.FSE.Common/SQL/RemoteSqlCommandResult.cs b/Software/Visual_Studio/FSE/Tango.FSE.Common/SQL/RemoteSqlCommandResult.cs
index 34ab74a68..da21ae24d 100644
--- a/Software/Visual_Studio/FSE/Tango.FSE.Common/SQL/RemoteSqlCommandResult.cs
+++ b/Software/Visual_Studio/FSE/Tango.FSE.Common/SQL/RemoteSqlCommandResult.cs
@@ -7,22 +7,68 @@ using Tango.PPC.Shared.SQL;
namespace Tango.FSE.Common.SQL
{
+ /// <summary>
+ /// Represents a <see cref="RemoteSqlCommand"/> result.
+ /// </summary>
+ /// <example>
+ /// <para>
+ /// <i>
+ /// The following example demonstrates how to set the connected machine's demo state and query for the connected machine's jobs.
+ /// </i>
+ /// </para>
+ /// <code lang="C#" source="../Tango.FSE.Procedures/Examples/Sql/Program.cs" title="Remote SQL" region="Example" />
+ /// </example>
public class RemoteSqlCommandResult
{
+ /// <summary>
+ /// Gets or sets the connected machine's database affected records.
+ /// </summary>
public int LocalAffectedRecords { get; set; }
+
+ /// <summary>
+ /// Gets or sets the data set retrieved from the connected machine's database.
+ /// </summary>
public RemoteSqlDataSet LocalDatSet { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether the query against the connected machine's database has encountered an error.
+ /// </summary>
public bool HasLocalError { get; set; }
+
+ /// <summary>
+ /// Gets or sets the error encountered by the query when it was executed against the connected machine's database.
+ /// Relevant only if <see cref="HasLocalError"/> equals true.
+ /// </summary>
public String LocalError { get; set; }
+ /// <summary>
+ /// Gets or sets the global database affected records.
+ /// </summary>
public int GlobalAffectedRecords { get; set; }
- public RemoteSqlDataSet GLobalDataSet { get; set; }
+
+ /// <summary>
+ /// Gets or sets the data set retrieved from the global database.
+ /// </summary>
+ public RemoteSqlDataSet GlobalDataSet { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether the query against the global database has encountered an error.
+ /// </summary>
public bool HasGlobalError { get; set; }
+
+ /// <summary>
+ /// Gets or sets the error encountered by the query when it was executed against the global database.
+ /// Relevant only if <see cref="HasGlobalError"/> equals true.
+ /// </summary>
public String GlobalError { get; set; }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="RemoteSqlCommandResult"/> class.
+ /// </summary>
public RemoteSqlCommandResult()
{
LocalDatSet = new RemoteSqlDataSet();
- GLobalDataSet = new RemoteSqlDataSet();
+ GlobalDataSet = new RemoteSqlDataSet();
}
}
}
diff --git a/Software/Visual_Studio/FSE/Tango.FSE.UI/SQL/DefaultRemoteSqlProvider.cs b/Software/Visual_Studio/FSE/Tango.FSE.UI/SQL/DefaultRemoteSqlProvider.cs
index feac992b3..a657ad55e 100644
--- a/Software/Visual_Studio/FSE/Tango.FSE.UI/SQL/DefaultRemoteSqlProvider.cs
+++ b/Software/Visual_Studio/FSE/Tango.FSE.UI/SQL/DefaultRemoteSqlProvider.cs
@@ -159,7 +159,7 @@ namespace Tango.FSE.UI.SQL
SqlDataReader reader = await cmd.ExecuteReaderAsync();
result.GlobalAffectedRecords = reader.RecordsAffected;
- result.GLobalDataSet = await RemoteSqlDataSet.Load(reader);
+ result.GlobalDataSet = await RemoteSqlDataSet.Load(reader);
}
}
}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Shared/SQL/RemoteSqlColumn.cs b/Software/Visual_Studio/PPC/Tango.PPC.Shared/SQL/RemoteSqlColumn.cs
index 328dbb492..54431bdbe 100644
--- a/Software/Visual_Studio/PPC/Tango.PPC.Shared/SQL/RemoteSqlColumn.cs
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Shared/SQL/RemoteSqlColumn.cs
@@ -6,19 +6,47 @@ using System.Threading.Tasks;
namespace Tango.PPC.Shared.SQL
{
+ /// <summary>
+ /// Represents a <see cref="RemoteSqlDataSet"/> column.
+ /// </summary>
public class RemoteSqlColumn
{
+ /// <summary>
+ /// Gets or sets the column name.
+ /// </summary>
public String Name { get; set; }
+
+ /// <summary>
+ /// Gets or sets the column index.
+ /// </summary>
public int Index { get; set; }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="RemoteSqlColumn"/> class.
+ /// </summary>
public RemoteSqlColumn()
{
}
+ /// <summary>
+ /// Initializes a new instance of the <see cref="RemoteSqlColumn"/> class.
+ /// </summary>
+ /// <param name="name">The column name.</param>
public RemoteSqlColumn(String name)
{
Name = name;
}
+
+ /// <summary>
+ /// Returns a <see cref="System.String" /> that represents this instance.
+ /// </summary>
+ /// <returns>
+ /// A <see cref="System.String" /> that represents this instance.
+ /// </returns>
+ public override string ToString()
+ {
+ return Name;
+ }
}
}
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Shared/SQL/RemoteSqlColumnCollection.cs b/Software/Visual_Studio/PPC/Tango.PPC.Shared/SQL/RemoteSqlColumnCollection.cs
index 5358e047b..dfda6c3b7 100644
--- a/Software/Visual_Studio/PPC/Tango.PPC.Shared/SQL/RemoteSqlColumnCollection.cs
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Shared/SQL/RemoteSqlColumnCollection.cs
@@ -7,15 +7,27 @@ using System.Threading.Tasks;
namespace Tango.PPC.Shared.SQL
{
+ /// <summary>
+ /// Represents a <see cref="RemoteSqlDataSet"/> columns collection.
+ /// </summary>
+ /// <seealso cref="System.Collections.ObjectModel.Collection{Tango.PPC.Shared.SQL.RemoteSqlColumn}" />
public class RemoteSqlColumnCollection : Collection<RemoteSqlColumn>
{
private Dictionary<String, RemoteSqlColumn> _dictionary;
+ /// <summary>
+ /// Initializes a new instance of the <see cref="RemoteSqlColumnCollection"/> class.
+ /// </summary>
public RemoteSqlColumnCollection()
{
_dictionary = new Dictionary<string, RemoteSqlColumn>();
}
+ /// <summary>
+ /// Inserts an element into the <see cref="T:System.Collections.ObjectModel.Collection`1" /> at the specified index.
+ /// </summary>
+ /// <param name="index">The zero-based index at which <paramref name="item" /> should be inserted.</param>
+ /// <param name="item">The object to insert. The value can be null for reference types.</param>
protected override void InsertItem(int index, RemoteSqlColumn item)
{
item.Index = Count;
@@ -23,22 +35,41 @@ namespace Tango.PPC.Shared.SQL
base.InsertItem(index, item);
}
+ /// <summary>
+ /// Removes the element at the specified index of the <see cref="T:System.Collections.ObjectModel.Collection`1" />.
+ /// </summary>
+ /// <param name="index">The zero-based index of the element to remove.</param>
+ /// <exception cref="NotSupportedException"></exception>
protected override void RemoveItem(int index)
{
throw new NotSupportedException();
}
+ /// <summary>
+ /// Removes all elements from the <see cref="T:System.Collections.ObjectModel.Collection`1" />.
+ /// </summary>
protected override void ClearItems()
{
_dictionary.Clear();
base.ClearItems();
}
+ /// <summary>
+ /// Replaces the element at the specified index.
+ /// </summary>
+ /// <param name="index">The zero-based index of the element to replace.</param>
+ /// <param name="item">The new value for the element at the specified index. The value can be null for reference types.</param>
+ /// <exception cref="NotSupportedException"></exception>
protected override void SetItem(int index, RemoteSqlColumn item)
{
throw new NotSupportedException();
}
+ /// <summary>
+ /// Gets the column index by column name.
+ /// </summary>
+ /// <param name="columnName">Column name.</param>
+ /// <returns></returns>
public int GetIndexOf(String columnName)
{
return _dictionary[columnName].Index;
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Shared/SQL/RemoteSqlDataSet.cs b/Software/Visual_Studio/PPC/Tango.PPC.Shared/SQL/RemoteSqlDataSet.cs
index 089908e5a..72b8d2eb2 100644
--- a/Software/Visual_Studio/PPC/Tango.PPC.Shared/SQL/RemoteSqlDataSet.cs
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Shared/SQL/RemoteSqlDataSet.cs
@@ -9,17 +9,37 @@ using System.Threading.Tasks;
namespace Tango.PPC.Shared.SQL
{
+ /// <summary>
+ /// Represents remote database query result composed of rows and columns.
+ /// </summary>
+ /// <example>
+ /// <para>
+ /// <i>
+ /// The following example demonstrates how to set the connected machine's demo state and query for the connected machine's jobs.
+ /// </i>
+ /// </para>
+ /// <code lang="C#" source="../Tango.FSE.Procedures/Examples/Sql/Program.cs" title="Remote SQL" region="Example" />
+ /// </example>
public class RemoteSqlDataSet
{
+ /// <summary>
+ /// Gets or sets the dataset columns.
+ /// </summary>
public RemoteSqlColumnCollection Columns { get; set; }
private ObservableCollection<RemoteSqlRow> _rows;
+ /// <summary>
+ /// Gets or sets the dataset rows.
+ /// </summary>
public ObservableCollection<RemoteSqlRow> Rows
{
get { return _rows; }
set { _rows = value; OnRowsChanged(); }
}
+ /// <summary>
+ /// Initializes a new instance of the <see cref="RemoteSqlDataSet"/> class.
+ /// </summary>
public RemoteSqlDataSet()
{
Columns = new RemoteSqlColumnCollection();
@@ -61,6 +81,11 @@ namespace Tango.PPC.Shared.SQL
}
}
+ /// <summary>
+ /// Creates a new <see cref="RemoteSqlDataSet"/> using the specified <see cref="SqlDataReader"/>.
+ /// </summary>
+ /// <param name="reader">The reader.</param>
+ /// <returns></returns>
public static Task<RemoteSqlDataSet> Load(SqlDataReader reader)
{
return Task.Factory.StartNew<RemoteSqlDataSet>(() =>
@@ -100,11 +125,21 @@ namespace Tango.PPC.Shared.SQL
});
}
+ /// <summary>
+ /// Returns a <see cref="System.String" /> that represents this instance.
+ /// </summary>
+ /// <returns>
+ /// A <see cref="System.String" /> that represents this instance.
+ /// </returns>
public override string ToString()
{
return String.Join(", ", Columns.Select(x => x.Name)) + "\n" + String.Join(Environment.NewLine, Rows.Select(x => x.ToString()));
}
+ /// <summary>
+ /// Formats this dataset as a string with columns and rows.
+ /// </summary>
+ /// <returns></returns>
public String ToTableString()
{
Dictionary<int, int> columnsMaxLength = new Dictionary<int, int>();
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Shared/SQL/RemoteSqlRow.cs b/Software/Visual_Studio/PPC/Tango.PPC.Shared/SQL/RemoteSqlRow.cs
index bf6b0ba0c..dfabacfea 100644
--- a/Software/Visual_Studio/PPC/Tango.PPC.Shared/SQL/RemoteSqlRow.cs
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Shared/SQL/RemoteSqlRow.cs
@@ -10,28 +10,61 @@ using Tango.BL.Entities;
namespace Tango.PPC.Shared.SQL
{
+ /// <summary>
+ /// Represents a <see cref="RemoteSqlDataSet"/> row.
+ /// </summary>
+ /// <example>
+ /// <para>
+ /// <i>
+ /// The following example demonstrates how to set the connected machine's demo state and query for the connected machine's jobs.
+ /// </i>
+ /// </para>
+ /// <code lang="C#" source="../Tango.FSE.Procedures/Examples/Sql/Program.cs" title="Remote SQL" region="Example" />
+ /// </example>
public class RemoteSqlRow
{
private Func<String, Object> _getFuncKey;
private Func<int, Object> _getFuncIndex;
+ /// <summary>
+ /// Gets or sets the row values.
+ /// </summary>
public List<Object> Values { get; set; }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="RemoteSqlRow"/> class.
+ /// </summary>
public RemoteSqlRow()
{
Values = new List<object>();
}
+ /// <summary>
+ /// Gets a row value by its column name.
+ /// </summary>
+ /// <param name="columnName">Name of the column.</param>
+ /// <returns>The column value.</returns>
public Object Get(String columnName)
{
return _getFuncKey.Invoke(columnName);
}
+ /// <summary>
+ /// Gets a row value by its column index.
+ /// </summary>
+ /// <param name="columnIndex">Index of the column.</param>
+ /// <returns>The column value.</returns>
public Object Get(int columnIndex)
{
return _getFuncIndex.Invoke(columnIndex);
}
+ /// <summary>
+ /// Gets a row value as type T by its column name.
+ /// </summary>
+ /// <typeparam name="T">Expected column type.</typeparam>
+ /// <param name="columnName">Name of the column.</param>
+ /// <returns>The column value.</returns>
public T Get<T>(String columnName)
{
var value = _getFuncKey.Invoke(columnName);
@@ -46,6 +79,12 @@ namespace Tango.PPC.Shared.SQL
}
}
+ /// <summary>
+ /// Gets a row value by its column index.
+ /// </summary>
+ /// <typeparam name="T">Expected column type</typeparam>
+ /// <param name="columnIndex">Index of the column.</param>
+ /// <returns>The column value.</returns>
public T Get<T>(int columnIndex)
{
var value = _getFuncIndex.Invoke(columnIndex);
@@ -66,11 +105,22 @@ namespace Tango.PPC.Shared.SQL
_getFuncIndex = getFuncIndex;
}
+ /// <summary>
+ /// Returns a <see cref="System.String" /> that represents this instance.
+ /// </summary>
+ /// <returns>
+ /// A <see cref="System.String" /> that represents this instance.
+ /// </returns>
public override string ToString()
{
return String.Join(", ", Values);
}
+ /// <summary>
+ /// Creates an object of type T and maps this row to it based on its properties decorated with <see cref="ColumnAttribute"/>.
+ /// </summary>
+ /// <typeparam name="T"></typeparam>
+ /// <returns></returns>
public T Map<T>() where T : class, new()
{
var obj = Activator.CreateInstance<T>();
@@ -78,6 +128,11 @@ namespace Tango.PPC.Shared.SQL
return obj;
}
+ /// <summary>
+ /// Maps this row to the specified object based on its properties decorated with <see cref="ColumnAttribute"/>.
+ /// </summary>
+ /// <typeparam name="T">Model type</typeparam>
+ /// <param name="obj">The object.</param>
public void Map<T>(T obj) where T : class
{
foreach (var prop in typeof(T).GetPropertiesWithAttribute<ColumnAttribute>())
diff --git a/Software/Visual_Studio/PPC/Tango.PPC.Shared/Tango.PPC.Shared.csproj b/Software/Visual_Studio/PPC/Tango.PPC.Shared/Tango.PPC.Shared.csproj
index 10993399c..a0cc9b153 100644
--- a/Software/Visual_Studio/PPC/Tango.PPC.Shared/Tango.PPC.Shared.csproj
+++ b/Software/Visual_Studio/PPC/Tango.PPC.Shared/Tango.PPC.Shared.csproj
@@ -22,6 +22,8 @@
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
+ <DocumentationFile>..\..\Build\PPC\Debug\Tango.PPC.Shared.xml</DocumentationFile>
+ <NoWarn>1591</NoWarn>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
@@ -30,6 +32,8 @@
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
+ <DocumentationFile>..\..\Build\PPC\Release\Tango.PPC.Shared.xml</DocumentationFile>
+ <NoWarn>1591</NoWarn>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
diff --git a/Software/Visual_Studio/Scripting/Tango.Scripting.Editors/TextEditor.cs b/Software/Visual_Studio/Scripting/Tango.Scripting.Editors/TextEditor.cs
index d2fc9e02b..cd9977520 100644
--- a/Software/Visual_Studio/Scripting/Tango.Scripting.Editors/TextEditor.cs
+++ b/Software/Visual_Studio/Scripting/Tango.Scripting.Editors/TextEditor.cs
@@ -3,6 +3,7 @@
using System;
using System.ComponentModel;
+using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
@@ -24,1121 +25,1229 @@ using Tango.Scripting.Editors.Utils;
namespace Tango.Scripting.Editors
{
- /// <summary>
- /// The text editor control.
- /// Contains a scrollable TextArea.
- /// </summary>
- [Localizability(LocalizationCategory.Text), ContentProperty("Text")]
- public class TextEditor : Control, ITextEditorComponent, IServiceProvider, IWeakEventListener
- {
- #region Constructors
- static TextEditor()
- {
- DefaultStyleKeyProperty.OverrideMetadata(typeof(TextEditor),
- new FrameworkPropertyMetadata(typeof(TextEditor)));
- FocusableProperty.OverrideMetadata(typeof(TextEditor),
- new FrameworkPropertyMetadata(Boxes.True));
- }
-
- /// <summary>
- /// Creates a new TextEditor instance.
- /// </summary>
- public TextEditor() : this(new TextArea())
- {
- }
-
- /// <summary>
- /// Creates a new TextEditor instance.
- /// </summary>
- protected TextEditor(TextArea textArea)
- {
- if (textArea == null)
- throw new ArgumentNullException("textArea");
- this.textArea = textArea;
-
- textArea.TextView.Services.AddService(typeof(TextEditor), this);
-
- SetCurrentValue(OptionsProperty, textArea.Options);
- SetCurrentValue(DocumentProperty, new TextDocument());
- }
-
- #endregion
-
- /// <inheritdoc/>
- protected override System.Windows.Automation.Peers.AutomationPeer OnCreateAutomationPeer()
- {
- return new TextEditorAutomationPeer(this);
- }
-
- /// Forward focus to TextArea.
- /// <inheritdoc/>
- protected override void OnGotKeyboardFocus(KeyboardFocusChangedEventArgs e)
- {
- base.OnGotKeyboardFocus(e);
- if (e.NewFocus == this) {
- Keyboard.Focus(this.TextArea);
- e.Handled = true;
- }
- }
-
- #region Document property
- /// <summary>
- /// Document property.
- /// </summary>
- public static readonly DependencyProperty DocumentProperty
- = TextView.DocumentProperty.AddOwner(
- typeof(TextEditor), new FrameworkPropertyMetadata(OnDocumentChanged));
-
- /// <summary>
- /// Gets/Sets the document displayed by the text editor.
- /// This is a dependency property.
- /// </summary>
- public TextDocument Document {
- get { return (TextDocument)GetValue(DocumentProperty); }
- set { SetValue(DocumentProperty, value); }
- }
-
- /// <summary>
- /// Occurs when the document property has changed.
- /// </summary>
- public event EventHandler DocumentChanged;
-
- /// <summary>
- /// Raises the <see cref="DocumentChanged"/> event.
- /// </summary>
- protected virtual void OnDocumentChanged(EventArgs e)
- {
- if (DocumentChanged != null) {
- DocumentChanged(this, e);
- }
- }
-
- static void OnDocumentChanged(DependencyObject dp, DependencyPropertyChangedEventArgs e)
- {
- ((TextEditor)dp).OnDocumentChanged((TextDocument)e.OldValue, (TextDocument)e.NewValue);
- }
-
- void OnDocumentChanged(TextDocument oldValue, TextDocument newValue)
- {
- if (oldValue != null) {
- TextDocumentWeakEventManager.TextChanged.RemoveListener(oldValue, this);
- PropertyChangedEventManager.RemoveListener(oldValue.UndoStack, this, "IsOriginalFile");
- }
- textArea.Document = newValue;
- if (newValue != null) {
- TextDocumentWeakEventManager.TextChanged.AddListener(newValue, this);
- PropertyChangedEventManager.AddListener(newValue.UndoStack, this, "IsOriginalFile");
- }
- OnDocumentChanged(EventArgs.Empty);
- OnTextChanged(EventArgs.Empty);
- }
- #endregion
-
- #region Options property
- /// <summary>
- /// Options property.
- /// </summary>
- public static readonly DependencyProperty OptionsProperty
- = TextView.OptionsProperty.AddOwner(typeof(TextEditor), new FrameworkPropertyMetadata(OnOptionsChanged));
-
- /// <summary>
- /// Gets/Sets the options currently used by the text editor.
- /// </summary>
- public TextEditorOptions Options {
- get { return (TextEditorOptions)GetValue(OptionsProperty); }
- set { SetValue(OptionsProperty, value); }
- }
-
- /// <summary>
- /// Occurs when a text editor option has changed.
- /// </summary>
- public event PropertyChangedEventHandler OptionChanged;
-
- /// <summary>
- /// Raises the <see cref="OptionChanged"/> event.
- /// </summary>
- protected virtual void OnOptionChanged(PropertyChangedEventArgs e)
- {
- if (OptionChanged != null) {
- OptionChanged(this, e);
- }
- }
-
- static void OnOptionsChanged(DependencyObject dp, DependencyPropertyChangedEventArgs e)
- {
- ((TextEditor)dp).OnOptionsChanged((TextEditorOptions)e.OldValue, (TextEditorOptions)e.NewValue);
- }
-
- void OnOptionsChanged(TextEditorOptions oldValue, TextEditorOptions newValue)
- {
- if (oldValue != null) {
- PropertyChangedWeakEventManager.RemoveListener(oldValue, this);
- }
- textArea.Options = newValue;
- if (newValue != null) {
- PropertyChangedWeakEventManager.AddListener(newValue, this);
- }
- OnOptionChanged(new PropertyChangedEventArgs(null));
- }
-
- /// <inheritdoc cref="IWeakEventListener.ReceiveWeakEvent"/>
- protected virtual bool ReceiveWeakEvent(Type managerType, object sender, EventArgs e)
- {
- if (managerType == typeof(PropertyChangedWeakEventManager)) {
- OnOptionChanged((PropertyChangedEventArgs)e);
- return true;
- } else if (managerType == typeof(TextDocumentWeakEventManager.TextChanged)) {
- OnTextChanged(e);
- return true;
- } else if (managerType == typeof(PropertyChangedEventManager)) {
- return HandleIsOriginalChanged((PropertyChangedEventArgs)e);
- }
- return false;
- }
-
- bool IWeakEventListener.ReceiveWeakEvent(Type managerType, object sender, EventArgs e)
- {
- return ReceiveWeakEvent(managerType, sender, e);
- }
- #endregion
-
- #region Text property
- /// <summary>
- /// Gets/Sets the text of the current document.
- /// </summary>
- [Localizability(LocalizationCategory.Text), DefaultValue("")]
- public string Text {
- get {
- TextDocument document = this.Document;
- return document != null ? document.Text : string.Empty;
- }
- set {
- TextDocument document = GetDocument();
- document.Text = value ?? string.Empty;
- // after replacing the full text, the caret is positioned at the end of the document
- // - reset it to the beginning.
- this.CaretOffset = 0;
- document.UndoStack.ClearAll();
- }
- }
-
- TextDocument GetDocument()
- {
- TextDocument document = this.Document;
- if (document == null)
- throw ThrowUtil.NoDocumentAssigned();
- return document;
- }
-
- /// <summary>
- /// Occurs when the Text property changes.
- /// </summary>
- public event EventHandler TextChanged;
-
- /// <summary>
- /// Raises the <see cref="TextChanged"/> event.
- /// </summary>
- protected virtual void OnTextChanged(EventArgs e)
- {
- if (TextChanged != null) {
- TextChanged(this, e);
- }
- }
- #endregion
-
- #region TextArea / ScrollViewer properties
- readonly TextArea textArea;
- ScrollViewer scrollViewer;
-
- /// <summary>
- /// Is called after the template was applied.
- /// </summary>
- public override void OnApplyTemplate()
- {
- base.OnApplyTemplate();
- scrollViewer = (ScrollViewer)Template.FindName("PART_ScrollViewer", this);
- }
-
- /// <summary>
- /// Gets the text area.
- /// </summary>
- public TextArea TextArea {
- get {
- return textArea;
- }
- }
-
- /// <summary>
- /// Gets the scroll viewer used by the text editor.
- /// This property can return null if the template has not been applied / does not contain a scroll viewer.
- /// </summary>
- internal ScrollViewer ScrollViewer {
- get { return scrollViewer; }
- }
-
- bool CanExecute(RoutedUICommand command)
- {
- TextArea textArea = this.TextArea;
- if (textArea == null)
- return false;
- else
- return command.CanExecute(null, textArea);
- }
-
- void Execute(RoutedUICommand command)
- {
- TextArea textArea = this.TextArea;
- if (textArea != null)
- command.Execute(null, textArea);
- }
- #endregion
-
- #region Syntax highlighting
- /// <summary>
- /// The <see cref="SyntaxHighlighting"/> property.
- /// </summary>
- public static readonly DependencyProperty SyntaxHighlightingProperty =
- DependencyProperty.Register("SyntaxHighlighting", typeof(IHighlightingDefinition), typeof(TextEditor),
- new FrameworkPropertyMetadata(OnSyntaxHighlightingChanged));
-
-
- /// <summary>
- /// Gets/sets the syntax highlighting definition used to colorize the text.
- /// </summary>
- public IHighlightingDefinition SyntaxHighlighting {
- get { return (IHighlightingDefinition)GetValue(SyntaxHighlightingProperty); }
- set { SetValue(SyntaxHighlightingProperty, value); }
- }
-
- IVisualLineTransformer colorizer;
-
- static void OnSyntaxHighlightingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
- {
- ((TextEditor)d).OnSyntaxHighlightingChanged(e.NewValue as IHighlightingDefinition);
- }
-
- void OnSyntaxHighlightingChanged(IHighlightingDefinition newValue)
- {
- if (colorizer != null) {
- this.TextArea.TextView.LineTransformers.Remove(colorizer);
- colorizer = null;
- }
- if (newValue != null) {
- colorizer = CreateColorizer(newValue);
- this.TextArea.TextView.LineTransformers.Insert(0, colorizer);
- }
- }
-
- /// <summary>
- /// Creates the highlighting colorizer for the specified highlighting definition.
- /// Allows derived classes to provide custom colorizer implementations for special highlighting definitions.
- /// </summary>
- /// <returns></returns>
- protected virtual IVisualLineTransformer CreateColorizer(IHighlightingDefinition highlightingDefinition)
- {
- if (highlightingDefinition == null)
- throw new ArgumentNullException("highlightingDefinition");
- return new HighlightingColorizer(highlightingDefinition.MainRuleSet);
- }
- #endregion
-
- #region WordWrap
- /// <summary>
- /// Word wrap dependency property.
- /// </summary>
- public static readonly DependencyProperty WordWrapProperty =
- DependencyProperty.Register("WordWrap", typeof(bool), typeof(TextEditor),
- new FrameworkPropertyMetadata(Boxes.False));
-
- /// <summary>
- /// Specifies whether the text editor uses word wrapping.
- /// </summary>
- /// <remarks>
- /// Setting WordWrap=true has the same effect as setting HorizontalScrollBarVisibility=Disabled and will override the
- /// HorizontalScrollBarVisibility setting.
- /// </remarks>
- public bool WordWrap {
- get { return (bool)GetValue(WordWrapProperty); }
- set { SetValue(WordWrapProperty, Boxes.Box(value)); }
- }
- #endregion
-
- #region IsReadOnly
- /// <summary>
- /// IsReadOnly dependency property.
- /// </summary>
- public static readonly DependencyProperty IsReadOnlyProperty =
- DependencyProperty.Register("IsReadOnly", typeof(bool), typeof(TextEditor),
- new FrameworkPropertyMetadata(Boxes.False, OnIsReadOnlyChanged));
-
- /// <summary>
- /// Specifies whether the user can change the text editor content.
- /// Setting this property will replace the
- /// <see cref="Editing.TextArea.ReadOnlySectionProvider">TextArea.ReadOnlySectionProvider</see>.
- /// </summary>
- public bool IsReadOnly {
- get { return (bool)GetValue(IsReadOnlyProperty); }
- set { SetValue(IsReadOnlyProperty, Boxes.Box(value)); }
- }
-
- static void OnIsReadOnlyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
- {
- TextEditor editor = d as TextEditor;
- if (editor != null) {
- if ((bool)e.NewValue)
- editor.TextArea.ReadOnlySectionProvider = ReadOnlyDocument.Instance;
- else
- editor.TextArea.ReadOnlySectionProvider = NoReadOnlySections.Instance;
-
- TextEditorAutomationPeer peer = TextEditorAutomationPeer.FromElement(editor) as TextEditorAutomationPeer;
- if (peer != null) {
- peer.RaiseIsReadOnlyChanged((bool)e.OldValue, (bool)e.NewValue);
- }
- }
- }
- #endregion
-
- #region IsModified
- /// <summary>
- /// Dependency property for <see cref="IsModified"/>
- /// </summary>
- public static readonly DependencyProperty IsModifiedProperty =
- DependencyProperty.Register("IsModified", typeof(bool), typeof(TextEditor),
- new FrameworkPropertyMetadata(Boxes.False, OnIsModifiedChanged));
-
- /// <summary>
- /// Gets/Sets the 'modified' flag.
- /// </summary>
- public bool IsModified {
- get { return (bool)GetValue(IsModifiedProperty); }
- set { SetValue(IsModifiedProperty, Boxes.Box(value)); }
- }
-
- static void OnIsModifiedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
- {
- TextEditor editor = d as TextEditor;
- if (editor != null) {
- TextDocument document = editor.Document;
- if (document != null) {
- UndoStack undoStack = document.UndoStack;
- if ((bool)e.NewValue) {
- if (undoStack.IsOriginalFile)
- undoStack.DiscardOriginalFileMarker();
- } else {
- undoStack.MarkAsOriginalFile();
- }
- }
- }
- }
-
- bool HandleIsOriginalChanged(PropertyChangedEventArgs e)
- {
- if (e.PropertyName == "IsOriginalFile") {
- TextDocument document = this.Document;
- if (document != null) {
- SetCurrentValue(IsModifiedProperty, Boxes.Box(!document.UndoStack.IsOriginalFile));
- }
- return true;
- } else {
- return false;
- }
- }
- #endregion
-
- #region ShowLineNumbers
- /// <summary>
- /// ShowLineNumbers dependency property.
- /// </summary>
- public static readonly DependencyProperty ShowLineNumbersProperty =
- DependencyProperty.Register("ShowLineNumbers", typeof(bool), typeof(TextEditor),
- new FrameworkPropertyMetadata(Boxes.False, OnShowLineNumbersChanged));
-
- /// <summary>
- /// Specifies whether line numbers are shown on the left to the text view.
- /// </summary>
- public bool ShowLineNumbers {
- get { return (bool)GetValue(ShowLineNumbersProperty); }
- set { SetValue(ShowLineNumbersProperty, Boxes.Box(value)); }
- }
-
- static void OnShowLineNumbersChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
- {
- TextEditor editor = (TextEditor)d;
- var leftMargins = editor.TextArea.LeftMargins;
- if ((bool)e.NewValue) {
- LineNumberMargin lineNumbers = new LineNumberMargin();
- Line line = (Line)DottedLineMargin.Create();
- leftMargins.Insert(0, lineNumbers);
- leftMargins.Insert(1, line);
- var lineNumbersForeground = new Binding("LineNumbersForeground") { Source = editor };
- line.SetBinding(Line.StrokeProperty, lineNumbersForeground);
- lineNumbers.SetBinding(Control.ForegroundProperty, lineNumbersForeground);
- } else {
- for (int i = 0; i < leftMargins.Count; i++) {
- if (leftMargins[i] is LineNumberMargin) {
- leftMargins.RemoveAt(i);
- if (i < leftMargins.Count && DottedLineMargin.IsDottedLineMargin(leftMargins[i])) {
- leftMargins.RemoveAt(i);
- }
- break;
- }
- }
- }
- }
- #endregion
-
- #region LineNumbersForeground
- /// <summary>
- /// LineNumbersForeground dependency property.
- /// </summary>
- public static readonly DependencyProperty LineNumbersForegroundProperty =
- DependencyProperty.Register("LineNumbersForeground", typeof(Brush), typeof(TextEditor),
- new FrameworkPropertyMetadata(Brushes.Gray, OnLineNumbersForegroundChanged));
-
- /// <summary>
- /// Gets/sets the Brush used for displaying the foreground color of line numbers.
- /// </summary>
- public Brush LineNumbersForeground {
- get { return (Brush)GetValue(LineNumbersForegroundProperty); }
- set { SetValue(LineNumbersForegroundProperty, value); }
- }
-
- static void OnLineNumbersForegroundChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
- {
- TextEditor editor = (TextEditor)d;
- var lineNumberMargin = editor.TextArea.LeftMargins.FirstOrDefault(margin => margin is LineNumberMargin) as LineNumberMargin;;
-
- if (lineNumberMargin != null) {
- lineNumberMargin.SetValue(Control.ForegroundProperty, e.NewValue);
- }
- }
- #endregion
-
- #region TextBoxBase-like methods
- /// <summary>
- /// Appends text to the end of the document.
- /// </summary>
- public void AppendText(string textData)
- {
- var document = GetDocument();
- document.Insert(document.TextLength, textData);
- }
-
- /// <summary>
- /// Begins a group of document changes.
- /// </summary>
- public void BeginChange()
- {
- GetDocument().BeginUpdate();
- }
-
- /// <summary>
- /// Copies the current selection to the clipboard.
- /// </summary>
- public void Copy()
- {
- Execute(ApplicationCommands.Copy);
- }
-
- /// <summary>
- /// Removes the current selection and copies it to the clipboard.
- /// </summary>
- public void Cut()
- {
- Execute(ApplicationCommands.Cut);
- }
-
- /// <summary>
- /// Begins a group of document changes and returns an object that ends the group of document
- /// changes when it is disposed.
- /// </summary>
- public IDisposable DeclareChangeBlock()
- {
- return GetDocument().RunUpdate();
- }
-
- /// <summary>
- /// Ends the current group of document changes.
- /// </summary>
- public void EndChange()
- {
- GetDocument().EndUpdate();
- }
-
- /// <summary>
- /// Scrolls one line down.
- /// </summary>
- public void LineDown()
- {
- if (scrollViewer != null)
- scrollViewer.LineDown();
- }
-
- /// <summary>
- /// Scrolls to the left.
- /// </summary>
- public void LineLeft()
- {
- if (scrollViewer != null)
- scrollViewer.LineLeft();
- }
-
- /// <summary>
- /// Scrolls to the right.
- /// </summary>
- public void LineRight()
- {
- if (scrollViewer != null)
- scrollViewer.LineRight();
- }
-
- /// <summary>
- /// Scrolls one line up.
- /// </summary>
- public void LineUp()
- {
- if (scrollViewer != null)
- scrollViewer.LineUp();
- }
-
- /// <summary>
- /// Scrolls one page down.
- /// </summary>
- public void PageDown()
- {
- if (scrollViewer != null)
- scrollViewer.PageDown();
- }
-
- /// <summary>
- /// Scrolls one page up.
- /// </summary>
- public void PageUp()
- {
- if (scrollViewer != null)
- scrollViewer.PageUp();
- }
-
- /// <summary>
- /// Scrolls one page left.
- /// </summary>
- public void PageLeft()
- {
- if (scrollViewer != null)
- scrollViewer.PageLeft();
- }
-
- /// <summary>
- /// Scrolls one page right.
- /// </summary>
- public void PageRight()
- {
- if (scrollViewer != null)
- scrollViewer.PageRight();
- }
-
- /// <summary>
- /// Pastes the clipboard content.
- /// </summary>
- public void Paste()
- {
- Execute(ApplicationCommands.Paste);
- }
-
- /// <summary>
- /// Redoes the most recent undone command.
- /// </summary>
- /// <returns>True is the redo operation was successful, false is the redo stack is empty.</returns>
- public bool Redo()
- {
- if (CanExecute(ApplicationCommands.Redo)) {
- Execute(ApplicationCommands.Redo);
- return true;
- }
- return false;
- }
-
- /// <summary>
- /// Scrolls to the end of the document.
- /// </summary>
- public void ScrollToEnd()
- {
- ApplyTemplate(); // ensure scrollViewer is created
- if (scrollViewer != null)
- scrollViewer.ScrollToEnd();
- }
-
- /// <summary>
- /// Scrolls to the start of the document.
- /// </summary>
- public void ScrollToHome()
- {
- ApplyTemplate(); // ensure scrollViewer is created
- if (scrollViewer != null)
- scrollViewer.ScrollToHome();
- }
-
- /// <summary>
- /// Scrolls to the specified position in the document.
- /// </summary>
- public void ScrollToHorizontalOffset(double offset)
- {
- ApplyTemplate(); // ensure scrollViewer is created
- if (scrollViewer != null)
- scrollViewer.ScrollToHorizontalOffset(offset);
- }
-
- /// <summary>
- /// Scrolls to the specified position in the document.
- /// </summary>
- public void ScrollToVerticalOffset(double offset)
- {
- ApplyTemplate(); // ensure scrollViewer is created
- if (scrollViewer != null)
- scrollViewer.ScrollToVerticalOffset(offset);
- }
-
- /// <summary>
- /// Selects the entire text.
- /// </summary>
- public void SelectAll()
- {
- Execute(ApplicationCommands.SelectAll);
- }
-
- /// <summary>
- /// Undoes the most recent command.
- /// </summary>
- /// <returns>True is the undo operation was successful, false is the undo stack is empty.</returns>
- public bool Undo()
- {
- if (CanExecute(ApplicationCommands.Undo)) {
- Execute(ApplicationCommands.Undo);
- return true;
- }
- return false;
- }
-
- /// <summary>
- /// Gets if the most recent undone command can be redone.
- /// </summary>
- public bool CanRedo {
- get { return CanExecute(ApplicationCommands.Redo); }
- }
-
- /// <summary>
- /// Gets if the most recent command can be undone.
- /// </summary>
- public bool CanUndo {
- get { return CanExecute(ApplicationCommands.Undo); }
- }
-
- /// <summary>
- /// Gets the vertical size of the document.
- /// </summary>
- public double ExtentHeight {
- get {
- return scrollViewer != null ? scrollViewer.ExtentHeight : 0;
- }
- }
-
- /// <summary>
- /// Gets the horizontal size of the current document region.
- /// </summary>
- public double ExtentWidth {
- get {
- return scrollViewer != null ? scrollViewer.ExtentWidth : 0;
- }
- }
-
- /// <summary>
- /// Gets the horizontal size of the viewport.
- /// </summary>
- public double ViewportHeight {
- get {
- return scrollViewer != null ? scrollViewer.ViewportHeight : 0;
- }
- }
-
- /// <summary>
- /// Gets the horizontal size of the viewport.
- /// </summary>
- public double ViewportWidth {
- get {
- return scrollViewer != null ? scrollViewer.ViewportWidth : 0;
- }
- }
-
- /// <summary>
- /// Gets the vertical scroll position.
- /// </summary>
- public double VerticalOffset {
- get {
- return scrollViewer != null ? scrollViewer.VerticalOffset : 0;
- }
- }
-
- /// <summary>
- /// Gets the horizontal scroll position.
- /// </summary>
- public double HorizontalOffset {
- get {
- return scrollViewer != null ? scrollViewer.HorizontalOffset : 0;
- }
- }
- #endregion
-
- #region TextBox methods
- /// <summary>
- /// Gets/Sets the selected text.
- /// </summary>
- [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
- public string SelectedText {
- get {
- TextArea textArea = this.TextArea;
- // We'll get the text from the whole surrounding segment.
- // This is done to ensure that SelectedText.Length == SelectionLength.
- if (textArea != null && textArea.Document != null && !textArea.Selection.IsEmpty)
- return textArea.Document.GetText(textArea.Selection.SurroundingSegment);
- else
- return string.Empty;
- }
- set {
- if (value == null)
- throw new ArgumentNullException("value");
- TextArea textArea = this.TextArea;
- if (textArea != null && textArea.Document != null) {
- int offset = this.SelectionStart;
- int length = this.SelectionLength;
- textArea.Document.Replace(offset, length, value);
- // keep inserted text selected
- textArea.Selection = SimpleSelection.Create(textArea, offset, offset + value.Length);
- }
- }
- }
-
- /// <summary>
- /// Gets/sets the caret position.
- /// </summary>
- [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
- public int CaretOffset {
- get {
- TextArea textArea = this.TextArea;
- if (textArea != null)
- return textArea.Caret.Offset;
- else
- return 0;
- }
- set {
- TextArea textArea = this.TextArea;
- if (textArea != null)
- textArea.Caret.Offset = value;
- }
- }
-
- /// <summary>
- /// Gets/sets the start position of the selection.
- /// </summary>
- [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
- public int SelectionStart {
- get {
- TextArea textArea = this.TextArea;
- if (textArea != null) {
- if (textArea.Selection.IsEmpty)
- return textArea.Caret.Offset;
- else
- return textArea.Selection.SurroundingSegment.Offset;
- } else {
- return 0;
- }
- }
- set {
- Select(value, SelectionLength);
- }
- }
-
- /// <summary>
- /// Gets/sets the length of the selection.
- /// </summary>
- [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
- public int SelectionLength {
- get {
- TextArea textArea = this.TextArea;
- if (textArea != null && !textArea.Selection.IsEmpty)
- return textArea.Selection.SurroundingSegment.Length;
- else
- return 0;
- }
- set {
- Select(SelectionStart, value);
- }
- }
-
- /// <summary>
- /// Selects the specified text section.
- /// </summary>
- public void Select(int start, int length)
- {
- int documentLength = Document != null ? Document.TextLength : 0;
- if (start < 0 || start > documentLength)
- throw new ArgumentOutOfRangeException("start", start, "Value must be between 0 and " + documentLength);
- if (length < 0 || start + length > documentLength)
- throw new ArgumentOutOfRangeException("length", length, "Value must be between 0 and " + (documentLength - length));
- textArea.Selection = SimpleSelection.Create(textArea, start, start + length);
- textArea.Caret.Offset = start + length;
- }
-
- /// <summary>
- /// Gets the number of lines in the document.
- /// </summary>
- [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
- public int LineCount {
- get {
- TextDocument document = this.Document;
- if (document != null)
- return document.LineCount;
- else
- return 1;
- }
- }
-
- /// <summary>
- /// Clears the text.
- /// </summary>
- public void Clear()
- {
- this.Text = string.Empty;
- }
- #endregion
-
- #region Loading from stream
- /// <summary>
- /// Loads the text from the stream, auto-detecting the encoding.
- /// </summary>
- /// <remarks>
- /// This method sets <see cref="IsModified"/> to false.
- /// </remarks>
- public void Load(Stream stream)
- {
- using (StreamReader reader = FileReader.OpenStream(stream, this.Encoding ?? Encoding.UTF8)) {
- this.Text = reader.ReadToEnd();
- this.Encoding = reader.CurrentEncoding; // assign encoding after ReadToEnd() so that the StreamReader can autodetect the encoding
- }
- this.IsModified = false;
- }
-
- /// <summary>
- /// Loads the text from the stream, auto-detecting the encoding.
- /// </summary>
- public void Load(string fileName)
- {
- if (fileName == null)
- throw new ArgumentNullException("fileName");
- using (FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read)) {
- Load(fs);
- }
- }
-
- /// <summary>
- /// Gets/sets the encoding used when the file is saved.
- /// </summary>
- [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
- public Encoding Encoding { get; set; }
-
- /// <summary>
- /// Saves the text to the stream.
- /// </summary>
- /// <remarks>
- /// This method sets <see cref="IsModified"/> to false.
- /// </remarks>
- public void Save(Stream stream)
- {
- if (stream == null)
- throw new ArgumentNullException("stream");
- StreamWriter writer = new StreamWriter(stream, this.Encoding ?? Encoding.UTF8);
- writer.Write(this.Text);
- writer.Flush();
- // do not close the stream
- this.IsModified = false;
- }
-
- /// <summary>
- /// Saves the text to the file.
- /// </summary>
- public void Save(string fileName)
- {
- if (fileName == null)
- throw new ArgumentNullException("fileName");
- using (FileStream fs = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None)) {
- Save(fs);
- }
- }
- #endregion
-
- #region MouseHover events
- /// <summary>
- /// The PreviewMouseHover event.
- /// </summary>
- public static readonly RoutedEvent PreviewMouseHoverEvent =
- TextView.PreviewMouseHoverEvent.AddOwner(typeof(TextEditor));
-
- /// <summary>
- /// The MouseHover event.
- /// </summary>
- public static readonly RoutedEvent MouseHoverEvent =
- TextView.MouseHoverEvent.AddOwner(typeof(TextEditor));
-
-
- /// <summary>
- /// The PreviewMouseHoverStopped event.
- /// </summary>
- public static readonly RoutedEvent PreviewMouseHoverStoppedEvent =
- TextView.PreviewMouseHoverStoppedEvent.AddOwner(typeof(TextEditor));
-
- /// <summary>
- /// The MouseHoverStopped event.
- /// </summary>
- public static readonly RoutedEvent MouseHoverStoppedEvent =
- TextView.MouseHoverStoppedEvent.AddOwner(typeof(TextEditor));
-
-
- /// <summary>
- /// Occurs when the mouse has hovered over a fixed location for some time.
- /// </summary>
- public event MouseEventHandler PreviewMouseHover {
- add { AddHandler(PreviewMouseHoverEvent, value); }
- remove { RemoveHandler(PreviewMouseHoverEvent, value); }
- }
-
- /// <summary>
- /// Occurs when the mouse has hovered over a fixed location for some time.
- /// </summary>
- public event MouseEventHandler MouseHover {
- add { AddHandler(MouseHoverEvent, value); }
- remove { RemoveHandler(MouseHoverEvent, value); }
- }
-
- /// <summary>
- /// Occurs when the mouse had previously hovered but now started moving again.
- /// </summary>
- public event MouseEventHandler PreviewMouseHoverStopped {
- add { AddHandler(PreviewMouseHoverStoppedEvent, value); }
- remove { RemoveHandler(PreviewMouseHoverStoppedEvent, value); }
- }
-
- /// <summary>
- /// Occurs when the mouse had previously hovered but now started moving again.
- /// </summary>
- public event MouseEventHandler MouseHoverStopped {
- add { AddHandler(MouseHoverStoppedEvent, value); }
- remove { RemoveHandler(MouseHoverStoppedEvent, value); }
- }
- #endregion
-
- #region ScrollBarVisibility
- /// <summary>
- /// Dependency property for <see cref="HorizontalScrollBarVisibility"/>
- /// </summary>
- public static readonly DependencyProperty HorizontalScrollBarVisibilityProperty = ScrollViewer.HorizontalScrollBarVisibilityProperty.AddOwner(typeof(TextEditor), new FrameworkPropertyMetadata(ScrollBarVisibility.Visible));
-
- /// <summary>
- /// Gets/Sets the horizontal scroll bar visibility.
- /// </summary>
- public ScrollBarVisibility HorizontalScrollBarVisibility {
- get { return (ScrollBarVisibility)GetValue(HorizontalScrollBarVisibilityProperty); }
- set { SetValue(HorizontalScrollBarVisibilityProperty, value); }
- }
-
- /// <summary>
- /// Dependency property for <see cref="VerticalScrollBarVisibility"/>
- /// </summary>
- public static readonly DependencyProperty VerticalScrollBarVisibilityProperty = ScrollViewer.VerticalScrollBarVisibilityProperty.AddOwner(typeof(TextEditor), new FrameworkPropertyMetadata(ScrollBarVisibility.Visible));
-
- /// <summary>
- /// Gets/Sets the vertical scroll bar visibility.
- /// </summary>
- public ScrollBarVisibility VerticalScrollBarVisibility {
- get { return (ScrollBarVisibility)GetValue(VerticalScrollBarVisibilityProperty); }
- set { SetValue(VerticalScrollBarVisibilityProperty, value); }
- }
- #endregion
-
- object IServiceProvider.GetService(Type serviceType)
- {
- return textArea.GetService(serviceType);
- }
-
- /// <summary>
- /// Gets the text view position from a point inside the editor.
- /// </summary>
- /// <param name="point">The position, relative to top left
- /// corner of TextEditor control</param>
- /// <returns>The text view position, or null if the point is outside the document.</returns>
- public TextViewPosition? GetPositionFromPoint(Point point)
- {
- if (this.Document == null)
- return null;
- TextView textView = this.TextArea.TextView;
- return textView.GetPosition(TranslatePoint(point, textView) + textView.ScrollOffset);
- }
-
- /// <summary>
- /// Scrolls to the specified line.
- /// This method requires that the TextEditor was already assigned a size (WPF layout must have run prior).
- /// </summary>
- public void ScrollToLine(int line)
- {
- ScrollTo(line, -1);
- }
-
- /// <summary>
- /// Scrolls to the specified line/column.
- /// This method requires that the TextEditor was already assigned a size (WPF layout must have run prior).
- /// </summary>
- public void ScrollTo(int line, int column)
- {
- const double MinimumScrollPercentage = 0.3;
-
- TextView textView = textArea.TextView;
- TextDocument document = textView.Document;
- if (scrollViewer != null && document != null) {
- if (line < 1)
- line = 1;
- if (line > document.LineCount)
- line = document.LineCount;
-
- IScrollInfo scrollInfo = textView;
- if (!scrollInfo.CanHorizontallyScroll) {
- // Word wrap is enabled. Ensure that we have up-to-date info about line height so that we scroll
- // to the correct position.
- // This avoids that the user has to repeat the ScrollTo() call several times when there are very long lines.
- VisualLine vl = textView.GetOrConstructVisualLine(document.GetLineByNumber(line));
- double remainingHeight = scrollViewer.ViewportHeight / 2;
- while (remainingHeight > 0) {
- DocumentLine prevLine = vl.FirstDocumentLine.PreviousLine;
- if (prevLine == null)
- break;
- vl = textView.GetOrConstructVisualLine(prevLine);
- remainingHeight -= vl.Height;
- }
- }
-
- Point p = textArea.TextView.GetVisualPosition(new TextViewPosition(line, Math.Max(1, column)), VisualYPosition.LineMiddle);
- double verticalPos = p.Y - scrollViewer.ViewportHeight / 2;
- if (Math.Abs(verticalPos - scrollViewer.VerticalOffset) > MinimumScrollPercentage * scrollViewer.ViewportHeight) {
- scrollViewer.ScrollToVerticalOffset(Math.Max(0, verticalPos));
- }
- if (column > 0) {
- if (p.X > scrollViewer.ViewportWidth - Caret.MinimumDistanceToViewBorder * 2) {
- double horizontalPos = Math.Max(0, p.X - scrollViewer.ViewportWidth / 2);
- if (Math.Abs(horizontalPos - scrollViewer.HorizontalOffset) > MinimumScrollPercentage * scrollViewer.ViewportWidth) {
- scrollViewer.ScrollToHorizontalOffset(horizontalPos);
- }
- } else {
- scrollViewer.ScrollToHorizontalOffset(0);
- }
- }
- }
- }
- }
+ /// <summary>
+ /// The text editor control.
+ /// Contains a scrollable TextArea.
+ /// </summary>
+ [Localizability(LocalizationCategory.Text), ContentProperty("Text")]
+ public class TextEditor : Control, ITextEditorComponent, IServiceProvider, IWeakEventListener
+ {
+ #region Constructors
+ static TextEditor()
+ {
+ DefaultStyleKeyProperty.OverrideMetadata(typeof(TextEditor),
+ new FrameworkPropertyMetadata(typeof(TextEditor)));
+ FocusableProperty.OverrideMetadata(typeof(TextEditor),
+ new FrameworkPropertyMetadata(Boxes.True));
+ }
+
+ /// <summary>
+ /// Creates a new TextEditor instance.
+ /// </summary>
+ public TextEditor() : this(new TextArea())
+ {
+ }
+
+ /// <summary>
+ /// Creates a new TextEditor instance.
+ /// </summary>
+ protected TextEditor(TextArea textArea)
+ {
+ if (textArea == null)
+ throw new ArgumentNullException("textArea");
+ this.textArea = textArea;
+
+ textArea.TextView.Services.AddService(typeof(TextEditor), this);
+
+ SetCurrentValue(OptionsProperty, textArea.Options);
+ SetCurrentValue(DocumentProperty, new TextDocument());
+ }
+
+ #endregion
+
+ /// <inheritdoc/>
+ protected override System.Windows.Automation.Peers.AutomationPeer OnCreateAutomationPeer()
+ {
+ return new TextEditorAutomationPeer(this);
+ }
+
+ /// Forward focus to TextArea.
+ /// <inheritdoc/>
+ protected override void OnGotKeyboardFocus(KeyboardFocusChangedEventArgs e)
+ {
+ base.OnGotKeyboardFocus(e);
+ if (e.NewFocus == this)
+ {
+ Keyboard.Focus(this.TextArea);
+ e.Handled = true;
+ }
+ }
+
+ #region Document property
+ /// <summary>
+ /// Document property.
+ /// </summary>
+ public static readonly DependencyProperty DocumentProperty
+ = TextView.DocumentProperty.AddOwner(
+ typeof(TextEditor), new FrameworkPropertyMetadata(OnDocumentChanged));
+
+ /// <summary>
+ /// Gets/Sets the document displayed by the text editor.
+ /// This is a dependency property.
+ /// </summary>
+ public TextDocument Document
+ {
+ get { return (TextDocument)GetValue(DocumentProperty); }
+ set { SetValue(DocumentProperty, value); }
+ }
+
+ /// <summary>
+ /// Occurs when the document property has changed.
+ /// </summary>
+ public event EventHandler DocumentChanged;
+
+ /// <summary>
+ /// Raises the <see cref="DocumentChanged"/> event.
+ /// </summary>
+ protected virtual void OnDocumentChanged(EventArgs e)
+ {
+ if (DocumentChanged != null)
+ {
+ DocumentChanged(this, e);
+ }
+ }
+
+ static void OnDocumentChanged(DependencyObject dp, DependencyPropertyChangedEventArgs e)
+ {
+ ((TextEditor)dp).OnDocumentChanged((TextDocument)e.OldValue, (TextDocument)e.NewValue);
+ }
+
+ void OnDocumentChanged(TextDocument oldValue, TextDocument newValue)
+ {
+ if (oldValue != null)
+ {
+ TextDocumentWeakEventManager.TextChanged.RemoveListener(oldValue, this);
+ PropertyChangedEventManager.RemoveListener(oldValue.UndoStack, this, "IsOriginalFile");
+ }
+ textArea.Document = newValue;
+ if (newValue != null)
+ {
+ TextDocumentWeakEventManager.TextChanged.AddListener(newValue, this);
+ PropertyChangedEventManager.AddListener(newValue.UndoStack, this, "IsOriginalFile");
+ }
+ OnDocumentChanged(EventArgs.Empty);
+ OnTextChanged(EventArgs.Empty);
+ }
+ #endregion
+
+ #region Options property
+ /// <summary>
+ /// Options property.
+ /// </summary>
+ public static readonly DependencyProperty OptionsProperty
+ = TextView.OptionsProperty.AddOwner(typeof(TextEditor), new FrameworkPropertyMetadata(OnOptionsChanged));
+
+ /// <summary>
+ /// Gets/Sets the options currently used by the text editor.
+ /// </summary>
+ public TextEditorOptions Options
+ {
+ get { return (TextEditorOptions)GetValue(OptionsProperty); }
+ set { SetValue(OptionsProperty, value); }
+ }
+
+ /// <summary>
+ /// Occurs when a text editor option has changed.
+ /// </summary>
+ public event PropertyChangedEventHandler OptionChanged;
+
+ /// <summary>
+ /// Raises the <see cref="OptionChanged"/> event.
+ /// </summary>
+ protected virtual void OnOptionChanged(PropertyChangedEventArgs e)
+ {
+ if (OptionChanged != null)
+ {
+ OptionChanged(this, e);
+ }
+ }
+
+ static void OnOptionsChanged(DependencyObject dp, DependencyPropertyChangedEventArgs e)
+ {
+ ((TextEditor)dp).OnOptionsChanged((TextEditorOptions)e.OldValue, (TextEditorOptions)e.NewValue);
+ }
+
+ void OnOptionsChanged(TextEditorOptions oldValue, TextEditorOptions newValue)
+ {
+ if (oldValue != null)
+ {
+ PropertyChangedWeakEventManager.RemoveListener(oldValue, this);
+ }
+ textArea.Options = newValue;
+ if (newValue != null)
+ {
+ PropertyChangedWeakEventManager.AddListener(newValue, this);
+ }
+ OnOptionChanged(new PropertyChangedEventArgs(null));
+ }
+
+ /// <inheritdoc cref="IWeakEventListener.ReceiveWeakEvent"/>
+ protected virtual bool ReceiveWeakEvent(Type managerType, object sender, EventArgs e)
+ {
+ if (managerType == typeof(PropertyChangedWeakEventManager))
+ {
+ OnOptionChanged((PropertyChangedEventArgs)e);
+ return true;
+ }
+ else if (managerType == typeof(TextDocumentWeakEventManager.TextChanged))
+ {
+ OnTextChanged(e);
+ return true;
+ }
+ else if (managerType == typeof(PropertyChangedEventManager))
+ {
+ return HandleIsOriginalChanged((PropertyChangedEventArgs)e);
+ }
+ return false;
+ }
+
+ bool IWeakEventListener.ReceiveWeakEvent(Type managerType, object sender, EventArgs e)
+ {
+ return ReceiveWeakEvent(managerType, sender, e);
+ }
+ #endregion
+
+ #region Text property
+ /// <summary>
+ /// Gets/Sets the text of the current document.
+ /// </summary>
+ [Localizability(LocalizationCategory.Text), DefaultValue("")]
+ public string Text
+ {
+ get
+ {
+ TextDocument document = this.Document;
+ return document != null ? document.Text : string.Empty;
+ }
+ set
+ {
+ TextDocument document = GetDocument();
+ document.Text = value ?? string.Empty;
+ // after replacing the full text, the caret is positioned at the end of the document
+ // - reset it to the beginning.
+ this.CaretOffset = 0;
+ document.UndoStack.ClearAll();
+ }
+ }
+
+ TextDocument GetDocument()
+ {
+ TextDocument document = this.Document;
+ if (document == null)
+ throw ThrowUtil.NoDocumentAssigned();
+ return document;
+ }
+
+ /// <summary>
+ /// Occurs when the Text property changes.
+ /// </summary>
+ public event EventHandler TextChanged;
+
+ /// <summary>
+ /// Raises the <see cref="TextChanged"/> event.
+ /// </summary>
+ protected virtual void OnTextChanged(EventArgs e)
+ {
+ if (TextChanged != null)
+ {
+ TextChanged(this, e);
+ }
+ }
+ #endregion
+
+ #region TextArea / ScrollViewer properties
+ readonly TextArea textArea;
+ ScrollViewer scrollViewer;
+
+ /// <summary>
+ /// Is called after the template was applied.
+ /// </summary>
+ public override void OnApplyTemplate()
+ {
+ base.OnApplyTemplate();
+ scrollViewer = (ScrollViewer)Template.FindName("PART_ScrollViewer", this);
+ }
+
+ /// <summary>
+ /// Gets the text area.
+ /// </summary>
+ public TextArea TextArea
+ {
+ get
+ {
+ return textArea;
+ }
+ }
+
+ /// <summary>
+ /// Gets the scroll viewer used by the text editor.
+ /// This property can return null if the template has not been applied / does not contain a scroll viewer.
+ /// </summary>
+ internal ScrollViewer ScrollViewer
+ {
+ get { return scrollViewer; }
+ }
+
+ bool CanExecute(RoutedUICommand command)
+ {
+ TextArea textArea = this.TextArea;
+ if (textArea == null)
+ return false;
+ else
+ return command.CanExecute(null, textArea);
+ }
+
+ void Execute(RoutedUICommand command)
+ {
+ TextArea textArea = this.TextArea;
+ if (textArea != null)
+ command.Execute(null, textArea);
+ }
+ #endregion
+
+ #region Syntax highlighting
+ /// <summary>
+ /// The <see cref="SyntaxHighlighting"/> property.
+ /// </summary>
+ public static readonly DependencyProperty SyntaxHighlightingProperty =
+ DependencyProperty.Register("SyntaxHighlighting", typeof(IHighlightingDefinition), typeof(TextEditor),
+ new FrameworkPropertyMetadata(OnSyntaxHighlightingChanged));
+
+
+ /// <summary>
+ /// Gets/sets the syntax highlighting definition used to colorize the text.
+ /// </summary>
+ public IHighlightingDefinition SyntaxHighlighting
+ {
+ get { return (IHighlightingDefinition)GetValue(SyntaxHighlightingProperty); }
+ set { SetValue(SyntaxHighlightingProperty, value); }
+ }
+
+ IVisualLineTransformer colorizer;
+
+ static void OnSyntaxHighlightingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ ((TextEditor)d).OnSyntaxHighlightingChanged(e.NewValue as IHighlightingDefinition);
+ }
+
+ void OnSyntaxHighlightingChanged(IHighlightingDefinition newValue)
+ {
+ if (colorizer != null)
+ {
+ this.TextArea.TextView.LineTransformers.Remove(colorizer);
+ colorizer = null;
+ }
+ if (newValue != null)
+ {
+ colorizer = CreateColorizer(newValue);
+ this.TextArea.TextView.LineTransformers.Insert(0, colorizer);
+ }
+ }
+
+ /// <summary>
+ /// Creates the highlighting colorizer for the specified highlighting definition.
+ /// Allows derived classes to provide custom colorizer implementations for special highlighting definitions.
+ /// </summary>
+ /// <returns></returns>
+ protected virtual IVisualLineTransformer CreateColorizer(IHighlightingDefinition highlightingDefinition)
+ {
+ if (highlightingDefinition == null)
+ throw new ArgumentNullException("highlightingDefinition");
+ return new HighlightingColorizer(highlightingDefinition.MainRuleSet);
+ }
+ #endregion
+
+ #region WordWrap
+ /// <summary>
+ /// Word wrap dependency property.
+ /// </summary>
+ public static readonly DependencyProperty WordWrapProperty =
+ DependencyProperty.Register("WordWrap", typeof(bool), typeof(TextEditor),
+ new FrameworkPropertyMetadata(Boxes.False));
+
+ /// <summary>
+ /// Specifies whether the text editor uses word wrapping.
+ /// </summary>
+ /// <remarks>
+ /// Setting WordWrap=true has the same effect as setting HorizontalScrollBarVisibility=Disabled and will override the
+ /// HorizontalScrollBarVisibility setting.
+ /// </remarks>
+ public bool WordWrap
+ {
+ get { return (bool)GetValue(WordWrapProperty); }
+ set { SetValue(WordWrapProperty, Boxes.Box(value)); }
+ }
+ #endregion
+
+ #region IsReadOnly
+ /// <summary>
+ /// IsReadOnly dependency property.
+ /// </summary>
+ public static readonly DependencyProperty IsReadOnlyProperty =
+ DependencyProperty.Register("IsReadOnly", typeof(bool), typeof(TextEditor),
+ new FrameworkPropertyMetadata(Boxes.False, OnIsReadOnlyChanged));
+
+ /// <summary>
+ /// Specifies whether the user can change the text editor content.
+ /// Setting this property will replace the
+ /// <see cref="Editing.TextArea.ReadOnlySectionProvider">TextArea.ReadOnlySectionProvider</see>.
+ /// </summary>
+ public bool IsReadOnly
+ {
+ get { return (bool)GetValue(IsReadOnlyProperty); }
+ set { SetValue(IsReadOnlyProperty, Boxes.Box(value)); }
+ }
+
+ static void OnIsReadOnlyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ TextEditor editor = d as TextEditor;
+ if (editor != null)
+ {
+ if ((bool)e.NewValue)
+ editor.TextArea.ReadOnlySectionProvider = ReadOnlyDocument.Instance;
+ else
+ editor.TextArea.ReadOnlySectionProvider = NoReadOnlySections.Instance;
+
+ TextEditorAutomationPeer peer = TextEditorAutomationPeer.FromElement(editor) as TextEditorAutomationPeer;
+ if (peer != null)
+ {
+ peer.RaiseIsReadOnlyChanged((bool)e.OldValue, (bool)e.NewValue);
+ }
+ }
+ }
+ #endregion
+
+ #region IsModified
+ /// <summary>
+ /// Dependency property for <see cref="IsModified"/>
+ /// </summary>
+ public static readonly DependencyProperty IsModifiedProperty =
+ DependencyProperty.Register("IsModified", typeof(bool), typeof(TextEditor),
+ new FrameworkPropertyMetadata(Boxes.False, OnIsModifiedChanged));
+
+ /// <summary>
+ /// Gets/Sets the 'modified' flag.
+ /// </summary>
+ public bool IsModified
+ {
+ get { return (bool)GetValue(IsModifiedProperty); }
+ set { SetValue(IsModifiedProperty, Boxes.Box(value)); }
+ }
+
+ static void OnIsModifiedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ TextEditor editor = d as TextEditor;
+ if (editor != null)
+ {
+ TextDocument document = editor.Document;
+ if (document != null)
+ {
+ UndoStack undoStack = document.UndoStack;
+ if ((bool)e.NewValue)
+ {
+ if (undoStack.IsOriginalFile)
+ undoStack.DiscardOriginalFileMarker();
+ }
+ else
+ {
+ undoStack.MarkAsOriginalFile();
+ }
+ }
+ }
+ }
+
+ bool HandleIsOriginalChanged(PropertyChangedEventArgs e)
+ {
+ if (e.PropertyName == "IsOriginalFile")
+ {
+ TextDocument document = this.Document;
+ if (document != null)
+ {
+ SetCurrentValue(IsModifiedProperty, Boxes.Box(!document.UndoStack.IsOriginalFile));
+ }
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ #endregion
+
+ #region ShowLineNumbers
+ /// <summary>
+ /// ShowLineNumbers dependency property.
+ /// </summary>
+ public static readonly DependencyProperty ShowLineNumbersProperty =
+ DependencyProperty.Register("ShowLineNumbers", typeof(bool), typeof(TextEditor),
+ new FrameworkPropertyMetadata(Boxes.False, OnShowLineNumbersChanged));
+
+ /// <summary>
+ /// Specifies whether line numbers are shown on the left to the text view.
+ /// </summary>
+ public bool ShowLineNumbers
+ {
+ get { return (bool)GetValue(ShowLineNumbersProperty); }
+ set { SetValue(ShowLineNumbersProperty, Boxes.Box(value)); }
+ }
+
+ static void OnShowLineNumbersChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ TextEditor editor = (TextEditor)d;
+ var leftMargins = editor.TextArea.LeftMargins;
+ if ((bool)e.NewValue)
+ {
+ LineNumberMargin lineNumbers = new LineNumberMargin();
+ Line line = (Line)DottedLineMargin.Create();
+ leftMargins.Insert(0, lineNumbers);
+ leftMargins.Insert(1, line);
+ var lineNumbersForeground = new Binding("LineNumbersForeground") { Source = editor };
+ line.SetBinding(Line.StrokeProperty, lineNumbersForeground);
+ lineNumbers.SetBinding(Control.ForegroundProperty, lineNumbersForeground);
+ }
+ else
+ {
+ for (int i = 0; i < leftMargins.Count; i++)
+ {
+ if (leftMargins[i] is LineNumberMargin)
+ {
+ leftMargins.RemoveAt(i);
+ if (i < leftMargins.Count && DottedLineMargin.IsDottedLineMargin(leftMargins[i]))
+ {
+ leftMargins.RemoveAt(i);
+ }
+ break;
+ }
+ }
+ }
+ }
+ #endregion
+
+ #region LineNumbersForeground
+ /// <summary>
+ /// LineNumbersForeground dependency property.
+ /// </summary>
+ public static readonly DependencyProperty LineNumbersForegroundProperty =
+ DependencyProperty.Register("LineNumbersForeground", typeof(Brush), typeof(TextEditor),
+ new FrameworkPropertyMetadata(Brushes.Gray, OnLineNumbersForegroundChanged));
+
+ /// <summary>
+ /// Gets/sets the Brush used for displaying the foreground color of line numbers.
+ /// </summary>
+ public Brush LineNumbersForeground
+ {
+ get { return (Brush)GetValue(LineNumbersForegroundProperty); }
+ set { SetValue(LineNumbersForegroundProperty, value); }
+ }
+
+ static void OnLineNumbersForegroundChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ TextEditor editor = (TextEditor)d;
+ var lineNumberMargin = editor.TextArea.LeftMargins.FirstOrDefault(margin => margin is LineNumberMargin) as LineNumberMargin; ;
+
+ if (lineNumberMargin != null)
+ {
+ lineNumberMargin.SetValue(Control.ForegroundProperty, e.NewValue);
+ }
+ }
+ #endregion
+
+ #region TextBoxBase-like methods
+ /// <summary>
+ /// Appends text to the end of the document.
+ /// </summary>
+ public void AppendText(string textData)
+ {
+ var document = GetDocument();
+ document.Insert(document.TextLength, textData);
+ }
+
+ /// <summary>
+ /// Begins a group of document changes.
+ /// </summary>
+ public void BeginChange()
+ {
+ GetDocument().BeginUpdate();
+ }
+
+ /// <summary>
+ /// Copies the current selection to the clipboard.
+ /// </summary>
+ public void Copy()
+ {
+ Execute(ApplicationCommands.Copy);
+ }
+
+ /// <summary>
+ /// Removes the current selection and copies it to the clipboard.
+ /// </summary>
+ public void Cut()
+ {
+ Execute(ApplicationCommands.Cut);
+ }
+
+ /// <summary>
+ /// Begins a group of document changes and returns an object that ends the group of document
+ /// changes when it is disposed.
+ /// </summary>
+ public IDisposable DeclareChangeBlock()
+ {
+ return GetDocument().RunUpdate();
+ }
+
+ /// <summary>
+ /// Ends the current group of document changes.
+ /// </summary>
+ public void EndChange()
+ {
+ GetDocument().EndUpdate();
+ }
+
+ /// <summary>
+ /// Scrolls one line down.
+ /// </summary>
+ public void LineDown()
+ {
+ if (scrollViewer != null)
+ scrollViewer.LineDown();
+ }
+
+ /// <summary>
+ /// Scrolls to the left.
+ /// </summary>
+ public void LineLeft()
+ {
+ if (scrollViewer != null)
+ scrollViewer.LineLeft();
+ }
+
+ /// <summary>
+ /// Scrolls to the right.
+ /// </summary>
+ public void LineRight()
+ {
+ if (scrollViewer != null)
+ scrollViewer.LineRight();
+ }
+
+ /// <summary>
+ /// Scrolls one line up.
+ /// </summary>
+ public void LineUp()
+ {
+ if (scrollViewer != null)
+ scrollViewer.LineUp();
+ }
+
+ /// <summary>
+ /// Scrolls one page down.
+ /// </summary>
+ public void PageDown()
+ {
+ if (scrollViewer != null)
+ scrollViewer.PageDown();
+ }
+
+ /// <summary>
+ /// Scrolls one page up.
+ /// </summary>
+ public void PageUp()
+ {
+ if (scrollViewer != null)
+ scrollViewer.PageUp();
+ }
+
+ /// <summary>
+ /// Scrolls one page left.
+ /// </summary>
+ public void PageLeft()
+ {
+ if (scrollViewer != null)
+ scrollViewer.PageLeft();
+ }
+
+ /// <summary>
+ /// Scrolls one page right.
+ /// </summary>
+ public void PageRight()
+ {
+ if (scrollViewer != null)
+ scrollViewer.PageRight();
+ }
+
+ /// <summary>
+ /// Pastes the clipboard content.
+ /// </summary>
+ public void Paste()
+ {
+ Execute(ApplicationCommands.Paste);
+ }
+
+ /// <summary>
+ /// Redoes the most recent undone command.
+ /// </summary>
+ /// <returns>True is the redo operation was successful, false is the redo stack is empty.</returns>
+ public bool Redo()
+ {
+ if (CanExecute(ApplicationCommands.Redo))
+ {
+ Execute(ApplicationCommands.Redo);
+ return true;
+ }
+ return false;
+ }
+
+ /// <summary>
+ /// Scrolls to the end of the document.
+ /// </summary>
+ public void ScrollToEnd()
+ {
+ ApplyTemplate(); // ensure scrollViewer is created
+ if (scrollViewer != null)
+ scrollViewer.ScrollToEnd();
+ }
+
+ /// <summary>
+ /// Scrolls to the start of the document.
+ /// </summary>
+ public void ScrollToHome()
+ {
+ ApplyTemplate(); // ensure scrollViewer is created
+ if (scrollViewer != null)
+ scrollViewer.ScrollToHome();
+ }
+
+ /// <summary>
+ /// Scrolls to the specified position in the document.
+ /// </summary>
+ public void ScrollToHorizontalOffset(double offset)
+ {
+ ApplyTemplate(); // ensure scrollViewer is created
+ if (scrollViewer != null)
+ scrollViewer.ScrollToHorizontalOffset(offset);
+ }
+
+ /// <summary>
+ /// Scrolls to the specified position in the document.
+ /// </summary>
+ public void ScrollToVerticalOffset(double offset)
+ {
+ ApplyTemplate(); // ensure scrollViewer is created
+ if (scrollViewer != null)
+ scrollViewer.ScrollToVerticalOffset(offset);
+ }
+
+ /// <summary>
+ /// Selects the entire text.
+ /// </summary>
+ public void SelectAll()
+ {
+ Execute(ApplicationCommands.SelectAll);
+ }
+
+ /// <summary>
+ /// Undoes the most recent command.
+ /// </summary>
+ /// <returns>True is the undo operation was successful, false is the undo stack is empty.</returns>
+ public bool Undo()
+ {
+ if (CanExecute(ApplicationCommands.Undo))
+ {
+ Execute(ApplicationCommands.Undo);
+ return true;
+ }
+ return false;
+ }
+
+ /// <summary>
+ /// Gets if the most recent undone command can be redone.
+ /// </summary>
+ public bool CanRedo
+ {
+ get { return CanExecute(ApplicationCommands.Redo); }
+ }
+
+ /// <summary>
+ /// Gets if the most recent command can be undone.
+ /// </summary>
+ public bool CanUndo
+ {
+ get { return CanExecute(ApplicationCommands.Undo); }
+ }
+
+ /// <summary>
+ /// Gets the vertical size of the document.
+ /// </summary>
+ public double ExtentHeight
+ {
+ get
+ {
+ return scrollViewer != null ? scrollViewer.ExtentHeight : 0;
+ }
+ }
+
+ /// <summary>
+ /// Gets the horizontal size of the current document region.
+ /// </summary>
+ public double ExtentWidth
+ {
+ get
+ {
+ return scrollViewer != null ? scrollViewer.ExtentWidth : 0;
+ }
+ }
+
+ /// <summary>
+ /// Gets the horizontal size of the viewport.
+ /// </summary>
+ public double ViewportHeight
+ {
+ get
+ {
+ return scrollViewer != null ? scrollViewer.ViewportHeight : 0;
+ }
+ }
+
+ /// <summary>
+ /// Gets the horizontal size of the viewport.
+ /// </summary>
+ public double ViewportWidth
+ {
+ get
+ {
+ return scrollViewer != null ? scrollViewer.ViewportWidth : 0;
+ }
+ }
+
+ /// <summary>
+ /// Gets the vertical scroll position.
+ /// </summary>
+ public double VerticalOffset
+ {
+ get
+ {
+ return scrollViewer != null ? scrollViewer.VerticalOffset : 0;
+ }
+ }
+
+ /// <summary>
+ /// Gets the horizontal scroll position.
+ /// </summary>
+ public double HorizontalOffset
+ {
+ get
+ {
+ return scrollViewer != null ? scrollViewer.HorizontalOffset : 0;
+ }
+ }
+ #endregion
+
+ #region TextBox methods
+ /// <summary>
+ /// Gets/Sets the selected text.
+ /// </summary>
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ public string SelectedText
+ {
+ get
+ {
+ TextArea textArea = this.TextArea;
+ // We'll get the text from the whole surrounding segment.
+ // This is done to ensure that SelectedText.Length == SelectionLength.
+ if (textArea != null && textArea.Document != null && !textArea.Selection.IsEmpty)
+ return textArea.Document.GetText(textArea.Selection.SurroundingSegment);
+ else
+ return string.Empty;
+ }
+ set
+ {
+ if (value == null)
+ throw new ArgumentNullException("value");
+ TextArea textArea = this.TextArea;
+ if (textArea != null && textArea.Document != null)
+ {
+ int offset = this.SelectionStart;
+ int length = this.SelectionLength;
+ textArea.Document.Replace(offset, length, value);
+ // keep inserted text selected
+ textArea.Selection = SimpleSelection.Create(textArea, offset, offset + value.Length);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets/sets the caret position.
+ /// </summary>
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ public int CaretOffset
+ {
+ get
+ {
+ TextArea textArea = this.TextArea;
+ if (textArea != null)
+ return textArea.Caret.Offset;
+ else
+ return 0;
+ }
+ set
+ {
+ TextArea textArea = this.TextArea;
+ if (textArea != null)
+ textArea.Caret.Offset = value;
+ }
+ }
+
+ /// <summary>
+ /// Gets/sets the start position of the selection.
+ /// </summary>
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ public int SelectionStart
+ {
+ get
+ {
+ TextArea textArea = this.TextArea;
+ if (textArea != null)
+ {
+ if (textArea.Selection.IsEmpty)
+ return textArea.Caret.Offset;
+ else
+ return textArea.Selection.SurroundingSegment.Offset;
+ }
+ else
+ {
+ return 0;
+ }
+ }
+ set
+ {
+ Select(value, SelectionLength);
+ }
+ }
+
+ /// <summary>
+ /// Gets/sets the length of the selection.
+ /// </summary>
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ public int SelectionLength
+ {
+ get
+ {
+ TextArea textArea = this.TextArea;
+ if (textArea != null && !textArea.Selection.IsEmpty)
+ return textArea.Selection.SurroundingSegment.Length;
+ else
+ return 0;
+ }
+ set
+ {
+ Select(SelectionStart, value);
+ }
+ }
+
+ /// <summary>
+ /// Selects the specified text section.
+ /// </summary>
+ public void Select(int start, int length)
+ {
+ int documentLength = Document != null ? Document.TextLength : 0;
+
+ if (start < 0 || start > documentLength)
+ {
+ Debug.WriteLine(new ArgumentOutOfRangeException("start", start, "Value must be between 0 and " + documentLength));
+ return;
+ }
+
+ if (length < 0 || start + length > documentLength)
+ {
+ Debug.WriteLine(new ArgumentOutOfRangeException("length", length, "Value must be between 0 and " + (documentLength - length)));
+ return;
+ }
+
+ textArea.Selection = SimpleSelection.Create(textArea, start, start + length);
+ textArea.Caret.Offset = start + length;
+ }
+
+ /// <summary>
+ /// Gets the number of lines in the document.
+ /// </summary>
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ public int LineCount
+ {
+ get
+ {
+ TextDocument document = this.Document;
+ if (document != null)
+ return document.LineCount;
+ else
+ return 1;
+ }
+ }
+
+ /// <summary>
+ /// Clears the text.
+ /// </summary>
+ public void Clear()
+ {
+ this.Text = string.Empty;
+ }
+ #endregion
+
+ #region Loading from stream
+ /// <summary>
+ /// Loads the text from the stream, auto-detecting the encoding.
+ /// </summary>
+ /// <remarks>
+ /// This method sets <see cref="IsModified"/> to false.
+ /// </remarks>
+ public void Load(Stream stream)
+ {
+ using (StreamReader reader = FileReader.OpenStream(stream, this.Encoding ?? Encoding.UTF8))
+ {
+ this.Text = reader.ReadToEnd();
+ this.Encoding = reader.CurrentEncoding; // assign encoding after ReadToEnd() so that the StreamReader can autodetect the encoding
+ }
+ this.IsModified = false;
+ }
+
+ /// <summary>
+ /// Loads the text from the stream, auto-detecting the encoding.
+ /// </summary>
+ public void Load(string fileName)
+ {
+ if (fileName == null)
+ throw new ArgumentNullException("fileName");
+ using (FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read))
+ {
+ Load(fs);
+ }
+ }
+
+ /// <summary>
+ /// Gets/sets the encoding used when the file is saved.
+ /// </summary>
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+ public Encoding Encoding { get; set; }
+
+ /// <summary>
+ /// Saves the text to the stream.
+ /// </summary>
+ /// <remarks>
+ /// This method sets <see cref="IsModified"/> to false.
+ /// </remarks>
+ public void Save(Stream stream)
+ {
+ if (stream == null)
+ throw new ArgumentNullException("stream");
+ StreamWriter writer = new StreamWriter(stream, this.Encoding ?? Encoding.UTF8);
+ writer.Write(this.Text);
+ writer.Flush();
+ // do not close the stream
+ this.IsModified = false;
+ }
+
+ /// <summary>
+ /// Saves the text to the file.
+ /// </summary>
+ public void Save(string fileName)
+ {
+ if (fileName == null)
+ throw new ArgumentNullException("fileName");
+ using (FileStream fs = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None))
+ {
+ Save(fs);
+ }
+ }
+ #endregion
+
+ #region MouseHover events
+ /// <summary>
+ /// The PreviewMouseHover event.
+ /// </summary>
+ public static readonly RoutedEvent PreviewMouseHoverEvent =
+ TextView.PreviewMouseHoverEvent.AddOwner(typeof(TextEditor));
+
+ /// <summary>
+ /// The MouseHover event.
+ /// </summary>
+ public static readonly RoutedEvent MouseHoverEvent =
+ TextView.MouseHoverEvent.AddOwner(typeof(TextEditor));
+
+
+ /// <summary>
+ /// The PreviewMouseHoverStopped event.
+ /// </summary>
+ public static readonly RoutedEvent PreviewMouseHoverStoppedEvent =
+ TextView.PreviewMouseHoverStoppedEvent.AddOwner(typeof(TextEditor));
+
+ /// <summary>
+ /// The MouseHoverStopped event.
+ /// </summary>
+ public static readonly RoutedEvent MouseHoverStoppedEvent =
+ TextView.MouseHoverStoppedEvent.AddOwner(typeof(TextEditor));
+
+
+ /// <summary>
+ /// Occurs when the mouse has hovered over a fixed location for some time.
+ /// </summary>
+ public event MouseEventHandler PreviewMouseHover
+ {
+ add { AddHandler(PreviewMouseHoverEvent, value); }
+ remove { RemoveHandler(PreviewMouseHoverEvent, value); }
+ }
+
+ /// <summary>
+ /// Occurs when the mouse has hovered over a fixed location for some time.
+ /// </summary>
+ public event MouseEventHandler MouseHover
+ {
+ add { AddHandler(MouseHoverEvent, value); }
+ remove { RemoveHandler(MouseHoverEvent, value); }
+ }
+
+ /// <summary>
+ /// Occurs when the mouse had previously hovered but now started moving again.
+ /// </summary>
+ public event MouseEventHandler PreviewMouseHoverStopped
+ {
+ add { AddHandler(PreviewMouseHoverStoppedEvent, value); }
+ remove { RemoveHandler(PreviewMouseHoverStoppedEvent, value); }
+ }
+
+ /// <summary>
+ /// Occurs when the mouse had previously hovered but now started moving again.
+ /// </summary>
+ public event MouseEventHandler MouseHoverStopped
+ {
+ add { AddHandler(MouseHoverStoppedEvent, value); }
+ remove { RemoveHandler(MouseHoverStoppedEvent, value); }
+ }
+ #endregion
+
+ #region ScrollBarVisibility
+ /// <summary>
+ /// Dependency property for <see cref="HorizontalScrollBarVisibility"/>
+ /// </summary>
+ public static readonly DependencyProperty HorizontalScrollBarVisibilityProperty = ScrollViewer.HorizontalScrollBarVisibilityProperty.AddOwner(typeof(TextEditor), new FrameworkPropertyMetadata(ScrollBarVisibility.Visible));
+
+ /// <summary>
+ /// Gets/Sets the horizontal scroll bar visibility.
+ /// </summary>
+ public ScrollBarVisibility HorizontalScrollBarVisibility
+ {
+ get { return (ScrollBarVisibility)GetValue(HorizontalScrollBarVisibilityProperty); }
+ set { SetValue(HorizontalScrollBarVisibilityProperty, value); }
+ }
+
+ /// <summary>
+ /// Dependency property for <see cref="VerticalScrollBarVisibility"/>
+ /// </summary>
+ public static readonly DependencyProperty VerticalScrollBarVisibilityProperty = ScrollViewer.VerticalScrollBarVisibilityProperty.AddOwner(typeof(TextEditor), new FrameworkPropertyMetadata(ScrollBarVisibility.Visible));
+
+ /// <summary>
+ /// Gets/Sets the vertical scroll bar visibility.
+ /// </summary>
+ public ScrollBarVisibility VerticalScrollBarVisibility
+ {
+ get { return (ScrollBarVisibility)GetValue(VerticalScrollBarVisibilityProperty); }
+ set { SetValue(VerticalScrollBarVisibilityProperty, value); }
+ }
+ #endregion
+
+ object IServiceProvider.GetService(Type serviceType)
+ {
+ return textArea.GetService(serviceType);
+ }
+
+ /// <summary>
+ /// Gets the text view position from a point inside the editor.
+ /// </summary>
+ /// <param name="point">The position, relative to top left
+ /// corner of TextEditor control</param>
+ /// <returns>The text view position, or null if the point is outside the document.</returns>
+ public TextViewPosition? GetPositionFromPoint(Point point)
+ {
+ if (this.Document == null)
+ return null;
+ TextView textView = this.TextArea.TextView;
+ return textView.GetPosition(TranslatePoint(point, textView) + textView.ScrollOffset);
+ }
+
+ /// <summary>
+ /// Scrolls to the specified line.
+ /// This method requires that the TextEditor was already assigned a size (WPF layout must have run prior).
+ /// </summary>
+ public void ScrollToLine(int line)
+ {
+ ScrollTo(line, -1);
+ }
+
+ /// <summary>
+ /// Scrolls to the specified line/column.
+ /// This method requires that the TextEditor was already assigned a size (WPF layout must have run prior).
+ /// </summary>
+ public void ScrollTo(int line, int column)
+ {
+ const double MinimumScrollPercentage = 0.3;
+
+ TextView textView = textArea.TextView;
+ TextDocument document = textView.Document;
+ if (scrollViewer != null && document != null)
+ {
+ if (line < 1)
+ line = 1;
+ if (line > document.LineCount)
+ line = document.LineCount;
+
+ IScrollInfo scrollInfo = textView;
+ if (!scrollInfo.CanHorizontallyScroll)
+ {
+ // Word wrap is enabled. Ensure that we have up-to-date info about line height so that we scroll
+ // to the correct position.
+ // This avoids that the user has to repeat the ScrollTo() call several times when there are very long lines.
+ VisualLine vl = textView.GetOrConstructVisualLine(document.GetLineByNumber(line));
+ double remainingHeight = scrollViewer.ViewportHeight / 2;
+ while (remainingHeight > 0)
+ {
+ DocumentLine prevLine = vl.FirstDocumentLine.PreviousLine;
+ if (prevLine == null)
+ break;
+ vl = textView.GetOrConstructVisualLine(prevLine);
+ remainingHeight -= vl.Height;
+ }
+ }
+
+ Point p = textArea.TextView.GetVisualPosition(new TextViewPosition(line, Math.Max(1, column)), VisualYPosition.LineMiddle);
+ double verticalPos = p.Y - scrollViewer.ViewportHeight / 2;
+ if (Math.Abs(verticalPos - scrollViewer.VerticalOffset) > MinimumScrollPercentage * scrollViewer.ViewportHeight)
+ {
+ scrollViewer.ScrollToVerticalOffset(Math.Max(0, verticalPos));
+ }
+ if (column > 0)
+ {
+ if (p.X > scrollViewer.ViewportWidth - Caret.MinimumDistanceToViewBorder * 2)
+ {
+ double horizontalPos = Math.Max(0, p.X - scrollViewer.ViewportWidth / 2);
+ if (Math.Abs(horizontalPos - scrollViewer.HorizontalOffset) > MinimumScrollPercentage * scrollViewer.ViewportWidth)
+ {
+ scrollViewer.ScrollToHorizontalOffset(horizontalPos);
+ }
+ }
+ else
+ {
+ scrollViewer.ScrollToHorizontalOffset(0);
+ }
+ }
+ }
+ }
+ }
} \ No newline at end of file