- Created by eduardo.bogo@tatsoft.com, last modified on Sep 23, 2026
You are viewing an old version of this page. View the current version.
Compare with Current View Page History
« Previous Version 2 Next »
Build a display control once in C# with the Portable API and use it in both the Windows clients and the browser: one source file, compiled into a WPF build and an HTML5 build.
Reference → Code → Extensions API → Custom Controls → Portable API Reference
A portable control is a WPF UserControl that implements T.Toolkit.IPortableControl and is compiled twice from the same source: once for the WPF clients and once, through OpenSilver, for the HTML5 (browser) client. After you copy the two DLLs into the FrameworX installation, the Designer lists the control in the Components panel like a built-in control, with its own configuration panel.
This page covers building the control, deploying it to FrameworX and finding it in the Designer, using a downloadable template.
A portable control is not placed with the WPF Control element. For a control that only needs to run on WPF clients, see WPF Control API Reference. For one that only runs in the browser, see Blazor Control API Reference.
How a portable control works
One control source file is linked into two projects. Each project produces a DLL with the same file name, and each DLL goes to its own folder of the FrameworX installation:
WPF build | HTML5 build | |
|---|---|---|
Project |
|
|
Target | .NET Framework 4.8 | .NET Standard 2.0 + OpenSilver |
User interface | WPF components (the template uses the Syncfusion WPF gauge shipped with FrameworX) | JavaScript components driven from C# through a wrapper class (the template uses the Syncfusion JavaScript gauge from the web client's |
Output |
|
|
Deploy to |
|
|
Used by | The Designer, the RichClient and the SmartClient | The HTML5 client in the browser, which downloads it by name |
There is no registration step. When the Designer starts, it scans the WpfControls folder and lists every control it finds there. If a DLL with the same name is also in HTML5\ExtensionControls, the control is portable; if not, it is listed with a (WPF) suffix and works only on WPF clients.
Download the template
Download PortableControl-SampleGauge.zip. It contains a complete portable control, TSampleGauge: a circular gauge linked to a tag, with a minimum, a maximum, a header and a Designer configuration panel.
File | Compiled into | Role |
|---|---|---|
| Both (linked) | The control: properties, user interface, runtime tag binding and the |
| WPF only | The configuration panel the Designer opens when you double-click the control. |
| WPF only | The icon shown in the Components panel. |
| HTML5 only | The wrapper: a C# class with the same members as the WPF gauge, implemented on the JavaScript gauge. |
| Adds the OpenSilver package feed the HTML5 project needs. | |
| Opens both projects. |
Validated with FrameworX 10.1.5, in the WPF client and in the browser.
Requirements
Windows and Visual Studio with the .NET desktop development workload, and a current .NET SDK (the template was built with .NET SDK 10).
A FrameworX installation. Both projects reference the FrameworX assemblies (
T.Library,T.Toolkitand, for WPF,T.Library.Wpf,T.Toolkit.Wpf,T.Kernel,T.Wpfand the Syncfusion gauge) fromC:\Program Files\Tatsoft\FrameworX\fx-10\.Access to nuget.org and to the OpenSilver package feed
https://www.myget.org/F/opensilver/api/v3/index.json, declared in the template'snuget.config.Place the control on a Portable display to use it in the WPF clients (Windows, RichClient, SmartClient) and in the browser.
Rules
Follow these rules for every portable control. Breaking one usually produces no error: the control is missing from the Components panel, shows as (WPF), or does not load in the browser.
Name the assembly
T.Portable.Controls.T<Name>, withRootNamespaceT.Portable.Controls. The Designer only loads DLLs fromWpfControlswhose file name containsT.Portable..Both builds have the same file name. The WPF build goes to
fx-10\WpfControls\, the HTML5 build tofx-10\HTML5\ExtensionControls\. The browser downloads the HTML5 build by that name.The control class is public and implements
IPortableControl(namespaceT.Toolkit). The Designer registers every public class in the DLL that implements it.The static
GetIconImage()returns the icon. Itspack://URI must name your own assembly and.pngfile. If it returns nothing, the Designer skips the control without an error.The class name decides where the control appears. The Components panel label is the class name without the leading
T. The section is chosen by a case-sensitive text match on the class name:Chartgoes to Charts,Gaugeto Gauges,Viewto Viewer; any other name goes to Extensions.Do not reuse a product control name. Your DLL would replace the product's. Four retired names are also never listed in the Components panel:
TCircularGauge,TCenterValueCircularGauge,TRangeCircularGaugeandTSemiCircleCircularGauge.The control source is linked into both projects, never copied. Put platform differences behind
#if OPENSILVER. The configuration panel and the icon belong to the WPF project only.The OpenSilver package version matches the FrameworX web client. For FrameworX 10.1.5 it is
3.4.0-preview-2026-07-03-134452-d65dd77f, withSystem.Formats.Asn1andSystem.Security.Cryptography.Xml10.0.10. A different version builds, but the HTML5 build fails to load in the browser.FrameworX assemblies are referenced, not copied. Every FrameworX and third-party reference has
<Private>False</Private>, and you deploy only the control DLL from each output folder.Close the Designer before replacing the DLLs, and restart it to see a new control. The Designer keeps the DLLs loaded, and it scans
WpfControlsonly when it starts.
Build, deploy and use the template
1. Build
Open SampleGauge.sln and build in Release. If FrameworX is not installed in C:\Program Files\Tatsoft\FrameworX, change FrameworXPath in both project files to your installation's fx-10 folder, keeping the trailing backslash. The two builds are:
WPF:
SampleGauge\bin\Release\T.Portable.Controls.TSampleGauge.dllHTML5:
SampleGauge.HTML5\bin\T.Portable.Controls.TSampleGauge.dll
Both output folders also contain other assemblies. Copy only the control DLL (rule 9).
2. Deploy to FrameworX
Close the Designer (rule 10).
Copy the WPF build to
C:\Program Files\Tatsoft\FrameworX\fx-10\WpfControls\.Copy the HTML5 build to
C:\Program Files\Tatsoft\FrameworX\fx-10\HTML5\ExtensionControls\.
Both folders are under Program Files, so copying may require administrator rights.
3. Find it in the Designer
Open the Designer and a Portable display in Displays / Draw. In the Components panel, open the Gauges section: the control is listed as SampleGauge, without a (WPF) suffix. A control whose class name contains none of Chart, Gauge or View is listed in an Extensions section instead (rule 5).
4. Place it and link a tag
Drag SampleGauge onto the display and double-click it to open its configuration panel. Enter a tag in Linked Value (for example Tag.Level) and, if needed, the Minimum Value, Maximum Value and Header. Run the solution: the gauge follows the tag in the WPF client and in the browser.
5. Make it your own control
Choose a class name that follows rules 1, 5 and 6, then change it everywhere below. SfCircularGauge is the name of the JavaScript gauge wrapper, not of the control, and can stay.
File | What to change |
|---|---|
File and folder names |
|
|
|
| The class and constructor names, the configuration class in |
|
|
| The title, product and project names. |
How the code works
One source, two platforms
The shared file switches between the WPF gauge and the JavaScript wrapper with a conditional using. The OpenSilver package defines the OPENSILVER symbol in the HTML5 project.
#if OPENSILVER using T.Portable.Components.Gauges; #else using Syncfusion.UI.Xaml.Gauges; #endif
Linking a tag
Every bindable property is a string that holds a link: a constant, a tag or an expression. The setter applies constants immediately so the Designer preview updates. Tag and expression values arrive at runtime.
public ObjectReference linkedValueRun = null;
private string _linkedValue = "";
[DefaultValue("")]
public string LinkedValue
{
get { return _linkedValue; }
set
{
if (this._linkedValue != value)
{
this._linkedValue = value;
NotifyDesignPropertyChanged();
if (IsStaticValue(value))
{
double x = TConvert.ToDouble(value);
gPointer.Value = x;
if (gPointer2 != null)
{
gPointer2.Value = x;
}
}
}
}
}
StartRuntime() runs when the display opens. It turns each link into an ObjectReference and registers HandleTagEvent, which runs on every change and pushes the new value into the gauge. Dispose() releases each reference with WK.Dispose when the display closes.
public void StartRuntime()
{
UpdateColors();
this.headerRun = ObjectReference.ParseString(WK.UntokenizeObjectName(this.HeaderLink).Trim('"'));
if (!WK.IsUpdatingReport && this.headerRun != null)
this.headerRun.RegisterEvent(this.HandleTagEvent);
this.linkedValueRun = ObjectReference.ParseExpression(this.LinkedValue.Trim(), null);
if (!WK.IsUpdatingReport && this.linkedValueRun != null)
this.linkedValueRun.RegisterEvent(this.HandleTagEvent);
ObjectReference.AddEventToExecute(this.HandleTagEvent);
}
if (this.linkedValueRun != null)
gScale.Pointers[0].Value = TK.To<double>(await this.linkedValueRun.GetValueAsync());
Symbols, copy and paste, and saving
Every link must also be listed in the token, label, clipboard and save members, one line per link. Leaving them empty compiles and seems to work, then breaks symbol parameters, renames, copy and paste, and cross references.
public void GetTokens(object symbol)
{
WK.GetTokens(symbol, MinValueLink, eFieldTypeTk.NumberOrObject);
WK.GetTokens(symbol, MaxValueLink, eFieldTypeTk.NumberOrObject);
WK.GetTokens(symbol, LinkedValue, eFieldTypeTk.Expression);
WK.GetTokens(symbol, HeaderLink, eFieldTypeTk.ObjectOrString);
WK.GetTokens(symbol, RimThicknessLink, eFieldTypeTk.NumberOrObject);
WK.GetTokens(symbol, PointerThicknessLink, eFieldTypeTk.NumberOrObject);
WK.GetTokens(symbol, MajorTicksQuantityLink, eFieldTypeTk.NumberOrObject);
WK.GetTokens(symbol, MinorTicksPerIntervalLink, eFieldTypeTk.NumberOrObject);
}
public void OnSave(int displayId, object el)
{
OnStartClipboardCopy();
OnFinishClipboardCopy();
WK.SaveHelper.SaveStart(displayId, el);
WK.SaveHelper.SaveLink(LinkedValue, "LinkedValue");
WK.SaveHelper.SaveLink(MinValueLink, "MinValue");
WK.SaveHelper.SaveLink(MaxValueLink, "MaxValue");
WK.SaveHelper.SaveLink(HeaderLink, "HeaderLink");
WK.SaveHelper.SaveLink(RimThicknessLink, "RimThickness");
WK.SaveHelper.SaveLink(PointerThicknessLink, "PointerThickness");
WK.SaveHelper.SaveLink(MajorTicksQuantityLink, "MajorTicksQuantity");
WK.SaveHelper.SaveLink(MinorTicksPerIntervalLink, "MinorTicksPerInterval");
Icon and configuration panel
static public byte[] GetIconImage()
{
string _imageUri = @"pack://application:,,,/T.Portable.Controls.TSampleGauge;component/TSampleGauge.png";
return WK.GetImageFromUri(_imageUri);
}
public IControlConfig GetConfigControl()
{
#if OPENSILVER
return null;
#else
return new TSampleGaugeConfig();
#endif
}
The browser side
The HTML5 build declares the JavaScript and CSS files the web client loads before the control renders, through IHTML5ControlDependencies. Paths are relative to the web client root or absolute URLs.
public string[] ControlDataJS
{
get
{
return new string[] {
@"Libraries/ej2-base/dist/global/ej2-base.min.js",
@"Libraries/ej2-circulargauge/dist/global/ej2-circulargauge.min.js",
};
}
}
public string[] ControlDataCSS
{
get
{
return new string[] {
"Libraries/ej2-popups/styles/material.css",
"https://fonts.googleapis.com/css?family=Roboto:400,500"
};
}
}
The wrapper exposes the same members as the WPF gauge. Each property that affects rendering must push its value to the JavaScript object in its setter. A property that is only stored in C# compiles, but a tag change never reaches the browser.
public double Value
{
get => objValue;
set
{
objValue = value;
if (this.CircularGaugeWebAssemblyObject == null || this.circularPointer == null)
return;
OpenSilver.Interop.ExecuteJavaScript("$0.value = $1", this.circularPointer, objValue);
}
The IPortableControl members
Member | When FrameworX calls it | What the control does |
|---|---|---|
| The Designer opens the configuration panel | Returns the configuration panel on WPF, |
| The display opens at runtime | Turns links into |
| The display closes | Releases every |
| The control is used in a symbol | Exposes every link so symbol parameters are replaced. |
| Symbol labels are renamed | Same links, so renames reach the control. |
| Translation | Exposes fixed texts for localization. |
| Copy and paste | Keeps every link valid, one line per link. |
| The display is saved | Calls |
| The Designer lists the control | Static method, not part of the interface. Returns the icon. |
Optional interfaces used by the template: IHTML5ControlDependencies (JavaScript and CSS for the browser), IWKUpdateColors (theme changes) and INotifyPropertyChanged (refreshes the Designer preview from the configuration panel).
Troubleshooting
Symptom | Cause | Fix |
|---|---|---|
The control is not in the Components panel. | The DLL is not in | Check rules 1, 3, 4, 6 and 10. |
The control appears in a different section than expected. | The section comes from the class name, with a case-sensitive match. | See rule 5. |
The control shows | No DLL with the same name in | Deploy the HTML5 build with the exact same file name (rule 2). |
It works in the WPF client but not in the browser. | OpenSilver version mismatch, a missing JavaScript or CSS dependency, or a wrapper property that does not push to JavaScript. | Check rule 8, |
Restore fails: package not found. | The OpenSilver preview packages are not on nuget.org. | Keep the template's |
Build error NU1605, package downgrade of | An older version is referenced than the one OpenSilver requires. | Use version 10.0.10 (rule 8). |
The WPF output folder is full of FrameworX DLLs. | A reference lacks | Set it on every FrameworX reference, and deploy only the control DLL (rule 9). |
Copying fails: "being used by another process". | The Designer or a client has the DLL loaded. | Close them and copy again (rule 10). |
Complete source
The project files and the full template, file by file. The same files are in PortableControl-SampleGauge.zip.
<?xml version="1.0" encoding="utf-8"?>
<!-- WPF half of the portable control. Deploy the output DLL to <FrameworX>\fx-10\WpfControls\ -->
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{80A4DE71-2304-4AD4-96DA-0407245FAEBC}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<!-- Keep RootNamespace = T.Portable.Controls and AssemblyName = T.Portable.Controls.T<Name>:
the Designer only loads DLLs whose name contains "T.Portable." -->
<RootNamespace>T.Portable.Controls</RootNamespace>
<AssemblyName>T.Portable.Controls.TSampleGauge</AssemblyName>
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<Deterministic>true</Deterministic>
<!-- FrameworX installation folder that holds the reference assemblies. Keep the trailing backslash. -->
<FrameworXPath Condition=" '$(FrameworXPath)' == '' ">C:\Program Files\Tatsoft\FrameworX\fx-10\</FrameworXPath>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" />
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Data" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xaml" />
<Reference Include="System.Xml" />
<Reference Include="System.Xml.Linq" />
<Reference Include="WindowsBase" />
<!-- FrameworX and third-party assemblies: referenced from the installation, never copied (Private=False). -->
<Reference Include="Microsoft.CSharp">
<HintPath>$(FrameworXPath)Microsoft.CSharp.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Text.Json">
<HintPath>$(FrameworXPath)System.Text.Json.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="Syncfusion.Licensing">
<HintPath>$(FrameworXPath)Syncfusion.Licensing.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="Syncfusion.SfGauge.WPF">
<HintPath>$(FrameworXPath)Syncfusion.SfGauge.WPF.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="T.Kernel">
<HintPath>$(FrameworXPath)T.Kernel.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="T.Library">
<HintPath>$(FrameworXPath)T.Library.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="T.Library.Wpf">
<HintPath>$(FrameworXPath)T.Library.Wpf.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="T.Toolkit">
<HintPath>$(FrameworXPath)T.Toolkit.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="T.Toolkit.Wpf">
<HintPath>$(FrameworXPath)T.Toolkit.Wpf.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="T.Wpf">
<HintPath>$(FrameworXPath)T.Wpf.dll</HintPath>
<Private>False</Private>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="TSampleGauge.cs" />
<Compile Include="TSampleGaugeConfig.xaml.cs">
<DependentUpon>TSampleGaugeConfig.xaml</DependentUpon>
</Compile>
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<Page Include="TSampleGaugeConfig.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
<ItemGroup>
<Resource Include="TSampleGauge.png" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
<Project Sdk="Microsoft.NET.Sdk">
<!-- HTML5 half of the portable control. Deploy the output DLL (same file name as the WPF half)
to <FrameworX>\fx-10\HTML5\ExtensionControls\ -->
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
<ProduceReferenceAssembly>false</ProduceReferenceAssembly>
<OutputPath>bin\</OutputPath>
<RootNamespace>T.Portable.Controls</RootNamespace>
<AssemblyName>T.Portable.Controls.TSampleGauge</AssemblyName>
<NoWarn>1701;1702;MSB3277</NoWarn>
<DebugType>embedded</DebugType>
<!-- FrameworX installation folder that holds the reference assemblies. Keep the trailing backslash. -->
<FrameworXPath Condition=" '$(FrameworXPath)' == '' ">C:\Program Files\Tatsoft\FrameworX\fx-10\</FrameworXPath>
</PropertyGroup>
<!-- The shared control source is LINKED from the WPF project, never copied. -->
<ItemGroup>
<Compile Include="..\SampleGauge\TSampleGauge.cs" Link="TSampleGauge.cs" />
</ItemGroup>
<!-- The OpenSilver version must match the FrameworX web client exactly (FrameworX 10.1.5 below).
Preview builds come from the OpenSilver MyGet feed declared in nuget.config. -->
<ItemGroup>
<PackageReference Include="OpenSilver" Version="3.4.0-preview-2026-07-03-134452-d65dd77f">
<PrivateAssets>All</PrivateAssets>
</PackageReference>
<PackageReference Include="System.Formats.Asn1" Version="10.0.10" />
<PackageReference Include="System.Security.Cryptography.Xml" Version="10.0.10" />
</ItemGroup>
<ItemGroup>
<Reference Include="T.Library">
<HintPath>$(FrameworXPath)T.Library.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="T.Toolkit">
<HintPath>$(FrameworXPath)T.Toolkit.dll</HintPath>
<Private>False</Private>
</Reference>
</ItemGroup>
</Project>
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
<add key="OpenSilver MyGet" value="https://www.myget.org/F/opensilver/api/v3/index.json" />
</packageSources>
</configuration>
using System;
using System.Windows;
using System.Windows.Media;
using System.ComponentModel;
using System.Windows.Controls;
using System.Runtime.CompilerServices;
using System.Collections.Generic;
using System.Threading.Tasks;
#if OPENSILVER
using T.Portable.Components.Gauges;
#else
using Syncfusion.UI.Xaml.Gauges;
#endif
using T.Library;
using T.Toolkit;
using T.Toolkit.Wpf;
using System.Collections;
using System.Xml.Serialization;
namespace T.Portable.Controls
{
public class TSampleGauge : UserControl, IPortableControl, INotifyPropertyChanged, IHTML5ControlDependencies, IWKUpdateColors
{
#region INotifyPropertyChanged Implementation
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyDesignPropertyChanged([CallerMemberName] string propertyName = "")
{
if (WK.IsDesignMode)
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
#region Private Fields
private readonly TextBlock gHeader = null;
private readonly SfCircularGauge gauge = null;
private readonly CircularScale gScale = null;
private readonly CircularPointer gPointer = null;
private readonly CircularPointer gPointer2 = null;
private int labelsCreated = 0;
private double majorTicksQuantityDouble = 11;
private Grid mainGrid = null;
#endregion
#region Properties
#region SampleValue Property
private string _sampleValue = "";
[DefaultValue("")]
[XmlIgnore]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public string SampleValue
{
get => _sampleValue;
set
{
if (_sampleValue != value)
{
_sampleValue = value;
UpdateDesignUI();
}
}
}
#endregion
#region Control Access Properties
public SfCircularGauge Gauge { get { return gauge; } }
public CircularScale Scale { get { return gScale; } }
public CircularPointer Pointer { get { return gPointer; } }
#endregion
#region DataEntry Validation Properties
#if !OPENSILVER
public EditInfo NumberOrObjectField
{
get => EditInfo.FactoryNumberOrObject;
}
public EditInfo StringWithExpressionsField
{
get => EditInfo.FactoryStringWithExpressions;
}
public EditInfo ObjectOrStringField
{
get => EditInfo.FactoryObjectOrString;
}
public EditInfo ExpressionField
{
get => EditInfo.FactoryExpression;
}
#endif
#endregion
#region Control Properties
#region Header Properties
public ObjectReference headerRun = null;
private string _headerLink = "";
[DefaultValue("")]
public string HeaderLink
{
get { return this._headerLink; }
set
{
if (this._headerLink != value)
{
this._headerLink = value;
NotifyDesignPropertyChanged();
string str = WK.UntokenizeObjectName(value);
if (this.gHeader != null)
gHeader.Text = str;
}
}
}
public string GaugeTitle { set { HeaderLink = value; } }
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public string LabelLink
{
get => this._headerLink.Contains("@") ? $"{_headerLink}" : _headerLink;
set
{
if (value == null)
return;
value = value.replace("{}", "");
if (!value.Contains("{"))
{
_headerLink = value;
return;
}
int index = value.IndexOf("{");
if (index >= 0)
{
_headerLink = value.Substring(index + 1);
index = value.IndexOf("}");
if (index >= 0)
_headerLink += value.Substring(0, index);
}
}
}
public bool HasDynamicHeader { get => _headerLink.Contains("@") || _headerLink.Contains("{"); }
public bool HasStaticHeader { get => !HasDynamicHeader && !String.IsNullOrWhiteSpace(_headerLink); }
[DefaultValue(12.0)]
public double HeaderFontSize
{
get { return this.gHeader.FontSize; }
set
{
if (this.gHeader.FontSize != value)
{
this.gHeader.FontSize = value;
NotifyDesignPropertyChanged();
}
}
}
public double FontSizeTitle { set { HeaderFontSize = value; } }
public double[] HeaderFontSizes
{
get { return new double[] { 6, 7, 8, 9, 10, 11, 12, 13, 14, 16, 18, 20, 22, 24, 32, 36, 48, 64, 72, 108, 144 }; }
}
public TK.eTkHeaderAlignment[] HeaderAlignments
{
get { return (TK.eTkHeaderAlignment[])Enum.GetValues(typeof(TK.eTkHeaderAlignment)); }
}
private TK.eTkHeaderAlignment _headerAlignment = TK.eTkHeaderAlignment.Bottom;
[DefaultValue(TK.eTkHeaderAlignment.Bottom)]
public TK.eTkHeaderAlignment HeaderAlignment
{
get { return _headerAlignment; }
set
{
if (_headerAlignment != value)
{
_headerAlignment = value;
NotifyDesignPropertyChanged();
this.UpdateGridContent();
}
}
}
#region Localize Property
private bool _localize = true;
[DefaultValue(false)]
public bool Localize
{
get { return this._localize; }
set
{
if (this._localize != value)
{
this._localize = value;
this.NotifyDesignPropertyChanged(nameof(Localize));
}
}
}
#endregion
#endregion
#region LinkedValue and Range Properties
public ObjectReference linkedValueRun = null;
private string _linkedValue = "";
[DefaultValue("")]
public string LinkedValue
{
get { return _linkedValue; }
set
{
if (this._linkedValue != value)
{
this._linkedValue = value;
NotifyDesignPropertyChanged();
if (IsStaticValue(value))
{
double x = TConvert.ToDouble(value);
gPointer.Value = x;
if (gPointer2 != null)
{
gPointer2.Value = x;
}
}
}
}
}
public string Value { set { LinkedValue = value; } }
public ObjectReference minValueRun = null;
private string _minValueLink = "0";
[DefaultValue("0")]
public string MinValueLink
{
get { return this._minValueLink; }
set
{
if (this._minValueLink != value)
{
this._minValueLink = value;
NotifyDesignPropertyChanged();
if (IsStaticValue(value))
{
double x = TConvert.ToDouble(value);
gScale.StartValue = x;
double interval = (gScale.EndValue - gScale.StartValue) / (this.majorTicksQuantityDouble - 1);
this.gScale.Interval = RoundDown(interval, 3);
}
this.labelsCreated = 0;
#if OPENSILVER
this.gauge.LabelsCreated = 0;
#endif
}
}
}
public string StartValue { set { _minValueLink = value; } }
public string RangeStartLink { set { _minValueLink = value; } }
public ObjectReference maxValueRun = null;
private string _maxValueLink = "100";
[DefaultValue("100")]
public string MaxValueLink
{
get { return this._maxValueLink; }
set
{
if (this._maxValueLink != value)
{
this._maxValueLink = value;
NotifyDesignPropertyChanged();
if (IsStaticValue(value))
{
double x = TConvert.ToDouble(value);
if (x != 0)
{
gScale.EndValue = x;
double pointerVal = (TConvert.ToDouble(MinValueLink) + TConvert.ToDouble(MaxValueLink)) * 0.5;
gPointer.Value = pointerVal;
if (gPointer2 != null)
{
gPointer2.Value = pointerVal;
}
}
double interval = (gScale.EndValue - gScale.StartValue) / (this.majorTicksQuantityDouble - 1);
this.gScale.Interval = RoundDown(interval, 3);
}
this.labelsCreated = 0;
#if OPENSILVER
this.gauge.LabelsCreated = 0;
#endif
}
}
}
public string EndValue { set { _maxValueLink = value; } }
public string RangeEndLink { set { _maxValueLink = value; } }
#endregion
#region Pointer Properties
[DefaultValue(PointerType.RangePointer)]
public PointerType PointerType
{
get
{
if (this.gPointer == null)
return PointerType.RangePointer;
return this.gPointer.PointerType;
}
set
{
if (this.gPointer == null)
return;
if (this.gPointer.PointerType != value)
{
this.gPointer.PointerType = value;
if (value == PointerType.NeedlePointer || !_showNeedlePointer)
{
if (gScale.Pointers.Contains(this.gPointer2))
gScale.Pointers.Remove(this.gPointer2);
}
else if (_showNeedlePointer)
{
if (!gScale.Pointers.Contains(this.gPointer2))
gScale.Pointers.Add(this.gPointer2);
}
NotifyDesignPropertyChanged();
NotifyDesignPropertyChanged(nameof(CanSecondPointer));
}
}
}
public int Pointer_Type { set { PointerType = TConvert.To<PointerType>(value); } }
public bool CanSecondPointer => PointerType != PointerType.NeedlePointer;
private bool _showNeedlePointer = true;
[DefaultValue(true)]
public bool ShowNeedlePointer
{
get => _showNeedlePointer;
set
{
if (_showNeedlePointer != value)
{
_showNeedlePointer = value;
if (PointerType == PointerType.NeedlePointer || !_showNeedlePointer)
{
if (gScale.Pointers.Contains(this.gPointer2))
gScale.Pointers.Remove(this.gPointer2);
}
else
{
if (_showNeedlePointer &&!gScale.Pointers.Contains(this.gPointer2))
gScale.Pointers.Add(this.gPointer2);
}
NotifyDesignPropertyChanged();
}
}
}
public ObjectReference pointerThicknessRun = null;
private string _pointerThicknessLink = "10";
[DefaultValue("10")]
public string PointerThicknessLink
{
get { return this._pointerThicknessLink; }
set
{
if (this._pointerThicknessLink != value)
{
this._pointerThicknessLink = value;
NotifyDesignPropertyChanged();
double x = TConvert.ToDouble(value);
if (x < 0)
return;
gPointer.RangePointerStrokeThickness = x;
gPointer.NeedlePointerStrokeThickness = x;
if (gPointer2 != null)
{
gPointer2.RangePointerStrokeThickness = x;
gPointer2.NeedlePointerStrokeThickness = x;
}
}
}
}
public string PointerThickness { set { PointerThicknessLink = value; } }
private Color _pointerColor = Color.FromArgb(0xff, 0x6A, 0x5A, 0xCD);
[DefaultValue(typeof(Color), "#FF6A5ACD")]
public Color PointerColor
{
get => _pointerColor;
set
{
if (_pointerColor != value)
{
_pointerColor = value;
Brush stroke = PointerStroke;
this.gPointer.RangePointerStroke = stroke;
this.gPointer.SymbolPointerStroke = stroke;
this.gPointer.KnobStroke = stroke;
this.gPointer.KnobFill = stroke;
this.gPointer.NeedlePointerStroke = stroke;
if (gPointer2 != null)
{
this.gPointer2.RangePointerStroke = stroke;
this.gPointer2.SymbolPointerStroke = stroke;
this.gPointer2.KnobStroke = stroke;
this.gPointer2.KnobFill = stroke;
this.gPointer2.NeedlePointerStroke = stroke;
}
NotifyDesignPropertyChanged();
NotifyDesignPropertyChanged(nameof(PointerStroke));
}
}
}
[XmlIgnore]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public Brush PointerStroke
{
get => new SolidColorBrush(_pointerColor);
set => _pointerColor = (value is SolidColorBrush solid) ? solid.Color : _pointerColor;
}
#endregion
#region Scale Properties
[DefaultValue(true)]
public bool ShowLabels
{
get
{
return this.gScale.ShowLabels;
}
set
{
if (this.gScale.ShowLabels != value)
{
this.gScale.ShowLabels = value;
NotifyDesignPropertyChanged();
}
}
}
[DefaultValue(8.0)]
public double ScaleFontSize
{
get { return this.gScale.FontSize; }
set
{
if (this.gScale.FontSize != value)
{
this.gScale.FontSize = value;
NotifyDesignPropertyChanged();
}
}
}
public double FontSizeLabel { set { ScaleFontSize = value; } }
[DefaultValue(true)]
public bool ShowTicks
{
get
{
return this.gScale.ShowTicks;
}
set
{
if (this.gScale.ShowTicks != value)
{
this.gScale.ShowTicks = value;
NotifyDesignPropertyChanged();
}
}
}
public ObjectReference majorTicksQuantityRun = null;
private string _majorTicksQuantityLink = "11";
[DefaultValue("11")]
public string MajorTicksQuantityLink
{
get { return this._majorTicksQuantityLink; }
set
{
if (this._majorTicksQuantityLink != value)
{
this._majorTicksQuantityLink = value;
NotifyDesignPropertyChanged();
if(this.gScale != null)
{
double start = this.gScale.StartValue;
double end = this.gScale.EndValue;
double tickQuantity = TConvert.ToDouble(value);
if (tickQuantity <= 2)
tickQuantity = 2;
this.majorTicksQuantityDouble = tickQuantity;
double interval = (end - start) / (tickQuantity - 1);
this.gScale.Interval = RoundDown(interval, 3);
}
}
this.labelsCreated = 0;
#if OPENSILVER
this.Gauge.LabelsCreated = 0;
this.Gauge.LabelsQuantity = TConvert.ToInt(this.majorTicksQuantityDouble);
#endif
}
}
public string MajorTicksQuantity { set { MajorTicksQuantityLink = value; } }
public ObjectReference minorTicksPerIntervalRun = null;
private string _minorTicksPerIntervalLink = "1";
[DefaultValue("1")]
public string MinorTicksPerIntervalLink
{
get { return this._minorTicksPerIntervalLink; }
set
{
if (this._minorTicksPerIntervalLink != value)
{
this._minorTicksPerIntervalLink = value;
NotifyDesignPropertyChanged();
if (this.gScale != null)
this.gScale.MinorTicksPerInterval = TConvert.ToInt(value);
}
}
}
public string MinorTicksPerInterval { set { MinorTicksPerIntervalLink = value; } }
#endregion
#region Rim Properties
public ObjectReference rimThicknessRun = null;
private string _rimThicknessLink = "20";
[DefaultValue("20")]
public string RimThicknessLink
{
get { return this._rimThicknessLink; }
set
{
if (this._rimThicknessLink != value)
{
this._rimThicknessLink = value;
NotifyDesignPropertyChanged();
double x = TConvert.ToDouble(value);
if (x < 0)
return;
gScale.RimStrokeThickness = x;
}
}
}
public string RimThickness { set { RimThicknessLink = value; } }
private Color _rimStrokeColor = Color.FromArgb(0xFF, 0xC1, 0xC1, 0xC1);
[DefaultValue(typeof(Color), "#FFC1C1C1")]
public Color RimStrokeColor
{
get => _rimStrokeColor;
set
{
if (_rimStrokeColor != value)
{
_rimStrokeColor = value;
gScale.RimStroke = RimStroke;
NotifyDesignPropertyChanged();
NotifyDesignPropertyChanged(nameof(RimStroke));
}
}
}
[XmlIgnore]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public Brush RimStroke
{
get => new SolidColorBrush(_rimStrokeColor);
set => _rimStrokeColor = (value as SolidColorBrush)?.Color ?? _rimStrokeColor;
}
public Brush RimBackground { set { RimStroke = value; } }
#endregion
#endregion Control Properties
#endregion Properties
#region Constructors and EndInit
public TSampleGauge()
{
this.mainGrid = new Grid();
this.MinHeight = this.MinWidth = 40;
this.gHeader = new TextBlock
{
Text = WK.UntokenizeObjectName(TConvert.ToString(HeaderLink)),
FontFamily = new FontFamily("Verdana"),
FontSize = 12,
Foreground = WK.GetBrush(WK.eThemeColor.TextForeground) as Brush,
VerticalAlignment = VerticalAlignment.Center,
HorizontalAlignment = HorizontalAlignment.Center,
};
this.gPointer = new CircularPointer
{
RangePointerStrokeThickness = TConvert.ToDouble(this.PointerThicknessLink ?? "20"),
RangeCap = RangeCap.None,
RangePointerStroke = this.PointerStroke,
PointerType = this.PointerType,
NeedlePointerType = NeedlePointerType.Triangle,
NeedlePointerStroke = this.PointerStroke,
Value = 20,
};
this.gPointer2 = new CircularPointer
{
RangePointerStrokeThickness = TConvert.ToDouble(this.PointerThicknessLink ?? "20"),
RangeCap = RangeCap.None,
RangePointerStroke = this.PointerStroke,
PointerType =PointerType.NeedlePointer,
NeedlePointerType = NeedlePointerType.Triangle,
NeedlePointerStroke = this.PointerStroke,
Value = 20,
};
this.gScale = new CircularScale
{
FontSize = 8,
StartValue = 0,
EndValue = 100,
StartAngle = 135,
SweepAngle = 270,
RimStroke = this.RimStroke,
RimStrokeThickness = TConvert.ToDouble(this.RimThicknessLink ?? "20"),
RangePointerPosition = RangePointerPosition.Custom,
ShowLabels = true,
LabelStroke = WK.GetBrush(WK.eThemeColor.TextForeground) as Brush,
ShowTicks = true,
MinorTicksPerInterval = 1,
Pointers = { gPointer, gPointer2 },
};
this.gauge = new SfCircularGauge
{
VerticalAlignment = VerticalAlignment.Stretch,
HorizontalAlignment = HorizontalAlignment.Stretch,
Scales = { gScale },
MainScale = gScale,
};
this.majorTicksQuantityDouble = 11;
double interval = (gScale.EndValue - gScale.StartValue) / (this.majorTicksQuantityDouble - 1);
gScale.Interval = this.RoundDown(interval, 3);
#if OPENSILVER
this.Gauge.LabelsQuantity = TConvert.ToInt(this.majorTicksQuantityDouble);
#else
this.gScale.LabelCreated += GScale_LabelCreated;
this.gScale.ShowFirstLabel = true;
this.gScale.ShowLastLabel = true;
#endif
this.UpdateGridContent();
_linkedValue = "#Value:";
}
private bool endInitAlreadyRun = false;
public override void EndInit()
{
#if !OPENSILVER
base.EndInit();
#endif
if (endInitAlreadyRun)
return;
endInitAlreadyRun = true;
WK.EndInit(this);
if (WK.IsDesignMode)
return;
this.gauge.Loaded += Gauge_Loaded;
this.SizeChanged += this.UserControl_SizeChanged;
WK.RuntimeThemeChanged += WK_RuntimeThemeChanged;
}
private void WK_RuntimeThemeChanged(object sender, EventArgs e)
{
this.UpdateColors();
}
public double RoundDown(double value, double digits)
{
double factor = Math.Pow(10, digits);
return Math.Truncate(value * factor) / factor;
}
public void UpdateDesignUI()
{
if (!WK.IsDesignMode)
return;
if (this.gScale.Pointers == null)
return;
if (this.gScale.Pointers.Count > 0)
this.gScale.Pointers[0].Value = TConvert.ToDouble(_sampleValue);
if (this.gScale.Pointers.Count > 1)
this.gScale.Pointers[1].Value = TConvert.ToDouble(_sampleValue);
}
#if !OPENSILVER
public override bool ShouldSerializeContent()
{
return false;
}
#endif
#endregion Constructors and EndInit
#region Event Handlers
private void Gauge_Loaded(object sender, RoutedEventArgs e)
{
this.labelsCreated = 0;
this.gauge.Background = this.Background;
#if OPENSILVER
this.Gauge.LabelsCreated = 0;
this.gauge.GaugeHeight = this.Height;
this.gauge.GaugeWidth = this.Width;
#endif
}
private void UserControl_SizeChanged(object sender, SizeChangedEventArgs e)
{
#if OPENSILVER
this.gauge.Width = e.NewSize.Width;
this.gauge.Height = e.NewSize.Height;
this.gauge.GaugeHeight = this.Height;
this.gauge.GaugeWidth = this.Width;
this.gauge.UpdateAxesRadius();
#else
this.gauge.Width = e.NewSize.Width - 20;
this.gauge.Height = e.NewSize.Height - 30;
#endif
}
#if !OPENSILVER
private void GScale_LabelCreated(object sender, LabelCreatedEventArgs e)
{
this.labelsCreated += 1;
e.LabelText = TConvert.ToString(Math.Round(TConvert.ToDouble(e.LabelText), 2));
if (this.labelsCreated < this.majorTicksQuantityDouble)
return;
double _endValue = 100;
if (IsStaticValue(this.MaxValueLink))
{
_endValue = this.gScale.EndValue;
}
e.LabelText = TConvert.ToString(_endValue);
this.labelsCreated = 0;
}
#endif
#endregion Event Handlers
#region IHTML5ControlDependencies Implementation
public string[] ControlDataJS
{
get
{
return new string[] {
@"Libraries/ej2-base/dist/global/ej2-base.min.js",
@"Libraries/ej2-circulargauge/dist/global/ej2-circulargauge.min.js",
};
}
}
public string[] ControlDataCSS
{
get
{
return new string[] {
"Libraries/ej2-popups/styles/material.css",
"https://fonts.googleapis.com/css?family=Roboto:400,500"
};
}
}
#endregion
#region IPortableControl Implementation
static public byte[] GetIconImage()
{
string _imageUri = @"pack://application:,,,/T.Portable.Controls.TSampleGauge;component/TSampleGauge.png";
return WK.GetImageFromUri(_imageUri);
}
public IControlConfig GetConfigControl()
{
#if OPENSILVER
return null;
#else
return new TSampleGaugeConfig();
#endif
}
public void UpdateColors()
{
if (gauge == null)
return;
Brush b = WK.GetBrush(WK.eThemeColor.TextForeground) as Brush;
this.gHeader.Foreground = b;
this.gScale.LabelStroke = b;
#if OPENSILVER
this.gScale.UpdateColors();
#endif
}
/// <summary>
/// Initialization method called when display opens and this control is loaded. This method is used to start the data bindings and events.
/// </summary>
public void StartRuntime()
{
UpdateColors();
this.headerRun = ObjectReference.ParseString(WK.UntokenizeObjectName(this.HeaderLink).Trim('"'));
if (!WK.IsUpdatingReport && this.headerRun != null)
this.headerRun.RegisterEvent(this.HandleTagEvent);
this.linkedValueRun = ObjectReference.ParseExpression(this.LinkedValue.Trim(), null);
if (!WK.IsUpdatingReport && this.linkedValueRun != null)
this.linkedValueRun.RegisterEvent(this.HandleTagEvent);
this.minValueRun = ObjectReference.ParseExpression(this.MinValueLink.Trim(), null);
if (!WK.IsUpdatingReport && this.minValueRun != null)
this.minValueRun.RegisterEvent(this.HandleTagEvent);
this.maxValueRun = ObjectReference.ParseExpression(this.MaxValueLink.Trim(), null);
if (!WK.IsUpdatingReport && this.maxValueRun != null)
this.maxValueRun.RegisterEvent(this.HandleTagEvent);
this.rimThicknessRun = ObjectReference.ParseExpression(this.RimThicknessLink, null);
if (!WK.IsUpdatingReport && this.rimThicknessRun != null)
this.rimThicknessRun.RegisterEvent(this.HandleTagEvent);
this.pointerThicknessRun = ObjectReference.ParseExpression(this.PointerThicknessLink, null);
if (!WK.IsUpdatingReport && this.pointerThicknessRun != null)
this.pointerThicknessRun.RegisterEvent(this.HandleTagEvent);
this.majorTicksQuantityRun = ObjectReference.ParseExpression(this.MajorTicksQuantityLink, null);
if (!WK.IsUpdatingReport && this.majorTicksQuantityRun != null)
this.majorTicksQuantityRun.RegisterEvent(this.HandleTagEvent);
this.minorTicksPerIntervalRun = ObjectReference.ParseExpression(this.MinorTicksPerIntervalLink, null);
if (!WK.IsUpdatingReport && this.minorTicksPerIntervalRun != null)
this.minorTicksPerIntervalRun.RegisterEvent(this.HandleTagEvent);
ObjectReference.AddEventToExecute(this.HandleTagEvent);
}
/// <summary>
/// Dispose control and unregister events
/// </summary>
public void Dispose()
{
WK.Dispose(ref this.headerRun);
WK.Dispose(ref this.minValueRun);
WK.Dispose(ref this.maxValueRun);
WK.Dispose(ref this.linkedValueRun);
WK.Dispose(ref this.rimThicknessRun);
WK.Dispose(ref this.pointerThicknessRun);
WK.Dispose(ref this.majorTicksQuantityRun);
WK.Dispose(ref this.minorTicksPerIntervalRun);
}
/// <summary>
/// Handle tag event to update control value
/// </summary>
public async Task HandleTagEvent()
{
try
{
if (this.headerRun != null && this.gHeader != null && this.gauge != null)
{
gHeader.Text = TK.To<string>(await this.headerRun.GetValueAsync());
}
if (this.gScale == null)
return;
if (this.minValueRun != null)
gScale.StartValue = TK.To<double>(await this.minValueRun.GetValueAsync());
if (this.maxValueRun != null)
gScale.EndValue = TK.To<double>(await this.maxValueRun.GetValueAsync());
if (gScale.StartValue > gScale.EndValue)
{
double tmp = gScale.StartValue;
gScale.StartValue = gScale.EndValue;
gScale.EndValue = tmp;
}
if (gScale.StartValue == gScale.EndValue)
gScale.EndValue = gScale.StartValue + 1;
if (this.majorTicksQuantityRun != null)
{
double quantity = TK.To<double>(await this.majorTicksQuantityRun.GetValueAsync());
quantity = (quantity >= 15) ? 10 : quantity;
if (quantity <= 2)
quantity = 2;
this.majorTicksQuantityDouble = quantity;
#if OPENSILVER
this.Gauge.LabelsQuantity = TConvert.ToInt(this.majorTicksQuantityDouble);
#endif
double interval = (gScale.EndValue - gScale.StartValue) / (quantity - 1);
gScale.Interval = this.RoundDown(interval, 3);
}
if (this.minorTicksPerIntervalRun != null)
{
gScale.MinorTicksPerInterval = TK.To<int>(await this.minorTicksPerIntervalRun.GetValueAsync());
if (gScale.MinorTicksPerInterval > 10)
gScale.MinorTicksPerInterval = 10;
}
if (this.rimThicknessRun != null)
gScale.RimStrokeThickness = TK.To<double>(await this.rimThicknessRun.GetValueAsync());
if (this.gScale.Pointers != null && this.gScale.Pointers.Count > 0)
{
if (this.linkedValueRun != null)
gScale.Pointers[0].Value = TK.To<double>(await this.linkedValueRun.GetValueAsync());
if (gScale.Pointers[0].Value < gScale.StartValue)
gScale.Pointers[0].Value = gScale.StartValue;
if (gScale.Pointers[0].Value > gScale.EndValue)
gScale.Pointers[0].Value = gScale.EndValue;
if (this.pointerThicknessRun != null)
{
gScale.Pointers[0].RangePointerStrokeThickness = TK.To<double>(await this.pointerThicknessRun.GetValueAsync());
gScale.Pointers[0].NeedlePointerStrokeThickness = gScale.Pointers[0].RangePointerStrokeThickness;
}
if (gScale.Pointers.Count > 1)
{
gScale.Pointers[1].Value = gScale.Pointers[0].Value;
if (this.pointerThicknessRun != null)
{
gScale.Pointers[1].RangePointerStrokeThickness = TK.To<double>(await this.pointerThicknessRun.GetValueAsync());
gScale.Pointers[1].NeedlePointerStrokeThickness = gScale.Pointers[1].RangePointerStrokeThickness;
}
}
}
}
catch (Exception e)
{
WK.ExceptionLog(e);
}
}
public void GetStrings(object replaceControl)
{
if (HasStaticHeader)
WK.GetStrings(replaceControl, this._headerLink, eFieldTypeTk.String);
}
public void ApplyStrings(object replaceControl)
{
if (HasStaticHeader)
WK.ApplyStrings(replaceControl, ref this._headerLink, eFieldTypeTk.String);
}
public void GetLabels(object symbol)
{
WK.GetLabel(symbol, MinValueLink, eFieldTypeTk.NumberOrObject);
WK.GetLabel(symbol, MaxValueLink, eFieldTypeTk.NumberOrObject);
WK.GetLabel(symbol, LinkedValue, eFieldTypeTk.Expression);
WK.GetLabel(symbol, HeaderLink, eFieldTypeTk.ObjectOrString);
WK.GetLabel(symbol, RimThicknessLink, eFieldTypeTk.NumberOrObject);
WK.GetLabel(symbol, PointerThicknessLink, eFieldTypeTk.NumberOrObject);
WK.GetLabel(symbol, MajorTicksQuantityLink, eFieldTypeTk.NumberOrObject);
WK.GetLabel(symbol, MinorTicksPerIntervalLink, eFieldTypeTk.NumberOrObject);
}
public void ResolveLabels(object symbol)
{
WK.ResolveLabel(symbol, ref _minValueLink, eFieldTypeTk.NumberOrObject);
WK.ResolveLabel(symbol, ref _maxValueLink, eFieldTypeTk.NumberOrObject);
WK.ResolveLabel(symbol, ref _linkedValue, eFieldTypeTk.Expression);
WK.ResolveLabel(symbol, ref this._headerLink, eFieldTypeTk.ObjectOrString);
WK.ResolveLabel(symbol, ref this._rimThicknessLink, eFieldTypeTk.NumberOrObject);
WK.ResolveLabel(symbol, ref this._pointerThicknessLink, eFieldTypeTk.NumberOrObject);
WK.ResolveLabel(symbol, ref this._majorTicksQuantityLink, eFieldTypeTk.NumberOrObject);
WK.ResolveLabel(symbol, ref this._minorTicksPerIntervalLink, eFieldTypeTk.NumberOrObject);
}
public void GetTokens(object symbol)
{
WK.GetTokens(symbol, MinValueLink, eFieldTypeTk.NumberOrObject);
WK.GetTokens(symbol, MaxValueLink, eFieldTypeTk.NumberOrObject);
WK.GetTokens(symbol, LinkedValue, eFieldTypeTk.Expression);
WK.GetTokens(symbol, HeaderLink, eFieldTypeTk.ObjectOrString);
WK.GetTokens(symbol, RimThicknessLink, eFieldTypeTk.NumberOrObject);
WK.GetTokens(symbol, PointerThicknessLink, eFieldTypeTk.NumberOrObject);
WK.GetTokens(symbol, MajorTicksQuantityLink, eFieldTypeTk.NumberOrObject);
WK.GetTokens(symbol, MinorTicksPerIntervalLink, eFieldTypeTk.NumberOrObject);
}
public void ApplyTokens(object symbol)
{
MinValueLink = WK.ApplyTokens(symbol, MinValueLink, eFieldTypeTk.NumberOrObject);
MaxValueLink = WK.ApplyTokens(symbol, MaxValueLink, eFieldTypeTk.NumberOrObject);
LinkedValue = WK.ApplyTokens(symbol, LinkedValue, eFieldTypeTk.Expression);
HeaderLink = WK.ApplyTokens(symbol, HeaderLink, eFieldTypeTk.ObjectOrString);
RimThicknessLink = WK.ApplyTokens(symbol, RimThicknessLink, eFieldTypeTk.NumberOrObject);
PointerThicknessLink = WK.ApplyTokens(symbol, PointerThicknessLink, eFieldTypeTk.NumberOrObject);
MajorTicksQuantityLink = WK.ApplyTokens(symbol, MajorTicksQuantityLink, eFieldTypeTk.NumberOrObject);
MinorTicksPerIntervalLink = WK.ApplyTokens(symbol, MinorTicksPerIntervalLink, eFieldTypeTk.NumberOrObject);
}
public void OnFinishClipboardCopy()
{
MinValueLink = WK.OnFinishClipboardCopy(MinValueLink);
MaxValueLink = WK.OnFinishClipboardCopy(MaxValueLink);
LinkedValue = WK.OnFinishClipboardCopy(LinkedValue);
HeaderLink = WK.OnFinishClipboardCopy(HeaderLink);
RimThicknessLink = WK.OnFinishClipboardCopy(RimThicknessLink);
PointerThicknessLink = WK.OnFinishClipboardCopy(PointerThicknessLink);
MajorTicksQuantityLink = WK.OnFinishClipboardCopy(MajorTicksQuantityLink);
MinorTicksPerIntervalLink = WK.OnFinishClipboardCopy(MinorTicksPerIntervalLink);
}
public void OnFinishClipboardPaste()
{
MinValueLink = WK.OnFinishClipboardPaste(MinValueLink);
MaxValueLink = WK.OnFinishClipboardPaste(MaxValueLink);
LinkedValue = WK.OnFinishClipboardPaste(LinkedValue);
HeaderLink = WK.OnFinishClipboardPaste(HeaderLink);
RimThicknessLink = WK.OnFinishClipboardPaste(RimThicknessLink);
PointerThicknessLink = WK.OnFinishClipboardPaste(PointerThicknessLink);
MajorTicksQuantityLink = WK.OnFinishClipboardPaste(MajorTicksQuantityLink);
MinorTicksPerIntervalLink = WK.OnFinishClipboardPaste(MinorTicksPerIntervalLink);
}
public void OnStartClipboardCopy()
{
MinValueLink = WK.OnStartClipboardCopy(MinValueLink);
MaxValueLink = WK.OnStartClipboardCopy(MaxValueLink);
LinkedValue = WK.OnStartClipboardCopy(LinkedValue);
HeaderLink = WK.OnStartClipboardCopy(HeaderLink);
RimThicknessLink = WK.OnStartClipboardCopy(RimThicknessLink);
PointerThicknessLink = WK.OnStartClipboardCopy(PointerThicknessLink);
MajorTicksQuantityLink = WK.OnStartClipboardCopy(MajorTicksQuantityLink);
MinorTicksPerIntervalLink = WK.OnStartClipboardCopy(MinorTicksPerIntervalLink);
}
public void OnSave(int displayId, object el)
{
OnStartClipboardCopy();
OnFinishClipboardCopy();
WK.SaveHelper.SaveStart(displayId, el);
WK.SaveHelper.SaveLink(LinkedValue, "LinkedValue");
WK.SaveHelper.SaveLink(MinValueLink, "MinValue");
WK.SaveHelper.SaveLink(MaxValueLink, "MaxValue");
WK.SaveHelper.SaveLink(HeaderLink, "HeaderLink");
WK.SaveHelper.SaveLink(RimThicknessLink, "RimThickness");
WK.SaveHelper.SaveLink(PointerThicknessLink, "PointerThickness");
WK.SaveHelper.SaveLink(MajorTicksQuantityLink, "MajorTicksQuantity");
WK.SaveHelper.SaveLink(MinorTicksPerIntervalLink, "MinorTicksPerInterval");
if (HasStaticHeader && _localize)
WK.SaveHelper.AddLocalizableString(this._headerLink);
}
#endregion
#region Aux Methods
private void UpdateGridContent()
{
this.mainGrid.Children.Clear();
this.mainGrid.RowDefinitions.Clear();
GridUnitType gridUnitTypeRow0 = GridUnitType.Auto;
GridUnitType gridUnitTypeRow1 = GridUnitType.Star;
int rowTitle = 1;
int rowGauge = 0;
HorizontalAlignment horizontalAlignmentTitle = HorizontalAlignment.Center;
switch (this.HeaderAlignment)
{
case TK.eTkHeaderAlignment.Top:
case TK.eTkHeaderAlignment.TopLeft:
case TK.eTkHeaderAlignment.TopRight:
rowTitle = 0;
rowGauge = 1;
gridUnitTypeRow0 = GridUnitType.Auto;
gridUnitTypeRow1 = GridUnitType.Star;
horizontalAlignmentTitle = HorizontalAlignment.Center;
if (this.HeaderAlignment == TK.eTkHeaderAlignment.TopLeft)
horizontalAlignmentTitle = HorizontalAlignment.Left;
if (this.HeaderAlignment == TK.eTkHeaderAlignment.TopRight)
horizontalAlignmentTitle = HorizontalAlignment.Right;
break;
case TK.eTkHeaderAlignment.Bottom:
case TK.eTkHeaderAlignment.BottomLeft:
case TK.eTkHeaderAlignment.BottomRight:
rowTitle = 1;
rowGauge = 0;
horizontalAlignmentTitle = HorizontalAlignment.Center;
gridUnitTypeRow0 = GridUnitType.Star;
gridUnitTypeRow1 = GridUnitType.Auto;
if (this.HeaderAlignment == TK.eTkHeaderAlignment.BottomLeft)
horizontalAlignmentTitle = HorizontalAlignment.Left;
if (this.HeaderAlignment == TK.eTkHeaderAlignment.BottomRight)
horizontalAlignmentTitle = HorizontalAlignment.Right;
break;
default:
break;
}
this.mainGrid.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(1, gridUnitTypeRow0) });
this.mainGrid.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(1, gridUnitTypeRow1) });
this.mainGrid.Children.Add(this.gauge);
Grid.SetRow(this.gauge, rowGauge);
this.mainGrid.Children.Add(this.gHeader);
Grid.SetRow(this.gHeader, rowTitle);
this.gHeader.HorizontalAlignment = horizontalAlignmentTitle;
this.Content = this.mainGrid;
}
private bool IsStaticValue(string value)
{
if (string.IsNullOrEmpty(value))
return false;
if (value.Contains("@") || (value.Contains("{") && value.Contains("}")))
return false;
return true;
}
#endregion
}
}
<UserControl x:Class="T.Portable.Controls.TSampleGaugeConfig"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ttw="clr-namespace:T.Toolkit.Wpf;assembly=T.Toolkit.Wpf"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:ttc="clr-namespace:T.Portable.Controls"
xmlns:gauge="clr-namespace:Syncfusion.UI.Xaml.Gauges;assembly=Syncfusion.SfGauge.Wpf"
d:DataContext="{d:DesignInstance Type=ttc:TSampleGauge}"
mc:Ignorable="d"
d:DesignHeight="340"
d:DesignWidth="440.982"
Loaded="UserControl_Loaded">
<UserControl.Resources>
<ObjectDataProvider x:Key="PointerType" MethodName="GetValues" ObjectType="{x:Type sys:Enum}">
<ObjectDataProvider.MethodParameters>
<x:Type TypeName="gauge:PointerType" />
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
</UserControl.Resources>
<Grid >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="140" />
<ColumnDefinition Width="150" />
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="28" />
<RowDefinition Height="28" />
<RowDefinition Height="28" />
<RowDefinition Height="28" />
<RowDefinition Height="28" />
<RowDefinition Height="28" />
<RowDefinition Height="28" />
<RowDefinition Height="28" />
<RowDefinition Height="28" />
<RowDefinition Height="28" />
<RowDefinition Height="28" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Label Content="Header:"
Grid.Row="0"
VerticalAlignment="Center"
HorizontalAlignment="Right" />
<ttw:FieldEditor EditInfo="{Binding Path=ObjectOrStringField}" Text="{Binding Path=HeaderLink, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Grid.Column="1" Grid.ColumnSpan="2" VerticalAlignment="Center" HorizontalAlignment="Stretch" />
<Label Grid.Row="1" Grid.Column="0" Content="Header FontSize:" VerticalAlignment="Center" HorizontalAlignment="Right" />
<Grid Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="3">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto" MinWidth="50"/>
<ColumnDefinition Width="auto" />
<ColumnDefinition Width="auto" MinWidth="105"/>
<ColumnDefinition Width="auto"/>
</Grid.ColumnDefinitions>
<ComboBox ItemsSource="{Binding Path=HeaderFontSizes}" SelectedItem="{Binding Path=HeaderFontSize, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Left" Width="50" SelectedIndex="6" IsEditable="True" />
<Label Content="Alignment:" Grid.Column="1" VerticalAlignment="Center" HorizontalAlignment="Right" Margin="4,0,0,0" />
<ComboBox Grid.Column="2" Margin="4,0,0,0" VerticalAlignment="Center" HorizontalAlignment="Stretch" ItemsSource="{Binding Path=HeaderAlignments}" SelectedItem="{Binding Path=HeaderAlignment, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<CheckBox Content="Localize" IsChecked="{Binding Path=Localize, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Center" Grid.Column="3" Margin="4,0,0,0" />
</Grid>
<Label Content="Linked Value:"
Grid.Row="2"
VerticalAlignment="Center"
HorizontalAlignment="Right" />
<ttw:FieldEditor EditInfo="{Binding Path=ExpressionField}"
Text="{Binding Path=LinkedValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Grid.Column="1" Grid.ColumnSpan="2"
Grid.Row="2"
VerticalAlignment="Center"
HorizontalAlignment="Stretch"/>
<Label Content="Minimum Value:"
Grid.Row="3"
VerticalAlignment="Center"
HorizontalAlignment="Right"
/>
<ttw:FieldEditor Text="{Binding Path=MinValueLink, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
EditInfo="{Binding Path=NumberOrObjectField}"
Grid.Column="1" Grid.ColumnSpan="2"
Grid.Row="3"
VerticalAlignment="Center"
HorizontalAlignment="Stretch"/>
<Label Content="Maximum Value:"
Grid.Row="4"
VerticalAlignment="Center"
HorizontalAlignment="Right"
/>
<ttw:FieldEditor Text="{Binding Path=MaxValueLink, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
EditInfo="{Binding Path=NumberOrObjectField}"
Grid.Column="1" Grid.ColumnSpan="2"
Grid.Row="4"
VerticalAlignment="Center"
HorizontalAlignment="Stretch" />
<Label
Grid.Row="5"
Content="Label FontSize:"
VerticalAlignment="Center"
HorizontalAlignment="Right"
/>
<ComboBox ItemsSource="{Binding Path=HeaderFontSizes}"
SelectedItem="{Binding Path=ScaleFontSize, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Grid.Column="1"
Grid.Row="5"
VerticalAlignment="Center"
HorizontalAlignment="Left"
Width="50" />
<Label Content="Rim Thickness:"
Grid.Row="6"
VerticalAlignment="Center"
HorizontalAlignment="Right" />
<ttw:FieldEditor Text="{Binding Path=RimThicknessLink, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
EditInfo="{Binding Path=NumberOrObjectField}"
Grid.Column="1"
Grid.Row="6"
VerticalAlignment="Center"
HorizontalAlignment="Stretch" />
<StackPanel Orientation="Horizontal" Grid.Row="6" Grid.Column="2" Margin="10,0,0,0" Grid.ColumnSpan="2">
<ttw:BrushEditorButton SelectedBrush="{Binding Path=RimStroke, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Height="20"
Width="40"
AllowThemeColors="False"
VerticalAlignment="Center" />
<Label Content="Rim Stroke" Margin="4,0,0,0"
VerticalAlignment="Center" />
</StackPanel>
<Label Content="Pointer Thickness:"
Grid.Row="7"
VerticalAlignment="Center"
HorizontalAlignment="Right" />
<ttw:FieldEditor Text="{Binding Path=PointerThicknessLink, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
EditInfo="{Binding Path=NumberOrObjectField}"
Grid.Column="1"
Grid.Row="7"
VerticalAlignment="Center"
HorizontalAlignment="Stretch" />
<StackPanel Orientation="Horizontal"
Grid.Row="7" Grid.Column="2" Margin="10,0,0,0" Grid.ColumnSpan="2">
<ttw:BrushEditorButton SelectedBrush="{Binding Path=PointerStroke, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Height="20"
Width="40"
AllowThemeColors="False"
VerticalAlignment="Center" />
<Label Content="Pointer Stroke" Margin="4,0,0,0"
VerticalAlignment="Center" />
</StackPanel>
<Label Content="Pointer Type:"
Grid.Row="8"
VerticalAlignment="Center"
HorizontalAlignment="Right" />
<ComboBox ItemsSource="{Binding Source={StaticResource PointerType}}"
SelectedItem="{Binding Path=PointerType, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
VerticalAlignment="Center"
Grid.Column="1"
Grid.Row="8"
>
</ComboBox>
<CheckBox Content="Show NeedlePointer" IsChecked="{Binding ShowNeedlePointer, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{Binding Path=CanSecondPointer}"
VerticalAlignment="Center"
Margin="10,0,0,0"
Grid.Column="2"
Grid.Row="8"
/>
<CheckBox IsChecked="{Binding Path=ShowTicks, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Grid.Column="2"
Grid.ColumnSpan="2"
Content="Show Ticks"
Grid.Row="5"
HorizontalAlignment="Left"
VerticalAlignment="Center"
/>
<CheckBox IsChecked="{Binding Path=ShowLabels, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Grid.Column="1"
Grid.ColumnSpan="2"
Content="Show Labels"
Grid.Row="5"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Margin="55,0,0,0"
/>
<Label Content="MajorTicks Quantity:"
Grid.Row="9"
VerticalAlignment="Center"
HorizontalAlignment="Right"/>
<ttw:FieldEditor Text="{Binding Path=MajorTicksQuantityLink, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
EditInfo="{Binding Path=NumberOrObjectField}"
Grid.Column="1"
Grid.Row="9"
VerticalAlignment="Center"
HorizontalAlignment="Stretch" />
<Label Content="MinorTicks per Interval:"
Grid.Row="10"
VerticalAlignment="Center"
HorizontalAlignment="Right"/>
<ttw:FieldEditor Text="{Binding Path=MinorTicksPerIntervalLink, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
EditInfo="{Binding Path=NumberOrObjectField}"
Grid.Column="1"
Grid.Row="10"
VerticalAlignment="Center"
HorizontalAlignment="Stretch" />
</Grid>
</UserControl>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.IO;
using T.Toolkit;
namespace T.Portable.Controls
{
public partial class TSampleGaugeConfig : UserControl, IControlConfig, IOnOK
{
#region Fields
private TSampleGauge controlRef = null;
#endregion Fields
public TSampleGaugeConfig()
{
InitializeComponent();
}
#region Public Methods
public void LoadControl(object _control)
{
this.controlRef = _control as TSampleGauge;
this.DataContext = this.controlRef;
}
public bool OnOK()
{
return true;
}
#endregion Public Methods
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
}
}
}
using System;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Globalization;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Interop;
using System.Windows.Media;
using T.Library;
using T.Toolkit.Wpf;
namespace T.Portable.Components.Gauges
{
#region Enums
public enum CircularGaugeTypes
{
Default,
East,
West,
North,
South,
NorthEast,
NorthWest,
SouthEast,
SouthWest
}
public enum HeaderAlignment
{
Left,
Right,
Center,
Top,
Bottom,
TopLeft,
TopRight,
BottomLeft,
BottomRight,
Custom
}
public enum TickShape
{
Ellipse,
Rectangle,
Triangle
}
public enum PointerType
{
NeedlePointer,
RangePointer,
SymbolPointer
}
public enum NumericScaleType
{
Auto,
Thousands,
Millions,
Billions,
Trillions,
Quadrillions,
Quintillions
}
public enum LabelPosition
{
Inside,
Outside,
Custom
}
public enum TickPosition
{
Inside,
Outside,
Cross,
Custom
}
public enum RangePointerPosition
{
Inside,
Outside,
Cross,
Custom
}
public enum RangePosition
{
Inside,
SetAsGaugeRim,
Outside,
Custom
}
public enum Symbol
{
Rectangle,
RoundedRectangle,
Ellipse,
Triangle,
InvertedTriangle,
Arrow,
InvertedArrow,
Hexagon,
Pentagon,
Cross,
Diamond,
Custom
}
public enum NeedlePointerType
{
Rectangle,
Triangle,
Arrow,
Tapered
}
public enum RangeCap
{
None,
Start,
End,
Both
}
public enum ViewAlignment
{
Start,
Center,
End
}
#endregion
#region SfCircularGauge class
public class SfCircularGauge : Canvas, IDisposable
{
#region Fields
internal object circularGauge = null;
private object gaugeHeader = null;
public bool IsCompass = false;
#endregion
#region Properties
public int LabelsQuantity { get; set; } = 10;
public int LabelsCreated { get; set; } = 0;
private HeaderAlignment headerAlignment = HeaderAlignment.Top;
public HeaderAlignment HeaderAlignment
{
get => headerAlignment;
set
{
headerAlignment = value;
if (this.circularGauge == null)
return;
OpenSilver.Interop.ExecuteJavaScript("$0.titleStyle.position = $1", this.circularGauge, headerAlignmentAsString);
}
}
private string headerAlignmentAsString
{
get
{
if (HeaderAlignment == HeaderAlignment.Bottom)
return "Bottom";
if (HeaderAlignment == HeaderAlignment.Left)
return "Left";
if (HeaderAlignment == HeaderAlignment.Right)
return "Right";
return "Top";
}
}
private TextBlock GaugeHeaderTextBlock
{
get => GaugeHeader as TextBlock;
}
public double GaugeHeight
{
set
{
if (this.circularGauge == null)
return;
string height = "100%";
if (!double.IsNaN(value))
height = TConvert.ToString(value > 100 ? value : 100);
OpenSilver.Interop.ExecuteJavaScript("$0.height = $1", this.circularGauge, height);
}
}
public double GaugeWidth
{
set
{
if (this.circularGauge == null)
return;
string width = "100%";
string radiusWidth = "100%";
if (!double.IsNaN(value))
{
radiusWidth = TConvert.ToString(value / 2);
width = TConvert.ToString(value > 100 ? value : 100);
}
OpenSilver.Interop.ExecuteJavaScript("$0.width = $1", this.circularGauge, width);
}
}
/// <summary>
/// This object is a TxtBlock
/// </summary>
public object GaugeHeader
{
get
{
TextBlock textBlock = new TextBlock
{
Text = "Header",
FontFamily = new FontFamily("Verdana"),
FontSize = 12,
FontWeight = FontWeights.Normal,
Foreground = WK.GetBrush(WK.eThemeColor.TextForeground) as Brush,
};
textBlock.Text = string.Empty;
if(this.gaugeHeader == null)
this.gaugeHeader = textBlock;
return this.gaugeHeader;
}
set
{
if (this.circularGauge == null)
return;
if (value == null)
return;
if (!(value is TextBlock))
return;
this.gaugeHeader = value;
}
}
public CircularGaugeTypes GaugeType { get; set; }
public Point GaugeHeaderPosition { get; set; }
public ObservableCollection<CircularScale> SubScales { get; set; }
private CircularScale _mainScale;
/// <summary>
/// Represents the <b>Axes</b> object of the WebAssembly Gauge
/// </summary>
public CircularScale MainScale
{
get => _mainScale;
set
{
_mainScale = value;
//if (value == null)
// OpenSilver.Interop.ExecuteJavaScript("$0.axes.shift()", this.circularGauge);
//else
// OpenSilver.Interop.ExecuteJavaScript("$0.axes.unshift($1)", this.circularGauge, value.circularScale);
}
}
public Style CircularScaleStyle { get; set; }
public double SpacingMargin { get; set; }
public CircularGaugeAnnotationCollection Annotations { get; set; }
public ObservableCollection<CircularScale> Scales { get; set; }
#endregion
#region Construtor, Load and Dispose
public SfCircularGauge()
{
this.Scales = new ObservableCollection<CircularScale>();
this.SubScales = new ObservableCollection<CircularScale>();
this.Annotations = new CircularGaugeAnnotationCollection();
this.HorizontalAlignment = HorizontalAlignment.Stretch;
this.VerticalAlignment = VerticalAlignment.Stretch;
this.Loaded += this.OnLoaded;
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
string initialTitle = this.GaugeHeaderTextBlock.Text ?? "";
double initialFontSize = this.GaugeHeaderTextBlock.FontSize;
if (initialFontSize <= 0)
initialFontSize = 12;
object pointers = OpenSilver.Interop.ExecuteJavaScript("[]");
if(this.Scales.Count > 0)
{
int numberOfPointers = this.Scales[0].Pointers.Count;
for (int i = 0; i < numberOfPointers; i++)
OpenSilver.Interop.ExecuteJavaScript("$0.push({})", pointers);
}
/// There will always be a defualt range to simbolize the Rim Stroke and Thickness
object ranges = OpenSilver.Interop.ExecuteJavaScript("[{}]");
if (this.Scales.Count > 0)
{
int numberOfRanges = this.Scales[0].Ranges.Count;
for (int i = 0; i < numberOfRanges; i++)
OpenSilver.Interop.ExecuteJavaScript("$0.push({})", ranges);
}
object annotations = OpenSilver.Interop.ExecuteJavaScript("[]");
if (this.Annotations.Count > 0)
{
int numberOfAnnotations = this.Annotations.Count;
for (int i = 0; i < numberOfAnnotations; i++)
OpenSilver.Interop.ExecuteJavaScript("$0.push({})", annotations);
}
string width = "100%";
string height = "100%";
string radiusWidth = "100%";
if (!double.IsNaN(this.Width))
{
radiusWidth = TConvert.ToString(Math.Min(this.Width, this.Height) / 2);
width = TConvert.ToString(this.Width > 100 ? this.Width : 100);
}
if (!double.IsNaN(this.Height))
height = TConvert.ToString(this.Height > 100 ? this.Height : 100);
object div = OpenSilver.Interop.GetDiv(this);
string id = TConvert.ToString(OpenSilver.Interop.ExecuteJavaScript("$0.id", div));
object parentDiv = OpenSilver.Interop.ExecuteJavaScript("$0.parentNode", div);
this.circularGauge = OpenSilver.Interop.ExecuteJavaScript(@"new ej.circulargauge.CircularGauge({
background : 'transparent',
enablePersistance: true,
annotations: $0,
axes: [{
pointers: $1,
ranges: $2,
radius: $3,
}],
})", annotations, pointers, ranges, radiusWidth);
OpenSilver.Interop.ExecuteJavaScript("$0.width = $1", this.circularGauge, width);
OpenSilver.Interop.ExecuteJavaScript("$0.height = $1", this.circularGauge, height);
this.LabelsCreated = 0;
if (this.IsCompass)
{
OpenSilver.Interop.ExecuteJavaScript(@"$0.axisLabelRender = function (args) {
var value = ['E', 'SE', 'S', 'SW', 'W', 'NW', 'N', 'NE', 'E'];
var pos = args.value;
if(pos < 0) { pos = 0; }
if(pos > 8) { pos = 8; }
args.text = value[args.value];
}", this.circularGauge);
}
else
OpenSilver.Interop.ExecuteJavaScript("$0.axisLabelRender = $1", this.circularGauge, OpenSilver.Interop.CreateJavascriptCallback((Action<object>)this.OnAxisLabelRender));
OpenSilver.Interop.ExecuteJavaScript("$0.appendTo($1)", this.circularGauge, "#" + id);
#if DEBUG
/// TEMP -> Variable for easy debug in Browser console
OpenSilver.Interop.ExecuteJavaScript("window[$0] = $1", "sfCircularGauge_" + id, this.circularGauge);
#endif
OpenSilver.Interop.ExecuteJavaScript("$0.style.overflow = 'hidden'", div);
if (this.Scales.Count > 0)
{
foreach (var scale in this.Scales)
scale.CircularGaugeWebAssemblyObject = this.circularGauge;
}
if (this.Annotations.Count > 0)
{
this.Annotations.CircularGaugeWebAssemblyObject = this.circularGauge;
}
}
public void Dispose()
{
}
#endregion
#region Static Methods
public static string BrushToRgbaString(Brush brush)
{
if (brush is SolidColorBrush solidColorBrush)
{
Color color = solidColorBrush.Color;
return ColorToRgbaString(color);
}
return ColorToRgbaString(Colors.Transparent);
}
public static string ColorToRgbaString(Color color)
{
double alpha = color.A / 255.0;
return $"rgba({color.R}, {color.G}, {color.B}, {alpha.ToString(CultureInfo.InvariantCulture)})";
}
#endregion
public void Refresh()
{
if (this.circularGauge == null)
return;
OpenSilver.Interop.ExecuteJavaScript("$0.refresh()", this.circularGauge);
}
internal void UpdateAxesRadius()
{
if (double.IsNaN(this.Height) || double.IsNaN(this.Width))
return;
double _height = this.Height > 100 ? this.Height : 100;
double _width = this.Width > 100 ? this.Width : 100;
//double radius = Math.Min(_height, _width) / 2;
//radius = (radius + (this.Scales[0].RimStrokeThickness / 2));
double centerX = _width / 2;
double centerY = _height / 1.5;
string radiusPercentage = "120%";
if (this.MainScale.SweepAngle >= 270)
{
double factor = this.MainScale.SweepAngle == 360 ? 2 : 1.85;
centerY = _height / factor;
radiusPercentage = "100%";
}
OpenSilver.Interop.ExecuteJavaScript("$0.centerY = $1", this.circularGauge, TConvert.ToString(centerY));
OpenSilver.Interop.ExecuteJavaScript("$0.centerX = $1", this.circularGauge, TConvert.ToString(centerX));
OpenSilver.Interop.ExecuteJavaScript("$0.axes[0].radius = $1", this.circularGauge, radiusPercentage);
}
private void OnAxisLabelRender(object args)
{
try
{
this.LabelsCreated += 1;
string originalText = TConvert.ToString(OpenSilver.Interop.ExecuteJavaScript("$0.text", args));
double roundedValue = Math.Round(TConvert.ToDouble(originalText), 2);
OpenSilver.Interop.ExecuteJavaScript("$0.text = $1", args, roundedValue);
if (this.LabelsCreated < this.LabelsQuantity)
return;
double endValue = this.Scales[0].EndValue;
OpenSilver.Interop.ExecuteJavaScript("$0.text = $1", args, endValue);
this.LabelsCreated = 0;
}
catch (Exception ex)
{
Console.WriteLine($"Error in OnAxisLabelRender : {ex.Message}");
TException.Log(ex);
}
}
}
#endregion
#region Auxiliary Classes
public class CircularScaleLabel
{
public object Content { get; set; }
public Brush Foreground { get; set; }
public double Angle { get; set; }
}
public class CircularScaleLabelCollection : ObservableCollection<CircularScaleLabel>
{
}
public class CircularScaleTick
{
public TickShape TickShape { get; set; }
}
public class CircularScaleTickCollection : ObservableCollection<CircularScaleTick>
{
}
public class GaugeGradientStop
{
public Color Color { get; set; }
public double Value { get; set; }
internal double ActualValue { get; set; }
}
public class GaugeGradientStopCollection : ObservableCollection<GaugeGradientStop>
{
}
public class CircularRange
{
public object RangeIndex { get; set; } = 0;
public object Tag { get; set; }
internal double InternalRangeOffset = 1.0;
public PathSegmentCollection RangeSeg { get; set; }
internal double RangeSize { get; set; }
internal Size ParentSize { get; set; }
internal double ParentStartValue { get; set; }
internal double ParentEndValue { get; set; }
internal double ParentStartAngle { get; set; }
internal double ClockWiseStartAngle { get; set; }
internal double ParentSweepAngle { get; set; }
internal CircularScale ParentScale { get; set; }
private double startValue = 0;
public double StartValue
{
get => startValue;
set
{
startValue = value;
if (this.CircularGaugeWebAssemblyObject == null)
return;
this.LoadCircularRangesFromWebAssemblyControl();
OpenSilver.Interop.ExecuteJavaScript("$0.start = $1", circularRange, startValue);
}
}
private double endValue = 0;
public double EndValue
{
get => endValue;
set
{
endValue = value;
if (this.CircularGaugeWebAssemblyObject == null)
return;
this.LoadCircularRangesFromWebAssemblyControl();
OpenSilver.Interop.ExecuteJavaScript("$0.end = $1", circularRange, endValue);
}
}
internal double InternalEndValue { get; set; }
internal double StartAngle { get; set; }
public double EndAngle { get; set; }
public Point StartPoint { get; set; }
public Point EndPoint { get; set; }
public Size Size { get; set; }
internal bool IsLargeArc { get; set; }
internal SweepDirection SweepDirection { get; set; }
private Visibility visibility = Visibility.Collapsed;
public Visibility Visibility
{
get { return this.visibility; }
set
{
this.visibility = value;
if (this.CircularGaugeWebAssemblyObject == null)
return;
this.LoadCircularRangesFromWebAssemblyControl();
if(this.visibility == Visibility.Visible)
OpenSilver.Interop.ExecuteJavaScript("$0.opacity = 1.0", this.circularRange);
if (this.visibility == Visibility.Collapsed)
OpenSilver.Interop.ExecuteJavaScript("$0.opacity = 0.0", this.circularRange);
}
}
private Brush stroke;
public Brush Stroke
{
get { return stroke; }
set
{
this.stroke = value;
if (this.CircularGaugeWebAssemblyObject == null)
return;
this.LoadCircularRangesFromWebAssemblyControl();
OpenSilver.Interop.ExecuteJavaScript("$0.color = $1", this.circularRange, SfCircularGauge.BrushToRgbaString(this.stroke));
}
}
private double strokeThickness = 2;
public double StrokeThickness
{
get { return strokeThickness; }
set
{
this.strokeThickness = value;
this.StartWidth = value;
this.EndWidth = value;
}
}
internal int LogBase { get; set; }
public bool IsLogarithmic { get; set; }
public double StartWidth
{
get => TConvert.ToDouble(OpenSilver.Interop.ExecuteJavaScript("$0.startWidth", circularRange));
set => OpenSilver.Interop.ExecuteJavaScript("$0.startWidth = $1", circularRange, value);
}
public double EndWidth
{
get => TConvert.ToDouble(OpenSilver.Interop.ExecuteJavaScript("$0.endWidth", circularRange));
set => OpenSilver.Interop.ExecuteJavaScript("$0.endWidth = $1", circularRange, value);
}
public GaugeGradientStopCollection GradientStops { get; set; }
public double InnerStartOffset { get; set; }
public double InnerEndOffset { get; set; }
public double OuterStartOffset { get; set; }
public double OuterEndOffset { get; set; }
public double Offset { get; set; }
private object circularGaugeWebAssemblyObject;
/// <summary>
/// Reference to the Circular Gauge control
/// </summary>
public object CircularGaugeWebAssemblyObject
{
get => circularGaugeWebAssemblyObject;
set
{
circularGaugeWebAssemblyObject = value;
if (circularGaugeWebAssemblyObject == null)
return;
this.ApplyInitialSettingsToWebAssemblyObject();
}
}
private object circularRange;
public CircularRange()
{
circularRange = OpenSilver.Interop.ExecuteJavaScript("new Object()");
}
private void ApplyInitialSettingsToWebAssemblyObject()
{
this.LoadCircularRangesFromWebAssemblyControl();
OpenSilver.Interop.ExecuteJavaScript("$0.start = $1", this.circularRange, this.StartValue);
OpenSilver.Interop.ExecuteJavaScript("$0.end = $1", this.circularRange, this.EndValue);
OpenSilver.Interop.ExecuteJavaScript("$0.color = $1", this.circularRange, SfCircularGauge.BrushToRgbaString(this.Stroke));
OpenSilver.Interop.ExecuteJavaScript("$0.startWidth = $1", this.circularRange, this.StartWidth);
OpenSilver.Interop.ExecuteJavaScript("$0.endWidth = $1", this.circularRange, this.EndWidth);
if (this.visibility == Visibility.Visible)
OpenSilver.Interop.ExecuteJavaScript("$0.opacity = 1.0", this.circularRange);
if (this.visibility == Visibility.Collapsed)
OpenSilver.Interop.ExecuteJavaScript("$0.opacity = 0.0", this.circularRange);
this.SaveCircularRangesToWebAssemblyControl();
}
/// <summary>
/// Method used to Load Range object that is currently being used in WebAssembly Runtime
/// </summary>
private void LoadCircularRangesFromWebAssemblyControl()
{
this.circularRange = OpenSilver.Interop.ExecuteJavaScript("$0.axes[0].ranges[$1]", this.CircularGaugeWebAssemblyObject, this.RangeIndex);
}
/// <summary>
/// Method used to replace current Range object with new one
/// Used when trying to go back to default properties.
/// </summary>
private void SaveCircularRangesToWebAssemblyControl()
{
OpenSilver.Interop.ExecuteJavaScript("$0.axes[0].ranges[$1] = $2", this.CircularGaugeWebAssemblyObject, this.RangeIndex, this.circularRange);
}
}
public class CircularRangeCollection : ObservableCollection<CircularRange>
{
internal object circularRange = null;
private object circularGaugeWebAssemblyObject;
/// <summary>
/// Reference to the Circular Gauge control
/// </summary>
public object CircularGaugeWebAssemblyObject
{
get => circularGaugeWebAssemblyObject;
set
{
circularGaugeWebAssemblyObject = value;
if (circularGaugeWebAssemblyObject == null)
return;
for (int i = 0; i < this.Count; i++)
this.Items[i].CircularGaugeWebAssemblyObject = circularGaugeWebAssemblyObject;
}
}
public CircularRangeCollection(object circularScale)
{
this.circularRange = circularScale;
}
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
base.OnCollectionChanged(e);
if (e.Action != NotifyCollectionChangedAction.Add)
return;
for (int i = 0; i < this.Count; i++)
this.Items[i].RangeIndex = i + 1; /// The index starts at 1 because we have created a default range to act as the Rim Stroke and Thickness
}
}
public class CircularPointer
{
public int PointerIndex { get; set; } = 0;
public double Width { get; set; }
public double Height { get; set; }
private Brush rangePointerStroke = new SolidColorBrush(Color.FromArgb(0xff, 0x00, 0x00, 0x90));
public Brush RangePointerStroke
{
get { return needlePointerStroke; }
set
{
if (this.PointerType != PointerType.RangePointer)
return;
needlePointerStroke = value;
if (this.CircularGaugeWebAssemblyObject == null)
return;
OpenSilver.Interop.ExecuteJavaScript("$0.color = $1", this.circularPointer, SfCircularGauge.BrushToRgbaString(rangePointerStroke));
OpenSilver.Interop.ExecuteJavaScript("$0.cap.color = 'transparent'", this.circularPointer);
}
}
private double rangePointerStrokeThickness = 10;
public double RangePointerStrokeThickness
{
get { return rangePointerStrokeThickness; }
set
{
if (this.PointerType == PointerType.NeedlePointer)
return;
rangePointerStrokeThickness = value;
if (this.CircularGaugeWebAssemblyObject == null)
return;
OpenSilver.Interop.ExecuteJavaScript("$0.pointerWidth = $1", this.circularPointer, rangePointerStrokeThickness);
}
}
public Symbol Symbol { get; set; }
private Brush symbolPointerStroke = new SolidColorBrush(Color.FromArgb(0xff, 0x00, 0x00, 0x90));
public Brush SymbolPointerStroke
{
get { return needlePointerStroke; }
set
{
if (this.PointerType != PointerType.SymbolPointer)
return;
symbolPointerStroke = value;
if (this.CircularGaugeWebAssemblyObject == null)
return;
OpenSilver.Interop.ExecuteJavaScript("$0.color = $1", this.circularPointer, SfCircularGauge.BrushToRgbaString(symbolPointerStroke));
OpenSilver.Interop.ExecuteJavaScript("$0.cap.color = 'transparent'", this.circularPointer);
}
}
public double SymbolPointerHeight { get; set; }
public double SymbolPointerWidth { get; set; }
public DataTemplate SymbolPointerTemplate { get; set; }
private Brush needlePointerStroke = new SolidColorBrush(Color.FromArgb(0xff, 0x00, 0x00, 0x90));
public Brush NeedlePointerStroke
{
get { return needlePointerStroke; }
set
{
if (this.PointerType != PointerType.NeedlePointer)
return;
needlePointerStroke = value;
if (this.CircularGaugeWebAssemblyObject == null)
return;
OpenSilver.Interop.ExecuteJavaScript("$0.color = $1", this.circularPointer, SfCircularGauge.BrushToRgbaString(needlePointerStroke));
OpenSilver.Interop.ExecuteJavaScript("$0.cap.color = $1", this.circularPointer, SfCircularGauge.BrushToRgbaString(needlePointerStroke));
}
}
private double needlePointerStrokeThickness = 10;
public double NeedlePointerStrokeThickness
{
get { return needlePointerStrokeThickness; }
set
{
if (this.PointerType == PointerType.RangePointer)
return;
needlePointerStrokeThickness = value;
if (this.CircularGaugeWebAssemblyObject == null)
return;
OpenSilver.Interop.ExecuteJavaScript("$0.pointerWidth = $1", this.circularPointer, needlePointerStrokeThickness);
}
}
public double NeedleLengthFactor { get; set; }
public NeedlePointerType NeedlePointerType { get; set; }
public Brush PointerCapStroke { get; set; }
public double PointerCapDiameter { get; set; }
public double KnobRadiusFactor { get; set; }
public Brush KnobStroke { get; set; }
public double KnobStrokeThickness { get; set; }
public Brush KnobFill { get; set; }
public Visibility RangePointerVisibility { get; set; }
public Visibility SymbolPointerVisibility { get; set; }
public Visibility NeedlePointerVisibility { get; set; }
private double objValue = 0;
public double Value
{
get => objValue;
set
{
objValue = value;
if (this.CircularGaugeWebAssemblyObject == null || this.circularPointer == null)
return;
OpenSilver.Interop.ExecuteJavaScript("$0.value = $1", this.circularPointer, objValue);
}
}
public double RangeStart { get; set; }
private PointerType pointerType = PointerType.NeedlePointer;
/// <summary>
/// In WPF this property is used to define the type of Pointer used <br/>
/// In WebAssembly we must use property <b>PointerTypeString</b>
/// </summary>
public PointerType PointerType
{
get => pointerType;
set
{
pointerType = value;
if (this.CircularGaugeWebAssemblyObject == null)
return;
this.LoadCircularPointerFromWebAssemblyControl();
switch (pointerType)
{
default:
case PointerType.NeedlePointer:
OpenSilver.Interop.ExecuteJavaScript("$0.type = 'Needle'", this.circularPointer);
return;
case PointerType.RangePointer:
OpenSilver.Interop.ExecuteJavaScript("$0.type = 'RangeBar'", this.circularPointer);
return;
case PointerType.SymbolPointer:
OpenSilver.Interop.ExecuteJavaScript("$0.type = 'Marker'", this.circularPointer);
return;
}
}
}
private string PointerTypeString
{
get
{
switch (pointerType)
{
default:
case PointerType.NeedlePointer:
return "Needle";
case PointerType.RangePointer:
return "RangeBar";
case PointerType.SymbolPointer:
return "Marker";
}
}
}
public bool EnableAnimation { get; set; }
public double Offset { get; set; }
public bool EnableDragging { get; set; }
public double RangeStartOffset { get; set; }
public double RangeEndOffset { get; set; }
public double TailLengthFactor { get; set; }
public Brush TailFill { get; set; }
public Brush TailStroke { get; set; }
public double TailStrokeThickness { get; set; }
public RangeCap RangeCap { get; set; }
internal double Angle { get; set; }
private object circularGaugeWebAssemblyObject;
/// <summary>
/// Reference to the Circular Gauge control
/// </summary>
public object CircularGaugeWebAssemblyObject
{
get => circularGaugeWebAssemblyObject;
set
{
circularGaugeWebAssemblyObject = value;
if (circularGaugeWebAssemblyObject == null)
return;
this.ApplyInitialSettingsToWebAssemblyObject();
}
}
private object circularPointer;
public CircularPointer()
{
//circularPointer = OpenSilver.Interop.ExecuteJavaScript("new Object()");
}
private void ApplyInitialSettingsToWebAssemblyObject()
{
this.LoadCircularPointerFromWebAssemblyControl();
OpenSilver.Interop.ExecuteJavaScript("$0.value = $1", this.circularPointer, this.Value);
OpenSilver.Interop.ExecuteJavaScript("$0.color = $1", this.circularPointer, SfCircularGauge.BrushToRgbaString(this.SymbolPointerStroke));
if (this.PointerType == PointerType.RangePointer)
{
OpenSilver.Interop.ExecuteJavaScript("$0.color = $1", this.circularPointer, SfCircularGauge.BrushToRgbaString(this.RangePointerStroke));
OpenSilver.Interop.ExecuteJavaScript("$0.pointerWidth = $1", this.circularPointer, this.RangePointerStrokeThickness);
}
if (this.PointerType == PointerType.NeedlePointer)
{
OpenSilver.Interop.ExecuteJavaScript("$0.color = $1", this.circularPointer, SfCircularGauge.BrushToRgbaString(this.NeedlePointerStroke));
OpenSilver.Interop.ExecuteJavaScript("$0.cap.color = $1", this.circularPointer, SfCircularGauge.BrushToRgbaString(this.NeedlePointerStroke));
OpenSilver.Interop.ExecuteJavaScript("$0.pointerWidth = $1", this.circularPointer, this.NeedlePointerStrokeThickness);
}
OpenSilver.Interop.ExecuteJavaScript("$0.type = $1", this.circularPointer, this.PointerTypeString);
OpenSilver.Interop.ExecuteJavaScript("$0.radius = '100%'", this.circularPointer);
//OpenSilver.Interop.ExecuteJavaScript("$0.animation = {enable: true, duration: 100}", this.circularPointer);
OpenSilver.Interop.ExecuteJavaScript("$0.animation = {enable: false}", this.circularPointer);
this.SaveCircularPointerToWebAssemblyControl();
}
/// <summary>
/// Method used to Load pointer object that is currently being used in WebAssembly Runtime
/// </summary>
private void LoadCircularPointerFromWebAssemblyControl()
{
this.circularPointer = OpenSilver.Interop.ExecuteJavaScript("$0.axes[0].pointers[$1]", this.CircularGaugeWebAssemblyObject, this.PointerIndex);
}
/// <summary>
/// Method used to replace current pointer object with new one
/// Used when trying to go back to default properties.
/// </summary>
private void SaveCircularPointerToWebAssemblyControl()
{
OpenSilver.Interop.ExecuteJavaScript("$0.axes[0].pointers[$1] = $2", this.CircularGaugeWebAssemblyObject, this.PointerIndex, this.circularPointer);
}
}
public class CircularPointerCollection : ObservableCollection<CircularPointer>
{
private bool isControlLoaded = false;
internal object circularScale = null;
internal object circularPointerCollection = null;
//internal CircularPointer mainPointer = null;
#region Properties
private object circularGaugeWebAssemblyObject;
/// <summary>
/// Reference to the Circular Gauge control
/// </summary>
public object CircularGaugeWebAssemblyObject
{
get => circularGaugeWebAssemblyObject;
set
{
circularGaugeWebAssemblyObject = value;
if (circularGaugeWebAssemblyObject == null)
return;
//isControlLoaded = true;
//if(this.mainPointer != null)
// this.mainPointer.CircularGaugeWebAssemblyObject = circularGaugeWebAssemblyObject;
for (int i = 0; i < this.Count; i++)
this.Items[i].CircularGaugeWebAssemblyObject = circularGaugeWebAssemblyObject;
}
}
#endregion
public CircularPointerCollection()
{
this.circularPointerCollection = OpenSilver.Interop.ExecuteJavaScript("[]");
}
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
base.OnCollectionChanged(e);
if (e.Action != NotifyCollectionChangedAction.Add)
return;
for (int i = 0; i < this.Count; i++)
this.Items[i].PointerIndex = i;
}
}
public class GaugeAnnotation
{
public double Angle { get; set; }
public double Offset { get; set; }
public ViewAlignment HorizontalPosition { get; set; }
public ViewAlignment VerticalPosition { get; set; }
#region Properties
private object circularGaugeWebAssemblyObject;
/// <summary>
/// Reference to the Circular Gauge control
/// </summary>
public object CircularGaugeWebAssemblyObject
{
get => circularGaugeWebAssemblyObject;
set
{
circularGaugeWebAssemblyObject = value;
}
}
private object content;
/// <summary>
/// Content is a TextBlock object
/// </summary>
public object Content
{
get { return content; }
set
{
this.content = value;
}
}
private Brush contentColor = new SolidColorBrush(Color.FromArgb(0xff, 0x00, 0x00, 0x90));
public Brush ContentColor
{
get { return contentColor; }
set
{
this.contentColor = value;
//}
}
}
private Point viewMargin = new Point(0, 0);
public Point ViewMargin
{
get => viewMargin;
set
{
viewMargin = value;
}
}
#endregion
public GaugeAnnotation()
{
}
}
public class CircularGaugeAnnotationCollection : ObservableCollection<GaugeAnnotation>
{
private bool isControlLoaded = false;
internal object circularAnnotationCollection = null;
#region Properties
private object circularGaugeWebAssemblyObject;
/// <summary>
/// Reference to the Circular Gauge control
/// </summary>
public object CircularGaugeWebAssemblyObject
{
get => circularGaugeWebAssemblyObject;
set
{
circularGaugeWebAssemblyObject = value;
if (circularGaugeWebAssemblyObject == null)
return;
isControlLoaded = true;
for (int i = 0; i < this.Count; i++)
this.Items[i].CircularGaugeWebAssemblyObject = circularGaugeWebAssemblyObject;
}
}
#endregion
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
base.OnCollectionChanged(e);
if (e.Action != NotifyCollectionChangedAction.Add)
return;
for (int i = 0; i < this.Count; i++)
{
this.Items[i].CircularGaugeWebAssemblyObject = this.circularGaugeWebAssemblyObject;
}
}
public CircularGaugeAnnotationCollection()
{
this.circularAnnotationCollection = OpenSilver.Interop.ExecuteJavaScript("[]");
}
}
public class CircularPanel
{
public double ScaleMaxWidth { get; set; }
public SweepDirection SweepDirection { get; set; }
public double SweepAngle { get; set; }
public double StartAngle { get; set; }
public double AngularSpace { get; set; }
public Brush Foreground { get; set; }
public CircularScaleLabelCollection Labels { get; set; }
}
public class TickSetting
{
public double Length { get; set; }
public double Offset { get; set; }
public double StartOffset { get; set; }
public double EndOffset { get; set; }
public Brush Stroke { get; set; }
public double StrokeThickness { get; set; }
}
public class MajorTickSetting : TickSetting
{
}
public class MinorTickSetting : TickSetting
{
}
public class TickLinesPanel
{
public SweepDirection SweepDirection { get; set; }
public double SweepAngle { get; set; }
public double StartAngle { get; set; }
public double MinorTickAngularSpace { get; set; }
public double MajorTickAngularSpace { get; set; }
public double Interval { get; set; }
public bool IsLogarithmic { get; set; } = false;
public int LogBase { get; set; }
public double StartValue { get; set; } = 0;
public double EndValue { get; set; } = 100;
public double HalfLength { get; set; }
public double Length { get; set; }
public Brush Stroke { get; set; }
public double TickStrokeThickness { get; set; }
public CircularScaleTickCollection Ticks { get; set; }
}
public class CircularScale
{
private const double DEFAULT_MAJOR_TICK_HEIGHT = 12;
private const double DEFAULT_MAJOR_TICK_WIDTH = 3;
private const double DEFAULT_MINOR_TICK_HEIGHT = 6;
private const double DEFAULT_MINOR_TICK_WIDTH = 2;
internal object circularScale = null;
private bool IsReady
{
get { return circularScale != null && CircularGaugeWebAssemblyObject != null; }
}
private object circularGaugeWebAssemblyObject;
/// <summary>
/// Reference to the Circular Gauge control
/// </summary>
public object CircularGaugeWebAssemblyObject
{
get => circularGaugeWebAssemblyObject;
set
{
circularGaugeWebAssemblyObject = value;
if (circularGaugeWebAssemblyObject == null)
return;
this.ApplyInitialSettingsToWebAssemblyObject();
this.Pointers.CircularGaugeWebAssemblyObject = circularGaugeWebAssemblyObject;
this.Ranges.CircularGaugeWebAssemblyObject = circularGaugeWebAssemblyObject;
}
}
public Brush Background { get; set; }
public PathSegmentCollection ScaleSeg { get; set; }
private bool showTicks = true;
public bool ShowTicks
{
get => showTicks;
set
{
showTicks = value;
if (this.CircularGaugeWebAssemblyObject == null)
return;
if (this.IsReady)
{
if(!showTicks)
{
OpenSilver.Interop.ExecuteJavaScript("$0.axes[0].majorTicks.width = $1", this.CircularGaugeWebAssemblyObject, 0);
OpenSilver.Interop.ExecuteJavaScript("$0.axes[0].majorTicks.height = $1", this.CircularGaugeWebAssemblyObject, 0);
OpenSilver.Interop.ExecuteJavaScript("$0.axes[0].minorTicks.width = $1", this.CircularGaugeWebAssemblyObject, 0);
OpenSilver.Interop.ExecuteJavaScript("$0.axes[0].minorTicks.height = $1", this.CircularGaugeWebAssemblyObject, 0);
return;
}
}
}
}
private bool showLabels = true;
public bool ShowLabels
{
get => showLabels;
set
{
showLabels = value;
if (this.CircularGaugeWebAssemblyObject == null)
return;
if (this.IsReady)
{
double size = 0;
if (showLabels)
size = 8;
OpenSilver.Interop.ExecuteJavaScript("$0.axes[0].labelStyle.font.size = $1", this.CircularGaugeWebAssemblyObject, size + "px" );
}
}
}
public bool ShowRim { get; set; }
public Brush RimStroke { get; set; } = new SolidColorBrush(Color.FromArgb(0x3E, 0x6C, 0x00, 0x00));
private double rimStrokeThickness = 5;
public double RimStrokeThickness
{
get { return rimStrokeThickness; }
set
{
rimStrokeThickness = value;
if (this.CircularGaugeWebAssemblyObject == null)
return;
OpenSilver.Interop.ExecuteJavaScript("$0.lineStyle.width = $1", this.circularScale, rimStrokeThickness);
OpenSilver.Interop.ExecuteJavaScript("$0.ranges[0].startWidth = $1", this.circularScale, rimStrokeThickness);
OpenSilver.Interop.ExecuteJavaScript("$0.ranges[0].endWidth = $1", this.circularScale, rimStrokeThickness);
double offsetTicks = this.RimStrokeThickness / 2;
OpenSilver.Interop.ExecuteJavaScript("$0.axes[0].majorTicks.offset = $1", this.CircularGaugeWebAssemblyObject, offsetTicks);
OpenSilver.Interop.ExecuteJavaScript("$0.axes[0].minorTicks.offset = $1", this.CircularGaugeWebAssemblyObject, offsetTicks);
}
}
public NumericScaleType NumericScaleType { get; set; }
public bool EnableSmartLabels { get; set; }
public int NoOfFractionalDigit { get; set; }
public LabelPosition LabelPosition { get; set; }
public double LabelOffset { get; set; }
public TickPosition TickPosition { get; set; }
public double MajorTickOffset { get; set; }
public double MinorTickOffset { get; set; }
public RangePointerPosition RangePointerPosition { get; set; }
public double RangePointerOffset { get; set; }
public RangePosition RangePosition { get; set; }
public double RangeOffset { get; set; }
public double RimStartOffset { get; set; }
public double RimEndOffset { get; set; }
public double RadiusFactor { get; set; }
internal CircularScaleLabelCollection Labels { get; set; }
public CircularRangeCollection Ranges { get; set; }
public CircularPointerCollection Pointers { get; set; }
public SweepDirection SweepDirection { get; set; }
private double sweepAngle = 270;
public double SweepAngle
{
get => sweepAngle;
set { sweepAngle = value; }
}
public double EndAngle
{
get
{
double angle = StartAngle + sweepAngle;
if (angle > 360)
angle -= 360;
return angle;
}
}
private double startAngle = 135;
/// <summary>
/// Sets and gets the start angle of an axis in circular gauge. <br/>
/// There is an offset of 90º from the WPF Component to WebAssembly that is compensated in this getter
/// </summary>
public double StartAngle
{
get => startAngle + 90;
set => startAngle = value;
}
public Point EndPoint { get; set; }
public Point StartPoint { get; set; }
public bool BindRangeStrokeToLabels { get; set; }
public bool BindRangeStrokeToTicks { get; set; }
public double TickLength { get; set; }
public double SmallTickLength { get; set; }
public Brush TickStroke { get ; set; }
public Brush LabelStroke { get; set; }
public bool LabelAutoSizeChange { get; set; }
public string LabelPostfix { get; set; }
public string LabelPrefix { get; set; }
public Brush SmallTickStroke { get; set; }
public double TickStrokeThickness { get; set; }
public double SmallTickStrokeThickness { get; set; }
private double startValue = 0;
public double StartValue
{
get { return startValue; }
set
{
startValue = value;
if (this.CircularGaugeWebAssemblyObject == null)
return;
if (!ShowTicks)
return;
OpenSilver.Interop.ExecuteJavaScript("$0.ranges[0].start = $1", this.circularScale, startValue);
}
}
private double endValue = 100;
public double EndValue
{
get { return endValue; }
set
{
endValue = value;
if (this.CircularGaugeWebAssemblyObject == null)
return;
if (!ShowTicks)
return;
OpenSilver.Interop.ExecuteJavaScript("$0.ranges[0].end = $1", this.circularScale, endValue);
}
}
private double interval = 10;
/// <summary>
/// In WPF: The Interval property is used to calculate the tick count for a scale.
/// In HTML5: The Interval sets the interval of values between the tick lines in circular gauge.
/// </summary>
public double Interval
{
get { return interval; }
set
{
interval = value;
if (this.CircularGaugeWebAssemblyObject == null)
return;
if (!ShowTicks)
return;
OpenSilver.Interop.ExecuteJavaScript("$0.majorTicks.interval = $1", this.circularScale, interval);
}
}
private int minorTicksPerInterval = 1;
/// <summary>
/// In WPF this parameters defines the quantity of minor ticks in each interval <br/>
/// In WebAssembly the control requires the interval to be defined, so we must do some calculations. <br/>
/// Use Property <b>MinorTicksInterval</b> in the control
/// </summary>
public int MinorTicksPerInterval
{
get { return minorTicksPerInterval; }
set
{
minorTicksPerInterval = value;
if (this.CircularGaugeWebAssemblyObject == null)
return;
if (!ShowTicks)
return;
OpenSilver.Interop.ExecuteJavaScript("$0.minorTicks.interval = $1", this.circularScale, MinorTicksInterval);
}
}
public double MinorTicksInterval
{
get
{
double minorTicksPerInterval = MinorTicksPerInterval;
if (minorTicksPerInterval <= 1)
minorTicksPerInterval = 1;
return Interval / (minorTicksPerInterval + 1);
}
}
public bool ShowFirstLabel { get; set; }
public bool ShowLastLabel { get; set;}
public TickShape TickShape { get; set; }
public double Radius { get; set; }
public MajorTickSetting MajorTickSettings { get; set; }
public MinorTickSetting MinorTickSettings { get; set; }
public bool IsLogarithmic { get; set; }
public int LogBase { get; set; }
public int LogExponentInterval { get; set; }
public bool IsLogLabelSuperscriptEnabled { get; set; }
public double FontSize { get; set; } = 10;
public CircularScale()
{
this.Pointers = new CircularPointerCollection();
this.Pointers.circularScale = this.circularScale;
this.Ranges = new CircularRangeCollection(circularScale);
}
public void UpdateColors()
{
if (this.circularScale == null)
return;
Color c = (Color)WK.GetThemeColor((int)WK.eThemeColor.TextForeground);
string colorStr = $"#{c.R:X2}{c.G:X2}{c.B:X2}";
OpenSilver.Interop.ExecuteJavaScript("$0.labelStyle.font.color = $1", this.circularScale, colorStr);
}
private void ApplyInitialSettingsToWebAssemblyObject()
{
if (this.CircularGaugeWebAssemblyObject == null)
return;
this.circularScale = OpenSilver.Interop.ExecuteJavaScript("$0.axes[0]", this.CircularGaugeWebAssemblyObject);
OpenSilver.Interop.ExecuteJavaScript("$0.ranges[0].start = $1", this.circularScale, this.StartValue);
OpenSilver.Interop.ExecuteJavaScript("$0.ranges[0].end = $1", this.circularScale, this.EndValue);
OpenSilver.Interop.ExecuteJavaScript("$0.ranges[0].radius = '100%'", this.circularScale);
OpenSilver.Interop.ExecuteJavaScript("$0.ranges[0].startWidth = $1", this.circularScale, this.RimStrokeThickness);
OpenSilver.Interop.ExecuteJavaScript("$0.ranges[0].endWidth = $1", this.circularScale, this.RimStrokeThickness);
OpenSilver.Interop.ExecuteJavaScript("$0.ranges[0].color = $1", this.circularScale, SfCircularGauge.BrushToRgbaString(this.RimStroke));
OpenSilver.Interop.ExecuteJavaScript("$0.lineStyle.width = $1", this.circularScale, this.RimStrokeThickness);
OpenSilver.Interop.ExecuteJavaScript("$0.lineStyle.color = 'transparent'", this.circularScale);
double labelSize = 0;
if (ShowLabels)
labelSize = this.FontSize;
string labelPosition = "Inside";
if (this.LabelPosition == LabelPosition.Outside)
labelPosition = "Outside";
Color c = (Color)WK.GetThemeColor((int)WK.eThemeColor.TextForeground);
string colorStr = $"#{c.R:X2}{c.G:X2}{c.B:X2}";
OpenSilver.Interop.ExecuteJavaScript("$0.labelStyle = {font: {size: $1, fontFamily: 'inherit', color:$2 }, position: $3, offset: -5}", this.circularScale, labelSize + "px", colorStr, labelPosition);
double offsetTicks = this.RimStrokeThickness / 2;
double majorLen = this.MajorTickSettings?.Length ?? 0;
double minorLen = this.MinorTickSettings?.Length ?? 0;
if (!showTicks)
{
OpenSilver.Interop.ExecuteJavaScript("$0.majorTicks.width = 0", this.circularScale);
OpenSilver.Interop.ExecuteJavaScript("$0.majorTicks.height = 0", this.circularScale);
OpenSilver.Interop.ExecuteJavaScript("$0.minorTicks.width = 0", this.circularScale);
OpenSilver.Interop.ExecuteJavaScript("$0.minorTicks.height = 0", this.circularScale);
}
else
{
OpenSilver.Interop.ExecuteJavaScript("$0.majorTicks = {interval: $1, offset: $2}", this.circularScale, this.Interval, offsetTicks);
OpenSilver.Interop.ExecuteJavaScript("$0.minorTicks = {interval: $1, offset: $2}", this.circularScale, this.MinorTicksInterval, offsetTicks);
if (majorLen > 0)
{
OpenSilver.Interop.ExecuteJavaScript("$0.majorTicks.height = $1", this.circularScale, majorLen);
}
if (minorLen > 0)
{
OpenSilver.Interop.ExecuteJavaScript("$0.minorTicks.height = $1", this.circularScale, minorLen);
}
}
OpenSilver.Interop.ExecuteJavaScript("$0.minimum = $1", this.circularScale, this.StartValue);
OpenSilver.Interop.ExecuteJavaScript("$0.maximum = $1", this.circularScale, this.EndValue);
OpenSilver.Interop.ExecuteJavaScript("$0.startAngle = $1", this.circularScale, TConvert.ToString(this.StartAngle));
OpenSilver.Interop.ExecuteJavaScript("$0.endAngle = $1", this.circularScale, TConvert.ToString(this.EndAngle));
OpenSilver.Interop.ExecuteJavaScript("$0.axes[0] = $1", this.CircularGaugeWebAssemblyObject, this.circularScale);
}
}
#endregion
}
Related
Custom Control API Reference: choose between WPF, Blazor and Portable controls.
WPF Control API Reference: a WPF-only control placed with the WPF Control element.
Blazor Control API Reference: a browser-only control built with Razor components.
In this section...
- No labels