607 lines
24 KiB
C#
607 lines
24 KiB
C#
using MainShell.Resources.CustomControl;
|
||
using Newtonsoft.Json;
|
||
using Stylet;
|
||
using System;
|
||
using System.Globalization;
|
||
using System.IO;
|
||
using System.Windows;
|
||
using System.Windows.Controls;
|
||
using System.Windows.Controls.Primitives;
|
||
using System.Windows.Data;
|
||
using System.Windows.Input;
|
||
using System.Windows.Media;
|
||
using System.Windows.Threading;
|
||
|
||
namespace MainShell.Common
|
||
{
|
||
public static class HeaderDeviceStatusOverlayHost
|
||
{
|
||
private static readonly object OverlayRootTag = new object();
|
||
private static readonly HeaderDeviceStatusOverlaySettings OverlaySettings = new HeaderDeviceStatusOverlaySettings();
|
||
|
||
public static void Attach(Window window, HeaderDeviceStatusController controller)
|
||
{
|
||
if (window == null)
|
||
{
|
||
throw new ArgumentNullException(nameof(window));
|
||
}
|
||
|
||
if (controller == null)
|
||
{
|
||
throw new ArgumentNullException(nameof(controller));
|
||
}
|
||
|
||
window.Dispatcher.BeginInvoke(
|
||
DispatcherPriority.Loaded,
|
||
new Action(() => AttachCore(window, controller)));
|
||
}
|
||
|
||
private static void AttachCore(Window window, HeaderDeviceStatusController controller)
|
||
{
|
||
Grid existingOverlayRoot = window.Content as Grid;
|
||
if (existingOverlayRoot != null && ReferenceEquals(existingOverlayRoot.Tag, OverlayRootTag))
|
||
{
|
||
return;
|
||
}
|
||
|
||
UIElement currentContent = window.Content as UIElement;
|
||
if (currentContent == null)
|
||
{
|
||
return;
|
||
}
|
||
|
||
Grid overlayRoot = new Grid();
|
||
overlayRoot.Tag = OverlayRootTag;
|
||
|
||
window.Content = null;
|
||
overlayRoot.Children.Add(currentContent);
|
||
overlayRoot.Children.Add(CreateOverlayElement(controller));
|
||
overlayRoot.Children.Add(CreateDebugPanelElement());
|
||
window.Content = overlayRoot;
|
||
}
|
||
|
||
private static FrameworkElement CreateOverlayElement(HeaderDeviceStatusController controller)
|
||
{
|
||
HeaderDeviceStatusView statusView = new HeaderDeviceStatusView();
|
||
statusView.DataContext = controller;
|
||
statusView.IsHitTestVisible = false;
|
||
statusView.HorizontalAlignment = HorizontalAlignment.Left;
|
||
statusView.VerticalAlignment = VerticalAlignment.Top;
|
||
|
||
Binding marginBinding = new Binding(nameof(HeaderDeviceStatusOverlaySettings.Margin));
|
||
marginBinding.Source = OverlaySettings;
|
||
BindingOperations.SetBinding(statusView, FrameworkElement.MarginProperty, marginBinding);
|
||
|
||
Panel.SetZIndex(statusView, short.MaxValue);
|
||
return statusView;
|
||
}
|
||
|
||
private static FrameworkElement CreateDebugPanelElement()
|
||
{
|
||
Border panelBorder = new Border();
|
||
panelBorder.Background = new SolidColorBrush(Color.FromArgb(235, 18, 26, 35));
|
||
panelBorder.BorderBrush = new SolidColorBrush(Color.FromRgb(72, 93, 118));
|
||
panelBorder.BorderThickness = new Thickness(1.0);
|
||
panelBorder.CornerRadius = new CornerRadius(6.0);
|
||
panelBorder.Padding = new Thickness(10.0, 8.0, 10.0, 8.0);
|
||
panelBorder.HorizontalAlignment = HorizontalAlignment.Left;
|
||
panelBorder.VerticalAlignment = VerticalAlignment.Top;
|
||
|
||
Binding panelMarginBinding = new Binding(nameof(HeaderDeviceStatusOverlaySettings.DebugPanelMargin));
|
||
panelMarginBinding.Source = OverlaySettings;
|
||
BindingOperations.SetBinding(panelBorder, FrameworkElement.MarginProperty, panelMarginBinding);
|
||
|
||
Binding panelVisibilityBinding = new Binding(nameof(HeaderDeviceStatusOverlaySettings.IsDebugPanelVisible));
|
||
panelVisibilityBinding.Source = OverlaySettings;
|
||
panelVisibilityBinding.Converter = new BooleanToVisibilityConverter();
|
||
BindingOperations.SetBinding(panelBorder, UIElement.VisibilityProperty, panelVisibilityBinding);
|
||
|
||
StackPanel rootPanel = new StackPanel();
|
||
rootPanel.Orientation = Orientation.Vertical;
|
||
|
||
DockPanel headerPanel = new DockPanel();
|
||
|
||
TextBlock titleText = new TextBlock();
|
||
titleText.Text = "Overlay Debug";
|
||
titleText.Foreground = Brushes.White;
|
||
titleText.FontSize = 13.0;
|
||
titleText.FontWeight = FontWeights.SemiBold;
|
||
titleText.Margin = new Thickness(0.0, 0.0, 8.0, 6.0);
|
||
DockPanel.SetDock(titleText, Dock.Left);
|
||
headerPanel.Children.Add(titleText);
|
||
|
||
Button closeButton = CreateDebugButton("<22><><EFBFBD><EFBFBD>", 44.0);
|
||
closeButton.Margin = new Thickness(6.0, 0.0, 0.0, 6.0);
|
||
closeButton.Click += delegate(object sender, RoutedEventArgs e)
|
||
{
|
||
OverlaySettings.HideDebugPanel();
|
||
};
|
||
DockPanel.SetDock(closeButton, Dock.Right);
|
||
headerPanel.Children.Add(closeButton);
|
||
|
||
rootPanel.Children.Add(headerPanel);
|
||
|
||
TextBlock summaryText = new TextBlock();
|
||
summaryText.Foreground = new SolidColorBrush(Color.FromRgb(210, 221, 236));
|
||
summaryText.FontSize = 12.0;
|
||
summaryText.Margin = new Thickness(0.0, 0.0, 0.0, 8.0);
|
||
Binding summaryBinding = new Binding(nameof(HeaderDeviceStatusOverlaySettings.PositionSummary));
|
||
summaryBinding.Source = OverlaySettings;
|
||
BindingOperations.SetBinding(summaryText, TextBlock.TextProperty, summaryBinding);
|
||
rootPanel.Children.Add(summaryText);
|
||
|
||
rootPanel.Children.Add(CreateDebugInputRow("Left", nameof(HeaderDeviceStatusOverlaySettings.LeftMarginText)));
|
||
rootPanel.Children.Add(CreateDebugInputRow("Top", nameof(HeaderDeviceStatusOverlaySettings.TopMarginText)));
|
||
rootPanel.Children.Add(CreateDebugInputRow("Step", nameof(HeaderDeviceStatusOverlaySettings.StepText)));
|
||
|
||
UniformGrid actionGrid = new UniformGrid();
|
||
actionGrid.Columns = 4;
|
||
actionGrid.Rows = 1;
|
||
actionGrid.Margin = new Thickness(0.0, 0.0, 0.0, 8.0);
|
||
|
||
Button leftButton = CreateDebugButton("<22><>", 40.0);
|
||
leftButton.Click += delegate(object sender, RoutedEventArgs e)
|
||
{
|
||
OverlaySettings.AdjustMargin(-OverlaySettings.Step, 0.0);
|
||
};
|
||
actionGrid.Children.Add(leftButton);
|
||
|
||
Button rightButton = CreateDebugButton("<22><>", 40.0);
|
||
rightButton.Click += delegate(object sender, RoutedEventArgs e)
|
||
{
|
||
OverlaySettings.AdjustMargin(OverlaySettings.Step, 0.0);
|
||
};
|
||
actionGrid.Children.Add(rightButton);
|
||
|
||
Button upButton = CreateDebugButton("<22><>", 40.0);
|
||
upButton.Click += delegate(object sender, RoutedEventArgs e)
|
||
{
|
||
OverlaySettings.AdjustMargin(0.0, -OverlaySettings.Step);
|
||
};
|
||
actionGrid.Children.Add(upButton);
|
||
|
||
Button downButton = CreateDebugButton("<22><>", 40.0);
|
||
downButton.Click += delegate(object sender, RoutedEventArgs e)
|
||
{
|
||
OverlaySettings.AdjustMargin(0.0, OverlaySettings.Step);
|
||
};
|
||
actionGrid.Children.Add(downButton);
|
||
|
||
rootPanel.Children.Add(actionGrid);
|
||
|
||
Button applyButton = CreateDebugButton("Ӧ<><D3A6>", 0.0);
|
||
applyButton.Width = 178.0;
|
||
applyButton.Margin = new Thickness(0.0, 0.0, 0.0, 0.0);
|
||
applyButton.Click += delegate(object sender, RoutedEventArgs e)
|
||
{
|
||
OverlaySettings.ApplyTextValues();
|
||
};
|
||
rootPanel.Children.Add(applyButton);
|
||
|
||
panelBorder.Child = rootPanel;
|
||
Panel.SetZIndex(panelBorder, short.MaxValue);
|
||
return panelBorder;
|
||
}
|
||
|
||
private static FrameworkElement CreateDebugInputRow(string labelText, string bindingPath)
|
||
{
|
||
Grid rowGrid = new Grid();
|
||
rowGrid.Margin = new Thickness(0.0, 0.0, 0.0, 6.0);
|
||
rowGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(44.0) });
|
||
rowGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(92.0) });
|
||
rowGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(56.0) });
|
||
|
||
TextBlock label = new TextBlock();
|
||
label.Text = labelText;
|
||
label.Foreground = new SolidColorBrush(Color.FromRgb(210, 221, 236));
|
||
label.FontSize = 12.0;
|
||
label.VerticalAlignment = VerticalAlignment.Center;
|
||
Grid.SetColumn(label, 0);
|
||
rowGrid.Children.Add(label);
|
||
|
||
TextBox inputBox = CreateDebugTextBox(bindingPath);
|
||
Grid.SetColumn(inputBox, 1);
|
||
rowGrid.Children.Add(inputBox);
|
||
|
||
TextBlock hint = new TextBlock();
|
||
hint.Text = "<22>س<EFBFBD>";
|
||
hint.Foreground = new SolidColorBrush(Color.FromRgb(150, 167, 188));
|
||
hint.FontSize = 11.0;
|
||
hint.VerticalAlignment = VerticalAlignment.Center;
|
||
hint.HorizontalAlignment = HorizontalAlignment.Right;
|
||
Grid.SetColumn(hint, 2);
|
||
rowGrid.Children.Add(hint);
|
||
|
||
return rowGrid;
|
||
}
|
||
|
||
private static TextBox CreateDebugTextBox(string bindingPath)
|
||
{
|
||
TextBox inputBox = new TextBox();
|
||
inputBox.Height = 26.0;
|
||
inputBox.Margin = new Thickness(0.0);
|
||
inputBox.Padding = new Thickness(6.0, 2.0, 6.0, 2.0);
|
||
inputBox.Background = new SolidColorBrush(Color.FromRgb(33, 46, 60));
|
||
inputBox.BorderBrush = new SolidColorBrush(Color.FromRgb(96, 122, 149));
|
||
inputBox.Foreground = Brushes.White;
|
||
inputBox.FontSize = 12.0;
|
||
inputBox.VerticalContentAlignment = VerticalAlignment.Center;
|
||
|
||
Binding textBinding = new Binding(bindingPath);
|
||
textBinding.Source = OverlaySettings;
|
||
textBinding.Mode = BindingMode.TwoWay;
|
||
textBinding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
|
||
BindingOperations.SetBinding(inputBox, TextBox.TextProperty, textBinding);
|
||
|
||
inputBox.LostFocus += delegate(object sender, RoutedEventArgs e)
|
||
{
|
||
OverlaySettings.ApplyTextValues();
|
||
};
|
||
inputBox.KeyDown += delegate(object sender, KeyEventArgs e)
|
||
{
|
||
if (e.Key != Key.Enter)
|
||
{
|
||
return;
|
||
}
|
||
|
||
OverlaySettings.ApplyTextValues();
|
||
e.Handled = true;
|
||
};
|
||
|
||
return inputBox;
|
||
}
|
||
|
||
private static Button CreateDebugButton(string content, double width)
|
||
{
|
||
Button button = new Button();
|
||
button.Content = content;
|
||
if (width > 0.0)
|
||
{
|
||
button.Width = width;
|
||
}
|
||
button.Height = 28.0;
|
||
button.Margin = new Thickness(0.0, 0.0, 6.0, 0.0);
|
||
button.Padding = new Thickness(4.0, 0.0, 4.0, 0.0);
|
||
button.Background = new SolidColorBrush(Color.FromRgb(43, 62, 81));
|
||
button.BorderBrush = new SolidColorBrush(Color.FromRgb(96, 122, 149));
|
||
button.Foreground = Brushes.White;
|
||
button.FontSize = 12.0;
|
||
return button;
|
||
}
|
||
|
||
private sealed class HeaderDeviceStatusOverlaySettings : PropertyChangedBase, IDisposable
|
||
{
|
||
private const double DefaultTopMargin = 8.0;
|
||
private const double DefaultLeftMargin = 8.0;
|
||
private const double DefaultDebugPanelTopOffset = 50.0;
|
||
private const double DefaultAdjustStep = 1.0;
|
||
private const string ConfigurationDirectoryName = "Configuration";
|
||
private const string ConfigurationFileName = "HeaderDeviceStatusOverlay.json";
|
||
|
||
private readonly string _configurationDirectoryPath;
|
||
private readonly string _configurationFilePath;
|
||
private readonly FileSystemWatcher _fileWatcher;
|
||
private double _topMargin = DefaultTopMargin;
|
||
private double _leftMargin = DefaultLeftMargin;
|
||
private bool _isDebugPanelVisible;
|
||
private double _step = DefaultAdjustStep;
|
||
private string _leftMarginText = DefaultLeftMargin.ToString("0.##", CultureInfo.InvariantCulture);
|
||
private string _topMarginText = DefaultTopMargin.ToString("0.##", CultureInfo.InvariantCulture);
|
||
private string _stepText = DefaultAdjustStep.ToString("0.##", CultureInfo.InvariantCulture);
|
||
|
||
public HeaderDeviceStatusOverlaySettings()
|
||
{
|
||
_configurationDirectoryPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ConfigurationDirectoryName);
|
||
_configurationFilePath = Path.Combine(_configurationDirectoryPath, ConfigurationFileName);
|
||
|
||
EnsureConfigurationFile();
|
||
LoadFromFile();
|
||
|
||
_fileWatcher = new FileSystemWatcher(_configurationDirectoryPath, ConfigurationFileName);
|
||
_fileWatcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.LastWrite | NotifyFilters.Size | NotifyFilters.CreationTime;
|
||
_fileWatcher.Changed += OnConfigurationFileChanged;
|
||
_fileWatcher.Created += OnConfigurationFileChanged;
|
||
_fileWatcher.Renamed += OnConfigurationFileRenamed;
|
||
_fileWatcher.EnableRaisingEvents = true;
|
||
}
|
||
|
||
public Thickness Margin
|
||
{
|
||
get
|
||
{
|
||
return new Thickness(_leftMargin, _topMargin, 0.0, 0.0);
|
||
}
|
||
}
|
||
|
||
public Thickness DebugPanelMargin
|
||
{
|
||
get
|
||
{
|
||
return new Thickness(_leftMargin, _topMargin + DefaultDebugPanelTopOffset, 0.0, 0.0);
|
||
}
|
||
}
|
||
|
||
public bool IsDebugPanelVisible
|
||
{
|
||
get
|
||
{
|
||
return _isDebugPanelVisible;
|
||
}
|
||
}
|
||
|
||
public double Step
|
||
{
|
||
get
|
||
{
|
||
return _step;
|
||
}
|
||
}
|
||
|
||
public string LeftMarginText
|
||
{
|
||
get
|
||
{
|
||
return _leftMarginText;
|
||
}
|
||
set
|
||
{
|
||
SetAndNotify(ref _leftMarginText, value);
|
||
}
|
||
}
|
||
|
||
public string TopMarginText
|
||
{
|
||
get
|
||
{
|
||
return _topMarginText;
|
||
}
|
||
set
|
||
{
|
||
SetAndNotify(ref _topMarginText, value);
|
||
}
|
||
}
|
||
|
||
public string StepText
|
||
{
|
||
get
|
||
{
|
||
return _stepText;
|
||
}
|
||
set
|
||
{
|
||
SetAndNotify(ref _stepText, value);
|
||
}
|
||
}
|
||
|
||
public string PositionSummary
|
||
{
|
||
get
|
||
{
|
||
return string.Format("Left={0:0.##}, Top={1:0.##}, Step={2:0.##}", _leftMargin, _topMargin, _step);
|
||
}
|
||
}
|
||
|
||
public void Dispose()
|
||
{
|
||
_fileWatcher.Changed -= OnConfigurationFileChanged;
|
||
_fileWatcher.Created -= OnConfigurationFileChanged;
|
||
_fileWatcher.Renamed -= OnConfigurationFileRenamed;
|
||
_fileWatcher.Dispose();
|
||
}
|
||
|
||
public void AdjustMargin(double leftOffset, double topOffset)
|
||
{
|
||
double leftMargin = _leftMargin + leftOffset;
|
||
double topMargin = _topMargin + topOffset;
|
||
|
||
UpdateSettings(leftMargin, topMargin, _isDebugPanelVisible, _step);
|
||
PersistCurrentConfiguration();
|
||
}
|
||
|
||
public void HideDebugPanel()
|
||
{
|
||
UpdateSettings(_leftMargin, _topMargin, false, _step);
|
||
PersistCurrentConfiguration();
|
||
}
|
||
|
||
public void ApplyTextValues()
|
||
{
|
||
double leftMargin;
|
||
double topMargin;
|
||
double step;
|
||
|
||
bool canParseLeft = TryParseInput(LeftMarginText, _leftMargin, out leftMargin);
|
||
bool canParseTop = TryParseInput(TopMarginText, _topMargin, out topMargin);
|
||
bool canParseStep = TryParseInput(StepText, _step, out step);
|
||
|
||
if (!canParseStep)
|
||
{
|
||
step = _step;
|
||
}
|
||
|
||
step = NormalizeStep(step);
|
||
|
||
UpdateSettings(leftMargin, topMargin, _isDebugPanelVisible, step);
|
||
PersistCurrentConfiguration();
|
||
}
|
||
|
||
private void OnConfigurationFileChanged(object sender, FileSystemEventArgs e)
|
||
{
|
||
ReloadOnUiThread();
|
||
}
|
||
|
||
private void OnConfigurationFileRenamed(object sender, RenamedEventArgs e)
|
||
{
|
||
ReloadOnUiThread();
|
||
}
|
||
|
||
private void ReloadOnUiThread()
|
||
{
|
||
Application currentApplication = Application.Current;
|
||
if (currentApplication == null)
|
||
{
|
||
LoadFromFile();
|
||
return;
|
||
}
|
||
|
||
currentApplication.Dispatcher.BeginInvoke(
|
||
DispatcherPriority.Background,
|
||
new Action(LoadFromFile));
|
||
}
|
||
|
||
private void EnsureConfigurationFile()
|
||
{
|
||
if (!Directory.Exists(_configurationDirectoryPath))
|
||
{
|
||
Directory.CreateDirectory(_configurationDirectoryPath);
|
||
}
|
||
|
||
if (File.Exists(_configurationFilePath))
|
||
{
|
||
return;
|
||
}
|
||
|
||
HeaderDeviceStatusOverlayPositionConfiguration configuration = new HeaderDeviceStatusOverlayPositionConfiguration();
|
||
configuration.TopMargin = DefaultTopMargin;
|
||
configuration.LeftMargin = DefaultLeftMargin;
|
||
configuration.ShowDebugPanel = false;
|
||
configuration.AdjustStep = DefaultAdjustStep;
|
||
SaveConfiguration(configuration);
|
||
}
|
||
|
||
private void LoadFromFile()
|
||
{
|
||
HeaderDeviceStatusOverlayPositionConfiguration configuration = TryReadConfiguration();
|
||
if (configuration == null)
|
||
{
|
||
return;
|
||
}
|
||
|
||
double topMargin = NormalizeMargin(configuration.TopMargin, DefaultTopMargin);
|
||
double leftMargin = NormalizeMargin(configuration.LeftMargin, DefaultLeftMargin);
|
||
double adjustStep = NormalizeStep(configuration.AdjustStep);
|
||
UpdateSettings(leftMargin, topMargin, configuration.ShowDebugPanel, adjustStep);
|
||
}
|
||
|
||
private HeaderDeviceStatusOverlayPositionConfiguration TryReadConfiguration()
|
||
{
|
||
for (int attempt = 0; attempt < 3; attempt++)
|
||
{
|
||
try
|
||
{
|
||
string content = File.ReadAllText(_configurationFilePath);
|
||
if (string.IsNullOrWhiteSpace(content))
|
||
{
|
||
return new HeaderDeviceStatusOverlayPositionConfiguration();
|
||
}
|
||
|
||
HeaderDeviceStatusOverlayPositionConfiguration configuration = JsonConvert.DeserializeObject<HeaderDeviceStatusOverlayPositionConfiguration>(content);
|
||
return configuration ?? new HeaderDeviceStatusOverlayPositionConfiguration();
|
||
}
|
||
catch (IOException)
|
||
{
|
||
}
|
||
catch (UnauthorizedAccessException)
|
||
{
|
||
}
|
||
catch (JsonException)
|
||
{
|
||
return null;
|
||
}
|
||
}
|
||
|
||
return null;
|
||
}
|
||
|
||
private void SaveConfiguration(HeaderDeviceStatusOverlayPositionConfiguration configuration)
|
||
{
|
||
string content = JsonConvert.SerializeObject(configuration, Formatting.Indented);
|
||
File.WriteAllText(_configurationFilePath, content);
|
||
}
|
||
|
||
private void PersistCurrentConfiguration()
|
||
{
|
||
HeaderDeviceStatusOverlayPositionConfiguration configuration = new HeaderDeviceStatusOverlayPositionConfiguration();
|
||
configuration.TopMargin = _topMargin;
|
||
configuration.LeftMargin = _leftMargin;
|
||
configuration.ShowDebugPanel = _isDebugPanelVisible;
|
||
configuration.AdjustStep = _step;
|
||
SaveConfiguration(configuration);
|
||
}
|
||
|
||
private static double NormalizeMargin(double value, double fallbackValue)
|
||
{
|
||
if (double.IsNaN(value) || double.IsInfinity(value))
|
||
{
|
||
return fallbackValue;
|
||
}
|
||
|
||
return value;
|
||
}
|
||
|
||
private static double NormalizeStep(double value)
|
||
{
|
||
if (double.IsNaN(value) || double.IsInfinity(value) || value <= 0.0)
|
||
{
|
||
return DefaultAdjustStep;
|
||
}
|
||
|
||
return value;
|
||
}
|
||
|
||
private void UpdateSettings(double leftMargin, double topMargin, bool isDebugPanelVisible, double step)
|
||
{
|
||
bool hasChanged = !leftMargin.Equals(_leftMargin)
|
||
|| !topMargin.Equals(_topMargin)
|
||
|| !isDebugPanelVisible.Equals(_isDebugPanelVisible)
|
||
|| !step.Equals(_step);
|
||
if (!hasChanged)
|
||
{
|
||
return;
|
||
}
|
||
|
||
_leftMargin = leftMargin;
|
||
_topMargin = topMargin;
|
||
_isDebugPanelVisible = isDebugPanelVisible;
|
||
_step = step;
|
||
LeftMarginText = FormatValue(_leftMargin);
|
||
TopMarginText = FormatValue(_topMargin);
|
||
StepText = FormatValue(_step);
|
||
NotifyOfPropertyChange(nameof(Margin));
|
||
NotifyOfPropertyChange(nameof(DebugPanelMargin));
|
||
NotifyOfPropertyChange(nameof(IsDebugPanelVisible));
|
||
NotifyOfPropertyChange(nameof(Step));
|
||
NotifyOfPropertyChange(nameof(PositionSummary));
|
||
}
|
||
|
||
private static bool TryParseInput(string text, double fallbackValue, out double value)
|
||
{
|
||
string trimmedText = string.IsNullOrWhiteSpace(text) ? string.Empty : text.Trim();
|
||
if (string.IsNullOrEmpty(trimmedText))
|
||
{
|
||
value = fallbackValue;
|
||
return false;
|
||
}
|
||
|
||
return double.TryParse(trimmedText, NumberStyles.Float, CultureInfo.InvariantCulture, out value)
|
||
|| double.TryParse(trimmedText, NumberStyles.Float, CultureInfo.CurrentCulture, out value);
|
||
}
|
||
|
||
private static string FormatValue(double value)
|
||
{
|
||
return value.ToString("0.##", CultureInfo.InvariantCulture);
|
||
}
|
||
}
|
||
|
||
private sealed class HeaderDeviceStatusOverlayPositionConfiguration
|
||
{
|
||
public double TopMargin { get; set; }
|
||
|
||
public double LeftMargin { get; set; }
|
||
|
||
public bool ShowDebugPanel { get; set; }
|
||
|
||
public double AdjustStep { get; set; }
|
||
}
|
||
}
|
||
} |