diff --git a/src/Artemis.UI.Shared/Behaviors/ScrollParentWhenAtMax.cs b/src/Artemis.UI.Shared/Behaviors/ScrollParentWhenAtMax.cs index 2cde2a73c..b0493fc20 100644 --- a/src/Artemis.UI.Shared/Behaviors/ScrollParentWhenAtMax.cs +++ b/src/Artemis.UI.Shared/Behaviors/ScrollParentWhenAtMax.cs @@ -6,14 +6,19 @@ using Microsoft.Xaml.Behaviors; namespace Artemis.UI.Shared { + /// + /// A behavior that makes a scroll viewer bubble up its events if it is at its scroll limit + /// public class ScrollParentWhenAtMax : Behavior { + /// protected override void OnAttached() { base.OnAttached(); AssociatedObject.PreviewMouseWheel += PreviewMouseWheel; } + /// protected override void OnDetaching() { AssociatedObject.PreviewMouseWheel -= PreviewMouseWheel; @@ -22,10 +27,12 @@ namespace Artemis.UI.Shared private void PreviewMouseWheel(object sender, MouseWheelEventArgs e) { - ScrollViewer scrollViewer = GetVisualChild(AssociatedObject); + ScrollViewer? scrollViewer = GetVisualChild(AssociatedObject); + if (scrollViewer == null) + return; double scrollPos = scrollViewer.ContentVerticalOffset; - if (scrollPos == scrollViewer.ScrollableHeight && e.Delta < 0 - || scrollPos == 0 && e.Delta > 0) + // ReSharper disable once CompareOfFloatsByEqualityOperator + if (scrollPos == scrollViewer.ScrollableHeight && e.Delta < 0 || scrollPos == 0 && e.Delta > 0) { e.Handled = true; MouseWheelEventArgs e2 = new MouseWheelEventArgs(e.MouseDevice, e.Timestamp, e.Delta); @@ -34,9 +41,9 @@ namespace Artemis.UI.Shared } } - private static T GetVisualChild(DependencyObject parent) where T : Visual + private static T? GetVisualChild(DependencyObject parent) where T : Visual { - T child = default; + T? child = default; int numVisuals = VisualTreeHelper.GetChildrenCount(parent); for (int i = 0; i < numVisuals; i++) diff --git a/src/Artemis.UI.Shared/Controls/DeviceVisualizer.cs b/src/Artemis.UI.Shared/Controls/DeviceVisualizer.cs index e833d3479..cf8a17afe 100644 --- a/src/Artemis.UI.Shared/Controls/DeviceVisualizer.cs +++ b/src/Artemis.UI.Shared/Controls/DeviceVisualizer.cs @@ -38,8 +38,8 @@ namespace Artemis.UI.Shared private readonly DrawingGroup _backingStore; private readonly List _deviceVisualizerLeds; private readonly DispatcherTimer _timer; - private BitmapImage _deviceImage; - private ArtemisDevice _oldDevice; + private BitmapImage? _deviceImage; + private ArtemisDevice? _oldDevice; /// /// Creates a new instance of the class @@ -60,7 +60,7 @@ namespace Artemis.UI.Shared /// /// Gets or sets the device to visualize /// - public ArtemisDevice Device + public ArtemisDevice? Device { get => (ArtemisDevice) GetValue(DeviceProperty); set => SetValue(DeviceProperty, value); @@ -78,16 +78,32 @@ namespace Artemis.UI.Shared /// /// Gets or sets a list of LEDs to highlight /// - public IEnumerable HighlightedLeds + public IEnumerable? HighlightedLeds { get => (IEnumerable) GetValue(HighlightedLedsProperty); set => SetValue(HighlightedLedsProperty, value); } + /// + /// Releases the unmanaged resources used by the object and optionally releases the managed resources. + /// + /// + /// to release both managed and unmanaged resources; + /// to release only unmanaged resources. + /// + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + _timer.Stop(); + } + } + /// public void Dispose() { - _timer.Stop(); + Dispose(true); + GC.SuppressFinalize(this); } /// @@ -174,7 +190,8 @@ namespace Artemis.UI.Shared if (_oldDevice != null) { - Device.RgbDevice.PropertyChanged -= DevicePropertyChanged; + if (Device != null) + Device.RgbDevice.PropertyChanged -= DevicePropertyChanged; _oldDevice = null; } } @@ -224,7 +241,7 @@ namespace Artemis.UI.Shared UpdateTransform(); // Load the device main image - if (Device.RgbDevice?.DeviceInfo?.Image?.AbsolutePath != null && File.Exists(Device.RgbDevice.DeviceInfo.Image.AbsolutePath)) + if (Device.RgbDevice.DeviceInfo?.Image?.AbsolutePath != null && File.Exists(Device.RgbDevice.DeviceInfo.Image.AbsolutePath)) _deviceImage = new BitmapImage(Device.RgbDevice.DeviceInfo.Image); // Create all the LEDs @@ -266,7 +283,7 @@ namespace Artemis.UI.Shared InvalidateMeasure(); } - private void DevicePropertyChanged(object sender, PropertyChangedEventArgs e) + private void DevicePropertyChanged(object? sender, PropertyChangedEventArgs e) { if (e.PropertyName == nameof(Device.RgbDevice.Scale) || e.PropertyName == nameof(Device.RgbDevice.Rotation)) UpdateTransform(); diff --git a/src/Artemis.UI.Shared/Converters/NullToVisibilityConverter.cs b/src/Artemis.UI.Shared/Converters/NullToVisibilityConverter.cs index 0ff1e40e3..dd70fbac2 100644 --- a/src/Artemis.UI.Shared/Converters/NullToVisibilityConverter.cs +++ b/src/Artemis.UI.Shared/Converters/NullToVisibilityConverter.cs @@ -5,9 +5,13 @@ using System.Windows.Data; namespace Artemis.UI.Shared { + /// + /// Converts to + /// public class NullToVisibilityConverter : IValueConverter { - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + /// + public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture) { Parameters direction; if (parameter == null) @@ -23,7 +27,8 @@ namespace Artemis.UI.Shared return value == null ? Visibility.Visible : Visibility.Collapsed; } - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + /// + public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) { throw new NotImplementedException(); } diff --git a/src/Artemis.UI.Shared/DataModelVisualization/DataModelDisplayViewModel.cs b/src/Artemis.UI.Shared/DataModelVisualization/DataModelDisplayViewModel.cs index 2d09153ef..623ed8a17 100644 --- a/src/Artemis.UI.Shared/DataModelVisualization/DataModelDisplayViewModel.cs +++ b/src/Artemis.UI.Shared/DataModelVisualization/DataModelDisplayViewModel.cs @@ -3,10 +3,17 @@ using Stylet; namespace Artemis.UI.Shared { + /// + /// Represents a display view model + /// + /// The type of the data model public abstract class DataModelDisplayViewModel : DataModelDisplayViewModel { private T _displayValue; + /// + /// Gets or sets value that the view model must display + /// public T DisplayValue { get => _displayValue; @@ -19,11 +26,15 @@ namespace Artemis.UI.Shared internal override object InternalGuard => null; + /// public override void UpdateValue(object model) { DisplayValue = model is T value ? value : default; } + /// + /// Occurs when the display value is updated + /// protected virtual void OnDisplayValueUpdated() { } @@ -50,6 +61,10 @@ namespace Artemis.UI.Shared /// internal abstract object InternalGuard { get; } + /// + /// Updates the display value + /// + /// The value to set public abstract void UpdateValue(object model); } } \ No newline at end of file diff --git a/src/Artemis.UI.Shared/DataModelVisualization/DataModelInputViewModel.cs b/src/Artemis.UI.Shared/DataModelVisualization/DataModelInputViewModel.cs index 30a24989e..97f5e5fa7 100644 --- a/src/Artemis.UI.Shared/DataModelVisualization/DataModelInputViewModel.cs +++ b/src/Artemis.UI.Shared/DataModelVisualization/DataModelInputViewModel.cs @@ -9,25 +9,41 @@ using Stylet; namespace Artemis.UI.Shared { + /// + /// Represents a input view model + /// + /// The type of the data model public abstract class DataModelInputViewModel : DataModelInputViewModel { private bool _closed; private T _inputValue; + /// + /// Creates a new instance of the class + /// + /// The description of the property this input VM is representing + /// The initial value to set the input value to protected DataModelInputViewModel(DataModelPropertyAttribute targetDescription, T initialValue) { TargetDescription = targetDescription; InputValue = initialValue; } + /// + /// Gets or sets the value shown in the input + /// public T InputValue { get => _inputValue; set => SetAndNotify(ref _inputValue, value); } + /// + /// Gets the description of the property this input VM is representing + /// public DataModelPropertyAttribute TargetDescription { get; } - internal override object InternalGuard { get; } = null; + + internal override object InternalGuard { get; } = new object(); /// public sealed override void Submit() @@ -74,23 +90,6 @@ namespace Artemis.UI.Shared /// internal IReadOnlyCollection CompatibleConversionTypes { get; set; } - public void AttachView(UIElement view) - { - if (View != null) - throw new InvalidOperationException(string.Format("Tried to attach View {0} to ViewModel {1}, but it already has a view attached", view.GetType().Name, GetType().Name)); - - View = view; - - // After the animation finishes attempt to focus the input field - Task.Run(async () => - { - await Task.Delay(50); - await Execute.OnUIThreadAsync(() => View.MoveFocus(new TraversalRequest(FocusNavigationDirection.First))); - }); - } - - public UIElement View { get; set; } - /// /// Submits the input value and removes this view model. /// This is called automatically when the user presses enter or clicks outside the view @@ -116,5 +115,22 @@ namespace Artemis.UI.Shared protected virtual void OnCancel() { } + + public void AttachView(UIElement view) + { + if (View != null) + throw new InvalidOperationException(string.Format("Tried to attach View {0} to ViewModel {1}, but it already has a view attached", view.GetType().Name, GetType().Name)); + + View = view; + + // After the animation finishes attempt to focus the input field + Task.Run(async () => + { + await Task.Delay(50); + await Execute.OnUIThreadAsync(() => View.MoveFocus(new TraversalRequest(FocusNavigationDirection.First))); + }); + } + + public UIElement View { get; set; } } } \ No newline at end of file diff --git a/src/Artemis.UI.Shared/DataModelVisualization/Input/DataModelDynamicView.xaml.cs b/src/Artemis.UI.Shared/DataModelVisualization/Input/DataModelDynamicView.xaml.cs index 8a45bde5d..a487bdfea 100644 --- a/src/Artemis.UI.Shared/DataModelVisualization/Input/DataModelDynamicView.xaml.cs +++ b/src/Artemis.UI.Shared/DataModelVisualization/Input/DataModelDynamicView.xaml.cs @@ -8,6 +8,9 @@ namespace Artemis.UI.Shared.Input /// public partial class DataModelDynamicView : UserControl { + /// + /// Creates a new instance of the class + /// public DataModelDynamicView() { InitializeComponent(); diff --git a/src/Artemis.UI.Shared/DataModelVisualization/Input/DataModelStaticViewModel.cs b/src/Artemis.UI.Shared/DataModelVisualization/Input/DataModelStaticViewModel.cs index 490b126c9..5b632f1ce 100644 --- a/src/Artemis.UI.Shared/DataModelVisualization/Input/DataModelStaticViewModel.cs +++ b/src/Artemis.UI.Shared/DataModelVisualization/Input/DataModelStaticViewModel.cs @@ -16,6 +16,7 @@ namespace Artemis.UI.Shared.Input private readonly IDataModelUIService _dataModelUIService; private readonly Window _rootView; private SolidColorBrush _buttonBrush = new SolidColorBrush(Color.FromRgb(171, 71, 188)); + private bool _displaySwitchButton; private DataModelDisplayViewModel _displayViewModel; private DataModelInputViewModel _inputViewModel; private bool _isEnabled; @@ -23,7 +24,6 @@ namespace Artemis.UI.Shared.Input private DataModelPropertyAttribute _targetDescription; private Type _targetType; private object _value; - private bool _displaySwitchButton; public DataModelStaticViewModel(Type targetType, DataModelPropertyAttribute targetDescription, IDataModelUIService dataModelUIService) { @@ -42,6 +42,9 @@ namespace Artemis.UI.Shared.Input } } + /// + /// Gets or sets the brush to use for the input button + /// public SolidColorBrush ButtonBrush { get => _buttonBrush; @@ -52,32 +55,50 @@ namespace Artemis.UI.Shared.Input } } + /// + /// Gets the brush to use for the switch button + /// public SolidColorBrush SwitchButtonBrush => new SolidColorBrush(ButtonBrush.Color.Darken()); + /// + /// Gets the view model used to display the value + /// public DataModelDisplayViewModel DisplayViewModel { get => _displayViewModel; - set => SetAndNotify(ref _displayViewModel, value); + private set => SetAndNotify(ref _displayViewModel, value); } + /// + /// Gets the view model used to edit the value + /// public DataModelInputViewModel InputViewModel { get => _inputViewModel; private set => SetAndNotify(ref _inputViewModel, value); } + /// + /// Gets the type of the target property + /// public Type TargetType { get => _targetType; private set => SetAndNotify(ref _targetType, value); } + /// + /// Gets the description of the target property + /// public DataModelPropertyAttribute TargetDescription { get => _targetDescription; set => SetAndNotify(ref _targetDescription, value); } + /// + /// Gets or sets the value of the target + /// public object Value { get => _value; @@ -88,24 +109,36 @@ namespace Artemis.UI.Shared.Input } } + /// + /// Gets or sets the placeholder text when no value is entered + /// public string Placeholder { get => _placeholder; set => SetAndNotify(ref _placeholder, value); } + /// + /// Gets or sets the enabled state of the input + /// public bool IsEnabled { get => _isEnabled; private set => SetAndNotify(ref _isEnabled, value); } + /// + /// Gets or sets whether the switch button should be displayed + /// public bool DisplaySwitchButton { get => _displaySwitchButton; set => SetAndNotify(ref _displaySwitchButton, value); } + /// + /// Activates the input view model + /// public void ActivateInputViewModel() { InputViewModel = _dataModelUIService.GetDataModelInputViewModel( @@ -116,6 +149,10 @@ namespace Artemis.UI.Shared.Input ); } + /// + /// Updates the target type + /// + /// The new target type public void UpdateTargetType(Type target) { TargetType = target; @@ -135,6 +172,9 @@ namespace Artemis.UI.Shared.Input ApplyFreeInput(TargetType.GetDefault(), true); } + /// + /// Requests switching the input type to dynamic + /// public void SwitchToDynamic() { InputViewModel?.Cancel(); @@ -154,17 +194,33 @@ namespace Artemis.UI.Shared.Input #region IDisposable + /// + /// Releases the unmanaged resources used by the object and optionally releases the managed resources. + /// + /// + /// to release both managed and unmanaged resources; + /// to release only unmanaged resources. + /// + protected virtual void Dispose(bool disposing) + { + if (disposing) + if (_rootView != null) + { + _rootView.MouseUp -= RootViewOnMouseUp; + _rootView.KeyUp -= RootViewOnKeyUp; + } + } + + /// public void Dispose() { - if (_rootView != null) - { - _rootView.MouseUp -= RootViewOnMouseUp; - _rootView.KeyUp -= RootViewOnKeyUp; - } + Dispose(true); + GC.SuppressFinalize(this); } #endregion + #region Event handlers private void RootViewOnKeyUp(object sender, KeyEventArgs e) @@ -191,14 +247,28 @@ namespace Artemis.UI.Shared.Input #region Events - public event EventHandler ValueUpdated; - public event EventHandler SwitchToDynamicRequested; + /// + /// Occurs when the value of the property has been updated + /// + public event EventHandler? ValueUpdated; + /// + /// Occurs when a switch to dynamic input has been requested + /// + public event EventHandler? SwitchToDynamicRequested; + + /// + /// Invokes the event + /// + /// protected virtual void OnValueUpdated(DataModelInputStaticEventArgs e) { ValueUpdated?.Invoke(this, e); } + /// + /// Invokes the event + /// protected virtual void OnSwitchToDynamicRequested() { SwitchToDynamicRequested?.Invoke(this, EventArgs.Empty);