Add project files.

This commit is contained in:
Ankitkumar Satapara
2026-04-17 22:31:58 +05:30
commit 21aaef6776
473 changed files with 50152 additions and 0 deletions

View File

@@ -0,0 +1,19 @@
using System.Windows;
namespace Nodify
{
public class BindingProxy : Freezable
{
public static readonly DependencyProperty DataContextProperty =
DependencyProperty.Register(nameof(DataContext), typeof(object), typeof(BindingProxy), new UIPropertyMetadata(default(object)));
public object DataContext
{
get => GetValue(DataContextProperty);
set => SetValue(DataContextProperty, value);
}
protected override Freezable CreateInstanceCore()
=> new BindingProxy();
}
}

View File

@@ -0,0 +1,19 @@
using System.Windows;
namespace Nodify.Shared
{
public static class BoxValue
{
public static readonly object Point = default(Point);
public static readonly object Size = default(Size);
public static readonly object Rect = default(Rect);
public static readonly object False = false;
public static readonly object True = true;
public static readonly object DoubleHalf = 0.5d;
public static readonly object Double0 = 0d;
public static readonly object Double1 = 1d;
public static readonly object Double2 = 2d;
public static readonly object Int0 = 0;
public static readonly object Int1 = 1;
}
}

View File

@@ -0,0 +1,179 @@
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using Nodify.Shared;
namespace Nodify
{
[TemplatePart(Name = ElementTextBox, Type = typeof(TextBox))]
public class EditableTextBlock : Control
{
private const string ElementTextBox = "PART_TextBox";
public static readonly DependencyProperty IsEditingProperty = DependencyProperty.Register(nameof(IsEditing), typeof(bool), typeof(EditableTextBlock), new FrameworkPropertyMetadata(BoxValue.False, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnIsEditingChanged, CoerceIsEditing));
public static readonly DependencyProperty IsEditableProperty = DependencyProperty.Register(nameof(IsEditable), typeof(bool), typeof(EditableTextBlock), new FrameworkPropertyMetadata(BoxValue.True));
public static readonly DependencyProperty TextProperty = TextBlock.TextProperty.AddOwner(typeof(EditableTextBlock), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
public static readonly DependencyProperty AcceptsReturnProperty = TextBoxBase.AcceptsReturnProperty.AddOwner(typeof(EditableTextBlock), new FrameworkPropertyMetadata(BoxValue.False));
public static readonly DependencyProperty TextWrappingProperty = TextBlock.TextWrappingProperty.AddOwner(typeof(EditableTextBlock), new FrameworkPropertyMetadata(TextWrapping.Wrap));
public static readonly DependencyProperty TextTrimmingProperty = TextBlock.TextTrimmingProperty.AddOwner(typeof(EditableTextBlock), new FrameworkPropertyMetadata(TextTrimming.CharacterEllipsis));
public static readonly DependencyProperty MinLinesProperty = TextBox.MinLinesProperty.AddOwner(typeof(EditableTextBlock));
public static readonly DependencyProperty MaxLinesProperty = TextBox.MaxLinesProperty.AddOwner(typeof(EditableTextBlock));
public static readonly DependencyProperty MaxLengthProperty = TextBox.MaxLengthProperty.AddOwner(typeof(EditableTextBlock));
private static void OnIsEditingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { }
private static object CoerceIsEditing(DependencyObject d, object value)
{
if (!((EditableTextBlock)d).IsEditable)
{
return BoxValue.False;
}
return value;
}
public string Text
{
get => (string)GetValue(TextProperty);
set => SetValue(TextProperty, value);
}
public bool IsEditing
{
get => (bool)GetValue(IsEditingProperty);
set => SetValue(IsEditingProperty, value);
}
public bool IsEditable
{
get => (bool)GetValue(IsEditableProperty);
set => SetValue(IsEditableProperty, value);
}
public bool AcceptsReturn
{
get => (bool)GetValue(AcceptsReturnProperty);
set => SetValue(AcceptsReturnProperty, value);
}
public int MaxLength
{
get => (int)GetValue(MaxLengthProperty);
set => SetValue(MaxLengthProperty, value);
}
public int MinLines
{
get => (int)GetValue(MinLinesProperty);
set => SetValue(MaxLinesProperty, value);
}
public int MaxLines
{
get => (int)GetValue(MaxLinesProperty);
set => SetValue(MaxLinesProperty, value);
}
public TextWrapping TextWrapping
{
get => (TextWrapping)GetValue(TextWrappingProperty);
set => SetValue(TextWrappingProperty, value);
}
public TextTrimming TextTrimming
{
get => (TextTrimming)GetValue(TextTrimmingProperty);
set => SetValue(TextTrimmingProperty, value);
}
protected TextBox? TextBox { get; private set; }
static EditableTextBlock()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(EditableTextBlock), new FrameworkPropertyMetadata(typeof(EditableTextBlock)));
FocusableProperty.OverrideMetadata(typeof(EditableTextBlock), new FrameworkPropertyMetadata(BoxValue.True));
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
if (TextBox != null)
{
TextBox.LostFocus -= OnLostFocus;
TextBox.LostKeyboardFocus -= OnLostFocus;
TextBox.IsVisibleChanged -= OnTextBoxVisiblityChanged;
}
TextBox = GetTemplateChild(ElementTextBox) as TextBox;
if (TextBox != null)
{
TextBox.LostFocus += OnLostFocus;
TextBox.LostKeyboardFocus += OnLostFocus;
TextBox.IsVisibleChanged += OnTextBoxVisiblityChanged;
if (IsEditing)
{
TextBox.Focus();
TextBox.SelectAll();
}
}
}
private void OnTextBoxVisiblityChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if (IsEditing && TextBox != null)
{
if (TextBox.Focus())
{
TextBox.SelectAll();
}
else
{
IsEditing = false;
}
}
}
protected override void OnMouseDown(MouseButtonEventArgs e)
{
if (IsEditing)
{
e.Handled = true;
}
else if (IsEditable && e.ChangedButton == MouseButton.Left && e.ClickCount == 2)
{
IsEditing = true;
e.Handled = true;
}
}
protected override void OnMouseUp(MouseButtonEventArgs e)
{
if (IsEditing)
{
e.Handled = true;
}
}
private void OnLostFocus(object sender, RoutedEventArgs e)
{
IsEditing = false;
}
protected override void OnKeyDown(KeyEventArgs e)
{
if (IsEditing && e.Key == Key.Escape || !AcceptsReturn && e.Key == Key.Enter)
{
IsEditing = false;
}
if (e.Key == Key.Enter && IsFocused && !IsEditing)
{
IsEditing = true;
}
}
}
}

View File

@@ -0,0 +1,196 @@
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
namespace Nodify
{
public class ResizablePanel : ContentControl
{
internal static readonly object BoxedResizeDirection = ResizeDirections.All;
public static readonly DependencyProperty DirectionsProperty
= DependencyProperty.Register(nameof(Directions), typeof(ResizeDirections), typeof(ResizablePanel), new FrameworkPropertyMetadata(BoxedResizeDirection));
public static readonly DependencyProperty ResizeStartedCommandProperty = DependencyProperty.Register(nameof(ResizeStartedCommand), typeof(ICommand), typeof(ResizablePanel));
public static readonly DependencyProperty ResizeCompletedCommandProperty = DependencyProperty.Register(nameof(ResizeCompletedCommand), typeof(ICommand), typeof(ResizablePanel));
public ResizeDirections Directions
{
get => (ResizeDirections)GetValue(DirectionsProperty);
set => SetValue(DirectionsProperty, value);
}
public ICommand? ResizeStartedCommand
{
get => (ICommand)GetValue(ResizeStartedCommandProperty);
set => SetValue(ResizeStartedCommandProperty, value);
}
public ICommand? ResizeCompletedCommand
{
get => (ICommand)GetValue(ResizeCompletedCommandProperty);
set => SetValue(ResizeCompletedCommandProperty, value);
}
static ResizablePanel()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(ResizablePanel), new FrameworkPropertyMetadata(typeof(ResizablePanel)));
}
public ResizablePanel()
{
AddHandler(Thumb.DragDeltaEvent, new DragDeltaEventHandler(OnResize));
AddHandler(Thumb.DragStartedEvent, new DragStartedEventHandler(OnDragStarted));
AddHandler(Thumb.DragCompletedEvent, new DragCompletedEventHandler(OnDragCompleted));
}
private void OnDragStarted(object sender, DragStartedEventArgs e)
{
if (ResizeStartedCommand?.CanExecute(null) ?? false)
{
ResizeStartedCommand.Execute(null);
}
}
private void OnDragCompleted(object sender, DragCompletedEventArgs e)
{
if (ResizeCompletedCommand?.CanExecute(null) ?? false)
{
ResizeCompletedCommand.Execute(null);
}
}
private void OnResize(object sender, DragDeltaEventArgs e)
{
if (e.OriginalSource is Resizer resizer)
{
double resizeX = 0;
double resizeY = 0;
double moveX = 0;
double moveY = 0;
if (resizer.Direction.HasFlag(ResizeDirections.Top))
{
moveY = resizeY = ResizeTop(e);
}
if (resizer.Direction.HasFlag(ResizeDirections.Bottom))
{
resizeY = ResizeBottom(e);
}
if (resizer.Direction.HasFlag(ResizeDirections.Left))
{
moveX = resizeX = ResizeLeft(e);
}
if (resizer.Direction.HasFlag(ResizeDirections.Right))
{
resizeX = ResizeRight(e);
}
if (resizer.Direction.HasFlag(ResizeDirections.TopLeft))
{
moveY = resizeY = ResizeTop(e);
moveX = resizeX = ResizeLeft(e);
}
if (resizer.Direction.HasFlag(ResizeDirections.TopRight))
{
moveY = resizeY = ResizeTop(e);
resizeX = ResizeRight(e);
}
if (resizer.Direction.HasFlag(ResizeDirections.BottomLeft))
{
resizeY = ResizeBottom(e);
moveX = resizeX = ResizeLeft(e);
}
if (resizer.Direction.HasFlag(ResizeDirections.BottomRight))
{
resizeY = ResizeBottom(e);
resizeX = ResizeRight(e);
}
OnProcessDelta(ref resizeX, ref resizeY);
OnProcessDelta(ref moveX, ref moveY);
OnMove(moveX, moveY);
Width -= resizeX;
Height -= resizeY;
e.Handled = true;
}
}
private double ResizeBottom(DragDeltaEventArgs e)
{
return Math.Min(-e.VerticalChange, ActualHeight - MinHeight);
}
private double ResizeTop(DragDeltaEventArgs e)
{
return Math.Min(e.VerticalChange, ActualHeight - MinHeight);
}
private double ResizeRight(DragDeltaEventArgs e)
{
return Math.Min(-e.HorizontalChange, ActualWidth - MinWidth);
}
private double ResizeLeft(DragDeltaEventArgs e)
{
return Math.Min(e.HorizontalChange, ActualWidth - MinWidth);
}
protected virtual void OnMove(double x, double y)
{
Canvas.SetTop(this, Canvas.GetTop(this) + y);
Canvas.SetLeft(this, Canvas.GetLeft(this) + x);
}
protected virtual void OnProcessDelta(ref double dx, ref double dy)
{
}
}
public class Resizer : Thumb
{
public static readonly DependencyProperty DirectionProperty
= DependencyProperty.Register(nameof(Direction), typeof(ResizeDirections), typeof(Resizer), new FrameworkPropertyMetadata(ResizablePanel.BoxedResizeDirection));
public ResizeDirections Direction
{
get => (ResizeDirections)GetValue(DirectionProperty);
set => SetValue(DirectionProperty, value);
}
static Resizer()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(Resizer), new FrameworkPropertyMetadata(typeof(Resizer)));
}
}
[Flags]
public enum ResizeDirections
{
Top = 1,
Left = 2,
Bottom = 4,
Right = 8,
TopLeft = 16,
TopRight = 32,
BottomLeft = 64,
BottomRight = 128,
Edges = Top | Left | Bottom | Right,
Corners = TopLeft | TopRight | BottomLeft | BottomRight,
All = Edges | Corners
}
}

View File

@@ -0,0 +1,48 @@
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using Nodify.Shared;
namespace Nodify
{
public partial class Swatches : Control
{
public static readonly DependencyProperty SelectedColorProperty
= DependencyProperty.Register(nameof(SelectedColor), typeof(Color), typeof(Swatches), new FrameworkPropertyMetadata(default(Color), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
public static readonly DependencyProperty ColorsProperty
= DependencyProperty.Register(nameof(Colors), typeof(IEnumerable<Color>), typeof(Swatches), new PropertyMetadata(Array.Empty<Color>()));
public Color SelectedColor
{
get => (Color)GetValue(SelectedColorProperty);
set => SetValue(SelectedColorProperty, value);
}
public IEnumerable<Color> Colors
{
get => (IEnumerable<Color>)GetValue(ColorsProperty);
set => SetValue(ColorsProperty, value);
}
static Swatches()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(Swatches), new FrameworkPropertyMetadata(typeof(Swatches)));
FocusableProperty.OverrideMetadata(typeof(Swatches), new FrameworkPropertyMetadata(BoxValue.True));
}
protected override void OnMouseDown(MouseButtonEventArgs e)
{
var color = e.OriginalSource is FrameworkElement fe && fe.DataContext is Color c ? c : (Color?)null;
if (color.HasValue)
{
SelectedColor = color.Value;
}
e.Handled = true;
}
}
}

View File

@@ -0,0 +1,57 @@
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace Nodify
{
[TemplatePart(Name = ElementScrollViewer, Type = typeof(ScrollViewer))]
public class TabControlEx : TabControl
{
private const string ElementScrollViewer = "PART_ScrollViewer";
public static readonly DependencyProperty AddTabCommandProperty = DependencyProperty.Register(nameof(AddTabCommand), typeof(ICommand), typeof(TabControlEx), new PropertyMetadata(null));
public static readonly DependencyProperty AutoScrollToEndProperty = DependencyProperty.Register(nameof(AutoScrollToEnd), typeof(bool), typeof(TabControlEx), new PropertyMetadata(false));
public ICommand AddTabCommand
{
get { return (ICommand)GetValue(AddTabCommandProperty); }
set { SetValue(AddTabCommandProperty, value); }
}
public bool AutoScrollToEnd
{
get { return (bool)GetValue(AutoScrollToEndProperty); }
set { SetValue(AutoScrollToEndProperty, value); }
}
protected ScrollViewer? ScrollViewer { get; private set; }
static TabControlEx()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(TabControlEx), new FrameworkPropertyMetadata(typeof(TabControlEx)));
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
ScrollViewer = GetTemplateChild(ElementScrollViewer) as ScrollViewer;
if(ScrollViewer != null)
{
ScrollViewer.ScrollChanged += OnScrollChanged;
}
}
private void OnScrollChanged(object sender, ScrollChangedEventArgs e)
{
if(e.ExtentWidthChange > 0 && e.ViewportWidth < e.ExtentWidth && AutoScrollToEnd)
{
ScrollViewer?.ScrollToRightEnd();
}
}
protected override DependencyObject GetContainerForItemOverride()
{
return new TabItemEx();
}
}
}

View File

@@ -0,0 +1,29 @@
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace Nodify
{
public class TabItemEx : TabItem
{
public static readonly DependencyProperty CloseTabCommandProperty = DependencyProperty.Register(nameof(CloseTabCommand), typeof(ICommand), typeof(TabItemEx), new PropertyMetadata(null));
public static readonly DependencyProperty CloseTabCommandParameterProperty = DependencyProperty.Register(nameof(CloseTabCommandParameter), typeof(object), typeof(TabItemEx), new PropertyMetadata(null));
public ICommand CloseTabCommand
{
get { return (ICommand)GetValue(CloseTabCommandProperty); }
set { SetValue(CloseTabCommandProperty, value); }
}
public object CloseTabCommandParameter
{
get { return (object)GetValue(CloseTabCommandParameterProperty); }
set { SetValue(CloseTabCommandParameterProperty, value); }
}
static TabItemEx()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(TabItemEx), new FrameworkPropertyMetadata(typeof(TabItemEx)));
}
}
}

View File

@@ -0,0 +1,35 @@
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
using System.Windows.Markup;
namespace Nodify
{
public class BooleanToVisibilityConverter : MarkupExtension, IValueConverter
{
public Visibility FalseVisibility { get; set; } = Visibility.Collapsed;
public bool Negate { get; set; }
public object? Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
string? stringValue = value?.ToString();
if (bool.TryParse(stringValue, out var b))
{
return (Negate ? !b : b) ? Visibility.Visible : FalseVisibility;
}
else if (double.TryParse(stringValue, out var d))
{
return (Negate ? !(d > 0) : (d > 0)) ? Visibility.Visible : FalseVisibility;
}
bool result = value != null;
return (Negate ? !result : result) ? Visibility.Visible : FalseVisibility;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
=> value is Visibility v && v == Visibility.Visible;
public override object ProvideValue(IServiceProvider serviceProvider) => this;
}
}

View File

@@ -0,0 +1,33 @@
using System;
using System.Globalization;
using System.Windows.Data;
using System.Windows.Media;
namespace Nodify
{
public class ColorToSolidColorBrushConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is Color color)
{
var brush = new SolidColorBrush(color);
if(double.TryParse(parameter?.ToString(), out double opacity))
brush.Opacity = opacity;
return brush;
}
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is SolidColorBrush brush)
return brush.Color;
return value;
}
}
}

View File

@@ -0,0 +1,24 @@
using System;
using System.Globalization;
using System.Windows.Data;
using System.Windows.Markup;
namespace Nodify
{
public class DebugConverter : MarkupExtension, IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
Console.WriteLine($"Value: {value} :: Parameter: {parameter}");
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
Console.WriteLine($"Value: {value} :: Parameter: {parameter}");
return value;
}
public override object ProvideValue(IServiceProvider serviceProvider) => this;
}
}

View File

@@ -0,0 +1,49 @@
using System;
using System.Globalization;
using System.Windows.Data;
using System.Windows.Markup;
namespace Nodify
{
public readonly struct EnumValue
{
public EnumValue(string name, object? value)
{
Name = name;
Value = value;
}
public string Name { get; }
public object? Value { get; }
}
public class EnumValuesConverter : MarkupExtension, IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is Enum enumValue)
{
var type = enumValue.GetType();
var values = Enum.GetValues(type);
var names = Enum.GetNames(type);
EnumValue[] result = new EnumValue[values.Length];
for (int i = 0; i < values.Length; i++)
{
result[i] = new EnumValue(names[i], values.GetValue(i));
}
return result;
}
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
public override object ProvideValue(IServiceProvider serviceProvider) => this;
}
}

View File

@@ -0,0 +1,25 @@
using System;
using System.Globalization;
using System.Windows.Data;
namespace Nodify
{
public class InverseBooleanConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (targetType != typeof(bool))
throw new InvalidOperationException("The value must be of type bool.");
return !(bool)value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (targetType != typeof(bool))
throw new InvalidOperationException("The value must be of type bool.");
return !(bool)value;
}
}
}

View File

@@ -0,0 +1,37 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Windows.Data;
namespace Nodify
{
public class MultiValueEqualityConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values == null)
{
return false;
}
return AllElementsEqual(values) || AllElementsNull(values);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
private static bool AllElementsEqual(IEnumerable<object> values)
{
object? firstElement = values.FirstOrDefault();
return values.All(o => o?.Equals(firstElement) == true);
}
private static bool AllElementsNull(IEnumerable<object> values)
{
return values.All(o => o == null);
}
}
}

View File

@@ -0,0 +1,30 @@
using System;
using System.Globalization;
using System.Windows.Data;
using System.Windows.Media;
namespace Nodify
{
public class RandomBrushConverter : IValueConverter
{
private readonly Random _rand = new Random();
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (double.TryParse(parameter?.ToString(), out double alpha))
{
return new SolidColorBrush(Color.FromRgb((byte)_rand.Next(256), (byte)_rand.Next(256), (byte)_rand.Next(256)))
{
Opacity = alpha
};
}
return new SolidColorBrush(Color.FromRgb((byte)_rand.Next(256), (byte)_rand.Next(256), (byte)_rand.Next(256)));
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return value;
}
}
}

View File

@@ -0,0 +1,30 @@
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
namespace Nodify
{
internal class ResizeDirectionToVisiblityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (!(value is ResizeDirections resizeDirections))
{
return Visibility.Collapsed;
}
if (Enum.TryParse(parameter.ToString(), out ResizeDirections direction))
{
return resizeDirections.HasFlag(direction) ? Visibility.Visible : Visibility.Collapsed;
}
return Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}

View File

@@ -0,0 +1,21 @@
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
using System.Windows.Markup;
namespace Nodify
{
public class StringToVisibilityConverter : MarkupExtension, IValueConverter
{
public Visibility NullVisibility { get; set; } = Visibility.Collapsed;
public object? Convert(object value, Type targetType, object parameter, CultureInfo culture)
=> string.IsNullOrEmpty(value as string) ? NullVisibility : Visibility.Visible;
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
=> throw new NotImplementedException();
public override object ProvideValue(IServiceProvider serviceProvider) => this;
}
}

View File

@@ -0,0 +1,35 @@
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
namespace Nodify
{
public class ToStringConverter : IValueConverter
{
public object? Convert(object? value, Type targetType, object parameter, CultureInfo culture)
{
if (value is Point p)
{
return $"{p.X:0.0}, {p.Y:0.0}";
}
if (value is Size s)
{
return $"{s.Width:0.0}, {s.Height:0.0}";
}
if(value is double d)
{
return d.ToString("0.00");
}
return value?.ToString();
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}

View File

@@ -0,0 +1,72 @@
using System;
using System.Windows.Input;
namespace Nodify
{
public interface INodifyCommand : ICommand
{
void RaiseCanExecuteChanged();
}
public class DelegateCommand : INodifyCommand
{
private readonly Action _action;
private readonly Func<bool>? _condition;
public event EventHandler? CanExecuteChanged;
public DelegateCommand(Action action, Func<bool>? executeCondition = default)
{
_action = action ?? throw new ArgumentNullException(nameof(action));
_condition = executeCondition;
}
public bool CanExecute(object? parameter)
=> _condition?.Invoke() ?? true;
public void Execute(object? parameter)
=> _action();
public void RaiseCanExecuteChanged()
=> CanExecuteChanged?.Invoke(this, new EventArgs());
}
public class DelegateCommand<T> : INodifyCommand
{
private readonly Action<T> _action;
private readonly Func<T, bool>? _condition;
public event EventHandler? CanExecuteChanged;
public DelegateCommand(Action<T> action, Func<T, bool>? executeCondition = default)
{
_action = action ?? throw new ArgumentNullException(nameof(action));
_condition = executeCondition;
}
public bool CanExecute(object? parameter)
{
if (parameter is T value)
{
return _condition?.Invoke(value) ?? true;
}
return _condition?.Invoke(default!) ?? true;
}
public void Execute(object? parameter)
{
if (parameter is T value)
{
_action(value);
}
else
{
_action(default!);
}
}
public void RaiseCanExecuteChanged()
=> CanExecuteChanged?.Invoke(this, new EventArgs());
}
}

View File

@@ -0,0 +1,74 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace Nodify
{
public static class FluentSyntax
{
public static void Then<T>(this T caller, Action<T> action)
=> action?.Invoke(caller);
public static bool Then(this bool condition, Action action)
{
if (condition)
{
action();
}
return condition;
}
public static bool Else(this bool condition, Action action)
{
if (!condition)
{
action();
}
return condition;
}
public static IEnumerable<T> ForEach<T>(this IEnumerable<T> collection, Action<T> action)
{
if (collection is IList<T> list)
{
for (int i = 0; i < list.Count; i++)
{
action(list[i]);
}
}
else
{
foreach (var item in collection)
{
action(item);
}
}
return collection;
}
public static ICollection<T> AddRange<T>(this ICollection<T> collection, IEnumerable<T> values)
{
values.ForEach(collection.Add);
return collection;
}
public static ICollection<T> RemoveRange<T>(this ICollection<T> collection, IEnumerable<T> values)
{
values.ForEach(v => collection.Remove(v));
return collection;
}
public static ICollection<T> RemoveOne<T>(this ICollection<T> collection, Func<T, bool> search)
{
if (collection.FirstOrDefault(search) is { } x)
{
collection.Remove(x);
}
return collection;
}
}
}

View File

@@ -0,0 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
<PropertyGroup>
<UseWPF>true</UseWPF>
<TargetFrameworks>net9-windows;net8-windows;net6-windows;net5-windows;netcoreapp3.1;net48;net472</TargetFrameworks>
<UseWPF>true</UseWPF>
<Nullable>enable</Nullable>
</PropertyGroup>
<PropertyGroup Condition="'$(TargetFramework)'=='net472' OR '$(TargetFramework)'=='net48'">
<LangVersion>8.0</LangVersion>
</PropertyGroup>
<ItemGroup>
<Page Update="Themes\Controls.xaml">
<SubType>Designer</SubType>
</Page>
<Page Update="Themes\Generic.xaml">
<SubType>Designer</SubType>
</Page>
<Page Update="Themes\Icons.xaml">
<SubType>Designer</SubType>
</Page>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,193 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
namespace Nodify
{
public interface INodifyObservableCollection<T>
{
/// <summary>
/// Called when a new item is added
/// </summary>
/// <param name="added">The callback to execute when an item is added</param>
/// <returns>Returns self</returns>
INodifyObservableCollection<T> WhenAdded(Action<T> added);
/// <summary>
/// Called when an existing item is removed
/// Note: It is not called when items are cleared if <see cref="WhenCleared(Action{IList{T}})"/> is used
/// </summary>
/// <param name="added">The callback to execute when an item is removed</param>
/// <returns>Returns self</returns>
INodifyObservableCollection<T> WhenRemoved(Action<T> removed);
/// <summary>
/// Called when the collection is cleared
/// NOTE: It does not call <see cref="WhenRemoved(Action{T})"/> on each item
/// </summary>
/// <param name="added">The callback to execute when the collection is cleared</param>
/// <returns>Returns self</returns>
INodifyObservableCollection<T> WhenCleared(Action<IList<T>> cleared);
}
public class NodifyObservableCollection<T> : Collection<T>, INodifyObservableCollection<T>, INotifyPropertyChanged, INotifyCollectionChanged
{
protected static readonly PropertyChangedEventArgs IndexerPropertyChanged = new PropertyChangedEventArgs("Item[]");
protected static readonly PropertyChangedEventArgs CountPropertyChanged = new PropertyChangedEventArgs("Count");
protected static readonly NotifyCollectionChangedEventArgs ResetCollectionChanged = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset);
private readonly List<Action<T>> _added = new List<Action<T>>();
private readonly List<Action<T>> _removed = new List<Action<T>>();
private readonly List<Action<IList<T>>> _cleared = new List<Action<IList<T>>>();
public event NotifyCollectionChangedEventHandler? CollectionChanged;
public event PropertyChangedEventHandler? PropertyChanged;
public NodifyObservableCollection()
{
}
public NodifyObservableCollection(IEnumerable<T> collection)
: base(new List<T>(collection))
{
}
#region Collection Events
public INodifyObservableCollection<T> WhenAdded(Action<T> added)
{
if (added != null)
{
_added.Add(added);
}
return this;
}
public INodifyObservableCollection<T> WhenRemoved(Action<T> removed)
{
if (removed != null)
{
_removed.Add(removed);
}
return this;
}
public INodifyObservableCollection<T> WhenCleared(Action<IList<T>> cleared)
{
if (cleared != null)
{
_cleared.Add(cleared);
}
return this;
}
protected virtual void NotifyOnItemAdded(T item)
{
for (int i = 0; i < _added.Count; i++)
{
_added[i](item);
}
}
protected virtual void NotifyOnItemRemoved(T item)
{
for (int i = 0; i < _removed.Count; i++)
{
_removed[i](item);
}
}
protected virtual void NotifyOnItemsCleared(IList<T> items)
{
for (int i = 0; i < _cleared.Count; i++)
{
_cleared[i](items);
}
}
#endregion
#region Collection Handlers
protected override void ClearItems()
{
var items = _cleared.Count > 0 || _removed.Count > 0 ? new List<T>(this) : new List<T>();
base.ClearItems();
if (_cleared.Count > 0)
{
NotifyOnItemsCleared(items);
}
else if (_removed.Count > 0)
{
for (int i = 0; i < items.Count; i++)
{
NotifyOnItemRemoved(items[i]);
}
}
OnPropertyChanged(CountPropertyChanged);
OnPropertyChanged(IndexerPropertyChanged);
OnCollectionChanged(ResetCollectionChanged);
}
protected override void InsertItem(int index, T item)
{
base.InsertItem(index, item);
OnPropertyChanged(CountPropertyChanged);
OnPropertyChanged(IndexerPropertyChanged);
OnCollectionChanged(NotifyCollectionChangedAction.Add, item, index);
NotifyOnItemAdded(item);
}
protected override void RemoveItem(int index)
{
var item = base[index];
base.RemoveItem(index);
OnPropertyChanged(CountPropertyChanged);
OnPropertyChanged(IndexerPropertyChanged);
OnCollectionChanged(NotifyCollectionChangedAction.Remove, item, index);
NotifyOnItemRemoved(item);
}
protected override void SetItem(int index, T item)
{
T prev = base[index];
base.SetItem(index, item);
OnPropertyChanged(IndexerPropertyChanged);
OnCollectionChanged(NotifyCollectionChangedAction.Replace, prev, item, index);
NotifyOnItemRemoved(prev);
NotifyOnItemAdded(item);
}
public void Move(int oldIndex, int newIndex)
{
T prev = base[oldIndex];
base.RemoveItem(oldIndex);
base.InsertItem(newIndex, prev);
OnPropertyChanged(IndexerPropertyChanged);
OnCollectionChanged(NotifyCollectionChangedAction.Move, prev, newIndex, oldIndex);
}
protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
=> CollectionChanged?.Invoke(this, e);
protected virtual void OnPropertyChanged(PropertyChangedEventArgs args)
=> PropertyChanged?.Invoke(this, args);
private void OnCollectionChanged(NotifyCollectionChangedAction action, object? item, int index)
=> OnCollectionChanged(new NotifyCollectionChangedEventArgs(action, item, index));
private void OnCollectionChanged(NotifyCollectionChangedAction action, object? item, int index, int oldIndex)
=> OnCollectionChanged(new NotifyCollectionChangedEventArgs(action, item, index, oldIndex));
private void OnCollectionChanged(NotifyCollectionChangedAction action, object? oldItem, object? newItem, int index)
=> OnCollectionChanged(new NotifyCollectionChangedEventArgs(action, newItem, oldItem, index));
#endregion
}
}

View File

@@ -0,0 +1,51 @@
using System.Collections.Generic;
using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows;
namespace Nodify
{
public class ObservableObject : INotifyPropertyChanged
{
/// <summary>
/// Gets or sets the dispatcher to use to dispatch PropertyChanged events. Defaults to UI thread.
/// </summary>
public virtual Action<Action> PropertyChangedDispatcher { get; set; } = Application.Current.Dispatcher.Invoke;
/// <summary>
/// Occurs when a property value changes
/// </summary>
public event PropertyChangedEventHandler? PropertyChanged;
/// <summary>
/// Fires the PropertyChanged notification.
/// </summary>
/// <remarks>Specially named so that Fody.PropertyChanged calls it</remarks>
/// <param name="propertyName">Name of the property to raise the notification for</param>
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChangedDispatcher(() => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)));
}
/// <summary>
/// Takes, by reference, a field, and its new value. If field != value, will set field = value and raise a PropertyChanged notification
/// </summary>
/// <typeparam name="T">Type of field being set and notified</typeparam>
/// <param name="field">Field to assign</param>
/// <param name="value">Value to assign to the field, if it differs</param>
/// <param name="propertyName">Name of the property to notify for. Defaults to the calling property</param>
/// <returns>True if field != value and a notification was raised; false otherwise</returns>
protected virtual bool SetProperty<T>(ref T field, T value, [CallerMemberName] string propertyName = "")
{
if (!EqualityComparer<T>.Default.Equals(field, value))
{
field = value;
OnPropertyChanged(propertyName);
return true;
}
return false;
}
}
}

View File

@@ -0,0 +1,10 @@
using System.Windows;
[assembly: ThemeInfo(
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
//(used if a resource is not found in the page,
// or application resource dictionaries)
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
//(used if a resource is not found in the page,
// app, or any theme specific resource dictionaries)
)]

View File

@@ -0,0 +1,73 @@
using System;
using System.Windows.Input;
namespace Nodify
{
public class RequeryCommand : INodifyCommand
{
private readonly Action _action;
private readonly Func<bool>? _condition;
public event EventHandler? CanExecuteChanged
{
add => CommandManager.RequerySuggested += value;
remove => CommandManager.RequerySuggested -= value;
}
public RequeryCommand(Action action, Func<bool>? executeCondition = default)
{
_action = action ?? throw new ArgumentNullException(nameof(action));
_condition = executeCondition;
}
public bool CanExecute(object? parameter)
=> _condition?.Invoke() ?? true;
public void Execute(object? parameter)
=> _action();
public void RaiseCanExecuteChanged() { }
}
public class RequeryCommand<T> : INodifyCommand
{
private readonly Action<T> _action;
private readonly Func<T, bool>? _condition;
public event EventHandler? CanExecuteChanged
{
add => CommandManager.RequerySuggested += value;
remove => CommandManager.RequerySuggested -= value;
}
public RequeryCommand(Action<T> action, Func<T, bool>? executeCondition = default)
{
_action = action ?? throw new ArgumentNullException(nameof(action));
_condition = executeCondition;
}
public bool CanExecute(object? parameter)
{
if (parameter is T value)
{
return _condition?.Invoke(value) ?? true;
}
return _condition?.Invoke(default!) ?? true;
}
public void Execute(object? parameter)
{
if (parameter is T value)
{
_action(value);
}
else
{
_action(default!);
}
}
public void RaiseCanExecuteChanged() { }
}
}

View File

@@ -0,0 +1,21 @@
using System.Collections.Generic;
namespace Nodify
{
public static class StringExtensions
{
public static string GetUnique(this ICollection<string> values, in string baseValue)
{
int counter = 1;
string result = baseValue;
while (values.Contains(result))
{
result = $"{baseValue}{counter}";
counter++;
}
return result;
}
}
}

View File

@@ -0,0 +1,134 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Windows;
using System.Windows.Input;
namespace Nodify
{
public static class ThemeManager
{
private static readonly string? _assemblyName = Assembly.GetEntryAssembly()?.GetName().Name;
private static readonly Dictionary<string, List<Uri>> _themesUris = new Dictionary<string, List<Uri>>();
private static readonly Dictionary<string, List<ResourceDictionary>> _themesResources = new Dictionary<string, List<ResourceDictionary>>();
public static string? ActiveTheme { get; private set; }
private static readonly List<string> _availableThemes = new List<string>();
public static IReadOnlyCollection<string> AvailableThemes => _availableThemes;
public static ICommand SetNextThemeCommand { get; }
static ThemeManager()
{
PreloadTheme("Dark");
PreloadTheme("Light");
PreloadTheme("Nodify");
SetNextThemeCommand = new DelegateCommand(SetNextTheme);
}
private static List<ResourceDictionary> FindExistingResources(List<Uri> uris)
{
var result = new List<ResourceDictionary>();
foreach (var d in Application.Current.Resources.MergedDictionaries)
{
if (d.Source != null && uris.Contains(d.Source))
{
result.Add(d);
}
}
return result;
}
private static void PreloadTheme(string themeName)
{
if (!_themesUris.TryGetValue(themeName, out var preload))
{
preload = new List<Uri>(3)
{
new Uri($"pack://application:,,,/Nodify;component/Themes/{themeName}.xaml"),
new Uri($"pack://application:,,,/Nodify.Shared;component/Themes/{themeName}.xaml")
};
if (_assemblyName != null)
{
preload.Add(new Uri($"pack://application:,,,/{_assemblyName};component/Themes/{themeName}.xaml"));
}
_themesUris.Add(themeName, preload);
}
var resources = FindExistingResources(preload);
if (resources.Count == 0)
{
for (int i = 0; i < preload.Count; i++)
{
try
{
resources.Add(new ResourceDictionary
{
Source = preload[i]
});
}
catch
{
}
}
}
else if (ActiveTheme == null)
{
ActiveTheme = themeName;
}
_themesResources.Add(themeName, resources);
_availableThemes.Add(themeName);
}
public static void SetNextTheme()
{
if (ActiveTheme != null)
{
var i = _availableThemes.IndexOf(ActiveTheme);
var next = i + 1 == _availableThemes.Count ? 0 : i + 1;
SetTheme(_availableThemes[next]);
}
else if (_availableThemes.Count > 0)
{
SetTheme(_availableThemes[0]);
}
}
public static void SetTheme(string themeName)
{
if (!_themesResources.ContainsKey(themeName))
{
PreloadTheme(themeName);
}
// Load new theme if it is valid
if (_themesResources.TryGetValue(themeName, out var resources))
{
foreach (var res in resources)
{
Application.Current.Resources.MergedDictionaries.Insert(0, res);
}
// Unload current theme
if (ActiveTheme != null)
{
foreach (var res in _themesResources[ActiveTheme])
{
Application.Current.Resources.MergedDictionaries.Remove(res);
}
}
ActiveTheme = themeName;
}
}
}
}

View File

@@ -0,0 +1,45 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:o="http://schemas.microsoft.com/winfx/2006/xaml/presentation/options">
<SolidColorBrush x:Key="ForegroundBrush"
o:Freeze="True"
Color="{DynamicResource ForegroundColor}" />
<SolidColorBrush x:Key="DisabledForegroundBrush"
o:Freeze="True"
Color="{DynamicResource DisabledForegroundColor}" />
<SolidColorBrush x:Key="BackgroundBrush"
o:Freeze="True"
Color="{DynamicResource BackgroundColor}" />
<SolidColorBrush x:Key="ContrastBackgroundBrush"
o:Freeze="True"
Color="{DynamicResource ContrastBackgroundColor}" />
<SolidColorBrush x:Key="HighlightedBackgroundBrush"
o:Freeze="True"
Color="{DynamicResource HighlightedBackgroundColor}" />
<SolidColorBrush x:Key="BorderBrush"
o:Freeze="True"
Color="{DynamicResource BorderColor}" />
<SolidColorBrush x:Key="DisabledBorderBrush"
o:Freeze="True"
Color="{DynamicResource DisabledBorderColor}" />
<SolidColorBrush x:Key="HighlightedBorderBrush"
o:Freeze="True"
Color="{DynamicResource HighlightedBorderColor}" />
<SolidColorBrush x:Key="FocusedBorderBrush"
o:Freeze="True"
Color="{DynamicResource FocusedBorderColor}" />
<SolidColorBrush x:Key="GridLinesBrush"
o:Freeze="True"
Color="{DynamicResource GridLinesColor}" />
</ResourceDictionary>

View File

@@ -0,0 +1,857 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style TargetType="{x:Type CheckBox}"
BasedOn="{StaticResource {x:Type CheckBox}}">
<Setter Property="SnapsToDevicePixels"
Value="False" />
<Setter Property="Foreground"
Value="{DynamicResource ForegroundBrush}" />
<Setter Property="Background"
Value="{DynamicResource BackgroundBrush}" />
<Setter Property="BorderBrush"
Value="{DynamicResource BorderBrush}" />
<Setter Property="BorderThickness"
Value="1" />
<Setter Property="IsThreeState"
Value="False" />
<Setter Property="FocusVisualStyle"
Value="{StaticResource {x:Static SystemParameters.FocusVisualStyleKey}}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="CheckBox">
<BulletDecorator Background="Transparent">
<BulletDecorator.Bullet>
<Border x:Name="Border"
Width="16"
Height="16"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
ClipToBounds="True">
<Path x:Name="PART_CheckMark"
Stroke="{TemplateBinding Foreground}"
Width="7"
Height="7"
Stretch="Fill"
VerticalAlignment="Center"
HorizontalAlignment="Center"
SnapsToDevicePixels="False"
StrokeThickness="2"
StrokeEndLineCap="Round"
StrokeStartLineCap="Round"
Data="M 30,100 L 80,140 L 160,60" />
</Border>
</BulletDecorator.Bullet>
<ContentPresenter Margin="4,0,0,0"
HorizontalAlignment="Left"
VerticalAlignment="Center"
RecognizesAccessKey="True" />
</BulletDecorator>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver"
Value="True">
<Setter Property="BorderBrush"
Value="{DynamicResource HighlightedBorderBrush}" />
</Trigger>
<Trigger Property="IsPressed"
Value="True">
<Setter Property="BorderBrush"
Value="{DynamicResource FocusedBorderBrush}" />
</Trigger>
<Trigger Property="IsChecked"
Value="False">
<Setter TargetName="PART_CheckMark"
Property="Visibility"
Value="Collapsed" />
</Trigger>
<Trigger Property="IsEnabled"
Value="False">
<Setter Property="BorderBrush"
Value="{DynamicResource DisabledBorderBrush}" />
<Setter TargetName="PART_CheckMark"
Property="Stroke"
Value="{DynamicResource DisabledBorderBrush}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type TextBox}"
BasedOn="{StaticResource {x:Type TextBox}}">
<Setter Property="Background"
Value="{DynamicResource BackgroundBrush}" />
<Setter Property="BorderBrush"
Value="{DynamicResource BorderBrush}" />
<Setter Property="Foreground"
Value="{DynamicResource ForegroundBrush}" />
<Setter Property="BorderThickness"
Value="1" />
<Setter Property="Padding"
Value="4 2" />
<Setter Property="VerticalContentAlignment"
Value="Center" />
<Setter Property="FocusVisualStyle"
Value="{StaticResource {x:Static SystemParameters.FocusVisualStyleKey}}" />
</Style>
<Style TargetType="{x:Type ToggleButton}"
BasedOn="{StaticResource {x:Type ToggleButton}}">
<Setter Property="Foreground"
Value="{DynamicResource ForegroundBrush}" />
<Setter Property="Background"
Value="{DynamicResource BackgroundBrush}" />
<Setter Property="BorderBrush"
Value="{DynamicResource BorderBrush}" />
<Setter Property="BorderThickness"
Value="1" />
<Setter Property="FocusVisualStyle"
Value="{StaticResource {x:Static SystemParameters.FocusVisualStyleKey}}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ToggleButton}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="20" />
</Grid.ColumnDefinitions>
<Border x:Name="PART_Border"
Grid.ColumnSpan="2"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}" />
<Path Grid.Column="1"
Fill="{TemplateBinding Foreground}"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Data="M0,0 L0,2 L4,6 L8,2 L8,0 L4,4 z" />
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver"
Value="True">
<Setter TargetName="PART_Border"
Property="Background"
Value="{DynamicResource HighlightedBackgroundBrush}" />
<Setter Property="BorderBrush"
TargetName="PART_Border"
Value="{DynamicResource HighlightedBorderBrush}" />
</Trigger>
<Trigger Property="IsChecked"
Value="True">
<Setter Property="BorderBrush"
TargetName="PART_Border"
Value="{DynamicResource FocusedBorderBrush}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type ComboBox}"
BasedOn="{StaticResource {x:Type ComboBox}}">
<Setter Property="Background"
Value="{DynamicResource BackgroundBrush}" />
<Setter Property="Foreground"
Value="{DynamicResource ForegroundBrush}" />
<Setter Property="BorderBrush"
Value="{DynamicResource BorderBrush}" />
<Setter Property="BorderThickness"
Value="1" />
<Setter Property="FocusVisualStyle"
Value="{StaticResource {x:Static SystemParameters.FocusVisualStyleKey}}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ComboBox}">
<Grid>
<ToggleButton Focusable="False"
IsThreeState="False"
IsChecked="{Binding Path=IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
IsEnabled="{TemplateBinding IsEnabled}"
ClickMode="Press" />
<ContentPresenter x:Name="PART_Content"
IsHitTestVisible="False"
Content="{TemplateBinding SelectionBoxItem}"
ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}"
ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}"
Margin="4 2 24 2"
VerticalAlignment="Center"
HorizontalAlignment="Left" />
<TextBox x:Name="PART_EditableTextBox"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Margin="4 2 24 2"
Focusable="True"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Background="{TemplateBinding Background}"
Foreground="{TemplateBinding Foreground}"
IsReadOnly="{TemplateBinding IsReadOnly}"
Visibility="Hidden" />
<Popup Placement="Bottom"
IsOpen="{TemplateBinding IsDropDownOpen}"
Focusable="False"
AllowsTransparency="True"
PopupAnimation="Slide">
<Grid SnapsToDevicePixels="True"
MinWidth="{TemplateBinding ActualWidth}"
MaxHeight="{TemplateBinding MaxDropDownHeight}">
<Border x:Name="PART_DropDown"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
MinHeight="15"
Margin="0 2 0 0" />
<ScrollViewer Margin="2 3"
SnapsToDevicePixels="True">
<StackPanel IsItemsHost="True"
KeyboardNavigation.DirectionalNavigation="Contained" />
</ScrollViewer>
</Grid>
</Popup>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled"
Value="False">
<Setter Property="Foreground"
Value="{DynamicResource DisabledForegroundBrush}" />
</Trigger>
<Trigger Property="IsEditable"
Value="True">
<Setter Property="IsTabStop"
Value="False" />
<Setter TargetName="PART_EditableTextBox"
Property="Visibility"
Value="Visible" />
<Setter TargetName="PART_Content"
Property="Visibility"
Value="Hidden" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type ComboBoxItem}"
BasedOn="{StaticResource {x:Type ComboBoxItem}}">
<Setter Property="VerticalContentAlignment"
Value="Center" />
<Setter Property="HorizontalContentAlignment"
Value="Left" />
<Setter Property="Background"
Value="{DynamicResource BackgroundBrush}" />
<Setter Property="BorderBrush"
Value="{DynamicResource HighlightedBackgroundBrush}" />
<Setter Property="FocusVisualStyle"
Value="{StaticResource {x:Static SystemParameters.FocusVisualStyleKey}}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ComboBoxItem}">
<Border Name="Border"
Padding="4 2"
SnapsToDevicePixels="True">
<ContentPresenter />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsHighlighted"
Value="True">
<Setter TargetName="Border"
Property="Background"
Value="{Binding BorderBrush, RelativeSource={RelativeSource TemplatedParent}}" />
</Trigger>
<Trigger Property="IsEnabled"
Value="False">
<Setter Property="Foreground"
Value="{DynamicResource DisabledForegroundBrush}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type ScrollViewer}"
BasedOn="{StaticResource {x:Type ScrollViewer}}">
<Setter Property="VerticalScrollBarVisibility"
Value="Auto" />
<Setter Property="HorizontalScrollBarVisibility"
Value="Auto" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ScrollViewer}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ScrollContentPresenter CanContentScroll="{TemplateBinding CanContentScroll}"
ContentSource="Content" />
<ScrollBar x:Name="PART_VerticalScrollBar"
Value="{TemplateBinding VerticalOffset}"
Maximum="{TemplateBinding ScrollableHeight}"
ViewportSize="{TemplateBinding ViewportHeight}"
Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}"
Orientation="Vertical"
Width="7"
Grid.Column="1"
Grid.Row="0" />
<ScrollBar x:Name="PART_HorizontalScrollBar"
Value="{TemplateBinding HorizontalOffset}"
Maximum="{TemplateBinding ScrollableWidth}"
ViewportSize="{TemplateBinding ViewportWidth}"
Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}"
Orientation="Horizontal"
Height="7"
Grid.Column="0"
Grid.Row="1" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type ScrollBar}"
BasedOn="{StaticResource {x:Type ScrollBar}}">
<Style.Resources>
<Style TargetType="{x:Type Thumb}"
BasedOn="{StaticResource {x:Type Thumb}}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Thumb}">
<Rectangle SnapsToDevicePixels="True"
Height="{TemplateBinding Height}"
Width="{TemplateBinding Width}"
Fill="{DynamicResource HighlightedBackgroundBrush}" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Style.Resources>
<Setter Property="Background"
Value="{DynamicResource BackgroundBrush}" />
<Setter Property="BorderBrush"
Value="{DynamicResource BorderBrush}" />
<Setter Property="Foreground"
Value="{DynamicResource ForegroundBrush}" />
<Setter Property="BorderThickness"
Value="1" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ScrollBar}">
<Grid x:Name="PART_Content"
SnapsToDevicePixels="True"
Height="{TemplateBinding Height}"
Width="{TemplateBinding Width}"
Margin="0 5 0 5">
<Border Background="{TemplateBinding Background}"
BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="{TemplateBinding BorderBrush}" />
<Track x:Name="PART_Track"
IsEnabled="{TemplateBinding IsMouseOver}"
IsDirectionReversed="True">
<Track.Thumb>
<Thumb />
</Track.Thumb>
</Track>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="Orientation"
Value="Horizontal">
<Setter Property="Width"
Value="Auto" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ScrollBar}">
<Grid x:Name="PART_Content"
SnapsToDevicePixels="True"
Height="{TemplateBinding Height}"
Width="{TemplateBinding Width}"
Margin="5 0 5 0">
<Border Background="{TemplateBinding Background}"
BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="{TemplateBinding BorderBrush}" />
<Track x:Name="PART_Track"
IsEnabled="{TemplateBinding IsMouseOver}">
<Track.Thumb>
<Thumb />
</Track.Thumb>
</Track>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
<Style TargetType="{x:Type Separator}"
BasedOn="{StaticResource {x:Type Separator}}">
<Setter Property="Focusable"
Value="False" />
<Setter Property="VerticalAlignment"
Value="Stretch" />
<Setter Property="HorizontalAlignment"
Value="Stretch" />
<Setter Property="Height"
Value="1" />
<Setter Property="Width"
Value="2" />
<Setter Property="Background"
Value="{DynamicResource BackgroundBrush}" />
<Setter Property="BorderBrush"
Value="{DynamicResource BorderBrush}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Separator}">
<Border BorderThickness="{TemplateBinding BorderThickness}"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
Height="{TemplateBinding Height}"
Width="{TemplateBinding Width}" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="IconCheckBox"
TargetType="{x:Type CheckBox}"
BasedOn="{StaticResource {x:Type CheckBox}}">
<Setter Property="Padding"
Value="1 2" />
<Setter Property="HorizontalAlignment"
Value="Left" />
<Setter Property="VerticalAlignment"
Value="Center" />
<Setter Property="Background"
Value="Transparent" />
<Setter Property="BorderBrush"
Value="{DynamicResource HighlightedBackgroundBrush}" />
<Setter Property="Foreground"
Value="{DynamicResource ForegroundBrush}" />
<Setter Property="FocusVisualStyle"
Value="{StaticResource {x:Static SystemParameters.FocusVisualStyleKey}}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type CheckBox}">
<Border x:Name="PART_Border"
BorderThickness="{TemplateBinding BorderThickness}"
Background="{TemplateBinding Background}"
Padding="{TemplateBinding Padding}"
SnapsToDevicePixels="True">
<ContentPresenter HorizontalAlignment="Left"
SnapsToDevicePixels="True"
VerticalAlignment="Center" />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver"
Value="True">
<Setter TargetName="PART_Border"
Property="Background"
Value="{Binding BorderBrush, RelativeSource={RelativeSource TemplatedParent}}" />
</Trigger>
<Trigger Property="IsPressed"
Value="True">
<Setter TargetName="PART_Border"
Property="Background"
Value="{Binding BorderBrush, RelativeSource={RelativeSource TemplatedParent}}" />
</Trigger>
<Trigger Property="IsEnabled"
Value="False">
<Setter Property="Foreground"
Value="{DynamicResource DisabledForegroundBrush}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="IconButton"
TargetType="{x:Type Button}"
BasedOn="{StaticResource {x:Type Button}}">
<Setter Property="Padding"
Value="1 2" />
<Setter Property="HorizontalAlignment"
Value="Left" />
<Setter Property="VerticalAlignment"
Value="Center" />
<Setter Property="Background"
Value="Transparent" />
<Setter Property="BorderBrush"
Value="{DynamicResource HighlightedBackgroundBrush}" />
<Setter Property="Foreground"
Value="{DynamicResource ForegroundBrush}" />
<Setter Property="FocusVisualStyle"
Value="{StaticResource {x:Static SystemParameters.FocusVisualStyleKey}}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border x:Name="PART_Border"
BorderThickness="{TemplateBinding BorderThickness}"
Background="{TemplateBinding Background}"
Padding="{TemplateBinding Padding}"
SnapsToDevicePixels="True">
<ContentPresenter x:Name="contentPresenter"
Focusable="False"
Margin="{TemplateBinding Padding}"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver"
Value="True">
<Setter TargetName="PART_Border"
Property="Background"
Value="{Binding BorderBrush, RelativeSource={RelativeSource TemplatedParent}}" />
</Trigger>
<Trigger Property="IsPressed"
Value="True">
<Setter TargetName="PART_Border"
Property="Background"
Value="{Binding BorderBrush, RelativeSource={RelativeSource TemplatedParent}}" />
</Trigger>
<Trigger Property="IsEnabled"
Value="False">
<Setter Property="Foreground"
Value="{DynamicResource DisabledForegroundBrush}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="HollowButton"
TargetType="{x:Type Button}"
BasedOn="{StaticResource {x:Type Button}}">
<Setter Property="Margin"
Value="0,0,5,0" />
<Setter Property="Background"
Value="Transparent" />
<Setter Property="Padding"
Value="7" />
<Setter Property="BorderBrush"
Value="OrangeRed" />
<Setter Property="Cursor"
Value="Hand" />
<Setter Property="Foreground"
Value="{DynamicResource ForegroundBrush}" />
<Setter Property="FocusVisualStyle"
Value="{StaticResource {x:Static SystemParameters.FocusVisualStyleKey}}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="3"
Margin="{TemplateBinding Margin}">
<ContentPresenter Margin="{TemplateBinding Padding}" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver"
Value="True">
<Setter Property="Background"
Value="{DynamicResource HighlightedBackgroundBrush}" />
</Trigger>
</Style.Triggers>
</Style>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="OverridesDefaultStyle"
Value="True" />
<Setter Property="Foreground"
Value="{Binding Path=Foreground, RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}}" />
<Setter Property="Background"
Value="{Binding Path=Background, RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}}" />
<Setter Property="BorderThickness"
Value="1" />
<Setter Property="Padding"
Value="3 0 0 0" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type MenuItem}">
<Border x:Name="PART_Border"
Margin="{TemplateBinding Margin}"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding Background}"
BorderThickness="{TemplateBinding BorderThickness}"
Padding="1">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition MinWidth="17"
Width="Auto"
SharedSizeGroup="MenuItemIcon" />
<ColumnDefinition Width="Auto"
SharedSizeGroup="MenuItemText" />
<ColumnDefinition Width="Auto"
SharedSizeGroup="MenuItemGesture" />
<ColumnDefinition Width="14" />
</Grid.ColumnDefinitions>
<ContentPresenter x:Name="Icon"
Margin="4 0 6 0"
VerticalAlignment="Center"
ContentSource="Icon"
Visibility="Visible" />
<ContentPresenter Grid.Column="1"
x:Name="Header"
Margin="{TemplateBinding Padding}"
RecognizesAccessKey="True"
ContentSource="Header"
Visibility="Visible" />
<ContentPresenter Grid.Column="2"
x:Name="InputGestureText"
Margin="32 1 8 1"
ContentSource="InputGestureText"
VerticalAlignment="Center"
Visibility="Visible" />
<Grid Grid.Column="3"
Margin="4 0 6 0"
x:Name="ArrowPanel"
VerticalAlignment="Center">
<Path x:Name="ArrowPanelPath"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Fill="{TemplateBinding Foreground}"
Data="M0,0 L0,8 L4,4 z" />
</Grid>
<Popup AllowsTransparency="True"
IsOpen="{Binding Path=IsSubmenuOpen, RelativeSource={RelativeSource TemplatedParent}}"
Placement="Right"
x:Name="PART_Popup"
Focusable="False"
PopupAnimation="{DynamicResource {x:Static SystemParameters.MenuPopupAnimationKey}}">
<Border Background="{Binding Path=Background, RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}}"
BorderBrush="{Binding Path=BorderBrush, RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}}"
BorderThickness="{Binding Path=BorderThickness, RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}}"
Margin="0 0 5 5">
<Border.Effect>
<DropShadowEffect BlurRadius="5"
Opacity="0.5" />
</Border.Effect>
<Grid Grid.IsSharedSizeScope="True">
<ItemsPresenter x:Name="ItemsPresenter"
KeyboardNavigation.DirectionalNavigation="Cycle"
Grid.IsSharedSizeScope="True"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
KeyboardNavigation.TabNavigation="Cycle" />
</Grid>
</Border>
</Popup>
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsSuspendingPopupAnimation"
Value="True">
<Setter Property="PopupAnimation"
TargetName="PART_Popup"
Value="None" />
</Trigger>
<Trigger Property="Role"
Value="SubmenuItem">
<Setter Property="DockPanel.Dock"
Value="Top" />
<Setter Property="Visibility"
Value="Collapsed"
TargetName="ArrowPanel" />
</Trigger>
<Trigger Property="Icon"
Value="{x:Null}">
<Setter Property="Visibility"
Value="Collapsed"
TargetName="Icon" />
</Trigger>
<Trigger Property="InputGestureText"
Value="{x:Null}">
<Setter Property="Visibility"
Value="Collapsed"
TargetName="InputGestureText" />
</Trigger>
<Trigger Property="IsHighlighted"
Value="True">
<Setter Property="BorderBrush"
TargetName="PART_Border"
Value="{DynamicResource HighlightedBorderBrush}" />
<Setter Property="Background">
<Setter.Value>
<SolidColorBrush Opacity="0.2"
Color="{DynamicResource FocusedBorderColor}" />
</Setter.Value>
</Setter>
<Setter Property="BorderThickness"
TargetName="PART_Border"
Value="1" />
</Trigger>
<Trigger Property="IsEnabled"
Value="False">
<Setter Property="Foreground"
Value="{DynamicResource DisabledForegroundBrush}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type ContextMenu}"
BasedOn="{StaticResource {x:Type ContextMenu}}">
<Setter Property="Background"
Value="{DynamicResource ContrastBackgroundBrush}" />
<Setter Property="BorderBrush"
Value="{DynamicResource BorderBrush}" />
<Setter Property="Foreground"
Value="{DynamicResource ForegroundBrush}" />
<Setter Property="BorderThickness"
Value="1" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ContextMenu}">
<Border SnapsToDevicePixels="True"
Margin="0 0 5 5"
BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="{TemplateBinding BorderBrush}"
Background="{TemplateBinding Background}">
<Border.Effect>
<DropShadowEffect BlurRadius="5"
Opacity="0.5" />
</Border.Effect>
<ItemsPresenter SnapsToDevicePixels="True" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type Expander}"
BasedOn="{StaticResource {x:Type Expander}}">
<Setter Property="BorderThickness"
Value="1" />
<Setter Property="Background"
Value="Transparent" />
<Setter Property="BorderBrush"
Value="Transparent" />
<Setter Property="Foreground"
Value="{DynamicResource ForegroundBrush}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Expander}">
<Border Background="{TemplateBinding Background}"
Padding="{TemplateBinding Padding}"
SnapsToDevicePixels="True">
<DockPanel LastChildFill="True">
<CheckBox ContentTemplate="{TemplateBinding HeaderTemplate}"
ContentTemplateSelector="{TemplateBinding HeaderTemplateSelector}"
Content="{TemplateBinding Header}"
FontStyle="{TemplateBinding FontStyle}"
FontStretch="{TemplateBinding FontStretch}"
FontWeight="{TemplateBinding FontWeight}"
FontFamily="{TemplateBinding FontFamily}"
FontSize="{TemplateBinding FontSize}"
Foreground="{TemplateBinding Foreground}"
Tag="{TemplateBinding Tag}"
IsChecked="{Binding IsExpanded, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
Background="{TemplateBinding Background}"
BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="{TemplateBinding BorderBrush}"
FocusVisualStyle="{StaticResource {x:Static SystemParameters.FocusVisualStyleKey}}"
Cursor="Hand"
x:Name="PART_Header">
<CheckBox.Template>
<ControlTemplate TargetType="{x:Type CheckBox}">
<Border Padding="{TemplateBinding Padding}"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<StackPanel Orientation="Horizontal">
<ContentPresenter Content="{TemplateBinding Tag}" />
<ContentPresenter SnapsToDevicePixels="True"
VerticalAlignment="Center" />
</StackPanel>
</Border>
</ControlTemplate>
</CheckBox.Template>
</CheckBox>
<ContentPresenter x:Name="PART_Content"
Focusable="False"
Margin="{TemplateBinding Padding}"
Visibility="Collapsed" />
</DockPanel>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsExpanded"
Value="True">
<Setter Property="Visibility"
TargetName="PART_Content"
Value="Visible" />
<Setter TargetName="PART_Header"
Property="Background"
Value="{Binding BorderBrush, RelativeSource={RelativeSource TemplatedParent}}" />
</Trigger>
<Trigger Property="IsEnabled"
Value="False">
<Setter Property="Foreground"
Value="{DynamicResource DisabledForegroundBrush}" />
</Trigger>
<Trigger Property="ExpandDirection"
Value="Right">
<Setter TargetName="PART_Header"
Property="LayoutTransform">
<Setter.Value>
<RotateTransform Angle="90" />
</Setter.Value>
</Setter>
<Setter TargetName="PART_Header"
Property="DockPanel.Dock"
Value="Left" />
<Setter TargetName="PART_Content"
Property="DockPanel.Dock"
Value="Right" />
</Trigger>
<Trigger Property="ExpandDirection"
Value="Down">
<Setter TargetName="PART_Header"
Property="DockPanel.Dock"
Value="Top" />
<Setter TargetName="PART_Content"
Property="DockPanel.Dock"
Value="Bottom" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>

View File

@@ -0,0 +1,23 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Brushes.xaml" />
<ResourceDictionary Source="Controls.xaml" />
</ResourceDictionary.MergedDictionaries>
<Color x:Key="ForegroundColor">White</Color>
<Color x:Key="DisabledForegroundColor">Gray</Color>
<Color x:Key="BackgroundColor">#333337</Color>
<Color x:Key="ContrastBackgroundColor">#1B1B1C</Color>
<Color x:Key="HighlightedBackgroundColor">#3F3F46</Color>
<Color x:Key="BorderColor">#3F3F3F</Color>
<Color x:Key="DisabledBorderColor">Gray</Color>
<Color x:Key="HighlightedBorderColor">#7EB4EA</Color>
<Color x:Key="FocusedBorderColor">#569DE5</Color>
<Color x:Key="GridLinesColor">#333337</Color>
</ResourceDictionary>

View File

@@ -0,0 +1,454 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Nodify">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/Nodify.Shared;component/Themes/Controls.xaml" />
<ResourceDictionary Source="pack://application:,,,/Nodify.Shared;component/Themes/Icons.xaml" />
</ResourceDictionary.MergedDictionaries>
<local:ResizeDirectionToVisiblityConverter x:Key="ResizeDirectionToVisiblityConverter" />
<Style x:Key="EditableTextBlockBaseStyle"
TargetType="{x:Type local:EditableTextBlock}">
<Setter Property="Background"
Value="Transparent" />
<Setter Property="BorderThickness"
Value="1" />
<Setter Property="BorderBrush"
Value="DodgerBlue" />
<Setter Property="MinHeight"
Value="{Binding FontSize, RelativeSource={RelativeSource Self}}" />
<Setter Property="Padding"
Value="0" />
<Setter Property="Foreground"
Value="White" />
<Setter Property="VerticalContentAlignment"
Value="Center" />
<Setter Property="HorizontalContentAlignment"
Value="Stretch" />
<Setter Property="Cursor"
Value="IBeam" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:EditableTextBlock}">
<Grid VerticalAlignment="{TemplateBinding VerticalAlignment}"
HorizontalAlignment="{TemplateBinding HorizontalAlignment}">
<TextBlock Text="{Binding Text, ElementName=PART_TextBox}"
Background="{TemplateBinding Background}"
Foreground="{TemplateBinding Foreground}"
TextWrapping="{TemplateBinding TextWrapping}"
Padding="{TemplateBinding Padding}"
TextTrimming="{TemplateBinding TextTrimming}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
x:Name="PART_Text" />
<TextBox Text="{Binding Text, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
MinWidth="{TemplateBinding MinWidth}"
MinHeight="{TemplateBinding MinHeight}"
Background="{TemplateBinding Background}"
Foreground="{TemplateBinding Foreground}"
TextWrapping="{TemplateBinding TextWrapping}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
AcceptsReturn="{TemplateBinding AcceptsReturn}"
FontSize="{TemplateBinding FontSize}"
Padding="{TemplateBinding Padding}"
MaxLength="{TemplateBinding MaxLength}"
MinLines="{TemplateBinding MinLines}"
MaxLines="{TemplateBinding MinLines}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
AcceptsTab="True"
Visibility="Collapsed"
Margin="-1"
x:Name="PART_TextBox" />
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsEditing"
Value="True">
<Setter TargetName="PART_TextBox"
Property="Visibility"
Value="Visible" />
<Setter TargetName="PART_Text"
Property="Visibility"
Value="Collapsed" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type local:EditableTextBlock}"
BasedOn="{StaticResource EditableTextBlockBaseStyle}" />
<Style TargetType="{x:Type local:TabControlEx}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:TabControlEx}">
<Grid KeyboardNavigation.TabNavigation="Local"
SnapsToDevicePixels="true"
ClipToBounds="true">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid Panel.ZIndex="1"
Margin="2,2,2,0"
Background="{TemplateBinding Background}"
KeyboardNavigation.TabIndex="1">
<ScrollViewer x:Name="PART_ScrollViewer"
HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Auto">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<StackPanel Orientation="Horizontal"
IsItemsHost="True" />
<Button Style="{StaticResource IconButton}"
Command="{TemplateBinding AddTabCommand}"
Height="34"
VerticalAlignment="Bottom"
BorderBrush="{DynamicResource HighlightedBackgroundBrush}"
Grid.Column="1"
ToolTip="Add new tab">
<Path Width="25"
Height="15"
Data="{StaticResource AddGeometry}"
Fill="{TemplateBinding Foreground}"
Stretch="Uniform" />
</Button>
</Grid>
</ScrollViewer>
</Grid>
<Border x:Name="ContentPanel"
Background="{TemplateBinding Background}"
BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="{TemplateBinding BorderBrush}"
KeyboardNavigation.TabNavigation="Local"
KeyboardNavigation.DirectionalNavigation="Contained"
KeyboardNavigation.TabIndex="2"
Grid.Row="1">
<ContentPresenter x:Name="PART_SelectedContentHost"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
Margin="{TemplateBinding Padding}"
ContentSource="SelectedContent" />
</Border>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled"
Value="false">
<Setter Property="Opacity"
Value=".5" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type local:TabItemEx}">
<Setter Property="Background"
Value="{DynamicResource BackgroundBrush}" />
<Setter Property="Foreground"
Value="{DynamicResource ForegroundBrush}" />
<Setter Property="RenderTransform">
<Setter.Value>
<ScaleTransform ScaleY="0" />
</Setter.Value>
</Setter>
<Setter Property="RenderTransformOrigin"
Value="1 1" />
<Setter Property="VerticalAlignment"
Value="Bottom" />
<Setter Property="VerticalContentAlignment"
Value="Stretch" />
<Setter Property="HorizontalContentAlignment"
Value="Stretch" />
<Setter Property="Padding"
Value="7" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:TabItemEx}">
<Border x:Name="mBorder"
BorderThickness="1"
CornerRadius="5 5 0 0"
Background="{TemplateBinding Background}"
Padding="{TemplateBinding Padding}">
<Grid>
<StackPanel Orientation="Horizontal">
<local:EditableTextBlock Text="{TemplateBinding Header}"
Foreground="{TemplateBinding Foreground}" />
<Button x:Name="mCloseBtn"
Command="{TemplateBinding CloseTabCommand}"
CommandParameter="{TemplateBinding CloseTabCommandParameter}"
Visibility="Hidden"
Margin="5 0 0 0"
Background="{DynamicResource HighlightedBackgroundBrush}"
BorderBrush="{DynamicResource BackgroundBrush}"
Style="{StaticResource IconButton}"
ToolTip="Close Tab">
<Path Width="16"
Height="8"
Data="{StaticResource CloseGeometry}"
Fill="{TemplateBinding Foreground}"
Stretch="Uniform" />
</Button>
</StackPanel>
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver"
Value="True">
<Setter Property="Visibility"
Value="Visible"
TargetName="mCloseBtn" />
<Setter Property="Background"
Value="{DynamicResource HighlightedBackgroundBrush}" />
</Trigger>
<Trigger Property="IsSelected"
Value="True">
<Setter Property="TextElement.FontWeight"
Value="Bold" />
<Setter Property="Background"
Value="{DynamicResource HighlightedBackgroundBrush}" />
<Setter Property="Padding"
Value="8" />
<Setter Property="Visibility"
Value="Visible"
TargetName="mCloseBtn" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<EventTrigger RoutedEvent="Loaded">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Duration="0:0:.5"
From="0"
To="1"
Storyboard.TargetProperty="(RenderTransform).(ScaleTransform.ScaleY)">
<DoubleAnimation.EasingFunction>
<ElasticEase Oscillations="1"
Springiness="3"
EasingMode="EaseOut" />
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Style.Triggers>
</Style>
<Style TargetType="{x:Type local:Swatches}">
<Style.Resources>
<local:MultiValueEqualityConverter x:Key="MultiValueEqualityConverter" />
<local:ColorToSolidColorBrushConverter x:Key="ColorToSolidColorBrushConverter" />
</Style.Resources>
<Setter Property="Background"
Value="#1e293b" />
<Setter Property="Padding"
Value="6" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:Swatches">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Padding="{TemplateBinding Padding}"
CornerRadius="8">
<ItemsControl ItemsSource="{TemplateBinding Colors}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border CornerRadius="50"
Padding="1"
Margin="3">
<Border.Style>
<Style TargetType="{x:Type Border}">
<Setter Property="Background"
Value="Transparent" />
<Style.Triggers>
<DataTrigger Value="True">
<DataTrigger.Binding>
<MultiBinding Converter="{StaticResource MultiValueEqualityConverter}">
<Binding RelativeSource="{RelativeSource AncestorType=local:Swatches}"
Path="SelectedColor"
Mode="OneWay" />
<Binding Mode="OneWay" />
</MultiBinding>
</DataTrigger.Binding>
<Setter Property="Background"
Value="White" />
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<Ellipse Width="24"
Height="24"
Stroke="#1e293b"
Fill="{Binding ., Converter={StaticResource ColorToSolidColorBrushConverter}}"
Cursor="Hand" />
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsEnabled"
Value="False">
<Setter Property="Opacity"
Value="0.7" />
</Trigger>
</Style.Triggers>
</Style>
<Style TargetType="{x:Type local:ResizablePanel}">
<Setter Property="BorderBrush"
Value="#1e293b" />
<Setter Property="BorderThickness"
Value="1" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:ResizablePanel">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<Grid>
<ContentPresenter Margin="{TemplateBinding Padding}" />
<local:Resizer Direction="TopLeft"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Visibility="{TemplateBinding Directions, Converter={StaticResource ResizeDirectionToVisiblityConverter}, ConverterParameter=TopLeft}" />
<local:Resizer Direction="TopRight"
HorizontalAlignment="Right"
VerticalAlignment="Top"
Visibility="{TemplateBinding Directions, Converter={StaticResource ResizeDirectionToVisiblityConverter}, ConverterParameter=TopRight}" />
<local:Resizer Direction="BottomLeft"
HorizontalAlignment="Left"
VerticalAlignment="Bottom"
Visibility="{TemplateBinding Directions, Converter={StaticResource ResizeDirectionToVisiblityConverter}, ConverterParameter=BottomLeft}" />
<local:Resizer Direction="BottomRight"
HorizontalAlignment="Right"
VerticalAlignment="Bottom"
Visibility="{TemplateBinding Directions, Converter={StaticResource ResizeDirectionToVisiblityConverter}, ConverterParameter=BottomRight}" />
<local:Resizer Direction="Left"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Visibility="{TemplateBinding Directions, Converter={StaticResource ResizeDirectionToVisiblityConverter}, ConverterParameter=Left}" />
<local:Resizer Direction="Right"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Visibility="{TemplateBinding Directions, Converter={StaticResource ResizeDirectionToVisiblityConverter}, ConverterParameter=Right}" />
<local:Resizer Direction="Top"
HorizontalAlignment="Center"
VerticalAlignment="Top"
Visibility="{TemplateBinding Directions, Converter={StaticResource ResizeDirectionToVisiblityConverter}, ConverterParameter=Top}" />
<local:Resizer Direction="Bottom"
HorizontalAlignment="Center"
VerticalAlignment="Bottom"
Visibility="{TemplateBinding Directions, Converter={StaticResource ResizeDirectionToVisiblityConverter}, ConverterParameter=Bottom}" />
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type local:Resizer}">
<Setter Property="Background"
Value="#1e293b" />
<Setter Property="Width"
Value="6" />
<Setter Property="Height"
Value="6" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:Resizer">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}" />
<ControlTemplate.Triggers>
<Trigger Property="Direction"
Value="TopLeft">
<Setter Property="Cursor"
Value="SizeNWSE" />
<Setter Property="Margin"
Value="-4 -4 0 0" />
</Trigger>
<Trigger Property="Direction"
Value="TopRight">
<Setter Property="Cursor"
Value="SizeNESW" />
<Setter Property="Margin"
Value="0 -4 -4 0" />
</Trigger>
<Trigger Property="Direction"
Value="BottomLeft">
<Setter Property="Cursor"
Value="SizeNESW" />
<Setter Property="Margin"
Value="-4 0 0 -4" />
</Trigger>
<Trigger Property="Direction"
Value="BottomRight">
<Setter Property="Cursor"
Value="SizeNWSE" />
<Setter Property="Margin"
Value="0 0 -4 -4" />
</Trigger>
<Trigger Property="Direction"
Value="Left">
<Setter Property="Cursor"
Value="SizeWE" />
<Setter Property="Margin"
Value="-4 0 0 0" />
</Trigger>
<Trigger Property="Direction"
Value="Right">
<Setter Property="Cursor"
Value="SizeWE" />
<Setter Property="Margin"
Value="0 0 -4 0" />
</Trigger>
<Trigger Property="Direction"
Value="Top">
<Setter Property="Cursor"
Value="SizeNS" />
<Setter Property="Margin"
Value="0 -4 0 0" />
</Trigger>
<Trigger Property="Direction"
Value="Bottom">
<Setter Property="Cursor"
Value="SizeNS" />
<Setter Property="Margin"
Value="0 0 0 -4" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>

View File

@@ -0,0 +1,773 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Rectangle x:Key="AlignCenterIcon"
x:Shared="False"
Width="16"
Height="16">
<Rectangle.Fill>
<DrawingBrush>
<DrawingBrush.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing Brush="#00FFFFFF"
Geometry="F1M16,16L0,16 0,0 16,0z" />
<GeometryDrawing Brush="#FFF6F6F6"
Geometry="F1M6.0003,-0.000199999999999534L6.0003,3.0008 1.0003,3.0008 1.0003,6.9998 6.0003,6.9998 6.0003,8.9998 1.9993,8.9998 1.9993,13.0008 6.0003,13.0008 6.0003,15.9998 9.0003,15.9998 9.0003,13.0008 12.9993,13.0008 12.9993,8.9998 9.0003,8.9998 9.0003,6.9998 14.0003,6.9998 14.0003,3.0008 9.0003,3.0008 9.0003,-0.000199999999999534z" />
<GeometryDrawing Brush="#FF424242"
Geometry="F1M8,6L8,10 12,10 12,12 8,12 8,15 7,15 7,12 3,12 3,10 7,10 7,6 2,6 2,4 7,4 7,1 8,1 8,4 13,4 13,6z" />
</DrawingGroup.Children>
</DrawingGroup>
</DrawingBrush.Drawing>
</DrawingBrush>
</Rectangle.Fill>
</Rectangle>
<Rectangle x:Key="AlignMiddleIcon"
x:Shared="False"
Width="16"
Height="16">
<Rectangle.Fill>
<DrawingBrush>
<DrawingBrush.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing Brush="#00FFFFFF"
Geometry="F1M16,16L0,16 0,0 16,0z" />
<GeometryDrawing Brush="#FFF6F6F6"
Geometry="F1M2.9997,1.9996L2.9997,6.9996 -0.000300000000000189,6.9996 -0.000300000000000189,9.9996 2.9997,9.9996 2.9997,15.0006 7.0007,15.0006 7.0007,9.9996 8.9997,9.9996 8.9997,14.0006 12.9997,14.0006 12.9997,9.9996 16.0007,9.9996 16.0007,6.9996 12.9997,6.9996 12.9997,3.0006 8.9997,3.0006 8.9997,6.9996 7.0007,6.9996 7.0007,1.9996z" />
<GeometryDrawing Brush="#FF424242"
Geometry="F1M6,8L10,8 10,4 12,4 12,8 15,8 15,9 12,9 12,13 10,13 10,9 6,9 6,14 4,14 4,9 1,9 1,8 4,8 4,3 6,3z" />
</DrawingGroup.Children>
</DrawingGroup>
</DrawingBrush.Drawing>
</DrawingBrush>
</Rectangle.Fill>
</Rectangle>
<Rectangle x:Key="AlignRightIcon"
x:Shared="False"
Width="16"
Height="16">
<Rectangle.Fill>
<DrawingBrush>
<DrawingBrush.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing Brush="#00FFFFFF"
Geometry="F1M16,16L0,16 0,0 16,0z" />
<GeometryDrawing Brush="#FFF6F6F6"
Geometry="F1M12.9996,-0.000199999999999534L12.9996,3.0008 -0.000400000000000844,3.0008 -0.000400000000000844,6.9998 12.9996,6.9998 12.9996,8.9998 2.9996,8.9998 2.9996,13.0008 12.9996,13.0008 12.9996,15.9998 16.0006,15.9998 16.0006,-0.000199999999999534z" />
<GeometryDrawing Brush="#FF424242"
Geometry="F1M13,10L4,10 4,12 13,12z M13,4L1,4 1,6 13,6z M14,1L15,1 15,15 14,15z" />
</DrawingGroup.Children>
</DrawingGroup>
</DrawingBrush.Drawing>
</DrawingBrush>
</Rectangle.Fill>
</Rectangle>
<Rectangle x:Key="AlignBottomIcon"
x:Shared="False"
Width="16"
Height="16">
<Rectangle.Fill>
<DrawingBrush>
<DrawingBrush.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing Brush="#00FFFFFF"
Geometry="F1M16,16L0,16 0,0 16,0z" />
<GeometryDrawing Brush="#FFF6F6F6"
Geometry="F1M2.9997,-0.000199999999999534L2.9997,13.0008 -0.000300000000000189,13.0008 -0.000300000000000189,15.9998 16.0007,15.9998 16.0007,13.0008 12.9997,13.0008 12.9997,3.0008 8.9997,3.0008 8.9997,13.0008 7.0007,13.0008 7.0007,-0.000199999999999534z" />
<GeometryDrawing Brush="#FF424242"
Geometry="F1M12,4L10,4 10,13 12,13z M6,1L4,1 4,13 6,13z M15,15L1,15 1,14 15,14z" />
</DrawingGroup.Children>
</DrawingGroup>
</DrawingBrush.Drawing>
</DrawingBrush>
</Rectangle.Fill>
</Rectangle>
<Rectangle x:Key="AlignLeftIcon"
x:Shared="False"
Width="16"
Height="16">
<Rectangle.Fill>
<DrawingBrush>
<DrawingBrush.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing Brush="#00FFFFFF"
Geometry="F1M16,16L0,16 0,0 16,0z" />
<GeometryDrawing Brush="#FFF6F6F6"
Geometry="F1M0,-0.000199999999999534L0,15.9998 3,15.9998 3,13.0008 13,13.0008 13,8.9998 3,8.9998 3,6.9998 16,6.9998 16,3.0008 3,3.0008 3,-0.000199999999999534z" />
<GeometryDrawing Brush="#FF424242"
Geometry="F1M12,10L3,10 3,12 12,12z M15,4L3,4 3,6 15,6z M1,1L2,1 2,15 1,15z" />
</DrawingGroup.Children>
</DrawingGroup>
</DrawingBrush.Drawing>
</DrawingBrush>
</Rectangle.Fill>
</Rectangle>
<Rectangle x:Key="AlignTopIcon"
x:Shared="False"
Width="16"
Height="16">
<Rectangle.Fill>
<DrawingBrush>
<DrawingBrush.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing Brush="#00FFFFFF"
Geometry="F1M16,16L0,16 0,0 16,0z" />
<GeometryDrawing Brush="#FFF6F6F6"
Geometry="F1M0,-0.000199999999999534L0,4.0008 3,4.0008 3,15.9998 7,15.9998 7,4.0008 9,4.0008 9,12.0008 13,12.0008 13,4.0008 16,4.0008 16,-0.000199999999999534z" />
<GeometryDrawing Brush="#FF424242"
Geometry="F1M10,11L12,11 12,4 10,4z M4,15L6,15 6,4 4,4z M15,3L1,3 1,1 15,1z" />
</DrawingGroup.Children>
</DrawingGroup>
</DrawingBrush.Drawing>
</DrawingBrush>
</Rectangle.Fill>
</Rectangle>
<Rectangle x:Key="ThemeIcon"
x:Shared="False"
Width="16"
Height="16">
<Rectangle.Fill>
<DrawingBrush>
<DrawingBrush.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing Brush="#FFF6F6F6"
Geometry="F1M16,8C16,12.418 12.418,16 8,16 3.582,16 0,12.418 0,8 0,3.582 3.582,0 8,0 12.418,0 16,3.582 16,8" />
<GeometryDrawing Brush="#FF414141"
Geometry="F1M7.999,14C7.657,14 7.325,13.965 6.999,13.91 9.836,13.434 12,10.973 12,8 12,5.027 9.836,2.567 6.999,2.09 7.325,2.035 7.657,2 7.999,2 11.312,2 13.999,4.687 13.999,8 13.999,11.313 11.312,14 7.999,14 M7.999,1C4.133,1 0.999,4.134 0.999,8 0.999,11.866 4.133,15 7.999,15 11.865,15 14.999,11.866 14.999,8 14.999,4.134 11.865,1 7.999,1" />
<GeometryDrawing Brush="#FFF0EFF1"
Geometry="F1M7.999,2C7.657,2 7.325,2.035 6.999,2.09 9.836,2.567 12,5.027 12,8 12,10.973 9.836,13.434 6.999,13.91 7.325,13.965 7.657,14 7.999,14 11.312,14 13.999,11.313 13.999,8 13.999,4.687 11.312,2 7.999,2" />
</DrawingGroup.Children>
</DrawingGroup>
</DrawingBrush.Drawing>
</DrawingBrush>
</Rectangle.Fill>
</Rectangle>
<Rectangle x:Key="DiamondFillIcon"
x:Shared="False"
Width="16"
Height="16">
<Rectangle.Fill>
<DrawingBrush>
<DrawingBrush.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing Brush="#00FFFFFF"
Geometry="F1M16,16L0,16 0,0 16,0z" />
<GeometryDrawing Brush="#FFF6F6F6"
Geometry="F1M1.5859,8L7.9999,1.586 14.4139,8 7.9999,14.414z" />
<GeometryDrawing Brush="#FF414141"
Geometry="F1M4.4141,8L8.0001,4.414 11.5861,8 8.0001,11.586z M8.0001,3L3.0001,8 8.0001,13 13.0001,8z" />
<GeometryDrawing Brush="#FFFFCC00"
Geometry="F1M8,4.4141L11.586,8.0001 8,11.5861 4.414,8.0001z" />
</DrawingGroup.Children>
</DrawingGroup>
</DrawingBrush.Drawing>
</DrawingBrush>
</Rectangle.Fill>
</Rectangle>
<Rectangle x:Key="DiamondIcon"
x:Shared="False"
Width="16"
Height="16">
<Rectangle.Fill>
<DrawingBrush>
<DrawingBrush.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing Brush="#00FFFFFF"
Geometry="F1M16,16L0,16 0,0 16,0z" />
<GeometryDrawing Brush="#FFF6F6F6"
Geometry="F1M1.5859,8L7.9999,1.586 14.4139,8 7.9999,14.414z" />
<GeometryDrawing Brush="#FFEFEFF0"
Geometry="F1M8,4.4141L11.586,8.0001 8,11.5861 4.414,8.0001z" />
<GeometryDrawing Brush="#FF424242"
Geometry="F1M4.4141,8L8.0001,4.414 11.5861,8 8.0001,11.586z M8.0001,3L3.0001,8 8.0001,13 13.0001,8z" />
</DrawingGroup.Children>
</DrawingGroup>
</DrawingBrush.Drawing>
</DrawingBrush>
</Rectangle.Fill>
</Rectangle>
<Rectangle x:Key="PlusIcon"
x:Shared="False"
Width="16"
Height="16">
<Rectangle.Fill>
<DrawingBrush>
<DrawingBrush.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing Brush="#00FFFFFF"
Geometry="F1M16,16L0,16 0,0 16,0z" />
<GeometryDrawing Brush="#FFF6F6F6"
Geometry="F1M6.0003,-0.000199999999999534L6.0003,5.9998 0.000300000000000189,5.9998 0.000300000000000189,9.9998 6.0003,9.9998 6.0003,15.9998 10.0003,15.9998 10.0003,9.9998 16.0003,9.9998 16.0003,5.9998 10.0003,5.9998 10.0003,-0.000199999999999534z" />
<GeometryDrawing Brush="#FF424242"
Geometry="F1M15,9L9,9 9,15 7,15 7,9 1,9 1,7 7,7 7,1 9,1 9,7 15,7z" />
</DrawingGroup.Children>
</DrawingGroup>
</DrawingBrush.Drawing>
</DrawingBrush>
</Rectangle.Fill>
</Rectangle>
<Rectangle x:Key="RemoveKeyIcon"
x:Shared="False"
Width="16"
Height="16">
<Rectangle.Fill>
<DrawingBrush>
<DrawingBrush.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing Brush="#00FFFFFF"
Geometry="F1M16,16L0,16 0,0 16,0z" />
<GeometryDrawing Brush="#FFF6F6F6"
Geometry="F1M13,10L4,10 4,6 13,6z" />
<GeometryDrawing Brush="#FF414141"
Geometry="F1M12,9L5,9 5,7 12,7z" />
</DrawingGroup.Children>
</DrawingGroup>
</DrawingBrush.Drawing>
</DrawingBrush>
</Rectangle.Fill>
</Rectangle>
<Rectangle x:Key="AddKeyIcon"
x:Shared="False"
Width="16"
Height="16">
<Rectangle.Fill>
<DrawingBrush>
<DrawingBrush.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing Brush="#00FFFFFF"
Geometry="F1M16,16L0,16 0,0 16,0z" />
<GeometryDrawing Brush="#FFF6F6F6"
Geometry="F1M16,8.4629C16,10.9239 13.997,12.9259 11.537,12.9259 11.118,12.9259 10.704,12.8669 10.3,12.7479L7.704,15.3439C7.595,15.4529 7.003,16.0019 6.147,16.0019 5.767,16.0019 5.2,15.8869 4.657,15.3439 3.538,14.2249 4.043,12.9079 4.657,12.2959L7.252,9.6989C7.133,9.2959 7.073,8.8819 7.073,8.4629 7.073,7.5529 7.35,6.7069 7.82,5.9999L6,5.9999 6,7.9999 2.019,7.9999 2.019,5.9999 0,5.9999 0,2.0179 2.019,2.0179 2.019,-9.99999999979906E-05 6,-9.99999999979906E-05 6,2.0179 8,2.0179 8,5.7719C8.816,4.7019 10.091,3.9999 11.537,3.9999 12.165,3.9999 13.132,4.2889 13.368,4.3999 13.605,4.5099 14.987,5.1659 15.601,6.6309 15.688,6.8399 16,7.8359 16,8.4629" />
<GeometryDrawing Brush="#FF424242"
Geometry="F1M14.9873,8.5049C14.9873,10.4339 13.4233,11.9969 11.4953,11.9969 10.9823,11.9969 10.4323,11.9349 9.9953,11.7359L7.1303,14.6149C7.1303,14.6149 6.2573,15.4879 5.3843,14.6149 4.5103,13.7419 5.3843,12.8689 5.3843,12.8689L8.3233,10.0179C8.1263,9.5779 8.0033,9.0169 8.0033,8.5049 8.0033,6.5769 9.5663,5.0139 11.4953,5.0139 12.0063,5.0139 12.5553,5.0849 12.9953,5.2829L10.6223,7.6329 12.3683,9.3789 14.7143,7.0179C14.9113,7.4549,14.9873,7.9939,14.9873,8.5049" />
<GeometryDrawing Brush="#FF388A34"
Geometry="F1M7,3.0176L5,3.0176 5,0.9996 3.019,0.9996 3.019,3.0176 1,3.0176 1,4.9996 3.019,4.9996 3.019,6.9996 5,6.9996 5,4.9996 7,4.9996z" />
</DrawingGroup.Children>
</DrawingGroup>
</DrawingBrush.Drawing>
</DrawingBrush>
</Rectangle.Fill>
</Rectangle>
<Rectangle x:Key="ExpandRightIcon"
x:Shared="False"
Width="16"
Height="16">
<Rectangle.Fill>
<DrawingBrush>
<DrawingBrush.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing Brush="#00FFFFFF"
Geometry="F1M16,16L0,16 0,0 16,0z" />
<GeometryDrawing Brush="#FF0081E4"
Geometry="F1M6.3906,13.6943L3.9156,11.2193 7.1356,8.0003 3.9156,4.7803 6.3906,2.3053 12.0836,8.0003z" />
<GeometryDrawing Brush="#FF0081E4"
Geometry="F1M6.3901,12.2803L5.3291,11.2193 8.5491,8.0003 5.3291,4.7803 6.3901,3.7193 10.6701,8.0003z" />
</DrawingGroup.Children>
</DrawingGroup>
</DrawingBrush.Drawing>
</DrawingBrush>
</Rectangle.Fill>
</Rectangle>
<Rectangle x:Key="ExpandLeftIcon"
x:Shared="False"
Width="16"
Height="16">
<Rectangle.LayoutTransform>
<RotateTransform Angle="180" />
</Rectangle.LayoutTransform>
<Rectangle.Fill>
<DrawingBrush>
<DrawingBrush.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing Brush="#00FFFFFF"
Geometry="F1M16,16L0,16 0,0 16,0z" />
<GeometryDrawing Brush="#FF0081E4"
Geometry="F1M6.3906,13.6943L3.9156,11.2193 7.1356,8.0003 3.9156,4.7803 6.3906,2.3053 12.0836,8.0003z" />
<GeometryDrawing Brush="#FF0081E4"
Geometry="F1M6.3901,12.2803L5.3291,11.2193 8.5491,8.0003 5.3291,4.7803 6.3901,3.7193 10.6701,8.0003z" />
</DrawingGroup.Children>
</DrawingGroup>
</DrawingBrush.Drawing>
</DrawingBrush>
</Rectangle.Fill>
</Rectangle>
<Rectangle x:Key="ExpandDownIcon"
x:Shared="False"
Width="16"
Height="16">
<Rectangle.Fill>
<DrawingBrush>
<DrawingBrush.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing Brush="#00FFFFFF"
Geometry="F1M0,0L16,0 16,16 0,16z" />
<GeometryDrawing Brush="#FF0081E4"
Geometry="F1M2.3057,6.3906L4.7807,3.9156 7.9997,7.1356 11.2197,3.9156 13.6947,6.3906 7.9997,12.0836z" />
<GeometryDrawing Brush="#FF0081E4"
Geometry="F1M3.7197,6.3901L4.7807,5.3291 7.9997,8.5491 11.2197,5.3291 12.2807,6.3901 7.9997,10.6701z" />
</DrawingGroup.Children>
</DrawingGroup>
</DrawingBrush.Drawing>
</DrawingBrush>
</Rectangle.Fill>
</Rectangle>
<Rectangle x:Key="EditIcon"
x:Shared="False"
Width="16"
Height="16">
<Rectangle.Fill>
<DrawingBrush>
<DrawingBrush.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing Brush="#00FFFFFF"
Geometry="F1M16,16L0,16 0,0 16,0z" />
<GeometryDrawing Brush="#FFF6F6F6"
Geometry="F1M2.5857,-0.000199999999999534L-0.000299999999999745,2.5858 -0.000299999999999745,3.4148 1.0867,4.4998 -0.000299999999999745,5.5858 -0.000299999999999745,12.4148 2.5857,14.9998 13.4147,14.9998 16.0007,12.4148 16.0007,5.5858 13.4147,3.0008 6.4147,3.0008 3.4137,-0.000199999999999534z" />
<GeometryDrawing Brush="#FF424242"
Geometry="F1M13,4L7.414,4 8.414,5 12.586,5 14,6.414 14,11.586 12.586,13 3.414,13 2,11.586 2,6.414 2.358,6.057 2.5,5.914 1.793,5.207 1.624,5.376 1,6 1,12 3,14 13,14 15,12 15,6z" />
<GeometryDrawing Brush="#FFF0EFF1"
Geometry="F1M12.5858,5L8.4138,5 10.9998,7.586 10.9998,11 7.5858,11 2.4998,5.914 2.3578,6.057 1.9998,6.414 1.9998,11.586 3.4138,13 7.5858,13 12.5858,13 13.4998,12.086 13.6428,11.943 13.9998,11.586 13.9998,6.414z" />
<GeometryDrawing Brush="#FF00539C"
Geometry="F1M5,3L3,5 7,8.999 9,6.999z" />
<GeometryDrawing Brush="#FF00539C"
Geometry="F1M4,2L2,4 1,3 3,1z" />
<GeometryDrawing Brush="#FF00539C"
Geometry="F1M8,10L10,9.999 10,8z" />
<GeometryDrawing Brush="#FF00539C"
Geometry="F1M5,3L3,5 7,8.999 9,6.999z" />
<GeometryDrawing Brush="#FF00539C"
Geometry="F1M4,2L2,4 1,3 3,1z" />
<GeometryDrawing Brush="#FF00539C"
Geometry="F1M8,10L10,9.999 10,8z" />
</DrawingGroup.Children>
</DrawingGroup>
</DrawingBrush.Drawing>
</DrawingBrush>
</Rectangle.Fill>
</Rectangle>
<Rectangle x:Key="UnpauseIcon"
x:Shared="False"
Width="16"
Height="16">
<Rectangle.Fill>
<DrawingBrush>
<DrawingBrush.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing Brush="#00FFFFFF"
Geometry="F1M16,16L0,16 0,0 16,0z" />
<GeometryDrawing Brush="#FF0081E4"
Geometry="F1M4,2L4,14 12,8z" />
</DrawingGroup.Children>
</DrawingGroup>
</DrawingBrush.Drawing>
</DrawingBrush>
</Rectangle.Fill>
</Rectangle>
<Rectangle x:Key="PauseIcon"
x:Shared="False"
Width="16"
Height="16">
<Rectangle.Fill>
<DrawingBrush>
<DrawingBrush.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing Brush="#00f6f6f6"
Geometry="M16,0V16H0V0Z" />
<GeometryDrawing Brush="Transparent"
Geometry="M13,2V13H3V2Z" />
<GeometryDrawing Brush="#FF0081E4"
Geometry="M4,3H7v9H4ZM9,3v9h3V3Z" />
</DrawingGroup.Children>
</DrawingGroup>
</DrawingBrush.Drawing>
</DrawingBrush>
</Rectangle.Fill>
</Rectangle>
<Rectangle x:Key="ZoomOutIcon"
x:Shared="False"
Width="16"
Height="16">
<Rectangle.Fill>
<DrawingBrush>
<DrawingBrush.Drawing>
<DrawingGroup ClipGeometry="M0,0 V16 H16 V0 H0 Z">
<GeometryDrawing Geometry="F1 M16,16z M0,0z M16,16L0,16 0,0 16,0 16,16z">
<GeometryDrawing.Brush>
<SolidColorBrush Color="#FFF6F6F6"
Opacity="0" />
</GeometryDrawing.Brush>
</GeometryDrawing>
<GeometryDrawing Brush="#FF252526"
Geometry="F1 M16,16z M0,0z M8.654,11.548C7.969,11.846 7.237,12 6.5,12 3.467,12 1,9.532 1,6.5 1,3.467 3.467,1 6.5,1 9.532,1 12,3.467 12,6.5 12,7.225 11.849,7.948 11.559,8.625L16,13.066 16,13.433 13.437,16 13.107,16 8.654,11.548z" />
<GeometryDrawing Brush="#FFC5C5C5"
Geometry="F1 M16,16z M0,0z M14.769,13.25L10.338,8.819C10.75,8.14 11,7.352 11,6.5 11,4.015 8.985,2 6.5,2 4.015,2 2,4.015 2,6.5 2,8.985 4.015,11 6.5,11 7.362,11 8.16,10.745 8.844,10.325L13.271,14.75 14.769,13.25z M3,6.5C3,4.567 4.567,3 6.5,3 8.433,3 10,4.567 10,6.5 10,8.433 8.433,10 6.5,10 4.567,10 3,8.433 3,6.5z" />
<GeometryDrawing Brush="#FF2A292C"
Geometry="F1 M16,16z M0,0z M6.5,3C4.567,3 3,4.567 3,6.5 3,8.433 4.567,10 6.5,10 8.433,10 10,8.433 10,6.5 10,4.567 8.433,3 6.5,3z M4,7L4,6 9,6 9,7 4,7z" />
<GeometryDrawing Brush="#FF75BEFF"
Geometry="F1 M16,16z M0,0z M9,7L4,7 4,6 9,6 9,7z" />
</DrawingGroup>
</DrawingBrush.Drawing>
</DrawingBrush>
</Rectangle.Fill>
</Rectangle>
<Rectangle x:Key="ZoomInIcon"
x:Shared="False"
Width="16"
Height="16">
<Rectangle.Fill>
<DrawingBrush>
<DrawingBrush.Drawing>
<DrawingGroup ClipGeometry="M0,0 V16 H16 V0 H0 Z">
<GeometryDrawing Geometry="F1 M16,16z M0,0z M16,16L0,16 0,0 16,0 16,16z">
<GeometryDrawing.Brush>
<SolidColorBrush Color="#FFF6F6F6"
Opacity="0" />
</GeometryDrawing.Brush>
</GeometryDrawing>
<GeometryDrawing Brush="#FF252526"
Geometry="F1 M16,16z M0,0z M8.654,11.548C7.969,11.846 7.237,12 6.5,12 3.467,12 1,9.532 1,6.5 1,3.467 3.467,1 6.5,1 9.532,1 12,3.467 12,6.5 12,7.225 11.849,7.948 11.559,8.625L16,13.066 16,13.433 13.437,16 13.107,16 8.654,11.548z" />
<GeometryDrawing Brush="#FFC5C5C5"
Geometry="F1 M16,16z M0,0z M14.769,13.25L10.338,8.819C10.75,8.14 11,7.352 11,6.5 11,4.015 8.985,2 6.5,2 4.015,2 2,4.015 2,6.5 2,8.985 4.015,11 6.5,11 7.362,11 8.16,10.745 8.844,10.325L13.271,14.75 14.769,13.25z M3,6.5C3,4.567 4.567,3 6.5,3 8.433,3 10,4.567 10,6.5 10,8.433 8.433,10 6.5,10 4.567,10 3,8.433 3,6.5z" />
<GeometryDrawing Brush="#FF2A292C"
Geometry="F1 M16,16z M0,0z M6.5,3C4.567,3 3,4.567 3,6.5 3,8.433 4.567,10 6.5,10 8.433,10 10,8.433 10,6.5 10,4.567 8.433,3 6.5,3z M7,7L7,9 6,9 6,7 4,7 4,6 6,6 6,4 7,4 7,6 9,6 9,7 7,7z" />
<GeometryDrawing Brush="#FF75BEFF"
Geometry="F1 M16,16z M0,0z M9,6L9,7 7,7 7,9 6,9 6,7 4,7 4,6 6,6 6,4 7,4 7,6 9,6z" />
</DrawingGroup>
</DrawingBrush.Drawing>
</DrawingBrush>
</Rectangle.Fill>
</Rectangle>
<Rectangle x:Key="StopIcon"
x:Shared="False"
Width="16"
Height="16">
<Rectangle.Fill>
<DrawingBrush>
<DrawingBrush.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing Brush="#00FFFFFF"
Geometry="F1M16,16L0,16 0,0 16,0z" />
<GeometryDrawing Brush="#F38B76"
Geometry="F1M13,13L3,13 3,3 13,3z" />
<GeometryDrawing Brush="#F38B76"
Geometry="F1M12,12L4,12 4,4 12,4z" />
</DrawingGroup.Children>
</DrawingGroup>
</DrawingBrush.Drawing>
</DrawingBrush>
</Rectangle.Fill>
</Rectangle>
<Rectangle x:Key="RunIcon"
x:Shared="False"
Width="16"
Height="16">
<Rectangle.Fill>
<DrawingBrush>
<DrawingBrush.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing Brush="#00FFFFFF"
Geometry="F1M16,16L0,16 0,0 16,0z" />
<GeometryDrawing Brush="#8DD28A"
Geometry="F1M4,2L4,14 12,8z" />
</DrawingGroup.Children>
</DrawingGroup>
</DrawingBrush.Drawing>
</DrawingBrush>
</Rectangle.Fill>
</Rectangle>
<Rectangle x:Key="AddStateIcon"
x:Shared="False"
Width="16"
Height="16">
<Rectangle.Fill>
<DrawingBrush>
<DrawingBrush.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing Brush="#00FFFFFF"
Geometry="F1M16,16L0,16 0,0 16,0z" />
<GeometryDrawing Brush="#FFF6F6F6"
Geometry="F1M1.9998,-0.000199999999999534L1.9998,1.9998 -0.000199999999999978,1.9998 -0.000199999999999978,5.9998 1.9998,5.9998 1.9998,13.4148 3.5858,14.9998 5.4148,14.9998 6.9998,13.4148 6.9998,12.0008 14.4148,12.0008 15.9998,10.4138 15.9998,6.5858 14.4148,4.9998 7.9998,4.9998 7.9998,1.9998 6.0008,1.9998 6.0008,-0.000199999999999534z" />
<GeometryDrawing Brush="#FF424242"
Geometry="F1M15,7L15,10 14,11 6,11 6,13 5,14 4,14 3,13 3,8 4,8 4,13 5,13 5,10 14,10 14,7 6,7 6,6 14,6z" />
<GeometryDrawing Brush="#FFF0EFF1"
Geometry="F1M14,7L14,10 5,10 5,13 4,13 4,8 6,8 6,7z" />
<GeometryDrawing Brush="#FF388A34"
Geometry="F1M3,5L1,5 1,3 3,3 3,1 5,1 5,3 7,3 7,5 5,5 5,7 3,7z" />
</DrawingGroup.Children>
</DrawingGroup>
</DrawingBrush.Drawing>
</DrawingBrush>
</Rectangle.Fill>
</Rectangle>
<Rectangle x:Key="RenameIcon"
x:Shared="False"
Width="16"
Height="16">
<Rectangle.Fill>
<DrawingBrush>
<DrawingBrush.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing Brush="#00FFFFFF"
Geometry="F1M16,16L0,16 0,0 16,0z" />
<GeometryDrawing Brush="#FFF6F6F6"
Geometry="F1M0,0.999700000000001L0,14.0007 8,14.0007 8,13.0007 16,13.0007 16,4.0007 8,4.0007 8,0.999700000000001z" />
<GeometryDrawing Brush="#FF414141"
Geometry="F1M1,13L3,13 3,11 1,11z" />
<GeometryDrawing Brush="#FF414141"
Geometry="F1M3,11L5,11 5,4 3,4z" />
<GeometryDrawing Brush="#FF414141"
Geometry="F1M7,11L5,11 5,13 7,13z" />
<GeometryDrawing Brush="#FF414141"
Geometry="F1M1,4L3,4 3,2 1,2z" />
<GeometryDrawing Brush="#FF414141"
Geometry="F1M1,6L2,6 2,5 1,5z" />
<GeometryDrawing Brush="#FF414141"
Geometry="F1M1,8L2,8 2,7 1,7z" />
<GeometryDrawing Brush="#FF414141"
Geometry="F1M6,6L7,6 7,5 6,5z" />
<GeometryDrawing Brush="#FF414141"
Geometry="F1M5,4L7,4 7,2 5,2z" />
<GeometryDrawing Brush="#FF414141"
Geometry="F1M1,10L2,10 2,9 1,9z" />
<GeometryDrawing Brush="#FF414141"
Geometry="F1M8,6L9,6 9,5 8,5z" />
<GeometryDrawing Brush="#FF414141"
Geometry="F1M14,10L15,10 15,9 14,9z" />
<GeometryDrawing Brush="#FF414141"
Geometry="F1M14,8L15,8 15,7 14,7z" />
<GeometryDrawing Brush="#FF414141"
Geometry="F1M14,12L15,12 15,10.999 14,10.999z" />
<GeometryDrawing Brush="#FF414141"
Geometry="F1M14,6L15,6 15,5 14,5z" />
<GeometryDrawing Brush="#FF414141"
Geometry="F1M12,12L13,12 13,10.999 12,10.999z" />
<GeometryDrawing Brush="#FF414141"
Geometry="F1M12,6L13,6 13,5 12,5z" />
<GeometryDrawing Brush="#FF414141"
Geometry="F1M10,12L11,12 11,10.999 10,10.999z" />
<GeometryDrawing Brush="#FF414141"
Geometry="F1M10,6L11,6 11,5 10,5z" />
<GeometryDrawing Brush="#FF414141"
Geometry="F1M8,12L9,12 9,10.999 8,10.999z" />
</DrawingGroup.Children>
</DrawingGroup>
</DrawingBrush.Drawing>
</DrawingBrush>
</Rectangle.Fill>
</Rectangle>
<Rectangle x:Key="DeleteIcon"
x:Shared="False"
Width="16"
Height="16">
<Rectangle.Fill>
<DrawingBrush>
<DrawingBrush.Drawing>
<DrawingGroup ClipGeometry="M0,0 V511.992 H511.992 V0 H0 Z">
<DrawingGroup.Transform>
<TranslateTransform X="3.5762786865234375E-07"
Y="3.5762786865234375E-07" />
</DrawingGroup.Transform>
<GeometryDrawing Brush="#FFE76E54"
Geometry="F1 M511.992,511.992z M0,0z M415.402344,495.421875L255.996094,336.011719 96.589844,495.421875C74.492188,517.515625 38.667969,517.515625 16.570312,495.421875 -5.52343799999997,473.324219 -5.52343799999997,437.5 16.570312,415.402344L175.980469,255.996094 16.570312,96.589844C-5.52343799999997,74.492188 -5.52343799999997,38.667969 16.570312,16.570312 38.667969,-5.52343800000003 74.492188,-5.52343800000003 96.589844,16.570312L255.996094,175.980469 415.402344,16.570312C437.5,-5.52343800000003 473.324219,-5.52343800000003 495.421875,16.570312 517.515625,38.667969 517.515625,74.492188 495.421875,96.589844L336.011719,255.996094 495.421875,415.402344C517.515625,437.5 517.515625,473.324219 495.421875,495.421875 473.324219,517.515625 437.5,517.515625 415.402344,495.421875z M415.402344,495.421875" />
</DrawingGroup>
</DrawingBrush.Drawing>
</DrawingBrush>
</Rectangle.Fill>
</Rectangle>
<Rectangle x:Key="DisconnectIcon"
x:Shared="False"
Width="16"
Height="16">
<Rectangle.Fill>
<DrawingBrush>
<DrawingBrush.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing Brush="#00FFFFFF"
Geometry="F1M16,16L0,16 0,0 16,0z" />
<GeometryDrawing Brush="#FFF6F6F6"
Geometry="F1M7.0002,-0.000199999999999534L7.0002,0.6468 6.3532,-0.000199999999999534 5.5252,-0.000199999999999534 3.9702,1.5558 2.4142,-0.000199999999999534 1.5862,-0.000199999999999534 0.000200000000000422,1.5858 0.000200000000000422,2.5348 1.4952,4.0298 0.000200000000000422,5.5258 0.000200000000000422,6.4748 1.5252,7.9998 1.0002,7.9998 1.0002,15.9998 10.0002,15.9998 10.0002,13.9998 14.0002,13.9998 14.0002,7.9998 16.0002,7.9998 16.0002,-0.000199999999999534z M6.4442,4.0298L7.0002,3.4748 7.0002,4.5858z M3.9702,6.5048L5.4652,7.9998 2.4752,7.9998z M7.0002,7.4138L7.0002,7.9998 6.4142,7.9998z M10.0002,7.9998L11.0002,7.9998 11.0002,10.9998 10.0002,10.9998z" />
<GeometryDrawing Brush="#FF424242"
Geometry="F1M14,6L9,6 9,3 14,3z M8,14L3,14 3,11 8,11z M8,1L8,7 12,7 12,12 9,12 9,9 2,9 2,15 9,15 9,13 13,13 13,7 15,7 15,1z" />
<GeometryDrawing Brush="#FFEFEFF0"
Geometry="F1M9,6L14,6 14,3 9,3z M3,11L8,11 8,14 3,14z" />
<GeometryDrawing Brush="#FFA1260D"
Geometry="F1M3.9699,2.9698L1.9999,0.999799999999999 0.9399,2.0608 2.9089,4.0298 0.9399,5.9998 1.9999,7.0598 3.9699,5.0908 5.9389,7.0598 6.9999,5.9998 5.0299,4.0298 6.9999,2.0608 5.9389,0.999799999999999z" />
</DrawingGroup.Children>
</DrawingGroup>
</DrawingBrush.Drawing>
</DrawingBrush>
</Rectangle.Fill>
</Rectangle>
<Rectangle x:Key="SelectAllIcon"
x:Shared="False"
Width="16"
Height="16">
<Rectangle.Fill>
<DrawingBrush>
<DrawingBrush.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing Brush="#00FFFFFF"
Geometry="F1M16,16L0,16 0,0 16,0z" />
<GeometryDrawing Brush="#FFF6F6F6"
Geometry="F1M0,-0.000199999999999534L0,13.0008 6,13.0008 6,14.9998 7.649,14.9998 8.337,14.2448 9.115,15.9998 9.983,15.9998 12.269,14.9598 11.406,13.0008 15,13.0008 15,-0.000199999999999534z" />
<GeometryDrawing Brush="#FF414141"
Geometry="F1M8.4141,7L9.4141,8 11.0001,8 11.0001,7z" />
<GeometryDrawing Brush="#FF414141"
Geometry="F1M1,12L2,12 2,10.999 1,10.999z" />
<GeometryDrawing Brush="#FF414141"
Geometry="F1M1,2L2,2 2,1 1,1z" />
<GeometryDrawing Brush="#FF414141"
Geometry="F1M1,10L2,10 2,9 1,9z" />
<GeometryDrawing Brush="#FF414141"
Geometry="F1M1,6L2,6 2,5 1,5z" />
<GeometryDrawing Brush="#FF414141"
Geometry="F1M1,4L2,4 2,3 1,3z" />
<GeometryDrawing Brush="#FF414141"
Geometry="F1M1,8L2,8 2,7 1,7z" />
<GeometryDrawing Brush="#FF414141"
Geometry="F1M9,2L10,2 10,1 9,1z" />
<GeometryDrawing Brush="#FF414141"
Geometry="F1M7,2L8,2 8,1 7,1z" />
<GeometryDrawing Brush="#FF414141"
Geometry="F1M5,2L6,2 6,1 5,1z" />
<GeometryDrawing Brush="#FF414141"
Geometry="F1M3,2L4,2 4,1 3,1z" />
<GeometryDrawing Brush="#FF414141"
Geometry="F1M3,6L10,6 10,5 3,5z" />
<GeometryDrawing Brush="#FF414141"
Geometry="F1M13,4L14.001,4 14.001,3 13,3z" />
<GeometryDrawing Brush="#FF414141"
Geometry="F1M13,10L14.001,10 14.001,9 13,9z" />
<GeometryDrawing Brush="#FF414141"
Geometry="F1M3,12L4,12 4,10.999 3,10.999z" />
<GeometryDrawing Brush="#FF414141"
Geometry="F1M12.9996,10.9996L12.9996,11.5856 13.4146,12.0006 14.0006,12.0006 14.0006,10.9996z" />
<GeometryDrawing Brush="#FF414141"
Geometry="F1M13,6L14.001,6 14.001,5 13,5z" />
<GeometryDrawing Brush="#FF414141"
Geometry="F1M13,2L14.001,2 14.001,1 13,1z" />
<GeometryDrawing Brush="#FF414141"
Geometry="F1M13,8L14.001,8 14.001,7 13,7z" />
<GeometryDrawing Brush="#FF414141"
Geometry="F1M11,2L12,2 12,1 11,1z" />
<GeometryDrawing Brush="#FF414141"
Geometry="F1M3,8L6.001,8 6.001,7 3,7z" />
<GeometryDrawing Brush="#FF414141"
Geometry="F1M5,12L6,12 6,10.999 5,10.999z" />
<GeometryDrawing Brush="#FF414141"
Geometry="F1M3,4L12,4 12,3 3,3z" />
<GeometryDrawing Brush="#FF414141"
Geometry="F1M10.9551,14.459L9.8691,11.994 12.3871,11.994 7.0001,6.996 7.0001,14.228 8.6291,12.438 9.7661,15z" />
</DrawingGroup.Children>
</DrawingGroup>
</DrawingBrush.Drawing>
</DrawingBrush>
</Rectangle.Fill>
</Rectangle>
<Rectangle x:Key="LockIcon"
x:Shared="False"
Width="16"
Height="16">
<Rectangle.Fill>
<DrawingBrush>
<DrawingBrush.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing Brush="#00FFFFFF"
Geometry="M16,16H0V0H16Z" />
<GeometryDrawing Brush="#0A212121"
Geometry="M2.5,14.5h10v-8H2.5Z" />
<GeometryDrawing Brush="#FF212121"
Geometry="M8,12H7V9H8Z" />
<GeometryDrawing Brush="#FF212121"
Geometry="M12.5,6H11V4.536a3.5,3.5,0,0,0-7,0V6H2.5L2,6.5v8l.5.5h10l.5-.5v-8ZM5,4.536a2.5,2.5,0,0,1,5,0V6H5ZM12,14H3V7h9Z" />
</DrawingGroup.Children>
</DrawingGroup>
</DrawingBrush.Drawing>
</DrawingBrush>
</Rectangle.Fill>
</Rectangle>
<Rectangle x:Key="UnlockIcon"
x:Shared="False"
Width="16"
Height="16">
<Rectangle.Fill>
<DrawingBrush>
<DrawingBrush.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing Brush="#00FFFFFF"
Geometry="M16,16H0V0H16Z" />
<GeometryDrawing Brush="#0A212121"
Geometry="M1.5,14.5h10v-8H1.5Z" />
<GeometryDrawing Brush="#FF212121"
Geometry="M7,12H6V9H7Z" />
<GeometryDrawing Brush="#FF212121"
Geometry="M12.5,1.036A3.5,3.5,0,0,0,9,4.536V6H1.5L1,6.5v8l.5.5h10l.5-.5v-8L11.5,6H10V4.536a2.5,2.5,0,0,1,5,0V5h1V4.536A3.5,3.5,0,0,0,12.5,1.036ZM11,7v7H2V7Z" />
</DrawingGroup.Children>
</DrawingGroup>
</DrawingBrush.Drawing>
</DrawingBrush>
</Rectangle.Fill>
</Rectangle>
<PathGeometry x:Key="CloseGeometry"
x:Shared="False"
Figures="M9.15625 6.3125L6.3125 9.15625L22.15625 25L6.21875 40.96875L9.03125 43.78125L25 27.84375L40.9375 43.78125L43.78125 40.9375L27.84375 25L43.6875 9.15625L40.84375 6.3125L25 22.15625Z" />
<PathGeometry x:Key="AddGeometry"
x:Shared="False"
Figures="M15 5L15 15L5 15L5 17L15 17L15 27L17 27L17 17L27 17L27 15L17 15L17 5Z" />
</ResourceDictionary>

View File

@@ -0,0 +1,23 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Brushes.xaml" />
<ResourceDictionary Source="Controls.xaml" />
</ResourceDictionary.MergedDictionaries>
<Color x:Key="ForegroundColor">Black</Color>
<Color x:Key="DisabledForegroundColor">Gray</Color>
<Color x:Key="BackgroundColor">#D4DCF2</Color>
<Color x:Key="ContrastBackgroundColor">#CCD6ED</Color>
<Color x:Key="HighlightedBackgroundColor">#B1C5FF</Color>
<Color x:Key="BorderColor">#CBCCDF</Color>
<Color x:Key="DisabledBorderColor">Gray</Color>
<Color x:Key="HighlightedBorderColor">#7EB4EA</Color>
<Color x:Key="FocusedBorderColor">#569DE5</Color>
<Color x:Key="GridLinesColor">#7EB4EA</Color>
</ResourceDictionary>

View File

@@ -0,0 +1,23 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Brushes.xaml" />
<ResourceDictionary Source="Controls.xaml" />
</ResourceDictionary.MergedDictionaries>
<Color x:Key="ForegroundColor">White</Color>
<Color x:Key="DisabledForegroundColor">Gray</Color>
<Color x:Key="BackgroundColor">#4C3180</Color>
<Color x:Key="ContrastBackgroundColor">#451E63</Color>
<Color x:Key="HighlightedBackgroundColor">#662D91</Color>
<Color x:Key="BorderColor">#662D91</Color>
<Color x:Key="DisabledBorderColor">Gray</Color>
<Color x:Key="HighlightedBorderColor">#7EB4EA</Color>
<Color x:Key="FocusedBorderColor">#569DE5</Color>
<Color x:Key="GridLinesColor">#4C3180</Color>
</ResourceDictionary>

View File

@@ -0,0 +1,215 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
namespace Nodify.UndoRedo
{
public interface IActionsHistory : INotifyPropertyChanged
{
int MaxSize { get; set; }
bool CanUndo { get; }
bool CanRedo { get; }
bool IsEnabled { get; set; }
IAction? Current { get; }
void Undo();
void Redo();
void Clear();
/// <summary>
/// All future modifications will be merged together to create a single history item until batch is disposed.
/// </summary>
IDisposable Batch(string? label = default);
void Record(IAction action);
/// <summary>
/// All future modifications will be merged together to create a single history item until history is resumed.
/// </summary>
void Pause(string? label = default);
/// <summary>Each future modifications will create a new history item.</summary>
void Resume();
}
public interface IAction
{
string? Label { get; }
void Execute();
void Undo();
}
public static class ActionsHistoryExtensions
{
public static void Record(this IActionsHistory history, Action execute, Action unexecute, string? label = default)
=> history.Record(new DelegateAction(execute, unexecute, label));
public static void ExecuteAction(this IActionsHistory history, IAction action)
{
history.Record(action);
action.Execute();
}
}
public class ActionsHistory : IActionsHistory
{
private readonly List<IAction> _history = new List<IAction>();
private readonly List<IAction> _batchHistory = new List<IAction>();
private int _position = -1;
private bool _isApplyingOperation = false;
private string? _batchLabel;
private int _batchDepth;
private static readonly PropertyChangedEventArgs _canRedoArgs = new PropertyChangedEventArgs(nameof(CanRedo));
private static readonly PropertyChangedEventArgs _canUndoArgs = new PropertyChangedEventArgs(nameof(CanUndo));
public event PropertyChangedEventHandler? PropertyChanged;
public static readonly ActionsHistory Global = new ActionsHistory();
public bool IsBatching { get; private set; }
public int MaxSize { get; set; } = 50;
public bool CanRedo => _history.Count > 0 && _position < _history.Count - 1;
public bool CanUndo => _position > -1;
public bool IsEnabled { get; set; } = true;
public IAction? Current => CanUndo ? _history[_position] : null;
public IDisposable Batch(string? label = default)
=> new BatchOperation(label, this);
public void Record(IAction op)
{
// Prevent recording the undo or redo operation
if (_isApplyingOperation || !IsEnabled)
{
return;
}
if (IsBatching)
{
_batchHistory.Add(op);
}
else
{
AddToUndoStack(op);
}
}
private void AddToUndoStack(IAction op)
{
if (_position < _history.Count - 1)
{
_history.RemoveRange(_position + 1, _history.Count - _position - 1);
}
if (_history.Count >= MaxSize)
{
_history.RemoveAt(0);
_position--;
}
_history.Add(op);
_position++;
PropertyChanged?.Invoke(this, _canRedoArgs);
PropertyChanged?.Invoke(this, _canUndoArgs);
}
public void Undo()
{
if (IsBatching)
{
throw new InvalidOperationException($"{nameof(Undo)} is not allowed during a batch.");
}
if (CanUndo)
{
var op = _history[_position];
_isApplyingOperation = true;
op.Undo();
_isApplyingOperation = false;
_position--;
}
}
public void Redo()
{
if (IsBatching)
{
throw new InvalidOperationException($"{nameof(Redo)} is not allowed during a batch.");
}
if (CanRedo)
{
_position++;
var op = _history[_position];
_isApplyingOperation = true;
op.Execute();
_isApplyingOperation = false;
}
}
public void Clear()
{
_history.Clear();
_batchHistory.Clear();
}
public void Pause(string? label = default)
{
if (_batchDepth > 0)
{
return;
}
_batchLabel = label;
IsBatching = true;
}
public void Resume()
{
if (_batchDepth > 0)
{
return;
}
if (_batchHistory.Count > 0)
{
AddToUndoStack(new BatchAction(_batchLabel, _batchHistory));
_batchHistory.Clear();
}
_batchLabel = null;
IsBatching = false;
}
private class BatchOperation : IDisposable
{
private readonly ActionsHistory _history;
private bool _disposed;
public BatchOperation(string? label, ActionsHistory history)
{
_history = history;
_history.Pause(label);
_history._batchDepth++;
}
public void Dispose()
{
if (!_disposed)
{
_disposed = true;
_history._batchDepth--;
_history.Resume();
}
}
}
}
}

View File

@@ -0,0 +1,37 @@
using System.Collections.Generic;
using System.Linq;
namespace Nodify.UndoRedo
{
public class BatchAction : IAction
{
public BatchAction(string? label, IEnumerable<IAction> history)
{
History = history.Reverse().ToList();
Label = label;
}
public IReadOnlyList<IAction> History { get; }
public string? Label { get; }
public void Execute()
{
for (int i = History.Count - 1; i >= 0; i--)
{
History[i].Execute();
}
}
public void Undo()
{
for (int i = 0; i < History.Count; i++)
{
History[i].Undo();
}
}
public override string? ToString()
=> Label;
}
}

View File

@@ -0,0 +1,25 @@
using System;
namespace Nodify.UndoRedo
{
public class DelegateAction : IAction
{
private readonly Action _execute;
private readonly Action _undo;
public string? Label { get; }
public DelegateAction(Action apply, Action unapply, string? label)
{
_execute = apply;
_undo = unapply;
Label = label;
}
public void Execute() => _execute();
public void Undo() => _undo();
public override string? ToString()
=> Label;
}
}

View File

@@ -0,0 +1,84 @@
using System;
using System.Collections.Generic;
using System.Reflection;
namespace Nodify.UndoRedo
{
public interface IPropertyAccessor
{
object? GetValue(object instance);
void SetValue(object instance, object? value);
bool CanRead { get; }
bool CanWrite { get; }
}
public sealed class PropertyAccessor<TInstanceType, TPropertyType> : IPropertyAccessor where TInstanceType : class
{
private readonly Func<TInstanceType, TPropertyType> _getter;
private readonly Action<TInstanceType, TPropertyType> _setter;
public bool CanRead { get; }
public bool CanWrite { get; }
public PropertyAccessor(Func<TInstanceType, TPropertyType> getter, Action<TInstanceType, TPropertyType> setter)
{
_getter = getter;
_setter = setter;
CanRead = getter != null;
CanWrite = setter != null;
}
public object? GetValue(object instance)
=> _getter((TInstanceType)instance);
public void SetValue(object instance, object? value)
=> _setter((TInstanceType)instance, (TPropertyType)value!);
}
public class PropertyCache
{
private static readonly Dictionary<string, IPropertyAccessor> _properties = new Dictionary<string, IPropertyAccessor>();
public static IPropertyAccessor Get(Type type, string name)
{
string propKey = $"{type.FullName}.{name}";
if (!_properties.TryGetValue(propKey, out var result))
{
var prop = type.GetProperty(name);
result = Create(type, prop!);
_properties.Add(propKey, result);
}
return result;
}
public static IPropertyAccessor Get<T>(string name)
=> Get(typeof(T), name);
private static IPropertyAccessor Create(Type type, PropertyInfo property)
{
Delegate? getterInvocation = default;
Delegate? setterInvocation = default;
if (property.CanRead)
{
MethodInfo getMethod = property.GetGetMethod(true)!;
Type getterType = typeof(Func<,>).MakeGenericType(type, property.PropertyType);
getterInvocation = Delegate.CreateDelegate(getterType, getMethod);
}
if (property.CanWrite)
{
MethodInfo setMethod = property.GetSetMethod(true)!;
Type setterType = typeof(Action<,>).MakeGenericType(type, property.PropertyType);
setterInvocation = Delegate.CreateDelegate(setterType, setMethod);
}
Type adapterType = typeof(PropertyAccessor<,>).MakeGenericType(type, property.PropertyType);
return (IPropertyAccessor)Activator.CreateInstance(adapterType, getterInvocation, setterInvocation)!;
}
}
}

View File

@@ -0,0 +1,80 @@
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Runtime.CompilerServices;
using Expression = System.Linq.Expressions.Expression;
namespace Nodify.UndoRedo
{
[Flags]
public enum PropertyFlags
{
Disable = 0,
Enable = 1
}
public abstract class Undoable : ObservableObject
{
private readonly HashSet<string> _trackedProperties = new HashSet<string>();
public IActionsHistory History { get; }
private void RecordHistory<TPropType>(string propName, TPropType previous, TPropType current)
{
if (_trackedProperties.Contains(propName))
{
var prop = PropertyCache.Get(GetType(), propName);
History.Record(() => prop.SetValue(this, current), () => prop.SetValue(this, previous), propName);
}
}
protected void RecordProperty(string propName, PropertyFlags flags = PropertyFlags.Enable)
{
if (flags == PropertyFlags.Disable)
{
_trackedProperties.Remove(propName);
}
else if (flags.HasFlag(PropertyFlags.Enable))
{
_trackedProperties.Add(propName);
}
}
protected void RecordProperty<TType>(Expression<Func<TType, object?>> selector, PropertyFlags flags = PropertyFlags.Enable)
{
string name = GetPropertyName(selector);
RecordProperty(name, flags);
}
private static string GetPropertyName(Expression memberAccess)
=> memberAccess switch
{
LambdaExpression lambda => GetPropertyName(lambda.Body),
MemberExpression mbr => mbr.Member.Name,
UnaryExpression unary => GetPropertyName(unary.Operand),
_ => throw new Exception($"Member name could not be extracted from {memberAccess}.")
};
protected override bool SetProperty<TPropType>(ref TPropType field, TPropType value, [CallerMemberName] string propertyName = "")
{
TPropType prev = field;
if (base.SetProperty(ref field, value, propertyName))
{
RecordHistory(propertyName, prev, value);
return true;
}
return false;
}
public Undoable()
{
History = ActionsHistory.Global;
}
public Undoable(IActionsHistory history)
{
History = history;
}
}
}