Merge branch 'master' into MenuItemAttach

This commit is contained in:
NaBian authored and GitHub committed 2023-09-28 21:14:31 +08:00
commit 60b359ec20
1325 files changed
+85761 -74012

No files matched your search

@@ -1,15 +0,0 @@
using System.Collections.Generic;
using HandyControl.Data;
namespace HandyControl.Collections
{
public class DateTimeRangeComparer : IComparer<DateTimeRange>
{
public int Compare(DateTimeRange x, DateTimeRange y)
{
if (x.Start > y.Start && x.End > y.End) return 1;
if (x.Start < y.Start && x.End < y.End) return -1;
return 0;
}
}
}
@@ -1,191 +0,0 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
namespace HandyControl.Collections
{
[Serializable]
public class ManualObservableCollection<T> : Collection<T>, INotifyCollectionChanged, INotifyPropertyChanged
{
private readonly SimpleMonitor _monitor = new SimpleMonitor();
private const string CountString = "Count";
private const string IndexerName = "Item[]";
private bool _canNotify = true;
public bool CanNotify
{
get => _canNotify;
set
{
_canNotify = value;
if (value)
{
if (_oldCount != Count)
{
OnPropertyChanged(CountString);
}
OnPropertyChanged(IndexerName);
OnCollectionReset();
}
else
{
_oldCount = Count;
}
}
}
private int _oldCount;
public event NotifyCollectionChangedEventHandler CollectionChanged;
public event PropertyChangedEventHandler PropertyChanged;
public ManualObservableCollection()
{
}
// ReSharper disable once AssignNullToNotNullAttribute
public ManualObservableCollection(List<T> list) : base(list != null ? new List<T>(list.Count) : list) => CopyFrom(list);
public ManualObservableCollection(IEnumerable<T> collection)
{
if (collection == null) throw new ArgumentNullException(nameof(collection));
CopyFrom(collection);
}
private void CopyFrom(IEnumerable<T> collection)
{
var items = Items;
if (collection != null)
{
using var enumerator = collection.GetEnumerator();
while (enumerator.MoveNext())
{
items.Add(enumerator.Current);
}
}
}
public void Move(int oldIndex, int newIndex) => MoveItem(oldIndex, newIndex);
protected override void ClearItems()
{
CheckReentrancy();
base.ClearItems();
OnPropertyChanged(CountString);
OnPropertyChanged(IndexerName);
OnCollectionReset();
}
protected override void RemoveItem(int index)
{
CheckReentrancy();
var removedItem = this[index];
base.RemoveItem(index);
OnPropertyChanged(CountString);
OnPropertyChanged(IndexerName);
OnCollectionChanged(NotifyCollectionChangedAction.Remove, removedItem, index);
}
protected override void InsertItem(int index, T item)
{
CheckReentrancy();
base.InsertItem(index, item);
OnPropertyChanged(CountString);
OnPropertyChanged(IndexerName);
OnCollectionChanged(NotifyCollectionChangedAction.Add, item, index);
}
protected override void SetItem(int index, T item)
{
CheckReentrancy();
var originalItem = this[index];
base.SetItem(index, item);
OnPropertyChanged(IndexerName);
OnCollectionChanged(NotifyCollectionChangedAction.Replace, originalItem, item, index);
}
protected virtual void MoveItem(int oldIndex, int newIndex)
{
CheckReentrancy();
var removedItem = this[oldIndex];
base.RemoveItem(oldIndex);
base.InsertItem(newIndex, removedItem);
OnPropertyChanged(IndexerName);
OnCollectionChanged(NotifyCollectionChangedAction.Move, removedItem, newIndex, oldIndex);
}
protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
{
if (!CanNotify || PropertyChanged == null) return;
PropertyChanged.Invoke(this, e);
}
protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (!CanNotify || CollectionChanged == null) return;
using (BlockReentrancy())
{
// ReSharper disable once PossibleNullReferenceException
CollectionChanged(this, e);
}
}
protected IDisposable BlockReentrancy()
{
_monitor.Enter();
return _monitor;
}
protected void CheckReentrancy()
{
if (_monitor.Busy && CollectionChanged != null && CollectionChanged.GetInvocationList().Length > 1)
{
throw new InvalidOperationException("ObservableCollectionReentrancyNotAllowed");
}
}
private void OnPropertyChanged(string propertyName) =>
OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
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));
private void OnCollectionReset() =>
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
private class SimpleMonitor : IDisposable
{
public void Enter() => ++_busyCount;
public void Dispose() => --_busyCount;
public bool Busy => _busyCount > 0;
private int _busyCount;
}
}
}
@@ -0,0 +1,84 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
namespace HandyControl.Collections;
[Serializable]
public class ManualObservableCollection<T> : ObservableCollection<T>
{
private const string CountString = "Count";
private const string IndexerName = "Item[]";
private int _oldCount;
private bool _canNotify = true;
public bool CanNotify
{
get => _canNotify;
set
{
_canNotify = value;
if (value)
{
if (_oldCount != Count)
{
OnPropertyChanged(new PropertyChangedEventArgs(CountString));
}
OnPropertyChanged(new PropertyChangedEventArgs(IndexerName));
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
else
{
_oldCount = Count;
}
}
}
public ManualObservableCollection()
{
}
public ManualObservableCollection(List<T> list) : base(list != null ? new List<T>(list.Count) : list) => CopyFrom(list);
public ManualObservableCollection(IEnumerable<T> collection)
{
if (collection == null) throw new ArgumentNullException(nameof(collection));
CopyFrom(collection);
}
protected override void OnPropertyChanged(PropertyChangedEventArgs e)
{
if (!CanNotify) return;
base.OnPropertyChanged(e);
}
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (!CanNotify) return;
base.OnCollectionChanged(e);
}
private void CopyFrom(IEnumerable<T> collection)
{
var items = Items;
if (collection != null)
{
using var enumerator = collection.GetEnumerator();
while (enumerator.MoveNext())
{
items.Add(enumerator.Current);
}
}
}
}
@@ -0,0 +1,8 @@
namespace HandyControl.Collections;
public interface IPool<T>
{
T Acquire();
bool Release(T instance);
}
@@ -0,0 +1,63 @@
//adapted from https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/util/Pools.java
using System;
namespace HandyControl.Collections;
public class SimplePool<T> : IPool<T>
{
private readonly object[] _pool;
private int _poolSize;
public SimplePool(int maxPoolSize)
{
if (maxPoolSize <= 0)
{
throw new ArgumentException("The max pool size must be > 0");
}
_pool = new object[maxPoolSize];
}
public virtual T Acquire()
{
if (_poolSize > 0)
{
var lastPooledIndex = _poolSize - 1;
var instance = (T) _pool[lastPooledIndex];
_pool[lastPooledIndex] = null;
_poolSize--;
return instance;
}
return default;
}
public virtual bool Release(T instance)
{
if (IsInPool(instance))
{
throw new Exception("Already in the pool!");
}
if (_poolSize < _pool.Length)
{
_pool[_poolSize] = instance;
_poolSize++;
return true;
}
return false;
}
private bool IsInPool(T instance)
{
for (var i = 0; i < _poolSize; i++)
{
if (Equals(_pool[i], instance))
{
return true;
}
}
return false;
}
}
@@ -0,0 +1,27 @@
namespace HandyControl.Collections;
public class SynchronizedPool<T> : SimplePool<T>
{
private readonly object _lockObj = new();
public SynchronizedPool(int maxPoolSize) : base(maxPoolSize)
{
}
public override T Acquire()
{
lock (_lockObj)
{
return base.Acquire();
}
}
public override bool Release(T element)
{
lock (_lockObj)
{
return base.Release(element);
}
}
}
@@ -0,0 +1,335 @@
// adapted from https://www.c-sharpcorner.com/uploadfile/b942f9/implementing-a-double-ended-queue-or-deque-in-c-sharp/
using System;
using System.Collections;
using System.Collections.Generic;
using System.Threading;
namespace HandyControl.Collections;
[Serializable]
public class Deque<T> : IEnumerable<T>, ICollection
{
[NonSerialized]
private object _syncRoot;
private List<T> _front;
private List<T> _back;
private int _frontDeleted;
private int _backDeleted;
public int Capacity => _front.Capacity + _back.Capacity;
public int Count => _front.Count + _back.Count - _frontDeleted - _backDeleted;
public bool IsEmpty => Count == 0;
public IEnumerable<T> Reversed
{
get
{
if (_back.Count - _backDeleted > 0)
{
for (var i = _back.Count - 1; i >= _backDeleted; i--) yield return _back[i];
}
if (_front.Count - _frontDeleted > 0)
{
for (var i = _frontDeleted; i < _front.Count; i++) yield return _front[i];
}
}
}
public Deque()
{
_front = new List<T>();
_back = new List<T>();
}
public Deque(int capacity)
{
if (capacity < 0) throw new ArgumentException("Capacity cannot be negative");
var temp = capacity / 2;
var temp2 = capacity - temp;
_front = new List<T>(temp);
_back = new List<T>(temp2);
}
public Deque(IEnumerable<T> backCollection) : this(backCollection, null)
{
}
public Deque(IEnumerable<T> backCollection, IEnumerable<T> frontCollection)
{
if (backCollection == null && frontCollection == null) throw new ArgumentException("Collections cannot both be null");
_front = frontCollection != null ? new List<T>(frontCollection) : new List<T>();
_back = backCollection != null ? new List<T>(backCollection) : new List<T>();
}
public void AddFirst(T item)
{
if (_frontDeleted > 0 && _front.Count == _front.Capacity)
{
_front.RemoveRange(0, _frontDeleted);
_frontDeleted = 0;
}
_front.Add(item);
}
public void AddLast(T item)
{
if (_backDeleted > 0 && _back.Count == _back.Capacity)
{
_back.RemoveRange(0, _backDeleted);
_backDeleted = 0;
}
_back.Add(item);
}
public void AddRangeFirst(IEnumerable<T> range)
{
if (range == null) return;
foreach (var item in range) AddFirst(item);
}
public void AddRangeLast(IEnumerable<T> range)
{
if (range == null) return;
foreach (var item in range) AddLast(item);
}
public void Clear()
{
_front.Clear();
_back.Clear();
_frontDeleted = 0;
_backDeleted = 0;
}
public bool Contains(T item)
{
for (var i = _frontDeleted; i < _front.Count; i++)
{
if (Equals(_front[i], item)) return true;
}
for (var i = _backDeleted; i < _back.Count; i++)
{
if (Equals(_back[i], item)) return true;
}
return false;
}
public void CopyTo(T[] array, int index)
{
if (array == null) throw new ArgumentNullException("Array cannot be null");
if (index < 0) throw new ArgumentOutOfRangeException("Index cannot be negative");
if (array.Length < index + Count) throw new ArgumentException("Index is invalid");
var i = index;
foreach (var item in this)
{
array[i++] = item;
}
}
public IEnumerator<T> GetEnumerator()
{
if (_front.Count - _frontDeleted > 0)
{
for (var i = _front.Count - 1; i >= _frontDeleted; i--) yield return _front[i];
}
if (_back.Count - _backDeleted > 0)
{
for (var i = _backDeleted; i < _back.Count; i++) yield return _back[i];
}
}
public T PeekFirst()
{
if (_front.Count > _frontDeleted)
{
return _front[_front.Count - 1];
}
if (_back.Count > _backDeleted)
{
return _back[_backDeleted];
}
throw new InvalidOperationException("Can't peek at empty Deque");
}
public T PeekLast()
{
if (_back.Count > _backDeleted)
{
return _back[_back.Count - 1];
}
if (_front.Count > _frontDeleted)
{
return _front[_frontDeleted];
}
throw new InvalidOperationException("Can't peek at empty Deque");
}
public T PopFirst()
{
T result;
if (_front.Count > _frontDeleted)
{
result = _front[_front.Count - 1];
_front.RemoveAt(_front.Count - 1);
}
else if (_back.Count > _backDeleted)
{
result = _back[_backDeleted];
_backDeleted++;
}
else
{
throw new InvalidOperationException("Can't pop empty Deque");
}
return result;
}
public T PopLast()
{
T result;
if (_back.Count > _backDeleted)
{
result = _back[_back.Count - 1];
_back.RemoveAt(_back.Count - 1);
}
else if (_front.Count > _frontDeleted)
{
result = _front[_frontDeleted];
_frontDeleted++;
}
else
{
throw new InvalidOperationException("Can't pop empty Deque");
}
return result;
}
public void Reverse()
{
var temp = _front;
_front = _back;
_back = temp;
var temp2 = _frontDeleted;
_frontDeleted = _backDeleted;
_backDeleted = temp2;
}
public T[] ToArray()
{
if (Count == 0) return new T[0];
var result = new T[Count];
CopyTo(result, 0);
return result;
}
public void TrimExcess()
{
if (_frontDeleted > 0)
{
_front.RemoveRange(0, _frontDeleted);
_frontDeleted = 0;
}
if (_backDeleted > 0)
{
_back.RemoveRange(0, _backDeleted);
_backDeleted = 0;
}
_front.TrimExcess();
_back.TrimExcess();
}
public bool TryPeekFirst(out T item)
{
if (!IsEmpty)
{
item = PeekFirst();
return true;
}
item = default;
return false;
}
public bool TryPeekLast(out T item)
{
if (!IsEmpty)
{
item = PeekLast();
return true;
}
item = default;
return false;
}
public bool TryPopFirst(out T item)
{
if (!IsEmpty)
{
item = PopFirst();
return true;
}
item = default;
return false;
}
public bool TryPopLast(out T item)
{
if (!IsEmpty)
{
item = PopLast();
return true;
}
item = default;
return false;
}
bool ICollection.IsSynchronized => false;
object ICollection.SyncRoot
{
get
{
if (_syncRoot == null)
{
Interlocked.CompareExchange<object>(ref _syncRoot, new object(), null);
}
return _syncRoot;
}
}
void ICollection.CopyTo(Array array, int index) => CopyTo((T[]) array, index);
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
@@ -1,22 +1,21 @@
using System.Windows;
using System.Windows.Media;
namespace HandyControl.Controls
namespace HandyControl.Controls;
public class BackgroundSwitchElement
{
public class BackgroundSwitchElement
{
public static readonly DependencyProperty MouseHoverBackgroundProperty = DependencyProperty.RegisterAttached(
"MouseHoverBackground", typeof(Brush), typeof(BackgroundSwitchElement), new FrameworkPropertyMetadata(Brushes.Transparent, FrameworkPropertyMetadataOptions.Inherits));
public static readonly DependencyProperty MouseHoverBackgroundProperty = DependencyProperty.RegisterAttached(
"MouseHoverBackground", typeof(Brush), typeof(BackgroundSwitchElement), new FrameworkPropertyMetadata(Brushes.Transparent, FrameworkPropertyMetadataOptions.Inherits));
public static void SetMouseHoverBackground(DependencyObject element, Brush value) => element.SetValue(MouseHoverBackgroundProperty, value);
public static void SetMouseHoverBackground(DependencyObject element, Brush value) => element.SetValue(MouseHoverBackgroundProperty, value);
public static Brush GetMouseHoverBackground(DependencyObject element) => (Brush) element.GetValue(MouseHoverBackgroundProperty);
public static Brush GetMouseHoverBackground(DependencyObject element) => (Brush) element.GetValue(MouseHoverBackgroundProperty);
public static readonly DependencyProperty MouseDownBackgroundProperty = DependencyProperty.RegisterAttached(
"MouseDownBackground", typeof(Brush), typeof(BackgroundSwitchElement), new FrameworkPropertyMetadata(Brushes.Transparent, FrameworkPropertyMetadataOptions.Inherits));
public static readonly DependencyProperty MouseDownBackgroundProperty = DependencyProperty.RegisterAttached(
"MouseDownBackground", typeof(Brush), typeof(BackgroundSwitchElement), new FrameworkPropertyMetadata(Brushes.Transparent, FrameworkPropertyMetadataOptions.Inherits));
public static void SetMouseDownBackground(DependencyObject element, Brush value) => element.SetValue(MouseDownBackgroundProperty, value);
public static void SetMouseDownBackground(DependencyObject element, Brush value) => element.SetValue(MouseDownBackgroundProperty, value);
public static Brush GetMouseDownBackground(DependencyObject element) => (Brush) element.GetValue(MouseDownBackgroundProperty);
}
public static Brush GetMouseDownBackground(DependencyObject element) => (Brush) element.GetValue(MouseDownBackgroundProperty);
}
@@ -4,47 +4,46 @@ using System.Windows.Data;
using HandyControl.Data;
using HandyControl.Tools.Converter;
namespace HandyControl.Controls
namespace HandyControl.Controls;
public class BorderElement
{
public class BorderElement
public static readonly DependencyProperty CornerRadiusProperty = DependencyProperty.RegisterAttached(
"CornerRadius", typeof(CornerRadius), typeof(BorderElement), new FrameworkPropertyMetadata(default(CornerRadius), FrameworkPropertyMetadataOptions.Inherits));
public static void SetCornerRadius(DependencyObject element, CornerRadius value) => element.SetValue(CornerRadiusProperty, value);
public static CornerRadius GetCornerRadius(DependencyObject element) => (CornerRadius) element.GetValue(CornerRadiusProperty);
public static readonly DependencyProperty CircularProperty = DependencyProperty.RegisterAttached(
"Circular", typeof(bool), typeof(BorderElement), new PropertyMetadata(ValueBoxes.FalseBox, OnCircularChanged));
private static void OnCircularChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
public static readonly DependencyProperty CornerRadiusProperty = DependencyProperty.RegisterAttached(
"CornerRadius", typeof(CornerRadius), typeof(BorderElement), new FrameworkPropertyMetadata(default(CornerRadius), FrameworkPropertyMetadataOptions.Inherits));
public static void SetCornerRadius(DependencyObject element, CornerRadius value) => element.SetValue(CornerRadiusProperty, value);
public static CornerRadius GetCornerRadius(DependencyObject element) => (CornerRadius) element.GetValue(CornerRadiusProperty);
public static readonly DependencyProperty CircularProperty = DependencyProperty.RegisterAttached(
"Circular", typeof(bool), typeof(BorderElement), new PropertyMetadata(ValueBoxes.FalseBox, OnCircularChanged));
private static void OnCircularChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
if (d is Border border)
{
if (d is Border border)
if ((bool) e.NewValue)
{
if ((bool) e.NewValue)
var binding = new MultiBinding
{
var binding = new MultiBinding
{
Converter = new BorderCircularConverter()
};
binding.Bindings.Add(new Binding(FrameworkElement.ActualWidthProperty.Name) { Source = border });
binding.Bindings.Add(new Binding(FrameworkElement.ActualHeightProperty.Name) { Source = border });
border.SetBinding(Border.CornerRadiusProperty, binding);
}
else
{
BindingOperations.ClearBinding(border, FrameworkElement.ActualWidthProperty);
BindingOperations.ClearBinding(border, FrameworkElement.ActualHeightProperty);
BindingOperations.ClearBinding(border, Border.CornerRadiusProperty);
}
Converter = new BorderCircularConverter()
};
binding.Bindings.Add(new Binding(FrameworkElement.ActualWidthProperty.Name) { Source = border });
binding.Bindings.Add(new Binding(FrameworkElement.ActualHeightProperty.Name) { Source = border });
border.SetBinding(Border.CornerRadiusProperty, binding);
}
else
{
BindingOperations.ClearBinding(border, FrameworkElement.ActualWidthProperty);
BindingOperations.ClearBinding(border, FrameworkElement.ActualHeightProperty);
BindingOperations.ClearBinding(border, Border.CornerRadiusProperty);
}
}
public static void SetCircular(DependencyObject element, bool value)
=> element.SetValue(CircularProperty, ValueBoxes.BooleanBox(value));
public static bool GetCircular(DependencyObject element)
=> (bool) element.GetValue(CircularProperty);
}
public static void SetCircular(DependencyObject element, bool value)
=> element.SetValue(CircularProperty, ValueBoxes.BooleanBox(value));
public static bool GetCircular(DependencyObject element)
=> (bool) element.GetValue(CircularProperty);
}
@@ -1,288 +1,279 @@
using System.Linq;
using System.Collections.Specialized;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using HandyControl.Data;
namespace HandyControl.Controls
namespace HandyControl.Controls;
public class DataGridAttach
{
public class DataGridAttach
public static readonly DependencyProperty ApplyDefaultStyleProperty = DependencyProperty.RegisterAttached(
"ApplyDefaultStyle", typeof(bool), typeof(DataGridAttach), new PropertyMetadata(ValueBoxes.FalseBox, OnApplyDefaultStyleChanged));
private static void OnApplyDefaultStyleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
public static readonly DependencyProperty ApplyDefaultStyleProperty = DependencyProperty.RegisterAttached(
"ApplyDefaultStyle", typeof(bool), typeof(DataGridAttach), new PropertyMetadata(ValueBoxes.FalseBox, OnApplyDefaultStyleChanged));
private static void OnApplyDefaultStyleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
var grid = (DataGrid) d;
if ((bool) e.NewValue)
{
var grid = (DataGrid) d;
if ((bool) e.NewValue)
{
if (grid.AutoGenerateColumns)
{
grid.AutoGeneratedColumns += Grid_AutoGeneratedColumns;
return;
}
UpdateTextColumnStyles(grid);
UpdateComboBoxColumnStyles(grid);
UpdateCheckBoxColumnStyles(grid);
}
else
{
grid.AutoGeneratedColumns -= Grid_AutoGeneratedColumns;
}
grid.Columns.CollectionChanged += OnDataGridColumnsCollectionChanged;
}
else
{
grid.Columns.CollectionChanged -= OnDataGridColumnsCollectionChanged;
}
private static void Grid_AutoGeneratedColumns(object sender, System.EventArgs e)
void OnDataGridColumnsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
var grid = (DataGrid) sender;
if (e.Action is NotifyCollectionChangedAction.Remove or NotifyCollectionChangedAction.Move)
{
return;
}
UpdateTextColumnStyles(grid);
UpdateComboBoxColumnStyles(grid);
UpdateCheckBoxColumnStyles(grid);
}
}
public static void SetApplyDefaultStyle(DependencyObject element, bool value)
=> element.SetValue(ApplyDefaultStyleProperty, ValueBoxes.BooleanBox(value));
public static void SetApplyDefaultStyle(DependencyObject element, bool value)
=> element.SetValue(ApplyDefaultStyleProperty, ValueBoxes.BooleanBox(value));
public static bool GetApplyDefaultStyle(DependencyObject element)
=> (bool) element.GetValue(ApplyDefaultStyleProperty);
public static bool GetApplyDefaultStyle(DependencyObject element)
=> (bool) element.GetValue(ApplyDefaultStyleProperty);
public static readonly DependencyProperty TextColumnStyleProperty = DependencyProperty.RegisterAttached(
"TextColumnStyle", typeof(Style), typeof(DataGridAttach), new PropertyMetadata(default(Style), OnTextColumnStyleChanged));
public static readonly DependencyProperty TextColumnStyleProperty = DependencyProperty.RegisterAttached(
"TextColumnStyle", typeof(Style), typeof(DataGridAttach), new PropertyMetadata(default(Style), OnTextColumnStyleChanged));
private static void OnTextColumnStyleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
private static void OnTextColumnStyleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var grid = (DataGrid) d;
if (e.OldValue == null && e.NewValue != null)
{
var grid = (DataGrid) d;
if (e.OldValue == null && e.NewValue != null)
{
UpdateTextColumnStyles(grid);
}
UpdateTextColumnStyles(grid);
}
}
public static void SetTextColumnStyle(DependencyObject element, Style value) => element.SetValue(TextColumnStyleProperty, value);
public static void SetTextColumnStyle(DependencyObject element, Style value) => element.SetValue(TextColumnStyleProperty, value);
public static Style GetTextColumnStyle(DependencyObject element) => (Style) element.GetValue(TextColumnStyleProperty);
public static Style GetTextColumnStyle(DependencyObject element) => (Style) element.GetValue(TextColumnStyleProperty);
public static readonly DependencyProperty EditingTextColumnStyleProperty = DependencyProperty.RegisterAttached(
"EditingTextColumnStyle", typeof(Style), typeof(DataGridAttach), new PropertyMetadata(default(Style), OnTextColumnStyleChanged));
public static readonly DependencyProperty EditingTextColumnStyleProperty = DependencyProperty.RegisterAttached(
"EditingTextColumnStyle", typeof(Style), typeof(DataGridAttach), new PropertyMetadata(default(Style), OnTextColumnStyleChanged));
public static void SetEditingTextColumnStyle(DependencyObject element, Style value) => element.SetValue(EditingTextColumnStyleProperty, value);
public static void SetEditingTextColumnStyle(DependencyObject element, Style value) => element.SetValue(EditingTextColumnStyleProperty, value);
public static Style GetEditingTextColumnStyle(DependencyObject element) => (Style) element.GetValue(EditingTextColumnStyleProperty);
public static Style GetEditingTextColumnStyle(DependencyObject element) => (Style) element.GetValue(EditingTextColumnStyleProperty);
private static void UpdateTextColumnStyles(DataGrid grid)
private static void UpdateTextColumnStyles(DataGrid grid)
{
var textColumnStyle = GetTextColumnStyle(grid);
var editingTextColumnStyle = GetEditingTextColumnStyle(grid);
if (textColumnStyle != null)
{
var textColumnStyle = GetTextColumnStyle(grid);
var editingTextColumnStyle = GetEditingTextColumnStyle(grid);
if (textColumnStyle != null)
foreach (var column in grid.Columns.OfType<DataGridTextColumn>())
{
foreach (var column in grid.Columns.OfType<DataGridTextColumn>())
var elementStyle = new Style
{
var elementStyle = new Style
{
BasedOn = column.ElementStyle,
TargetType = textColumnStyle.TargetType
};
BasedOn = column.ElementStyle,
TargetType = textColumnStyle.TargetType
};
foreach (var setter in textColumnStyle.Setters.OfType<Setter>())
{
elementStyle.Setters.Add(setter);
}
column.ElementStyle = elementStyle;
}
}
if (editingTextColumnStyle != null)
{
foreach (var column in grid.Columns.OfType<DataGridTextColumn>())
foreach (var setter in textColumnStyle.Setters.OfType<Setter>())
{
var editingElementStyle = new Style
{
BasedOn = column.EditingElementStyle,
TargetType = editingTextColumnStyle.TargetType
};
foreach (var setter in editingTextColumnStyle.Setters.OfType<Setter>())
{
editingElementStyle.Setters.Add(setter);
}
column.EditingElementStyle = editingElementStyle;
elementStyle.Setters.Add(setter);
}
column.ElementStyle = elementStyle;
}
}
public static readonly DependencyProperty ComboBoxColumnStyleProperty = DependencyProperty.RegisterAttached(
"ComboBoxColumnStyle", typeof(Style), typeof(DataGridAttach), new PropertyMetadata(default(Style), OnComboBoxColumnStyleChanged));
private static void OnComboBoxColumnStyleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
if (editingTextColumnStyle != null)
{
var grid = (DataGrid) d;
if (e.OldValue == null && e.NewValue != null)
foreach (var column in grid.Columns.OfType<DataGridTextColumn>())
{
UpdateComboBoxColumnStyles(grid);
}
}
public static void SetComboBoxColumnStyle(DependencyObject element, Style value) => element.SetValue(ComboBoxColumnStyleProperty, value);
public static Style GetComboBoxColumnStyle(DependencyObject element) => (Style) element.GetValue(ComboBoxColumnStyleProperty);
public static readonly DependencyProperty EditingComboBoxColumnStyleProperty = DependencyProperty.RegisterAttached(
"EditingComboBoxColumnStyle", typeof(Style), typeof(DataGridAttach), new PropertyMetadata(default(Style), OnComboBoxColumnStyleChanged));
public static void SetEditingComboBoxColumnStyle(DependencyObject element, Style value) => element.SetValue(EditingComboBoxColumnStyleProperty, value);
public static Style GetEditingComboBoxColumnStyle(DependencyObject element) => (Style) element.GetValue(EditingComboBoxColumnStyleProperty);
private static void UpdateComboBoxColumnStyles(DataGrid grid)
{
var comboBoxColumnStyle = GetComboBoxColumnStyle(grid);
var editingComboBoxColumnStyle = GetEditingComboBoxColumnStyle(grid);
if (comboBoxColumnStyle != null)
{
foreach (var column in grid.Columns.OfType<DataGridComboBoxColumn>())
var editingElementStyle = new Style
{
var elementStyle = new Style
{
BasedOn = column.ElementStyle,
TargetType = comboBoxColumnStyle.TargetType
};
BasedOn = column.EditingElementStyle,
TargetType = editingTextColumnStyle.TargetType
};
foreach (var setter in comboBoxColumnStyle.Setters.OfType<Setter>())
{
elementStyle.Setters.Add(setter);
}
column.ElementStyle = elementStyle;
}
}
if (editingComboBoxColumnStyle != null)
{
foreach (var column in grid.Columns.OfType<DataGridComboBoxColumn>())
foreach (var setter in editingTextColumnStyle.Setters.OfType<Setter>())
{
var editingElementStyle = new Style
{
BasedOn = column.EditingElementStyle,
TargetType = editingComboBoxColumnStyle.TargetType
};
foreach (var setter in editingComboBoxColumnStyle.Setters.OfType<Setter>())
{
editingElementStyle.Setters.Add(setter);
}
column.EditingElementStyle = editingElementStyle;
editingElementStyle.Setters.Add(setter);
}
column.EditingElementStyle = editingElementStyle;
}
}
}
public static readonly DependencyProperty CheckBoxColumnStyleProperty = DependencyProperty.RegisterAttached(
"CheckBoxColumnStyle", typeof(Style), typeof(DataGridAttach), new PropertyMetadata(default(Style), OnCheckBoxColumnStyleChanged));
public static readonly DependencyProperty ComboBoxColumnStyleProperty = DependencyProperty.RegisterAttached(
"ComboBoxColumnStyle", typeof(Style), typeof(DataGridAttach), new PropertyMetadata(default(Style), OnComboBoxColumnStyleChanged));
private static void OnCheckBoxColumnStyleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
private static void OnComboBoxColumnStyleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var grid = (DataGrid) d;
if (e.OldValue == null && e.NewValue != null)
{
var grid = (DataGrid) d;
if (e.OldValue == null && e.NewValue != null)
{
UpdateCheckBoxColumnStyles(grid);
}
UpdateComboBoxColumnStyles(grid);
}
}
public static void SetCheckBoxColumnStyle(DependencyObject element, Style value) => element.SetValue(CheckBoxColumnStyleProperty, value);
public static void SetComboBoxColumnStyle(DependencyObject element, Style value) => element.SetValue(ComboBoxColumnStyleProperty, value);
public static Style GetCheckBoxColumnStyle(DependencyObject element) => (Style) element.GetValue(CheckBoxColumnStyleProperty);
public static Style GetComboBoxColumnStyle(DependencyObject element) => (Style) element.GetValue(ComboBoxColumnStyleProperty);
public static readonly DependencyProperty EditingCheckBoxColumnStyleProperty = DependencyProperty.RegisterAttached(
"EditingCheckBoxColumnStyle", typeof(Style), typeof(DataGridAttach), new PropertyMetadata(default(Style), OnCheckBoxColumnStyleChanged));
public static readonly DependencyProperty EditingComboBoxColumnStyleProperty = DependencyProperty.RegisterAttached(
"EditingComboBoxColumnStyle", typeof(Style), typeof(DataGridAttach), new PropertyMetadata(default(Style), OnComboBoxColumnStyleChanged));
public static void SetEditingCheckBoxColumnStyle(DependencyObject element, Style value) => element.SetValue(EditingCheckBoxColumnStyleProperty, value);
public static void SetEditingComboBoxColumnStyle(DependencyObject element, Style value) => element.SetValue(EditingComboBoxColumnStyleProperty, value);
public static Style GetEditingCheckBoxColumnStyle(DependencyObject element) => (Style) element.GetValue(EditingCheckBoxColumnStyleProperty);
public static Style GetEditingComboBoxColumnStyle(DependencyObject element) => (Style) element.GetValue(EditingComboBoxColumnStyleProperty);
private static void UpdateCheckBoxColumnStyles(DataGrid grid)
private static void UpdateComboBoxColumnStyles(DataGrid grid)
{
var comboBoxColumnStyle = GetComboBoxColumnStyle(grid);
var editingComboBoxColumnStyle = GetEditingComboBoxColumnStyle(grid);
if (comboBoxColumnStyle != null)
{
var checkBoxColumnStyle = GetCheckBoxColumnStyle(grid);
var editingCheckBoxColumnStyle = GetEditingCheckBoxColumnStyle(grid);
if (checkBoxColumnStyle != null)
foreach (var column in grid.Columns.OfType<DataGridComboBoxColumn>())
{
foreach (var column in grid.Columns.OfType<DataGridCheckBoxColumn>())
var elementStyle = new Style
{
var elementStyle = new Style
{
BasedOn = column.ElementStyle,
TargetType = checkBoxColumnStyle.TargetType
};
BasedOn = column.ElementStyle,
TargetType = comboBoxColumnStyle.TargetType
};
foreach (var setter in checkBoxColumnStyle.Setters.OfType<Setter>())
{
elementStyle.Setters.Add(setter);
}
column.ElementStyle = elementStyle;
}
}
if (editingCheckBoxColumnStyle != null)
{
foreach (var column in grid.Columns.OfType<DataGridCheckBoxColumn>())
foreach (var setter in comboBoxColumnStyle.Setters.OfType<Setter>())
{
var editingElementStyle = new Style
{
BasedOn = column.EditingElementStyle,
TargetType = editingCheckBoxColumnStyle.TargetType
};
foreach (var setter in editingCheckBoxColumnStyle.Setters.OfType<Setter>())
{
editingElementStyle.Setters.Add(setter);
}
column.EditingElementStyle = editingElementStyle;
elementStyle.Setters.Add(setter);
}
column.ElementStyle = elementStyle;
}
}
public static DependencyProperty ShowRowNumberProperty = DependencyProperty.RegisterAttached("ShowRowNumber",
typeof(bool), typeof(DataGridAttach),
new FrameworkPropertyMetadata(ValueBoxes.FalseBox, FrameworkPropertyMetadataOptions.Inherits, OnShowRowNumberChanged));
public static bool GetShowRowNumber(DependencyObject target) => (bool) target.GetValue(ShowRowNumberProperty);
public static void SetShowRowNumber(DependencyObject target, bool value) => target.SetValue(ShowRowNumberProperty, ValueBoxes.BooleanBox(value));
private static void OnShowRowNumberChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)
if (editingComboBoxColumnStyle != null)
{
if (!(target is DataGrid dataGrid)) return;
var show = (bool) e.NewValue;
if (show)
foreach (var column in grid.Columns.OfType<DataGridComboBoxColumn>())
{
dataGrid.LoadingRow += DataGrid_LoadingRow;
dataGrid.ItemContainerGenerator.ItemsChanged += ItemContainerGenerator_ItemsChanged;
var editingElementStyle = new Style
{
BasedOn = column.EditingElementStyle,
TargetType = editingComboBoxColumnStyle.TargetType
};
foreach (var setter in editingComboBoxColumnStyle.Setters.OfType<Setter>())
{
editingElementStyle.Setters.Add(setter);
}
column.EditingElementStyle = editingElementStyle;
}
else
}
}
public static readonly DependencyProperty CheckBoxColumnStyleProperty = DependencyProperty.RegisterAttached(
"CheckBoxColumnStyle", typeof(Style), typeof(DataGridAttach), new PropertyMetadata(default(Style), OnCheckBoxColumnStyleChanged));
private static void OnCheckBoxColumnStyleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var grid = (DataGrid) d;
if (e.OldValue == null && e.NewValue != null)
{
UpdateCheckBoxColumnStyles(grid);
}
}
public static void SetCheckBoxColumnStyle(DependencyObject element, Style value) => element.SetValue(CheckBoxColumnStyleProperty, value);
public static Style GetCheckBoxColumnStyle(DependencyObject element) => (Style) element.GetValue(CheckBoxColumnStyleProperty);
public static readonly DependencyProperty EditingCheckBoxColumnStyleProperty = DependencyProperty.RegisterAttached(
"EditingCheckBoxColumnStyle", typeof(Style), typeof(DataGridAttach), new PropertyMetadata(default(Style), OnCheckBoxColumnStyleChanged));
public static void SetEditingCheckBoxColumnStyle(DependencyObject element, Style value) => element.SetValue(EditingCheckBoxColumnStyleProperty, value);
public static Style GetEditingCheckBoxColumnStyle(DependencyObject element) => (Style) element.GetValue(EditingCheckBoxColumnStyleProperty);
private static void UpdateCheckBoxColumnStyles(DataGrid grid)
{
var checkBoxColumnStyle = GetCheckBoxColumnStyle(grid);
var editingCheckBoxColumnStyle = GetEditingCheckBoxColumnStyle(grid);
if (checkBoxColumnStyle != null)
{
foreach (var column in grid.Columns.OfType<DataGridCheckBoxColumn>())
{
dataGrid.LoadingRow -= DataGrid_LoadingRow;
dataGrid.ItemContainerGenerator.ItemsChanged -= ItemContainerGenerator_ItemsChanged;
var elementStyle = new Style
{
BasedOn = column.ElementStyle,
TargetType = checkBoxColumnStyle.TargetType
};
foreach (var setter in checkBoxColumnStyle.Setters.OfType<Setter>())
{
elementStyle.Setters.Add(setter);
}
column.ElementStyle = elementStyle;
}
UpdateItems(dataGrid.ItemContainerGenerator, dataGrid.Items.Count, show);
}
private static void ItemContainerGenerator_ItemsChanged(object sender, ItemsChangedEventArgs e)
if (editingCheckBoxColumnStyle != null)
{
if (!(sender is ItemContainerGenerator generator)) return;
UpdateItems(generator, e.ItemCount);
foreach (var column in grid.Columns.OfType<DataGridCheckBoxColumn>())
{
var editingElementStyle = new Style
{
BasedOn = column.EditingElementStyle,
TargetType = editingCheckBoxColumnStyle.TargetType
};
foreach (var setter in editingCheckBoxColumnStyle.Setters.OfType<Setter>())
{
editingElementStyle.Setters.Add(setter);
}
column.EditingElementStyle = editingElementStyle;
}
}
}
public static DependencyProperty ShowRowNumberProperty = DependencyProperty.RegisterAttached("ShowRowNumber",
typeof(bool), typeof(DataGridAttach),
new FrameworkPropertyMetadata(ValueBoxes.FalseBox, FrameworkPropertyMetadataOptions.Inherits, OnShowRowNumberChanged));
public static bool GetShowRowNumber(DependencyObject target) => (bool) target.GetValue(ShowRowNumberProperty);
public static void SetShowRowNumber(DependencyObject target, bool value) => target.SetValue(ShowRowNumberProperty, ValueBoxes.BooleanBox(value));
private static void OnShowRowNumberChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)
{
if (target is not DataGrid dataGrid) return;
var show = (bool) e.NewValue;
if (show)
{
dataGrid.LoadingRow += DataGrid_LoadingRow;
dataGrid.ItemContainerGenerator.ItemsChanged += ItemContainerGeneratorItemsChanged;
}
else
{
dataGrid.LoadingRow -= DataGrid_LoadingRow;
dataGrid.ItemContainerGenerator.ItemsChanged -= ItemContainerGeneratorItemsChanged;
}
private static void UpdateItems(ItemContainerGenerator generator, int itemsCount, bool show = true)
void ItemContainerGeneratorItemsChanged(object sender, ItemsChangedEventArgs e)
{
var generator = dataGrid.ItemContainerGenerator;
var itemsCount = dataGrid.Items.Count;
if (show)
{
for (var i = 0; i < itemsCount; i++)
@@ -306,40 +297,49 @@ namespace HandyControl.Controls
}
}
}
private static void DataGrid_LoadingRow(object sender, DataGridRowEventArgs e) => e.Row.Header = (e.Row.GetIndex() + 1).ToString();
public static readonly DependencyProperty CanUnselectAllWithBlankAreaProperty = DependencyProperty.RegisterAttached(
"CanUnselectAllWithBlankArea", typeof(bool), typeof(DataGridAttach), new PropertyMetadata(ValueBoxes.FalseBox, OnCanUnselectAllWithBlankAreaChanged));
private static void OnCanUnselectAllWithBlankAreaChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is DataGrid dataGrid)
{
if ((bool) e.NewValue)
{
dataGrid.PreviewMouseDown += DataGrid_PreviewMouseDown;
}
else
{
dataGrid.PreviewMouseDown -= DataGrid_PreviewMouseDown;
}
}
}
private static void DataGrid_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
if (sender is DataGrid dataGrid && e.OriginalSource is System.Windows.Controls.ScrollViewer)
{
dataGrid.CommitEdit();
dataGrid.UnselectAll();
}
}
public static void SetCanUnselectAllWithBlankArea(DependencyObject element, bool value)
=> element.SetValue(CanUnselectAllWithBlankAreaProperty, value);
public static bool GetCanUnselectAllWithBlankArea(DependencyObject element)
=> (bool) element.GetValue(CanUnselectAllWithBlankAreaProperty);
}
private static void DataGrid_LoadingRow(object sender, DataGridRowEventArgs e) => e.Row.Header = (e.Row.GetIndex() + 1).ToString();
public static readonly DependencyProperty CanUnselectAllWithBlankAreaProperty = DependencyProperty.RegisterAttached(
"CanUnselectAllWithBlankArea", typeof(bool), typeof(DataGridAttach), new PropertyMetadata(ValueBoxes.FalseBox, OnCanUnselectAllWithBlankAreaChanged));
private static void OnCanUnselectAllWithBlankAreaChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is DataGrid dataGrid)
{
if ((bool) e.NewValue)
{
dataGrid.PreviewMouseDown += DataGrid_PreviewMouseDown;
}
else
{
dataGrid.PreviewMouseDown -= DataGrid_PreviewMouseDown;
}
}
}
private static void DataGrid_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
if (sender is DataGrid dataGrid && e.OriginalSource is System.Windows.Controls.ScrollViewer)
{
dataGrid.CommitEdit();
dataGrid.UnselectAll();
}
}
public static void SetCanUnselectAllWithBlankArea(DependencyObject element, bool value)
=> element.SetValue(CanUnselectAllWithBlankAreaProperty, ValueBoxes.BooleanBox(value));
public static bool GetCanUnselectAllWithBlankArea(DependencyObject element)
=> (bool) element.GetValue(CanUnselectAllWithBlankAreaProperty);
public static readonly DependencyProperty ShowSelectAllButtonProperty = DependencyProperty.RegisterAttached(
"ShowSelectAllButton", typeof(bool), typeof(DataGridAttach), new PropertyMetadata(ValueBoxes.TrueBox));
public static void SetShowSelectAllButton(DependencyObject element, bool value)
=> element.SetValue(ShowSelectAllButtonProperty, ValueBoxes.BooleanBox(value));
public static bool GetShowSelectAllButton(DependencyObject element)
=> (bool) element.GetValue(ShowSelectAllButtonProperty);
}
@@ -1,28 +1,27 @@
using System.Windows;
using HandyControl.Data;
namespace HandyControl.Controls
namespace HandyControl.Controls;
public class DropDownElement
{
public class DropDownElement
{
public static readonly DependencyProperty ConsistentWidthProperty = DependencyProperty.RegisterAttached(
"ConsistentWidth", typeof(bool), typeof(DropDownElement),
new FrameworkPropertyMetadata(ValueBoxes.TrueBox, FrameworkPropertyMetadataOptions.Inherits));
public static readonly DependencyProperty ConsistentWidthProperty = DependencyProperty.RegisterAttached(
"ConsistentWidth", typeof(bool), typeof(DropDownElement),
new FrameworkPropertyMetadata(ValueBoxes.FalseBox, FrameworkPropertyMetadataOptions.Inherits));
public static void SetConsistentWidth(DependencyObject element, bool value)
=> element.SetValue(ConsistentWidthProperty, ValueBoxes.BooleanBox(value));
public static void SetConsistentWidth(DependencyObject element, bool value)
=> element.SetValue(ConsistentWidthProperty, ValueBoxes.BooleanBox(value));
public static bool GetConsistentWidth(DependencyObject element)
=> (bool) element.GetValue(ConsistentWidthProperty);
public static bool GetConsistentWidth(DependencyObject element)
=> (bool) element.GetValue(ConsistentWidthProperty);
public static readonly DependencyProperty AutoWidthProperty = DependencyProperty.RegisterAttached(
"AutoWidth", typeof(bool), typeof(DropDownElement),
new FrameworkPropertyMetadata(ValueBoxes.FalseBox, FrameworkPropertyMetadataOptions.Inherits));
public static readonly DependencyProperty AutoWidthProperty = DependencyProperty.RegisterAttached(
"AutoWidth", typeof(bool), typeof(DropDownElement),
new FrameworkPropertyMetadata(ValueBoxes.FalseBox, FrameworkPropertyMetadataOptions.Inherits));
public static void SetAutoWidth(DependencyObject element, bool value)
=> element.SetValue(AutoWidthProperty, value);
public static void SetAutoWidth(DependencyObject element, bool value)
=> element.SetValue(AutoWidthProperty, ValueBoxes.BooleanBox(value));
public static bool GetAutoWidth(DependencyObject element)
=> (bool) element.GetValue(AutoWidthProperty);
}
public static bool GetAutoWidth(DependencyObject element)
=> (bool) element.GetValue(AutoWidthProperty);
}
@@ -1,47 +1,46 @@
using System.Windows;
using HandyControl.Data;
namespace HandyControl.Controls
namespace HandyControl.Controls;
public class EdgeElement
{
public class EdgeElement
{
public static readonly DependencyProperty LeftContentProperty = DependencyProperty.RegisterAttached(
"LeftContent", typeof(object), typeof(EdgeElement), new PropertyMetadata(default(object), OnEdgeContentChanged));
public static readonly DependencyProperty LeftContentProperty = DependencyProperty.RegisterAttached(
"LeftContent", typeof(object), typeof(EdgeElement), new PropertyMetadata(default(object), OnEdgeContentChanged));
public static void SetLeftContent(DependencyObject element, object value) => element.SetValue(LeftContentProperty, value);
public static void SetLeftContent(DependencyObject element, object value) => element.SetValue(LeftContentProperty, value);
public static object GetLeftContent(DependencyObject element) => element.GetValue(LeftContentProperty);
public static object GetLeftContent(DependencyObject element) => element.GetValue(LeftContentProperty);
public static readonly DependencyProperty TopContentProperty = DependencyProperty.RegisterAttached(
"TopContent", typeof(object), typeof(EdgeElement), new PropertyMetadata(default(object), OnEdgeContentChanged));
public static readonly DependencyProperty TopContentProperty = DependencyProperty.RegisterAttached(
"TopContent", typeof(object), typeof(EdgeElement), new PropertyMetadata(default(object), OnEdgeContentChanged));
public static void SetTopContent(DependencyObject element, object value) => element.SetValue(TopContentProperty, value);
public static void SetTopContent(DependencyObject element, object value) => element.SetValue(TopContentProperty, value);
public static object GetTopContent(DependencyObject element) => element.GetValue(TopContentProperty);
public static object GetTopContent(DependencyObject element) => element.GetValue(TopContentProperty);
public static readonly DependencyProperty RightContentProperty = DependencyProperty.RegisterAttached(
"RightContent", typeof(object), typeof(EdgeElement), new PropertyMetadata(default(object), OnEdgeContentChanged));
public static readonly DependencyProperty RightContentProperty = DependencyProperty.RegisterAttached(
"RightContent", typeof(object), typeof(EdgeElement), new PropertyMetadata(default(object), OnEdgeContentChanged));
public static void SetRightContent(DependencyObject element, object value) => element.SetValue(RightContentProperty, value);
public static void SetRightContent(DependencyObject element, object value) => element.SetValue(RightContentProperty, value);
public static object GetRightContent(DependencyObject element) => element.GetValue(RightContentProperty);
public static object GetRightContent(DependencyObject element) => element.GetValue(RightContentProperty);
public static readonly DependencyProperty BottomContentProperty = DependencyProperty.RegisterAttached(
"BottomContent", typeof(object), typeof(EdgeElement), new PropertyMetadata(default(object), OnEdgeContentChanged));
public static readonly DependencyProperty BottomContentProperty = DependencyProperty.RegisterAttached(
"BottomContent", typeof(object), typeof(EdgeElement), new PropertyMetadata(default(object), OnEdgeContentChanged));
public static void SetBottomContent(DependencyObject element, object value) => element.SetValue(BottomContentProperty, value);
public static void SetBottomContent(DependencyObject element, object value) => element.SetValue(BottomContentProperty, value);
public static object GetBottomContent(DependencyObject element) => element.GetValue(BottomContentProperty);
public static object GetBottomContent(DependencyObject element) => element.GetValue(BottomContentProperty);
public static readonly DependencyProperty ShowEdgeContentProperty = DependencyProperty.RegisterAttached(
"ShowEdgeContent", typeof(bool), typeof(EdgeElement), new PropertyMetadata(ValueBoxes.FalseBox));
public static readonly DependencyProperty ShowEdgeContentProperty = DependencyProperty.RegisterAttached(
"ShowEdgeContent", typeof(bool), typeof(EdgeElement), new PropertyMetadata(ValueBoxes.FalseBox));
public static void SetShowEdgeContent(DependencyObject element, bool value) => element.SetValue(ShowEdgeContentProperty, ValueBoxes.BooleanBox(value));
public static void SetShowEdgeContent(DependencyObject element, bool value) => element.SetValue(ShowEdgeContentProperty, ValueBoxes.BooleanBox(value));
public static bool GetShowEdgeContent(DependencyObject element) => (bool) element.GetValue(ShowEdgeContentProperty);
public static bool GetShowEdgeContent(DependencyObject element) => (bool) element.GetValue(ShowEdgeContentProperty);
private static void OnEdgeContentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) =>
SetShowEdgeContent(d, !(GetLeftContent(d) == null && GetTopContent(d) == null &&
GetRightContent(d) == null && GetBottomContent(d) == null));
}
private static void OnEdgeContentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) =>
SetShowEdgeContent(d, !(GetLeftContent(d) == null && GetTopContent(d) == null &&
GetRightContent(d) == null && GetBottomContent(d) == null));
}
@@ -0,0 +1,95 @@
using System;
using System.Windows;
using System.Windows.Controls;
namespace HandyControl.Controls;
public class GridAttach
{
public static readonly DependencyProperty NameProperty = DependencyProperty.RegisterAttached(
"Name", typeof(string), typeof(GridAttach), new PropertyMetadata(default(string)));
public static void SetName(DependencyObject element, string value)
=> element.SetValue(NameProperty, value);
public static string GetName(DependencyObject element)
=> (string) element.GetValue(NameProperty);
public static readonly DependencyProperty RowNameProperty = DependencyProperty.RegisterAttached(
"RowName", typeof(string), typeof(GridAttach), new PropertyMetadata(default(string), OnRowNameChanged));
private static void OnRowNameChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is FrameworkElement frameworkElement)
{
if (e.NewValue is string rowName)
{
if (string.IsNullOrEmpty(rowName))
{
return;
}
if (frameworkElement.Parent is Grid grid)
{
for (var i = 0; i < grid.RowDefinitions.Count; i++)
{
var gridRowDefinition = grid.RowDefinitions[i];
var gridRowName = GetName(gridRowDefinition);
if (!string.IsNullOrEmpty(gridRowName) &&
gridRowName.Equals(rowName, StringComparison.Ordinal))
{
Grid.SetRow(frameworkElement, i);
return;
}
}
}
}
}
}
public static void SetRowName(DependencyObject element, string value)
=> element.SetValue(RowNameProperty, value);
public static string GetRowName(DependencyObject element)
=> (string) element.GetValue(RowNameProperty);
public static readonly DependencyProperty ColumnNameProperty = DependencyProperty.RegisterAttached(
"ColumnName", typeof(string), typeof(GridAttach), new PropertyMetadata(default(string), OnColumnNameChanged));
private static void OnColumnNameChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is FrameworkElement frameworkElement)
{
if (e.NewValue is string columnName)
{
if (string.IsNullOrEmpty(columnName))
{
return;
}
if (frameworkElement.Parent is Grid grid)
{
for (var i = 0; i < grid.ColumnDefinitions.Count; i++)
{
var gridColumnDefinition = grid.ColumnDefinitions[i];
var gridColumnName = GetName(gridColumnDefinition);
if (!string.IsNullOrEmpty(gridColumnName) &&
gridColumnName.Equals(columnName, StringComparison.Ordinal))
{
Grid.SetColumn(frameworkElement, i);
return;
}
}
}
}
}
}
public static void SetColumnName(DependencyObject element, string value)
=> element.SetValue(ColumnNameProperty, value);
public static string GetColumnName(DependencyObject element)
=> (string) element.GetValue(ColumnNameProperty);
}
@@ -0,0 +1,16 @@
using System.Windows;
using HandyControl.Data;
namespace HandyControl.Controls;
public class GridViewAttach
{
public static readonly DependencyProperty ColumnHeaderHeightProperty = DependencyProperty.RegisterAttached(
"ColumnHeaderHeight", typeof(double), typeof(GridViewAttach), new PropertyMetadata(ValueBoxes.Double0Box));
public static void SetColumnHeaderHeight(DependencyObject element, double value)
=> element.SetValue(ColumnHeaderHeightProperty, value);
public static double GetColumnHeaderHeight(DependencyObject element)
=> (double) element.GetValue(ColumnHeaderHeightProperty);
}
@@ -1,35 +1,34 @@
using System.Windows;
using System.Windows.Media;
namespace HandyControl.Controls
namespace HandyControl.Controls;
public class IconElement
{
public class IconElement
{
public static readonly DependencyProperty GeometryProperty = DependencyProperty.RegisterAttached(
"Geometry", typeof(Geometry), typeof(IconElement), new PropertyMetadata(default(Geometry)));
public static readonly DependencyProperty GeometryProperty = DependencyProperty.RegisterAttached(
"Geometry", typeof(Geometry), typeof(IconElement), new PropertyMetadata(default(Geometry)));
public static void SetGeometry(DependencyObject element, Geometry value)
=> element.SetValue(GeometryProperty, value);
public static void SetGeometry(DependencyObject element, Geometry value)
=> element.SetValue(GeometryProperty, value);
public static Geometry GetGeometry(DependencyObject element)
=> (Geometry) element.GetValue(GeometryProperty);
public static Geometry GetGeometry(DependencyObject element)
=> (Geometry) element.GetValue(GeometryProperty);
public static readonly DependencyProperty WidthProperty = DependencyProperty.RegisterAttached(
"Width", typeof(double), typeof(IconElement), new PropertyMetadata(double.NaN));
public static readonly DependencyProperty WidthProperty = DependencyProperty.RegisterAttached(
"Width", typeof(double), typeof(IconElement), new PropertyMetadata(double.NaN));
public static void SetWidth(DependencyObject element, double value)
=> element.SetValue(WidthProperty, value);
public static void SetWidth(DependencyObject element, double value)
=> element.SetValue(WidthProperty, value);
public static double GetWidth(DependencyObject element)
=> (double) element.GetValue(WidthProperty);
public static double GetWidth(DependencyObject element)
=> (double) element.GetValue(WidthProperty);
public static readonly DependencyProperty HeightProperty = DependencyProperty.RegisterAttached(
"Height", typeof(double), typeof(IconElement), new PropertyMetadata(double.NaN));
public static readonly DependencyProperty HeightProperty = DependencyProperty.RegisterAttached(
"Height", typeof(double), typeof(IconElement), new PropertyMetadata(double.NaN));
public static void SetHeight(DependencyObject element, double value)
=> element.SetValue(HeightProperty, value);
public static void SetHeight(DependencyObject element, double value)
=> element.SetValue(HeightProperty, value);
public static double GetHeight(DependencyObject element)
=> (double) element.GetValue(HeightProperty);
}
public static double GetHeight(DependencyObject element)
=> (double) element.GetValue(HeightProperty);
}
@@ -1,21 +1,20 @@
using System.Windows;
using System.Windows.Media;
namespace HandyControl.Controls
namespace HandyControl.Controls;
public class IconSwitchElement : IconElement
{
public class IconSwitchElement : IconElement
public static readonly DependencyProperty GeometrySelectedProperty = DependencyProperty.RegisterAttached(
"GeometrySelected", typeof(Geometry), typeof(IconSwitchElement), new PropertyMetadata(default(Geometry)));
public static void SetGeometrySelected(DependencyObject element, Geometry value)
{
public static readonly DependencyProperty GeometrySelectedProperty = DependencyProperty.RegisterAttached(
"GeometrySelected", typeof(Geometry), typeof(IconSwitchElement), new PropertyMetadata(default(Geometry)));
element.SetValue(GeometrySelectedProperty, value);
}
public static void SetGeometrySelected(DependencyObject element, Geometry value)
{
element.SetValue(GeometrySelectedProperty, value);
}
public static Geometry GetGeometrySelected(DependencyObject element)
{
return (Geometry) element.GetValue(GeometrySelectedProperty);
}
public static Geometry GetGeometrySelected(DependencyObject element)
{
return (Geometry) element.GetValue(GeometrySelectedProperty);
}
}
@@ -2,41 +2,40 @@
using System.Windows.Controls;
using System.Windows.Media;
namespace HandyControl.Controls
namespace HandyControl.Controls;
public class ImageAttach
{
public class ImageAttach
public static readonly DependencyProperty SourceFailedProperty = DependencyProperty.RegisterAttached(
"SourceFailed", typeof(ImageSource), typeof(ImageAttach), new FrameworkPropertyMetadata(default(ImageSource), FrameworkPropertyMetadataOptions.Inherits, OnSourceFailedChanged));
private static void OnSourceFailedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
public static readonly DependencyProperty SourceFailedProperty = DependencyProperty.RegisterAttached(
"SourceFailed", typeof(ImageSource), typeof(ImageAttach), new FrameworkPropertyMetadata(default(ImageSource), FrameworkPropertyMetadataOptions.Inherits, OnSourceFailedChanged));
private static void OnSourceFailedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
if (d is Image image)
{
if (d is Image image)
if (e.NewValue is ImageSource)
{
if (e.NewValue is ImageSource)
{
image.ImageFailed += Image_ImageFailed;
}
else
{
image.ImageFailed -= Image_ImageFailed;
}
image.ImageFailed += Image_ImageFailed;
}
}
private static void Image_ImageFailed(object sender, ExceptionRoutedEventArgs e)
{
if (sender is Image image)
else
{
image.SetCurrentValue(Image.SourceProperty, GetSourceFailed(image));
image.ImageFailed -= Image_ImageFailed;
}
}
public static void SetSourceFailed(DependencyObject element, ImageSource value)
=> element.SetValue(SourceFailedProperty, value);
public static ImageSource GetSourceFailed(DependencyObject element)
=> (ImageSource) element.GetValue(SourceFailedProperty);
}
private static void Image_ImageFailed(object sender, ExceptionRoutedEventArgs e)
{
if (sender is Image image)
{
image.SetCurrentValue(Image.SourceProperty, GetSourceFailed(image));
image.ImageFailed -= Image_ImageFailed;
}
}
public static void SetSourceFailed(DependencyObject element, ImageSource value)
=> element.SetValue(SourceFailedProperty, value);
public static ImageSource GetSourceFailed(DependencyObject element)
=> (ImageSource) element.GetValue(SourceFailedProperty);
}
@@ -1,84 +1,92 @@
using System.Windows;
using HandyControl.Data;
namespace HandyControl.Controls
namespace HandyControl.Controls;
public class InfoElement : TitleElement
{
public class InfoElement : TitleElement
{
/// <summary>
/// 占位符
/// </summary>
public static readonly DependencyProperty PlaceholderProperty = DependencyProperty.RegisterAttached(
"Placeholder", typeof(string), typeof(InfoElement), new FrameworkPropertyMetadata(default(string), FrameworkPropertyMetadataOptions.Inherits));
/// <summary>
/// 占位符
/// </summary>
public static readonly DependencyProperty PlaceholderProperty = DependencyProperty.RegisterAttached(
"Placeholder", typeof(string), typeof(InfoElement), new FrameworkPropertyMetadata(default(string), FrameworkPropertyMetadataOptions.Inherits));
public static void SetPlaceholder(DependencyObject element, string value) => element.SetValue(PlaceholderProperty, value);
public static void SetPlaceholder(DependencyObject element, string value) => element.SetValue(PlaceholderProperty, value);
public static string GetPlaceholder(DependencyObject element) => (string) element.GetValue(PlaceholderProperty);
public static string GetPlaceholder(DependencyObject element) => (string) element.GetValue(PlaceholderProperty);
/// <summary>
/// 是否必填
/// </summary>
public static readonly DependencyProperty NecessaryProperty = DependencyProperty.RegisterAttached(
"Necessary", typeof(bool), typeof(InfoElement), new FrameworkPropertyMetadata(ValueBoxes.FalseBox, FrameworkPropertyMetadataOptions.Inherits));
/// <summary>
/// 是否必填
/// </summary>
public static readonly DependencyProperty NecessaryProperty = DependencyProperty.RegisterAttached(
"Necessary", typeof(bool), typeof(InfoElement), new FrameworkPropertyMetadata(ValueBoxes.FalseBox, FrameworkPropertyMetadataOptions.Inherits));
public static void SetNecessary(DependencyObject element, bool value) => element.SetValue(NecessaryProperty, ValueBoxes.BooleanBox(value));
public static void SetNecessary(DependencyObject element, bool value) => element.SetValue(NecessaryProperty, ValueBoxes.BooleanBox(value));
public static bool GetNecessary(DependencyObject element) => (bool) element.GetValue(NecessaryProperty);
public static bool GetNecessary(DependencyObject element) => (bool) element.GetValue(NecessaryProperty);
/// <summary>
/// 标记
/// </summary>
public static readonly DependencyProperty SymbolProperty = DependencyProperty.RegisterAttached(
"Symbol", typeof(string), typeof(InfoElement), new FrameworkPropertyMetadata(default(string), FrameworkPropertyMetadataOptions.Inherits));
/// <summary>
/// 标记
/// </summary>
public static readonly DependencyProperty SymbolProperty = DependencyProperty.RegisterAttached(
"Symbol", typeof(string), typeof(InfoElement), new FrameworkPropertyMetadata(default(string), FrameworkPropertyMetadataOptions.Inherits));
public static void SetSymbol(DependencyObject element, string value) => element.SetValue(SymbolProperty, value);
public static void SetSymbol(DependencyObject element, string value) => element.SetValue(SymbolProperty, value);
public static string GetSymbol(DependencyObject element) => (string) element.GetValue(SymbolProperty);
public static string GetSymbol(DependencyObject element) => (string) element.GetValue(SymbolProperty);
/// <summary>
/// 内容高度
/// </summary>
public static readonly DependencyProperty ContentHeightProperty = DependencyProperty.RegisterAttached(
"ContentHeight", typeof(double), typeof(InfoElement), new FrameworkPropertyMetadata(30.0, FrameworkPropertyMetadataOptions.Inherits));
/// <summary>
/// 内容高度
/// </summary>
public static readonly DependencyProperty ContentHeightProperty = DependencyProperty.RegisterAttached(
"ContentHeight", typeof(double), typeof(InfoElement), new FrameworkPropertyMetadata(28.0, FrameworkPropertyMetadataOptions.Inherits));
public static void SetContentHeight(DependencyObject element, double value) => element.SetValue(ContentHeightProperty, value);
public static void SetContentHeight(DependencyObject element, double value) => element.SetValue(ContentHeightProperty, value);
public static double GetContentHeight(DependencyObject element) => (double) element.GetValue(ContentHeightProperty);
public static double GetContentHeight(DependencyObject element) => (double) element.GetValue(ContentHeightProperty);
/// <summary>
/// 最小内容高度
/// </summary>
public static readonly DependencyProperty MinContentHeightProperty = DependencyProperty.RegisterAttached(
"MinContentHeight", typeof(double), typeof(InfoElement), new PropertyMetadata(30.0));
/// <summary>
/// 最小内容高度
/// </summary>
public static readonly DependencyProperty MinContentHeightProperty = DependencyProperty.RegisterAttached(
"MinContentHeight", typeof(double), typeof(InfoElement), new PropertyMetadata(28.0));
public static void SetMinContentHeight(DependencyObject element, double value)
=> element.SetValue(MinContentHeightProperty, value);
public static void SetMinContentHeight(DependencyObject element, double value)
=> element.SetValue(MinContentHeightProperty, value);
public static double GetMinContentHeight(DependencyObject element)
=> (double) element.GetValue(MinContentHeightProperty);
public static double GetMinContentHeight(DependencyObject element)
=> (double) element.GetValue(MinContentHeightProperty);
/// <summary>
/// 最大内容高度
/// </summary>
public static readonly DependencyProperty MaxContentHeightProperty = DependencyProperty.RegisterAttached(
"MaxContentHeight", typeof(double), typeof(InfoElement), new PropertyMetadata(double.PositiveInfinity));
/// <summary>
/// 最大内容高度
/// </summary>
public static readonly DependencyProperty MaxContentHeightProperty = DependencyProperty.RegisterAttached(
"MaxContentHeight", typeof(double), typeof(InfoElement), new PropertyMetadata(double.PositiveInfinity));
public static void SetMaxContentHeight(DependencyObject element, double value)
=> element.SetValue(MaxContentHeightProperty, value);
public static void SetMaxContentHeight(DependencyObject element, double value)
=> element.SetValue(MaxContentHeightProperty, value);
public static double GetMaxContentHeight(DependencyObject element)
=> (double) element.GetValue(MaxContentHeightProperty);
public static double GetMaxContentHeight(DependencyObject element)
=> (double) element.GetValue(MaxContentHeightProperty);
/// <summary>
/// 正则表达式
/// </summary>
public static readonly DependencyProperty RegexPatternProperty = DependencyProperty.RegisterAttached(
"RegexPattern", typeof(string), typeof(InfoElement), new PropertyMetadata(default(string)));
/// <summary>
/// 正则表达式
/// </summary>
public static readonly DependencyProperty RegexPatternProperty = DependencyProperty.RegisterAttached(
"RegexPattern", typeof(string), typeof(InfoElement), new PropertyMetadata(default(string)));
public static void SetRegexPattern(DependencyObject element, string value)
=> element.SetValue(RegexPatternProperty, value);
public static void SetRegexPattern(DependencyObject element, string value)
=> element.SetValue(RegexPatternProperty, value);
public static string GetRegexPattern(DependencyObject element)
=> (string) element.GetValue(RegexPatternProperty);
}
public static string GetRegexPattern(DependencyObject element)
=> (string) element.GetValue(RegexPatternProperty);
public static readonly DependencyProperty ShowClearButtonProperty = DependencyProperty.RegisterAttached(
"ShowClearButton", typeof(bool), typeof(InfoElement), new PropertyMetadata(ValueBoxes.FalseBox));
public static void SetShowClearButton(DependencyObject element, bool value)
=> element.SetValue(ShowClearButtonProperty, ValueBoxes.BooleanBox(value));
public static bool GetShowClearButton(DependencyObject element)
=> (bool) element.GetValue(ShowClearButtonProperty);
}
@@ -0,0 +1,66 @@
using System.Collections;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using HandyControl.Data;
namespace HandyControl.Controls;
public class ListBoxAttach
{
public static readonly DependencyProperty SelectedItemsProperty = DependencyProperty.RegisterAttached(
"SelectedItems", typeof(IList), typeof(ListBoxAttach),
new FrameworkPropertyMetadata(default(IList), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
OnSelectedItemsChanged));
public static void SetSelectedItems(DependencyObject element, IList value)
=> element.SetValue(SelectedItemsProperty, value);
public static IList GetSelectedItems(DependencyObject element)
=> (IList) element.GetValue(SelectedItemsProperty);
internal static readonly DependencyProperty InternalActionProperty = DependencyProperty.RegisterAttached(
"InternalAction", typeof(bool), typeof(ListBoxAttach), new PropertyMetadata(ValueBoxes.FalseBox));
internal static void SetInternalAction(DependencyObject element, bool value)
=> element.SetValue(InternalActionProperty, ValueBoxes.BooleanBox(value));
internal static bool GetInternalAction(DependencyObject element)
=> (bool) element.GetValue(InternalActionProperty);
private static void OnSelectedItemsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is not ListBox listBox)
{
return;
}
if (GetInternalAction(listBox))
{
return;
}
listBox.SelectionChanged -= OnListBoxSelectionChanged;
listBox.SelectedItems.Clear();
if (e.NewValue is IList selectedItems)
{
foreach (object selectedItem in selectedItems)
{
listBox.SelectedItems.Add(selectedItem);
}
}
listBox.SelectionChanged += OnListBoxSelectionChanged;
}
private static void OnListBoxSelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (sender is ListBox listBox)
{
SetInternalAction(listBox, true);
SetSelectedItems(listBox, listBox.SelectedItems.Cast<object>().ToArray());
SetInternalAction(listBox, false);
}
}
}
@@ -0,0 +1,34 @@
using System.Windows;
using HandyControl.Data;
namespace HandyControl.Controls;
public class MenuAttach
{
public static readonly DependencyProperty PopupVerticalOffsetProperty = DependencyProperty.RegisterAttached(
"PopupVerticalOffset", typeof(double), typeof(MenuAttach), new PropertyMetadata(ValueBoxes.Double0Box));
public static void SetPopupVerticalOffset(DependencyObject element, double value)
=> element.SetValue(PopupVerticalOffsetProperty, value);
public static double GetPopupVerticalOffset(DependencyObject element)
=> (double) element.GetValue(PopupVerticalOffsetProperty);
public static readonly DependencyProperty PopupHorizontalOffsetProperty = DependencyProperty.RegisterAttached(
"PopupHorizontalOffset", typeof(double), typeof(MenuAttach), new PropertyMetadata(ValueBoxes.Double0Box));
public static void SetPopupHorizontalOffset(DependencyObject element, double value)
=> element.SetValue(PopupHorizontalOffsetProperty, value);
public static double GetPopupHorizontalOffset(DependencyObject element)
=> (double) element.GetValue(PopupHorizontalOffsetProperty);
public static readonly DependencyProperty ItemPaddingProperty = DependencyProperty.RegisterAttached(
"ItemPadding", typeof(Thickness), typeof(MenuAttach), new PropertyMetadata(default(Thickness)));
public static void SetItemPadding(DependencyObject element, Thickness value)
=> element.SetValue(ItemPaddingProperty, value);
public static Thickness GetItemPadding(DependencyObject element)
=> (Thickness) element.GetValue(ItemPaddingProperty);
}
@@ -3,108 +3,102 @@ using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using HandyControl.Tools;
using HandyControl.Tools.Interop;
namespace HandyControl.Controls
namespace HandyControl.Controls;
public class MenuTopLineAttach
{
public class MenuTopLineAttach
public static readonly DependencyProperty PopupProperty = DependencyProperty.RegisterAttached(
"Popup", typeof(Popup), typeof(MenuTopLineAttach), new PropertyMetadata(default(Popup), OnPopupChanged));
private static void OnPopupChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
public static readonly DependencyProperty PopupProperty = DependencyProperty.RegisterAttached(
"Popup", typeof(Popup), typeof(MenuTopLineAttach), new PropertyMetadata(default(Popup), OnPopupChanged));
var topLine = (FrameworkElement) d;
private static void OnPopupChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
if (e.NewValue is Popup { TemplatedParent: MenuItem menuItem })
{
var topLine = (FrameworkElement) d;
if (e.NewValue is Popup popup)
{
if (popup.TemplatedParent is MenuItem menuItem)
{
SetTopLine(menuItem, topLine);
menuItem.Loaded += MenuItem_Loaded;
}
}
SetTopLine(menuItem, topLine);
menuItem.Loaded += MenuItem_Loaded;
}
private static void MenuItem_Loaded(object sender, RoutedEventArgs e)
{
var menuItem = (FrameworkElement) sender;
menuItem.Unloaded += MenuItem_Unloaded;
var topLine = GetTopLine(menuItem);
var popup = GetPopup(topLine);
if (popup != null)
{
popup.Opened += Popup_Opened;
}
}
private static void MenuItem_Unloaded(object sender, RoutedEventArgs e)
{
var menuItem = (FrameworkElement) sender;
menuItem.Unloaded -= MenuItem_Unloaded;
var topLine = GetTopLine(menuItem);
var popup = GetPopup(topLine);
if (popup != null)
{
popup.Opened -= Popup_Opened;
}
}
private static void Popup_Opened(object sender, EventArgs e)
{
var popup = (Popup) sender;
if (popup.TemplatedParent is MenuItem menuItem)
{
var topLine = GetTopLine(menuItem);
if (topLine == null) return;
topLine.HorizontalAlignment = HorizontalAlignment.Left;
topLine.Width = menuItem.ActualWidth;
topLine.Margin = new Thickness();
var positionLeftTop = menuItem.PointToScreen(new Point());
var positionRightBottom = menuItem.PointToScreen(new Point(menuItem.ActualWidth, menuItem.ActualHeight));
ScreenHelper.FindMonitorRectsFromPoint(InteropMethods.GetCursorPos(), out _, out var workAreaRect);
var panel = VisualHelper.GetParent<Panel>(topLine);
if (positionLeftTop.X < 0)
{
topLine.Margin = new Thickness(positionLeftTop.X - panel.Margin.Left, 0, 0, 0);
}
else if (positionLeftTop.X + panel.ActualWidth > workAreaRect.Right)
{
var overflowWidth = positionRightBottom.X - workAreaRect.Right;
if (overflowWidth > 0)
{
topLine.Width -= overflowWidth + panel.Margin.Right;
}
topLine.HorizontalAlignment = HorizontalAlignment.Left;
topLine.Margin = new Thickness(positionLeftTop.X + panel.ActualWidth - workAreaRect.Right + panel.Margin.Right, 0, 0, 0);
}
if (positionRightBottom.Y > workAreaRect.Bottom)
{
topLine.Width = 0;
topLine.HorizontalAlignment = HorizontalAlignment.Stretch;
topLine.Margin = new Thickness();
}
}
}
public static void SetPopup(DependencyObject element, Popup value)
=> element.SetValue(PopupProperty, value);
public static Popup GetPopup(DependencyObject element)
=> (Popup) element.GetValue(PopupProperty);
internal static readonly DependencyProperty TopLineProperty = DependencyProperty.RegisterAttached(
"TopLine", typeof(FrameworkElement), typeof(MenuTopLineAttach), new PropertyMetadata(default(FrameworkElement)));
internal static void SetTopLine(DependencyObject element, FrameworkElement value)
=> element.SetValue(TopLineProperty, value);
internal static FrameworkElement GetTopLine(DependencyObject element)
=> (FrameworkElement) element.GetValue(TopLineProperty);
}
private static void MenuItem_Loaded(object sender, RoutedEventArgs e)
{
var menuItem = (FrameworkElement) sender;
menuItem.Unloaded += MenuItem_Unloaded;
var topLine = GetTopLine(menuItem);
var popup = GetPopup(topLine);
if (popup != null)
{
popup.Opened += Popup_Opened;
}
}
private static void MenuItem_Unloaded(object sender, RoutedEventArgs e)
{
var menuItem = (FrameworkElement) sender;
menuItem.Unloaded -= MenuItem_Unloaded;
var topLine = GetTopLine(menuItem);
var popup = GetPopup(topLine);
if (popup != null)
{
popup.Opened -= Popup_Opened;
}
}
private static void Popup_Opened(object sender, EventArgs e)
{
var popup = (Popup) sender;
if (popup.TemplatedParent is MenuItem menuItem)
{
var topLine = GetTopLine(menuItem);
if (topLine == null) return;
topLine.HorizontalAlignment = HorizontalAlignment.Left;
topLine.Width = menuItem.ActualWidth;
topLine.Margin = new Thickness();
var positionLeftTop = menuItem.PointToScreen(new Point());
var positionRightBottom = menuItem.PointToScreen(new Point(menuItem.ActualWidth, menuItem.ActualHeight));
ScreenHelper.FindMonitorRectsFromPoint(positionLeftTop, out _, out var workAreaRect);
var panel = VisualHelper.GetParent<Panel>(topLine);
if (positionLeftTop.X < 0)
{
topLine.Margin = new Thickness(positionLeftTop.X - panel.Margin.Left, 0, 0, 0);
}
else if (positionLeftTop.X + panel.ActualWidth > workAreaRect.Right)
{
var overflowWidth = positionRightBottom.X - workAreaRect.Right;
if (overflowWidth > 0)
{
topLine.Width -= overflowWidth + panel.Margin.Right;
}
topLine.HorizontalAlignment = HorizontalAlignment.Left;
topLine.Margin = new Thickness(positionLeftTop.X + panel.ActualWidth - workAreaRect.Right + panel.Margin.Right, 0, 0, 0);
}
if (positionRightBottom.Y > workAreaRect.Bottom)
{
topLine.Width = 0;
topLine.HorizontalAlignment = HorizontalAlignment.Stretch;
topLine.Margin = new Thickness();
}
}
}
public static void SetPopup(DependencyObject element, Popup value)
=> element.SetValue(PopupProperty, value);
public static Popup GetPopup(DependencyObject element)
=> (Popup) element.GetValue(PopupProperty);
internal static readonly DependencyProperty TopLineProperty = DependencyProperty.RegisterAttached(
"TopLine", typeof(FrameworkElement), typeof(MenuTopLineAttach), new PropertyMetadata(default(FrameworkElement)));
internal static void SetTopLine(DependencyObject element, FrameworkElement value)
=> element.SetValue(TopLineProperty, value);
internal static FrameworkElement GetTopLine(DependencyObject element)
=> (FrameworkElement) element.GetValue(TopLineProperty);
}
@@ -2,43 +2,42 @@
using System.Windows.Controls;
using HandyControl.Interactivity;
namespace HandyControl.Controls
namespace HandyControl.Controls;
public class PanelElement
{
public class PanelElement
public static readonly DependencyProperty FluidMoveBehaviorProperty = DependencyProperty.RegisterAttached(
"FluidMoveBehavior", typeof(FluidMoveBehavior), typeof(PanelElement), new PropertyMetadata(default(FluidMoveBehavior), OnFluidMoveBehaviorChanged));
private static void OnFluidMoveBehaviorChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
public static readonly DependencyProperty FluidMoveBehaviorProperty = DependencyProperty.RegisterAttached(
"FluidMoveBehavior", typeof(FluidMoveBehavior), typeof(PanelElement), new PropertyMetadata(default(FluidMoveBehavior), OnFluidMoveBehaviorChanged));
private static void OnFluidMoveBehaviorChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
if (d is Panel panel)
{
if (d is Panel panel)
{
var collection = Interaction.GetBehaviors(panel);
var oldBehavior = GetTempFluidMoveBehavior(panel);
collection.ItemRemoved(oldBehavior);
panel.SetCurrentValue(TempFluidMoveBehaviorProperty, DependencyProperty.UnsetValue);
var collection = Interaction.GetBehaviors(panel);
var oldBehavior = GetTempFluidMoveBehavior(panel);
collection.ItemRemoved(oldBehavior);
panel.SetCurrentValue(TempFluidMoveBehaviorProperty, DependencyProperty.UnsetValue);
if (e.NewValue is FluidMoveBehavior behavior)
{
collection.ItemAdded(behavior);
SetTempFluidMoveBehavior(panel, behavior);
}
if (e.NewValue is FluidMoveBehavior behavior)
{
collection.ItemAdded(behavior);
SetTempFluidMoveBehavior(panel, behavior);
}
}
public static void SetFluidMoveBehavior(DependencyObject element, FluidMoveBehavior value)
=> element.SetValue(FluidMoveBehaviorProperty, value);
public static FluidMoveBehavior GetFluidMoveBehavior(DependencyObject element)
=> (FluidMoveBehavior) element.GetValue(FluidMoveBehaviorProperty);
private static readonly DependencyProperty TempFluidMoveBehaviorProperty = DependencyProperty.RegisterAttached(
"TempFluidMoveBehavior", typeof(FluidMoveBehavior), typeof(PanelElement), new PropertyMetadata(default(FluidMoveBehavior)));
private static void SetTempFluidMoveBehavior(DependencyObject element, FluidMoveBehavior value)
=> element.SetValue(TempFluidMoveBehaviorProperty, value);
private static FluidMoveBehavior GetTempFluidMoveBehavior(DependencyObject element)
=> (FluidMoveBehavior) element.GetValue(TempFluidMoveBehaviorProperty);
}
public static void SetFluidMoveBehavior(DependencyObject element, FluidMoveBehavior value)
=> element.SetValue(FluidMoveBehaviorProperty, value);
public static FluidMoveBehavior GetFluidMoveBehavior(DependencyObject element)
=> (FluidMoveBehavior) element.GetValue(FluidMoveBehaviorProperty);
private static readonly DependencyProperty TempFluidMoveBehaviorProperty = DependencyProperty.RegisterAttached(
"TempFluidMoveBehavior", typeof(FluidMoveBehavior), typeof(PanelElement), new PropertyMetadata(default(FluidMoveBehavior)));
private static void SetTempFluidMoveBehavior(DependencyObject element, FluidMoveBehavior value)
=> element.SetValue(TempFluidMoveBehaviorProperty, value);
private static FluidMoveBehavior GetTempFluidMoveBehavior(DependencyObject element)
=> (FluidMoveBehavior) element.GetValue(TempFluidMoveBehaviorProperty);
}
@@ -1,54 +1,53 @@
using System.Windows;
using HandyControl.Data;
namespace HandyControl.Controls
namespace HandyControl.Controls;
public class PasswordBoxAttach
{
public class PasswordBoxAttach
/// <summary>
/// 密码长度
/// </summary>
public static readonly DependencyProperty PasswordLengthProperty = DependencyProperty.RegisterAttached(
"PasswordLength", typeof(int), typeof(PasswordBoxAttach), new PropertyMetadata(ValueBoxes.Int0Box));
public static void SetPasswordLength(DependencyObject element, int value) => element.SetValue(PasswordLengthProperty, value);
public static int GetPasswordLength(DependencyObject element) => (int) element.GetValue(PasswordLengthProperty);
/// <summary>
/// 是否监测
/// </summary>
public static readonly DependencyProperty IsMonitoringProperty = DependencyProperty.RegisterAttached(
"IsMonitoring", typeof(bool), typeof(PasswordBoxAttach), new FrameworkPropertyMetadata(ValueBoxes.FalseBox, FrameworkPropertyMetadataOptions.Inherits, OnIsMonitoringChanged));
private static void OnIsMonitoringChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
/// <summary>
/// 密码长度
/// </summary>
public static readonly DependencyProperty PasswordLengthProperty = DependencyProperty.RegisterAttached(
"PasswordLength", typeof(int), typeof(PasswordBoxAttach), new PropertyMetadata(ValueBoxes.Int0Box));
public static void SetPasswordLength(DependencyObject element, int value) => element.SetValue(PasswordLengthProperty, value);
public static int GetPasswordLength(DependencyObject element) => (int) element.GetValue(PasswordLengthProperty);
/// <summary>
/// 是否监测
/// </summary>
public static readonly DependencyProperty IsMonitoringProperty = DependencyProperty.RegisterAttached(
"IsMonitoring", typeof(bool), typeof(PasswordBoxAttach), new FrameworkPropertyMetadata(ValueBoxes.FalseBox, FrameworkPropertyMetadataOptions.Inherits, OnIsMonitoringChanged));
private static void OnIsMonitoringChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
if (d is System.Windows.Controls.PasswordBox passwordBox)
{
if (d is System.Windows.Controls.PasswordBox passwordBox)
if (e.NewValue is bool boolValue)
{
if (e.NewValue is bool boolValue)
if (boolValue)
{
if (boolValue)
{
passwordBox.PasswordChanged += PasswordChanged;
}
else
{
passwordBox.PasswordChanged -= PasswordChanged;
}
passwordBox.PasswordChanged += PasswordChanged;
}
else
{
passwordBox.PasswordChanged -= PasswordChanged;
}
}
}
}
public static void SetIsMonitoring(DependencyObject element, bool value) => element.SetValue(IsMonitoringProperty, ValueBoxes.BooleanBox(value));
public static void SetIsMonitoring(DependencyObject element, bool value) => element.SetValue(IsMonitoringProperty, ValueBoxes.BooleanBox(value));
public static bool GetIsMonitoring(DependencyObject element) => (bool) element.GetValue(IsMonitoringProperty);
public static bool GetIsMonitoring(DependencyObject element) => (bool) element.GetValue(IsMonitoringProperty);
private static void PasswordChanged(object sender, RoutedEventArgs e)
private static void PasswordChanged(object sender, RoutedEventArgs e)
{
if (sender is System.Windows.Controls.PasswordBox passwordBox)
{
if (sender is System.Windows.Controls.PasswordBox passwordBox)
{
SetPasswordLength(passwordBox, passwordBox.Password.Length);
}
SetPasswordLength(passwordBox, passwordBox.Password.Length);
}
}
}
@@ -4,42 +4,41 @@ using System.Windows.Shapes;
using HandyControl.Data;
using HandyControl.Tools.Converter;
namespace HandyControl.Controls
{
public class RectangleAttach
{
public static readonly DependencyProperty CircularProperty = DependencyProperty.RegisterAttached(
"Circular", typeof(bool), typeof(RectangleAttach), new PropertyMetadata(ValueBoxes.FalseBox, OnCircularChanged));
namespace HandyControl.Controls;
private static void OnCircularChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
public class RectangleAttach
{
public static readonly DependencyProperty CircularProperty = DependencyProperty.RegisterAttached(
"Circular", typeof(bool), typeof(RectangleAttach), new PropertyMetadata(ValueBoxes.FalseBox, OnCircularChanged));
private static void OnCircularChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is Rectangle rectangle)
{
if (d is Rectangle rectangle)
if ((bool) e.NewValue)
{
if ((bool) e.NewValue)
var binding = new MultiBinding
{
var binding = new MultiBinding
{
Converter = new RectangleCircularConverter()
};
binding.Bindings.Add(new Binding(FrameworkElement.ActualWidthProperty.Name) { Source = rectangle });
binding.Bindings.Add(new Binding(FrameworkElement.ActualHeightProperty.Name) { Source = rectangle });
rectangle.SetBinding(Rectangle.RadiusXProperty, binding);
rectangle.SetBinding(Rectangle.RadiusYProperty, binding);
}
else
{
BindingOperations.ClearBinding(rectangle, FrameworkElement.ActualWidthProperty);
BindingOperations.ClearBinding(rectangle, FrameworkElement.ActualHeightProperty);
BindingOperations.ClearBinding(rectangle, Rectangle.RadiusXProperty);
BindingOperations.ClearBinding(rectangle, Rectangle.RadiusYProperty);
}
Converter = new RectangleCircularConverter()
};
binding.Bindings.Add(new Binding(FrameworkElement.ActualWidthProperty.Name) { Source = rectangle });
binding.Bindings.Add(new Binding(FrameworkElement.ActualHeightProperty.Name) { Source = rectangle });
rectangle.SetBinding(Rectangle.RadiusXProperty, binding);
rectangle.SetBinding(Rectangle.RadiusYProperty, binding);
}
else
{
BindingOperations.ClearBinding(rectangle, FrameworkElement.ActualWidthProperty);
BindingOperations.ClearBinding(rectangle, FrameworkElement.ActualHeightProperty);
BindingOperations.ClearBinding(rectangle, Rectangle.RadiusXProperty);
BindingOperations.ClearBinding(rectangle, Rectangle.RadiusYProperty);
}
}
public static void SetCircular(DependencyObject element, bool value)
=> element.SetValue(CircularProperty, ValueBoxes.BooleanBox(value));
public static bool GetCircular(DependencyObject element)
=> (bool) element.GetValue(CircularProperty);
}
public static void SetCircular(DependencyObject element, bool value)
=> element.SetValue(CircularProperty, ValueBoxes.BooleanBox(value));
public static bool GetCircular(DependencyObject element)
=> (bool) element.GetValue(CircularProperty);
}
@@ -0,0 +1,100 @@
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using HandyControl.Data;
using HandyControl.Tools;
namespace HandyControl.Controls;
public class ScrollViewerAttach
{
public static readonly DependencyProperty AutoHideProperty = DependencyProperty.RegisterAttached(
"AutoHide", typeof(bool), typeof(ScrollViewerAttach), new FrameworkPropertyMetadata(ValueBoxes.TrueBox, FrameworkPropertyMetadataOptions.Inherits));
public static void SetAutoHide(DependencyObject element, bool value)
=> element.SetValue(AutoHideProperty, ValueBoxes.BooleanBox(value));
public static bool GetAutoHide(DependencyObject element)
=> (bool) element.GetValue(AutoHideProperty);
public static readonly DependencyProperty OrientationProperty = DependencyProperty.RegisterAttached(
"Orientation", typeof(Orientation), typeof(ScrollViewerAttach), new FrameworkPropertyMetadata(ValueBoxes.VerticalBox, FrameworkPropertyMetadataOptions.Inherits, OnOrientationChanged));
private static void OnOrientationChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is ScrollViewer)
{
return;
}
if (d is System.Windows.Controls.ScrollViewer scrollViewer)
{
if ((Orientation) e.NewValue == Orientation.Horizontal)
{
scrollViewer.PreviewMouseWheel += ScrollViewerPreviewMouseWheel;
}
else
{
scrollViewer.PreviewMouseWheel -= ScrollViewerPreviewMouseWheel;
}
}
void ScrollViewerPreviewMouseWheel(object sender, MouseWheelEventArgs args)
{
var scrollViewerNative = (System.Windows.Controls.ScrollViewer) sender;
scrollViewerNative.ScrollToHorizontalOffset(Math.Min(Math.Max(0, scrollViewerNative.HorizontalOffset - args.Delta), scrollViewerNative.ScrollableWidth));
args.Handled = true;
}
}
public static void SetOrientation(DependencyObject element, Orientation value)
=> element.SetValue(OrientationProperty, ValueBoxes.OrientationBox(value));
public static Orientation GetOrientation(DependencyObject element)
=> (Orientation) element.GetValue(OrientationProperty);
public static readonly DependencyProperty IsDisabledProperty = DependencyProperty.RegisterAttached(
"IsDisabled", typeof(bool), typeof(ScrollViewerAttach), new PropertyMetadata(ValueBoxes.FalseBox, OnIsDisabledChanged));
private static void OnIsDisabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is UIElement element)
{
if ((bool) e.NewValue)
{
element.PreviewMouseWheel += ScrollViewerPreviewMouseWheel;
}
else
{
element.PreviewMouseWheel -= ScrollViewerPreviewMouseWheel;
}
}
void ScrollViewerPreviewMouseWheel(object sender, MouseWheelEventArgs args)
{
if (args.Handled)
{
return;
}
args.Handled = true;
if (VisualHelper.GetParent<System.Windows.Controls.ScrollViewer>((UIElement) sender) is { } scrollViewer)
{
scrollViewer.RaiseEvent(new MouseWheelEventArgs(args.MouseDevice, args.Timestamp, args.Delta)
{
RoutedEvent = UIElement.MouseWheelEvent,
Source = sender
});
}
}
}
public static void SetIsDisabled(DependencyObject element, bool value)
=> element.SetValue(IsDisabledProperty, ValueBoxes.BooleanBox(value));
public static bool GetIsDisabled(DependencyObject element)
=> (bool) element.GetValue(IsDisabledProperty);
}
@@ -1,28 +1,27 @@
using System.Windows;
using HandyControl.Data;
namespace HandyControl.Controls
namespace HandyControl.Controls;
public class StatusSwitchElement
{
public class StatusSwitchElement
{
/// <summary>
/// 选中时展示的元素
/// </summary>
public static readonly DependencyProperty CheckedElementProperty = DependencyProperty.RegisterAttached(
"CheckedElement", typeof(object), typeof(StatusSwitchElement), new PropertyMetadata(default(object)));
/// <summary>
/// 选中时展示的元素
/// </summary>
public static readonly DependencyProperty CheckedElementProperty = DependencyProperty.RegisterAttached(
"CheckedElement", typeof(object), typeof(StatusSwitchElement), new PropertyMetadata(default(object)));
public static void SetCheckedElement(DependencyObject element, object value) => element.SetValue(CheckedElementProperty, value);
public static void SetCheckedElement(DependencyObject element, object value) => element.SetValue(CheckedElementProperty, value);
public static object GetCheckedElement(DependencyObject element) => element.GetValue(CheckedElementProperty);
public static object GetCheckedElement(DependencyObject element) => element.GetValue(CheckedElementProperty);
/// <summary>
/// 是否隐藏元素
/// </summary>
public static readonly DependencyProperty HideUncheckedElementProperty = DependencyProperty.RegisterAttached(
"HideUncheckedElement", typeof(bool), typeof(StatusSwitchElement), new PropertyMetadata(ValueBoxes.FalseBox));
/// <summary>
/// 是否隐藏元素
/// </summary>
public static readonly DependencyProperty HideUncheckedElementProperty = DependencyProperty.RegisterAttached(
"HideUncheckedElement", typeof(bool), typeof(StatusSwitchElement), new PropertyMetadata(ValueBoxes.FalseBox));
public static void SetHideUncheckedElement(DependencyObject element, bool value) => element.SetValue(HideUncheckedElementProperty, ValueBoxes.BooleanBox(value));
public static void SetHideUncheckedElement(DependencyObject element, bool value) => element.SetValue(HideUncheckedElementProperty, ValueBoxes.BooleanBox(value));
public static bool GetHideUncheckedElement(DependencyObject element) => (bool) element.GetValue(HideUncheckedElementProperty);
}
public static bool GetHideUncheckedElement(DependencyObject element) => (bool) element.GetValue(HideUncheckedElementProperty);
}
@@ -1,53 +1,70 @@
using System.Windows;
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using HandyControl.Data;
using HandyControl.Tools.Helper;
namespace HandyControl.Controls
namespace HandyControl.Controls;
public class TextBlockAttach
{
public class TextBlockAttach
public static readonly DependencyProperty AutoTooltipProperty = DependencyProperty.RegisterAttached(
"AutoTooltip", typeof(bool), typeof(TextBlockAttach), new PropertyMetadata(ValueBoxes.FalseBox, OnAutoTooltipChanged));
private static void OnAutoTooltipChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
public static readonly DependencyProperty AutoTooltipProperty = DependencyProperty.RegisterAttached(
"AutoTooltip", typeof(bool), typeof(TextBlockAttach), new PropertyMetadata(ValueBoxes.FalseBox, OnAutoTooltipChanged));
private static void OnAutoTooltipChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
if (d is TextBlock ctl)
{
if (d is TextBlock ctl)
if ((bool) e.NewValue)
{
if ((bool) e.NewValue)
{
ctl.TextTrimming = TextTrimming.WordEllipsis;
UpdateTooltip(ctl);
ctl.SizeChanged += TextBlock_SizeChanged;
}
else
{
ctl.ClearValue(TextBlock.TextTrimmingProperty);
ctl.SizeChanged -= TextBlock_SizeChanged;
}
UpdateTooltip(ctl);
ctl.SizeChanged += TextBlock_SizeChanged;
}
}
public static void SetAutoTooltip(DependencyObject element, bool value)
=> element.SetValue(AutoTooltipProperty, ValueBoxes.BooleanBox(value));
public static bool GetAutoTooltip(DependencyObject element)
=> (bool) element.GetValue(AutoTooltipProperty);
private static void TextBlock_SizeChanged(object sender, SizeChangedEventArgs e)
{
if (sender is TextBlock textBlock)
else
{
UpdateTooltip(textBlock);
ctl.SizeChanged -= TextBlock_SizeChanged;
}
}
}
private static void UpdateTooltip(TextBlock textBlock)
public static void SetAutoTooltip(DependencyObject element, bool value)
=> element.SetValue(AutoTooltipProperty, ValueBoxes.BooleanBox(value));
public static bool GetAutoTooltip(DependencyObject element)
=> (bool) element.GetValue(AutoTooltipProperty);
private static void TextBlock_SizeChanged(object sender, SizeChangedEventArgs e)
{
if (sender is TextBlock textBlock)
{
textBlock.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
var width = textBlock.DesiredSize.Width - textBlock.Margin.Left - textBlock.Margin.Right;
UpdateTooltip(textBlock);
}
}
ToolTipService.SetToolTip(textBlock,
textBlock.RenderSize.Width > width || textBlock.ActualWidth < width ? textBlock.Text : null);
private static void UpdateTooltip(TextBlock textBlock)
{
textBlock.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
var width = textBlock.DesiredSize.Width - textBlock.Margin.Left - textBlock.Margin.Right;
// the code Math.Abs(CalcTextWidth(textBlock) - width) > 1 is not elegant end even bugly,
// while it does solve the problem of Tooltip failure in some cases
if (textBlock.RenderSize.Width > width || textBlock.ActualWidth < width || Math.Abs(CalcTextWidth(textBlock) - width) > 1)
{
ToolTipService.SetToolTip(textBlock, textBlock.Text);
}
else
{
ToolTipService.SetToolTip(textBlock, null);
}
}
private static double CalcTextWidth(TextBlock textBlock)
{
var formattedText = TextHelper.CreateFormattedText(textBlock.Text, textBlock.FlowDirection,
new Typeface(textBlock.FontFamily, textBlock.FontStyle, textBlock.FontWeight, textBlock.FontStretch),
textBlock.FontSize);
return formattedText.WidthIncludingTrailingWhitespace;
}
}
@@ -1,31 +1,30 @@
using System.Windows;
using HandyControl.Data;
namespace HandyControl.Controls
namespace HandyControl.Controls;
public class TipElement
{
public class TipElement
{
public static readonly DependencyProperty VisibilityProperty = DependencyProperty.RegisterAttached(
"Visibility", typeof(Visibility), typeof(TipElement), new PropertyMetadata(Visibility.Collapsed));
public static readonly DependencyProperty VisibilityProperty = DependencyProperty.RegisterAttached(
"Visibility", typeof(Visibility), typeof(TipElement), new PropertyMetadata(Visibility.Collapsed));
public static void SetVisibility(DependencyObject element, Visibility value) => element.SetValue(VisibilityProperty, value);
public static void SetVisibility(DependencyObject element, Visibility value) => element.SetValue(VisibilityProperty, value);
public static Visibility GetVisibility(DependencyObject element) => (Visibility) element.GetValue(VisibilityProperty);
public static Visibility GetVisibility(DependencyObject element) => (Visibility) element.GetValue(VisibilityProperty);
public static readonly DependencyProperty PlacementProperty = DependencyProperty.RegisterAttached(
"Placement", typeof(PlacementType), typeof(TipElement), new PropertyMetadata(default(PlacementType)));
public static readonly DependencyProperty PlacementProperty = DependencyProperty.RegisterAttached(
"Placement", typeof(PlacementType), typeof(TipElement), new PropertyMetadata(default(PlacementType)));
public static void SetPlacement(DependencyObject element, PlacementType value) => element.SetValue(PlacementProperty, value);
public static void SetPlacement(DependencyObject element, PlacementType value) => element.SetValue(PlacementProperty, value);
public static PlacementType GetPlacement(DependencyObject element) => (PlacementType) element.GetValue(PlacementProperty);
public static PlacementType GetPlacement(DependencyObject element) => (PlacementType) element.GetValue(PlacementProperty);
public static readonly DependencyProperty StringFormatProperty = DependencyProperty.RegisterAttached(
"StringFormat", typeof(string), typeof(TipElement), new PropertyMetadata("#0.0"));
public static readonly DependencyProperty StringFormatProperty = DependencyProperty.RegisterAttached(
"StringFormat", typeof(string), typeof(TipElement), new PropertyMetadata("#0.0"));
public static void SetStringFormat(DependencyObject element, string value)
=> element.SetValue(StringFormatProperty, value);
public static void SetStringFormat(DependencyObject element, string value)
=> element.SetValue(StringFormatProperty, value);
public static string GetStringFormat(DependencyObject element)
=> (string) element.GetValue(StringFormatProperty);
}
public static string GetStringFormat(DependencyObject element)
=> (string) element.GetValue(StringFormatProperty);
}
@@ -2,78 +2,116 @@
using System.Windows.Media;
using HandyControl.Data;
namespace HandyControl.Controls
namespace HandyControl.Controls;
public class TitleElement
{
public class TitleElement
{
public static readonly DependencyProperty TitleProperty = DependencyProperty.RegisterAttached(
"Title", typeof(string), typeof(TitleElement), new PropertyMetadata(default(string)));
public static readonly DependencyProperty TitleProperty = DependencyProperty.RegisterAttached(
"Title", typeof(string), typeof(TitleElement), new PropertyMetadata(default(string)));
public static void SetTitle(DependencyObject element, string value)
=> element.SetValue(TitleProperty, value);
public static void SetTitle(DependencyObject element, string value)
=> element.SetValue(TitleProperty, value);
public static string GetTitle(DependencyObject element)
=> (string) element.GetValue(TitleProperty);
public static string GetTitle(DependencyObject element)
=> (string) element.GetValue(TitleProperty);
public static readonly DependencyProperty BackgroundProperty = DependencyProperty.RegisterAttached(
"Background", typeof(Brush), typeof(TitleElement), new FrameworkPropertyMetadata(default(Brush), FrameworkPropertyMetadataOptions.Inherits));
public static readonly DependencyProperty BackgroundProperty = DependencyProperty.RegisterAttached(
"Background", typeof(Brush), typeof(TitleElement), new FrameworkPropertyMetadata(default(Brush), FrameworkPropertyMetadataOptions.Inherits));
public static void SetBackground(DependencyObject element, Brush value)
=> element.SetValue(BackgroundProperty, value);
public static void SetBackground(DependencyObject element, Brush value)
=> element.SetValue(BackgroundProperty, value);
public static Brush GetBackground(DependencyObject element)
=> (Brush) element.GetValue(BackgroundProperty);
public static Brush GetBackground(DependencyObject element)
=> (Brush) element.GetValue(BackgroundProperty);
public static readonly DependencyProperty ForegroundProperty = DependencyProperty.RegisterAttached(
"Foreground", typeof(Brush), typeof(TitleElement), new FrameworkPropertyMetadata(default(Brush), FrameworkPropertyMetadataOptions.Inherits));
public static readonly DependencyProperty ForegroundProperty = DependencyProperty.RegisterAttached(
"Foreground", typeof(Brush), typeof(TitleElement), new FrameworkPropertyMetadata(default(Brush), FrameworkPropertyMetadataOptions.Inherits));
public static void SetForeground(DependencyObject element, Brush value)
=> element.SetValue(ForegroundProperty, value);
public static void SetForeground(DependencyObject element, Brush value)
=> element.SetValue(ForegroundProperty, value);
public static Brush GetForeground(DependencyObject element)
=> (Brush) element.GetValue(ForegroundProperty);
public static Brush GetForeground(DependencyObject element)
=> (Brush) element.GetValue(ForegroundProperty);
public static readonly DependencyProperty BorderBrushProperty = DependencyProperty.RegisterAttached(
"BorderBrush", typeof(Brush), typeof(TitleElement), new FrameworkPropertyMetadata(default(Brush), FrameworkPropertyMetadataOptions.Inherits));
public static readonly DependencyProperty BorderBrushProperty = DependencyProperty.RegisterAttached(
"BorderBrush", typeof(Brush), typeof(TitleElement), new FrameworkPropertyMetadata(default(Brush), FrameworkPropertyMetadataOptions.Inherits));
public static void SetBorderBrush(DependencyObject element, Brush value)
=> element.SetValue(BorderBrushProperty, value);
public static void SetBorderBrush(DependencyObject element, Brush value)
=> element.SetValue(BorderBrushProperty, value);
public static Brush GetBorderBrush(DependencyObject element)
=> (Brush) element.GetValue(BorderBrushProperty);
public static Brush GetBorderBrush(DependencyObject element)
=> (Brush) element.GetValue(BorderBrushProperty);
public static readonly DependencyProperty TitlePlacementProperty = DependencyProperty.RegisterAttached(
"TitlePlacement", typeof(TitlePlacementType), typeof(TitleElement), new FrameworkPropertyMetadata(TitlePlacementType.Top, FrameworkPropertyMetadataOptions.Inherits));
public static readonly DependencyProperty TitlePlacementProperty = DependencyProperty.RegisterAttached(
"TitlePlacement", typeof(TitlePlacementType), typeof(TitleElement), new FrameworkPropertyMetadata(TitlePlacementType.Top, FrameworkPropertyMetadataOptions.Inherits));
public static void SetTitlePlacement(DependencyObject element, TitlePlacementType value)
=> element.SetValue(TitlePlacementProperty, value);
public static void SetTitlePlacement(DependencyObject element, TitlePlacementType value)
=> element.SetValue(TitlePlacementProperty, value);
public static TitlePlacementType GetTitlePlacement(DependencyObject element)
=> (TitlePlacementType) element.GetValue(TitlePlacementProperty);
public static TitlePlacementType GetTitlePlacement(DependencyObject element)
=> (TitlePlacementType) element.GetValue(TitlePlacementProperty);
public static readonly DependencyProperty TitleWidthProperty = DependencyProperty.RegisterAttached(
"TitleWidth", typeof(GridLength), typeof(TitleElement), new FrameworkPropertyMetadata(GridLength.Auto, FrameworkPropertyMetadataOptions.Inherits));
public static readonly DependencyProperty TitleWidthProperty = DependencyProperty.RegisterAttached(
"TitleWidth", typeof(GridLength), typeof(TitleElement), new FrameworkPropertyMetadata(GridLength.Auto, FrameworkPropertyMetadataOptions.Inherits));
public static void SetTitleWidth(DependencyObject element, GridLength value) => element.SetValue(TitleWidthProperty, value);
public static void SetTitleWidth(DependencyObject element, GridLength value) => element.SetValue(TitleWidthProperty, value);
public static GridLength GetTitleWidth(DependencyObject element) => (GridLength) element.GetValue(TitleWidthProperty);
public static GridLength GetTitleWidth(DependencyObject element) => (GridLength) element.GetValue(TitleWidthProperty);
public static readonly DependencyProperty HorizontalAlignmentProperty = DependencyProperty.RegisterAttached(
"HorizontalAlignment", typeof(HorizontalAlignment), typeof(TitleElement), new FrameworkPropertyMetadata(default(HorizontalAlignment), FrameworkPropertyMetadataOptions.Inherits));
public static readonly DependencyProperty HorizontalAlignmentProperty = DependencyProperty.RegisterAttached(
"HorizontalAlignment", typeof(HorizontalAlignment), typeof(TitleElement), new FrameworkPropertyMetadata(default(HorizontalAlignment), FrameworkPropertyMetadataOptions.Inherits));
public static void SetHorizontalAlignment(DependencyObject element, HorizontalAlignment value)
=> element.SetValue(HorizontalAlignmentProperty, value);
public static void SetHorizontalAlignment(DependencyObject element, HorizontalAlignment value)
=> element.SetValue(HorizontalAlignmentProperty, value);
public static HorizontalAlignment GetHorizontalAlignment(DependencyObject element)
=> (HorizontalAlignment) element.GetValue(HorizontalAlignmentProperty);
public static HorizontalAlignment GetHorizontalAlignment(DependencyObject element)
=> (HorizontalAlignment) element.GetValue(HorizontalAlignmentProperty);
public static readonly DependencyProperty VerticalAlignmentProperty = DependencyProperty.RegisterAttached(
"VerticalAlignment", typeof(VerticalAlignment), typeof(TitleElement), new FrameworkPropertyMetadata(default(VerticalAlignment), FrameworkPropertyMetadataOptions.Inherits));
public static readonly DependencyProperty VerticalAlignmentProperty = DependencyProperty.RegisterAttached(
"VerticalAlignment", typeof(VerticalAlignment), typeof(TitleElement), new FrameworkPropertyMetadata(default(VerticalAlignment), FrameworkPropertyMetadataOptions.Inherits));
public static void SetVerticalAlignment(DependencyObject element, VerticalAlignment value)
=> element.SetValue(VerticalAlignmentProperty, value);
public static void SetVerticalAlignment(DependencyObject element, VerticalAlignment value)
=> element.SetValue(VerticalAlignmentProperty, value);
public static VerticalAlignment GetVerticalAlignment(DependencyObject element)
=> (VerticalAlignment) element.GetValue(VerticalAlignmentProperty);
}
public static VerticalAlignment GetVerticalAlignment(DependencyObject element)
=> (VerticalAlignment) element.GetValue(VerticalAlignmentProperty);
public static readonly DependencyProperty MarginOnTheLeftProperty = DependencyProperty.RegisterAttached(
"MarginOnTheLeft", typeof(Thickness), typeof(TitleElement), new FrameworkPropertyMetadata(default(Thickness), FrameworkPropertyMetadataOptions.Inherits));
public static void SetMarginOnTheLeft(DependencyObject element, Thickness value)
=> element.SetValue(MarginOnTheLeftProperty, value);
public static Thickness GetMarginOnTheLeft(DependencyObject element)
=> (Thickness) element.GetValue(MarginOnTheLeftProperty);
public static readonly DependencyProperty MarginOnTheTopProperty = DependencyProperty.RegisterAttached(
"MarginOnTheTop", typeof(Thickness), typeof(TitleElement), new FrameworkPropertyMetadata(default(Thickness), FrameworkPropertyMetadataOptions.Inherits));
public static void SetMarginOnTheTop(DependencyObject element, Thickness value)
=> element.SetValue(MarginOnTheTopProperty, value);
public static Thickness GetMarginOnTheTop(DependencyObject element)
=> (Thickness) element.GetValue(MarginOnTheTopProperty);
public static readonly DependencyProperty PaddingProperty = DependencyProperty.RegisterAttached(
"Padding", typeof(Thickness), typeof(TitleElement), new FrameworkPropertyMetadata(default(Thickness), FrameworkPropertyMetadataOptions.Inherits));
public static void SetPadding(DependencyObject element, Thickness value) => element.SetValue(PaddingProperty, value);
public static Thickness GetPadding(DependencyObject element) => (Thickness) element.GetValue(PaddingProperty);
public static readonly DependencyProperty MinHeightProperty =
DependencyProperty.RegisterAttached("MinHeight", typeof(double), typeof(TitleElement), new PropertyMetadata(ValueBoxes.Double0Box));
public static double GetMinHeight(DependencyObject obj) => (double) obj.GetValue(MinHeightProperty);
public static void SetMinHeight(DependencyObject obj, double value) => obj.SetValue(MinHeightProperty, value);
public static readonly DependencyProperty MinWidthProperty =
DependencyProperty.RegisterAttached("MinWidth", typeof(double), typeof(TitleElement), new PropertyMetadata(ValueBoxes.Double0Box));
public static double GetMinWidth(DependencyObject obj) => (double) obj.GetValue(MinWidthProperty);
public static void SetMinWidth(DependencyObject obj, double value) => obj.SetValue(MinWidthProperty, value);
}
@@ -0,0 +1,14 @@
using System.Windows;
using HandyControl.Data;
namespace HandyControl.Controls;
public class ToggleButtonAttach
{
public static readonly DependencyProperty ShowLabelProperty = DependencyProperty.RegisterAttached(
"ShowLabel", typeof(bool), typeof(ToggleButtonAttach), new FrameworkPropertyMetadata(ValueBoxes.FalseBox, FrameworkPropertyMetadataOptions.Inherits));
public static void SetShowLabel(DependencyObject element, bool value) => element.SetValue(ShowLabelProperty, ValueBoxes.BooleanBox(value));
public static bool GetShowLabel(DependencyObject element) => (bool) element.GetValue(ShowLabelProperty);
}
@@ -1,53 +1,52 @@
using System.Windows;
using System.Windows.Media;
namespace HandyControl.Controls
namespace HandyControl.Controls;
public class VisualElement
{
public class VisualElement
{
public static readonly DependencyProperty HighlightBrushProperty = DependencyProperty.RegisterAttached(
"HighlightBrush", typeof(Brush), typeof(VisualElement), new FrameworkPropertyMetadata(default(Brush), FrameworkPropertyMetadataOptions.Inherits));
public static readonly DependencyProperty HighlightBrushProperty = DependencyProperty.RegisterAttached(
"HighlightBrush", typeof(Brush), typeof(VisualElement), new FrameworkPropertyMetadata(default(Brush), FrameworkPropertyMetadataOptions.Inherits));
public static void SetHighlightBrush(DependencyObject element, Brush value)
=> element.SetValue(HighlightBrushProperty, value);
public static void SetHighlightBrush(DependencyObject element, Brush value)
=> element.SetValue(HighlightBrushProperty, value);
public static Brush GetHighlightBrush(DependencyObject element)
=> (Brush) element.GetValue(HighlightBrushProperty);
public static Brush GetHighlightBrush(DependencyObject element)
=> (Brush) element.GetValue(HighlightBrushProperty);
public static readonly DependencyProperty HighlightBackgroundProperty = DependencyProperty.RegisterAttached(
"HighlightBackground", typeof(Brush), typeof(VisualElement), new FrameworkPropertyMetadata(default(Brush), FrameworkPropertyMetadataOptions.Inherits));
public static readonly DependencyProperty HighlightBackgroundProperty = DependencyProperty.RegisterAttached(
"HighlightBackground", typeof(Brush), typeof(VisualElement), new FrameworkPropertyMetadata(default(Brush), FrameworkPropertyMetadataOptions.Inherits));
public static void SetHighlightBackground(DependencyObject element, Brush value)
=> element.SetValue(HighlightBackgroundProperty, value);
public static void SetHighlightBackground(DependencyObject element, Brush value)
=> element.SetValue(HighlightBackgroundProperty, value);
public static Brush GetHighlightBackground(DependencyObject element)
=> (Brush) element.GetValue(HighlightBackgroundProperty);
public static Brush GetHighlightBackground(DependencyObject element)
=> (Brush) element.GetValue(HighlightBackgroundProperty);
public static readonly DependencyProperty HighlightBorderBrushProperty = DependencyProperty.RegisterAttached(
"HighlightBorderBrush", typeof(Brush), typeof(VisualElement), new FrameworkPropertyMetadata(default(Brush), FrameworkPropertyMetadataOptions.Inherits));
public static readonly DependencyProperty HighlightBorderBrushProperty = DependencyProperty.RegisterAttached(
"HighlightBorderBrush", typeof(Brush), typeof(VisualElement), new FrameworkPropertyMetadata(default(Brush), FrameworkPropertyMetadataOptions.Inherits));
public static void SetHighlightBorderBrush(DependencyObject element, Brush value)
=> element.SetValue(HighlightBorderBrushProperty, value);
public static void SetHighlightBorderBrush(DependencyObject element, Brush value)
=> element.SetValue(HighlightBorderBrushProperty, value);
public static Brush GetHighlightBorderBrush(DependencyObject element)
=> (Brush) element.GetValue(HighlightBorderBrushProperty);
public static Brush GetHighlightBorderBrush(DependencyObject element)
=> (Brush) element.GetValue(HighlightBorderBrushProperty);
public static readonly DependencyProperty HighlightForegroundProperty = DependencyProperty.RegisterAttached(
"HighlightForeground", typeof(Brush), typeof(VisualElement), new FrameworkPropertyMetadata(default(Brush), FrameworkPropertyMetadataOptions.Inherits));
public static readonly DependencyProperty HighlightForegroundProperty = DependencyProperty.RegisterAttached(
"HighlightForeground", typeof(Brush), typeof(VisualElement), new FrameworkPropertyMetadata(default(Brush), FrameworkPropertyMetadataOptions.Inherits));
public static void SetHighlightForeground(DependencyObject element, Brush value)
=> element.SetValue(HighlightForegroundProperty, value);
public static void SetHighlightForeground(DependencyObject element, Brush value)
=> element.SetValue(HighlightForegroundProperty, value);
public static Brush GetHighlightForeground(DependencyObject element)
=> (Brush) element.GetValue(HighlightForegroundProperty);
public static Brush GetHighlightForeground(DependencyObject element)
=> (Brush) element.GetValue(HighlightForegroundProperty);
public static readonly DependencyProperty TextProperty = DependencyProperty.RegisterAttached(
"Text", typeof(string), typeof(VisualElement), new PropertyMetadata(default(string)));
public static readonly DependencyProperty TextProperty = DependencyProperty.RegisterAttached(
"Text", typeof(string), typeof(VisualElement), new PropertyMetadata(default(string)));
public static void SetText(DependencyObject element, string value)
=> element.SetValue(TextProperty, value);
public static void SetText(DependencyObject element, string value)
=> element.SetValue(TextProperty, value);
public static string GetText(DependencyObject element)
=> (string) element.GetValue(TextProperty);
}
public static string GetText(DependencyObject element)
=> (string) element.GetValue(TextProperty);
}
@@ -6,144 +6,152 @@ using System.Windows.Interop;
using HandyControl.Data;
using HandyControl.Tools.Interop;
namespace HandyControl.Controls
namespace HandyControl.Controls;
public static class WindowAttach
{
public static class WindowAttach
public static readonly DependencyProperty IsDragElementProperty = DependencyProperty.RegisterAttached(
"IsDragElement", typeof(bool), typeof(WindowAttach), new PropertyMetadata(ValueBoxes.FalseBox, OnIsDragElementChanged));
public static void SetIsDragElement(DependencyObject element, bool value)
=> element.SetValue(IsDragElementProperty, ValueBoxes.BooleanBox(value));
public static bool GetIsDragElement(DependencyObject element)
=> (bool) element.GetValue(IsDragElementProperty);
private static void OnIsDragElementChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
public static readonly DependencyProperty IsDragElementProperty = DependencyProperty.RegisterAttached(
"IsDragElement", typeof(bool), typeof(WindowAttach), new PropertyMetadata(ValueBoxes.FalseBox, OnIsDragElementChanged));
public static void SetIsDragElement(DependencyObject element, bool value)
=> element.SetValue(IsDragElementProperty, ValueBoxes.BooleanBox(value));
public static bool GetIsDragElement(DependencyObject element)
=> (bool) element.GetValue(IsDragElementProperty);
private static void OnIsDragElementChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
if (d is UIElement ctl)
{
if (d is UIElement ctl)
if ((bool) e.NewValue)
{
if ((bool) e.NewValue)
{
ctl.MouseLeftButtonDown += DragElement_MouseLeftButtonDown;
}
else
{
ctl.MouseLeftButtonDown -= DragElement_MouseLeftButtonDown;
}
ctl.MouseLeftButtonDown += DragElement_MouseLeftButtonDown;
}
else
{
ctl.MouseLeftButtonDown -= DragElement_MouseLeftButtonDown;
}
}
private static void DragElement_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (sender is DependencyObject obj && e.ButtonState == MouseButtonState.Pressed)
{
System.Windows.Window.GetWindow(obj)?.DragMove();
}
}
public static readonly DependencyProperty IgnoreAltF4Property = DependencyProperty.RegisterAttached(
"IgnoreAltF4", typeof(bool), typeof(WindowAttach), new PropertyMetadata(ValueBoxes.FalseBox, OnIgnoreAltF4Changed));
private static void OnIgnoreAltF4Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is System.Windows.Window window)
{
if ((bool) e.NewValue)
{
window.PreviewKeyDown += Window_PreviewKeyDown;
}
else
{
window.PreviewKeyDown -= Window_PreviewKeyDown;
}
}
}
private static void Window_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.System && e.SystemKey == Key.F4)
{
e.Handled = true;
}
}
public static void SetIgnoreAltF4(DependencyObject element, bool value)
=> element.SetValue(IgnoreAltF4Property, ValueBoxes.BooleanBox(value));
public static bool GetIgnoreAltF4(DependencyObject element)
=> (bool) element.GetValue(IgnoreAltF4Property);
public static readonly DependencyProperty ShowInTaskManagerProperty = DependencyProperty.RegisterAttached(
"ShowInTaskManager", typeof(bool), typeof(WindowAttach), new PropertyMetadata(ValueBoxes.TrueBox, OnShowInTaskManagerChanged));
private static void OnShowInTaskManagerChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is System.Windows.Window window)
{
var v = (bool) e.NewValue;
window.SetCurrentValue(System.Windows.Window.ShowInTaskbarProperty, v);
if (v)
{
window.SourceInitialized -= Window_SourceInitialized;
}
else
{
window.SourceInitialized += Window_SourceInitialized;
}
}
}
private static void Window_SourceInitialized(object sender, EventArgs e)
{
if (sender is System.Windows.Window window)
{
var _ = new WindowInteropHelper(window)
{
Owner = InteropMethods.GetDesktopWindow()
};
}
}
public static void SetShowInTaskManager(DependencyObject element, bool value)
=> element.SetValue(ShowInTaskManagerProperty, ValueBoxes.BooleanBox(value));
public static bool GetShowInTaskManager(DependencyObject element)
=> (bool) element.GetValue(ShowInTaskManagerProperty);
public static readonly DependencyProperty HideWhenClosingProperty = DependencyProperty.RegisterAttached(
"HideWhenClosing", typeof(bool), typeof(WindowAttach), new PropertyMetadata(ValueBoxes.FalseBox, OnHideWhenClosingChanged));
private static void OnHideWhenClosingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is System.Windows.Window window)
{
var v = (bool) e.NewValue;
if (v)
{
window.Closing += Window_Closing;
}
else
{
window.Closing -= Window_Closing;
}
}
}
private static void Window_Closing(object sender, CancelEventArgs e)
{
if (sender is System.Windows.Window window)
{
window.Hide();
e.Cancel = true;
}
}
public static void SetHideWhenClosing(DependencyObject element, bool value)
=> element.SetValue(HideWhenClosingProperty, value);
public static bool GetHideWhenClosing(DependencyObject element)
=> (bool) element.GetValue(HideWhenClosingProperty);
}
private static void DragElement_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (sender is DependencyObject obj && e.ButtonState == MouseButtonState.Pressed)
{
System.Windows.Window.GetWindow(obj)?.DragMove();
}
}
public static readonly DependencyProperty IgnoreAltF4Property = DependencyProperty.RegisterAttached(
"IgnoreAltF4", typeof(bool), typeof(WindowAttach), new PropertyMetadata(ValueBoxes.FalseBox, OnIgnoreAltF4Changed));
private static void OnIgnoreAltF4Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is System.Windows.Window window)
{
if ((bool) e.NewValue)
{
window.PreviewKeyDown += Window_PreviewKeyDown;
}
else
{
window.PreviewKeyDown -= Window_PreviewKeyDown;
}
}
}
private static void Window_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.System && e.SystemKey == Key.F4)
{
e.Handled = true;
}
}
public static void SetIgnoreAltF4(DependencyObject element, bool value)
=> element.SetValue(IgnoreAltF4Property, ValueBoxes.BooleanBox(value));
public static bool GetIgnoreAltF4(DependencyObject element)
=> (bool) element.GetValue(IgnoreAltF4Property);
public static readonly DependencyProperty ShowInTaskManagerProperty = DependencyProperty.RegisterAttached(
"ShowInTaskManager", typeof(bool), typeof(WindowAttach), new PropertyMetadata(ValueBoxes.TrueBox, OnShowInTaskManagerChanged));
private static void OnShowInTaskManagerChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is System.Windows.Window window)
{
var v = (bool) e.NewValue;
window.SetCurrentValue(System.Windows.Window.ShowInTaskbarProperty, v);
if (v)
{
window.SourceInitialized -= Window_SourceInitialized;
}
else
{
window.SourceInitialized += Window_SourceInitialized;
}
}
}
private static void Window_SourceInitialized(object sender, EventArgs e)
{
if (sender is System.Windows.Window window)
{
var _ = new WindowInteropHelper(window)
{
Owner = InteropMethods.GetDesktopWindow()
};
}
}
public static void SetShowInTaskManager(DependencyObject element, bool value)
=> element.SetValue(ShowInTaskManagerProperty, ValueBoxes.BooleanBox(value));
public static bool GetShowInTaskManager(DependencyObject element)
=> (bool) element.GetValue(ShowInTaskManagerProperty);
public static readonly DependencyProperty HideWhenClosingProperty = DependencyProperty.RegisterAttached(
"HideWhenClosing", typeof(bool), typeof(WindowAttach), new PropertyMetadata(ValueBoxes.FalseBox, OnHideWhenClosingChanged));
private static void OnHideWhenClosingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is System.Windows.Window window)
{
var v = (bool) e.NewValue;
if (v)
{
window.Closing += Window_Closing;
}
else
{
window.Closing -= Window_Closing;
}
}
}
private static void Window_Closing(object sender, CancelEventArgs e)
{
if (sender is System.Windows.Window window)
{
window.Hide();
e.Cancel = true;
}
}
public static void SetHideWhenClosing(DependencyObject element, bool value)
=> element.SetValue(HideWhenClosingProperty, ValueBoxes.BooleanBox(value));
public static bool GetHideWhenClosing(DependencyObject element)
=> (bool) element.GetValue(HideWhenClosingProperty);
public static readonly DependencyProperty ExtendContentToNonClientAreaProperty = DependencyProperty.RegisterAttached(
"ExtendContentToNonClientArea", typeof(bool), typeof(WindowAttach), new PropertyMetadata(ValueBoxes.FalseBox));
public static void SetExtendContentToNonClientArea(DependencyObject element, bool value)
=> element.SetValue(ExtendContentToNonClientAreaProperty, ValueBoxes.BooleanBox(value));
public static bool GetExtendContentToNonClientArea(DependencyObject element)
=> (bool) element.GetValue(ExtendContentToNonClientAreaProperty);
}
@@ -4,84 +4,83 @@ using System.Windows;
using System.Windows.Controls;
using HandyControl.Data;
namespace HandyControl.Controls
namespace HandyControl.Controls;
public abstract class AdornerElement : Control, IDisposable
{
public abstract class AdornerElement : Control, IDisposable
protected FrameworkElement ElementTarget { get; set; }
public static readonly DependencyProperty TargetProperty = DependencyProperty.Register(
nameof(Target), typeof(FrameworkElement), typeof(AdornerElement), new PropertyMetadata(default(FrameworkElement), OnTargetChanged));
private static void OnTargetChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
protected FrameworkElement ElementTarget { get; set; }
public static readonly DependencyProperty TargetProperty = DependencyProperty.Register(
"Target", typeof(FrameworkElement), typeof(AdornerElement), new PropertyMetadata(default(FrameworkElement), OnTargetChanged));
private static void OnTargetChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var ctl = (AdornerElement) d;
ctl.OnTargetChanged(ctl.ElementTarget, false);
ctl.OnTargetChanged((FrameworkElement) e.NewValue, true);
}
[Bindable(true), Category("Layout")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public FrameworkElement Target
{
get => (FrameworkElement) GetValue(TargetProperty);
set => SetValue(TargetProperty, value);
}
public static readonly DependencyProperty InstanceProperty = DependencyProperty.RegisterAttached(
"Instance", typeof(AdornerElement), typeof(AdornerElement), new PropertyMetadata(default(AdornerElement), OnInstanceChanged));
private static void OnInstanceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (!(d is FrameworkElement target)) return;
var element = (AdornerElement) e.NewValue;
element.OnInstanceChanged(target);
}
protected virtual void OnInstanceChanged(FrameworkElement target) => Target = target;
public static void SetInstance(DependencyObject element, AdornerElement value)
=> element.SetValue(InstanceProperty, value);
public static AdornerElement GetInstance(DependencyObject element)
=> (AdornerElement) element.GetValue(InstanceProperty);
public static readonly DependencyProperty IsInstanceProperty = DependencyProperty.RegisterAttached(
"IsInstance", typeof(bool), typeof(AdornerElement), new PropertyMetadata(ValueBoxes.TrueBox));
public static void SetIsInstance(DependencyObject element, bool value)
=> element.SetValue(IsInstanceProperty, ValueBoxes.BooleanBox(value));
public static bool GetIsInstance(DependencyObject element)
=> (bool) element.GetValue(IsInstanceProperty);
protected virtual void OnTargetChanged(FrameworkElement element, bool isNew)
{
if (element == null) return;
if (!isNew)
{
element.Unloaded -= TargetElement_Unloaded;
ElementTarget = null;
}
else
{
element.Unloaded += TargetElement_Unloaded;
ElementTarget = element;
}
}
private void TargetElement_Unloaded(object sender, RoutedEventArgs e)
{
if (sender is FrameworkElement element)
{
element.Unloaded -= TargetElement_Unloaded;
Dispose();
}
}
protected abstract void Dispose();
void IDisposable.Dispose() => Dispose();
var ctl = (AdornerElement) d;
ctl.OnTargetChanged(ctl.ElementTarget, false);
ctl.OnTargetChanged((FrameworkElement) e.NewValue, true);
}
[Bindable(true), Category("Layout")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public FrameworkElement Target
{
get => (FrameworkElement) GetValue(TargetProperty);
set => SetValue(TargetProperty, value);
}
public static readonly DependencyProperty InstanceProperty = DependencyProperty.RegisterAttached(
"Instance", typeof(AdornerElement), typeof(AdornerElement), new PropertyMetadata(default(AdornerElement), OnInstanceChanged));
private static void OnInstanceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is not FrameworkElement target) return;
var element = (AdornerElement) e.NewValue;
element.OnInstanceChanged(target);
}
protected virtual void OnInstanceChanged(FrameworkElement target) => Target = target;
public static void SetInstance(DependencyObject element, AdornerElement value)
=> element.SetValue(InstanceProperty, value);
public static AdornerElement GetInstance(DependencyObject element)
=> (AdornerElement) element.GetValue(InstanceProperty);
public static readonly DependencyProperty IsInstanceProperty = DependencyProperty.RegisterAttached(
"IsInstance", typeof(bool), typeof(AdornerElement), new PropertyMetadata(ValueBoxes.TrueBox));
public static void SetIsInstance(DependencyObject element, bool value)
=> element.SetValue(IsInstanceProperty, ValueBoxes.BooleanBox(value));
public static bool GetIsInstance(DependencyObject element)
=> (bool) element.GetValue(IsInstanceProperty);
protected virtual void OnTargetChanged(FrameworkElement element, bool isNew)
{
if (element == null) return;
if (!isNew)
{
element.Unloaded -= TargetElement_Unloaded;
ElementTarget = null;
}
else
{
element.Unloaded += TargetElement_Unloaded;
ElementTarget = element;
}
}
private void TargetElement_Unloaded(object sender, RoutedEventArgs e)
{
if (sender is FrameworkElement element)
{
element.Unloaded -= TargetElement_Unloaded;
Dispose();
}
}
protected abstract void Dispose();
void IDisposable.Dispose() => Dispose();
}
@@ -1,25 +1,24 @@
using System.Windows;
namespace HandyControl.Controls
namespace HandyControl.Controls;
public class HeaderedSelectableItem : SelectableItem
{
public class HeaderedSelectableItem : SelectableItem
public static readonly DependencyProperty HeaderProperty = DependencyProperty.Register(
nameof(Header), typeof(object), typeof(HeaderedSelectableItem), new PropertyMetadata(default(object)));
public object Header
{
public static readonly DependencyProperty HeaderProperty = DependencyProperty.Register(
"Header", typeof(object), typeof(HeaderedSelectableItem), new PropertyMetadata(default(object)));
get => GetValue(HeaderProperty);
set => SetValue(HeaderProperty, value);
}
public object Header
{
get => GetValue(HeaderProperty);
set => SetValue(HeaderProperty, value);
}
public static readonly DependencyProperty HeaderTemplateProperty = DependencyProperty.Register(
nameof(HeaderTemplate), typeof(DataTemplate), typeof(HeaderedSelectableItem), new PropertyMetadata(default(DataTemplate)));
public static readonly DependencyProperty HeaderTemplateProperty = DependencyProperty.Register(
"HeaderTemplate", typeof(DataTemplate), typeof(HeaderedSelectableItem), new PropertyMetadata(default(DataTemplate)));
public DataTemplate HeaderTemplate
{
get => (DataTemplate) GetValue(HeaderTemplateProperty);
set => SetValue(HeaderTemplateProperty, value);
}
public DataTemplate HeaderTemplate
{
get => (DataTemplate) GetValue(HeaderTemplateProperty);
set => SetValue(HeaderTemplateProperty, value);
}
}
@@ -1,16 +1,15 @@
using System.Windows;
namespace HandyControl.Controls
{
public class HeaderedSimpleItemsControl : SimpleItemsControl
{
public static readonly DependencyProperty HeaderProperty = DependencyProperty.Register(
"Header", typeof(object), typeof(HeaderedSimpleItemsControl), new PropertyMetadata(default(object)));
namespace HandyControl.Controls;
public object Header
{
get => GetValue(HeaderProperty);
set => SetValue(HeaderProperty, value);
}
public class HeaderedSimpleItemsControl : SimpleItemsControl
{
public static readonly DependencyProperty HeaderProperty = DependencyProperty.Register(
nameof(Header), typeof(object), typeof(HeaderedSimpleItemsControl), new PropertyMetadata(default(object)));
public object Header
{
get => GetValue(HeaderProperty);
set => SetValue(HeaderProperty, value);
}
}
@@ -1,7 +1,6 @@
namespace HandyControl.Controls
namespace HandyControl.Controls;
public interface IGravatarGenerator
{
public interface IGravatarGenerator
{
object GetGravatar(string id);
}
object GetGravatar(string id);
}
@@ -1,11 +1,10 @@
using System.Windows;
namespace HandyControl.Controls
{
public interface ISelectable
{
event RoutedEventHandler Selected;
namespace HandyControl.Controls;
bool IsSelected { get; set; }
}
public interface ISelectable
{
event RoutedEventHandler Selected;
bool IsSelected { get; set; }
}
@@ -1,9 +1,8 @@
using System;
namespace HandyControl.Controls
namespace HandyControl.Controls;
public interface ISingleOpen : IDisposable
{
public interface ISingleOpen : IDisposable
{
bool CanDispose { get; }
}
bool CanDispose { get; }
}
@@ -1,41 +1,40 @@
using System.Windows;
using HandyControl.Data;
namespace HandyControl.Controls
namespace HandyControl.Controls;
/// <summary>
/// 规则ItemsControl
/// </summary>
/// <remarks>
/// 该类的每一项都具有相同的大小和外边距
/// </remarks>
public class RegularItemsControl : SimpleItemsControl
{
/// <summary>
/// 规则ItemsControl
/// </summary>
/// <remarks>
/// 该类的每一项都具有相同的大小和外边距
/// </remarks>
public class RegularItemsControl : SimpleItemsControl
public static readonly DependencyProperty ItemWidthProperty = DependencyProperty.Register(
nameof(ItemWidth), typeof(double), typeof(RegularItemsControl), new PropertyMetadata(ValueBoxes.Double200Box));
public double ItemWidth
{
public static readonly DependencyProperty ItemWidthProperty = DependencyProperty.Register(
"ItemWidth", typeof(double), typeof(RegularItemsControl), new PropertyMetadata(ValueBoxes.Double200Box));
get => (double) GetValue(ItemWidthProperty);
set => SetValue(ItemWidthProperty, value);
}
public double ItemWidth
{
get => (double) GetValue(ItemWidthProperty);
set => SetValue(ItemWidthProperty, value);
}
public static readonly DependencyProperty ItemHeightProperty = DependencyProperty.Register(
nameof(ItemHeight), typeof(double), typeof(RegularItemsControl), new PropertyMetadata(ValueBoxes.Double200Box));
public static readonly DependencyProperty ItemHeightProperty = DependencyProperty.Register(
"ItemHeight", typeof(double), typeof(RegularItemsControl), new PropertyMetadata(ValueBoxes.Double200Box));
public double ItemHeight
{
get => (double) GetValue(ItemHeightProperty);
set => SetValue(ItemHeightProperty, value);
}
public double ItemHeight
{
get => (double) GetValue(ItemHeightProperty);
set => SetValue(ItemHeightProperty, value);
}
public static readonly DependencyProperty ItemMarginProperty = DependencyProperty.Register(
nameof(ItemMargin), typeof(Thickness), typeof(RegularItemsControl), new PropertyMetadata(default(Thickness)));
public static readonly DependencyProperty ItemMarginProperty = DependencyProperty.Register(
"ItemMargin", typeof(Thickness), typeof(RegularItemsControl), new PropertyMetadata(default(Thickness)));
public Thickness ItemMargin
{
get => (Thickness) GetValue(ItemMarginProperty);
set => SetValue(ItemMarginProperty, value);
}
public Thickness ItemMargin
{
get => (Thickness) GetValue(ItemMarginProperty);
set => SetValue(ItemMarginProperty, value);
}
}
@@ -3,109 +3,108 @@ using System.Windows.Controls;
using System.Windows.Input;
using HandyControl.Data;
namespace HandyControl.Controls
namespace HandyControl.Controls;
public class SelectableItem : ContentControl, ISelectable
{
public class SelectableItem : ContentControl, ISelectable
private bool _isMouseLeftButtonDown;
protected override void OnMouseLeave(MouseEventArgs e)
{
private bool _isMouseLeftButtonDown;
base.OnMouseLeave(e);
protected override void OnMouseLeave(MouseEventArgs e)
_isMouseLeftButtonDown = false;
}
protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
{
base.OnMouseLeftButtonDown(e);
_isMouseLeftButtonDown = true;
}
protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
{
base.OnMouseLeftButtonUp(e);
if (_isMouseLeftButtonDown)
{
base.OnMouseLeave(e);
_isMouseLeftButtonDown = false;
}
protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
{
base.OnMouseLeftButtonDown(e);
_isMouseLeftButtonDown = true;
}
protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
{
base.OnMouseLeftButtonUp(e);
if (_isMouseLeftButtonDown)
if (SelfManage)
{
if (SelfManage)
if (!IsSelected)
{
if (!IsSelected)
{
IsSelected = true;
OnSelected(new RoutedEventArgs(SelectedEvent, this));
}
else if (CanDeselect)
{
IsSelected = false;
OnSelected(new RoutedEventArgs(DeselectedEvent, this));
}
IsSelected = true;
OnSelected(new RoutedEventArgs(SelectedEvent, this));
}
else if (CanDeselect)
{
IsSelected = false;
OnSelected(new RoutedEventArgs(DeselectedEvent, this));
}
}
else
{
if (CanDeselect)
{
OnSelected(IsSelected
? new RoutedEventArgs(DeselectedEvent, this)
: new RoutedEventArgs(SelectedEvent, this));
}
else
{
if (CanDeselect)
{
OnSelected(IsSelected
? new RoutedEventArgs(DeselectedEvent, this)
: new RoutedEventArgs(SelectedEvent, this));
}
else
{
OnSelected(new RoutedEventArgs(SelectedEvent, this));
}
OnSelected(new RoutedEventArgs(SelectedEvent, this));
}
_isMouseLeftButtonDown = false;
}
}
protected virtual void OnSelected(RoutedEventArgs e) => RaiseEvent(e);
public static readonly DependencyProperty IsSelectedProperty = DependencyProperty.Register(
"IsSelected", typeof(bool), typeof(SelectableItem), new PropertyMetadata(ValueBoxes.FalseBox));
public bool IsSelected
{
get => (bool) GetValue(IsSelectedProperty);
set => SetValue(IsSelectedProperty, ValueBoxes.BooleanBox(value));
}
public static readonly DependencyProperty SelfManageProperty = DependencyProperty.Register(
"SelfManage", typeof(bool), typeof(SelectableItem), new PropertyMetadata(ValueBoxes.FalseBox));
public bool SelfManage
{
get => (bool) GetValue(SelfManageProperty);
set => SetValue(SelfManageProperty, ValueBoxes.BooleanBox(value));
}
public static readonly DependencyProperty CanDeselectProperty = DependencyProperty.Register(
"CanDeselect", typeof(bool), typeof(SelectableItem), new PropertyMetadata(ValueBoxes.FalseBox));
public bool CanDeselect
{
get => (bool) GetValue(CanDeselectProperty);
set => SetValue(CanDeselectProperty, ValueBoxes.BooleanBox(value));
}
public static readonly RoutedEvent SelectedEvent =
EventManager.RegisterRoutedEvent("Selected", RoutingStrategy.Bubble,
typeof(RoutedEventHandler), typeof(SelectableItem));
public event RoutedEventHandler Selected
{
add => AddHandler(SelectedEvent, value);
remove => RemoveHandler(SelectedEvent, value);
}
public static readonly RoutedEvent DeselectedEvent =
EventManager.RegisterRoutedEvent("Deselected", RoutingStrategy.Bubble,
typeof(RoutedEventHandler), typeof(SelectableItem));
public event RoutedEventHandler Deselected
{
add => AddHandler(DeselectedEvent, value);
remove => RemoveHandler(DeselectedEvent, value);
_isMouseLeftButtonDown = false;
}
}
protected virtual void OnSelected(RoutedEventArgs e) => RaiseEvent(e);
public static readonly DependencyProperty IsSelectedProperty = DependencyProperty.Register(
nameof(IsSelected), typeof(bool), typeof(SelectableItem), new PropertyMetadata(ValueBoxes.FalseBox));
public bool IsSelected
{
get => (bool) GetValue(IsSelectedProperty);
set => SetValue(IsSelectedProperty, ValueBoxes.BooleanBox(value));
}
public static readonly DependencyProperty SelfManageProperty = DependencyProperty.Register(
nameof(SelfManage), typeof(bool), typeof(SelectableItem), new PropertyMetadata(ValueBoxes.FalseBox));
public bool SelfManage
{
get => (bool) GetValue(SelfManageProperty);
set => SetValue(SelfManageProperty, ValueBoxes.BooleanBox(value));
}
public static readonly DependencyProperty CanDeselectProperty = DependencyProperty.Register(
nameof(CanDeselect), typeof(bool), typeof(SelectableItem), new PropertyMetadata(ValueBoxes.FalseBox));
public bool CanDeselect
{
get => (bool) GetValue(CanDeselectProperty);
set => SetValue(CanDeselectProperty, ValueBoxes.BooleanBox(value));
}
public static readonly RoutedEvent SelectedEvent =
EventManager.RegisterRoutedEvent("Selected", RoutingStrategy.Bubble,
typeof(RoutedEventHandler), typeof(SelectableItem));
public event RoutedEventHandler Selected
{
add => AddHandler(SelectedEvent, value);
remove => RemoveHandler(SelectedEvent, value);
}
public static readonly RoutedEvent DeselectedEvent =
EventManager.RegisterRoutedEvent("Deselected", RoutingStrategy.Bubble,
typeof(RoutedEventHandler), typeof(SelectableItem));
public event RoutedEventHandler Deselected
{
add => AddHandler(DeselectedEvent, value);
remove => RemoveHandler(DeselectedEvent, value);
}
}
@@ -7,161 +7,160 @@ using System.Windows.Controls;
using System.Windows.Markup;
using HandyControl.Data;
namespace HandyControl.Controls
namespace HandyControl.Controls;
/// <summary>
/// ItemsControl的轻量级版本
/// </summary>
[DefaultProperty("Items")]
[ContentProperty("Items")]
[TemplatePart(Name = ElementPanel, Type = typeof(Panel))]
public class SimpleItemsControl : Control
{
/// <summary>
/// ItemsControl的轻量级版本
/// </summary>
[DefaultProperty("Items")]
[ContentProperty("Items")]
[TemplatePart(Name = ElementPanel, Type = typeof(Panel))]
public class SimpleItemsControl : Control
private const string ElementPanel = "PART_Panel";
public static readonly DependencyProperty ItemTemplateProperty = DependencyProperty.Register(
nameof(ItemTemplate), typeof(DataTemplate), typeof(SimpleItemsControl),
new FrameworkPropertyMetadata(default(DataTemplate), OnItemTemplateChanged));
public static readonly DependencyProperty ItemContainerStyleProperty = DependencyProperty.Register(
nameof(ItemContainerStyle), typeof(Style), typeof(SimpleItemsControl),
new PropertyMetadata(default(Style), OnItemContainerStyleChanged));
public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register(
nameof(ItemsSource), typeof(IEnumerable), typeof(SimpleItemsControl),
new PropertyMetadata(default(IEnumerable), OnItemsSourceChanged));
public SimpleItemsControl()
{
private const string ElementPanel = "PART_Panel";
public static readonly DependencyProperty ItemTemplateProperty = DependencyProperty.Register(
"ItemTemplate", typeof(DataTemplate), typeof(SimpleItemsControl),
new FrameworkPropertyMetadata(default(DataTemplate), OnItemTemplateChanged));
public static readonly DependencyProperty ItemContainerStyleProperty = DependencyProperty.Register(
"ItemContainerStyle", typeof(Style), typeof(SimpleItemsControl),
new PropertyMetadata(default(Style), OnItemContainerStyleChanged));
public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register(
"ItemsSource", typeof(IEnumerable), typeof(SimpleItemsControl),
new PropertyMetadata(default(IEnumerable), OnItemsSourceChanged));
public SimpleItemsControl()
var items = new ObservableCollection<object>();
items.CollectionChanged += (s, e) =>
{
var items = new ObservableCollection<object>();
items.CollectionChanged += (s, e) =>
if (e.NewItems != null && e.NewItems.Count > 0)
{
if (e.NewItems != null && e.NewItems.Count > 0)
{
SetValue(HasItemsPropertyKey, ValueBoxes.TrueBox);
}
OnItemsChanged(s, e);
};
Items = items;
}
public IEnumerable ItemsSource
{
get => (IEnumerable) GetValue(ItemsSourceProperty);
set => SetValue(ItemsSourceProperty, value);
}
[Bindable(true)]
[Category("Content")]
public Style ItemContainerStyle
{
get => (Style) GetValue(ItemContainerStyleProperty);
set => SetValue(ItemContainerStyleProperty, value);
}
[Bindable(true)]
public DataTemplate ItemTemplate
{
get => (DataTemplate) GetValue(ItemTemplateProperty);
set => SetValue(ItemTemplateProperty, value);
}
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
[Bindable(true)]
public Collection<object> Items { get; }
internal Panel ItemsHost { get; set; }
private static void OnItemsSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((SimpleItemsControl) d).OnItemsSourceChanged((IEnumerable) e.OldValue, (IEnumerable) e.NewValue);
}
protected virtual void OnItemsSourceChanged(IEnumerable oldValue, IEnumerable newValue)
{
}
public override void OnApplyTemplate()
{
ItemsHost?.Children.Clear();
base.OnApplyTemplate();
ItemsHost = GetTemplateChild(ElementPanel) as Panel;
Refresh();
}
protected virtual void OnItemsChanged(object sender, NotifyCollectionChangedEventArgs e)
{
Refresh();
UpdateItems();
}
protected virtual DependencyObject GetContainerForItemOverride() => new ContentPresenter();
protected virtual bool IsItemItsOwnContainerOverride(object item) => item is ContentPresenter;
protected virtual void PrepareContainerForItemOverride(DependencyObject element, object item)
{
switch (element)
{
case ContentControl contentControl:
contentControl.Content = item;
contentControl.ContentTemplate = ItemTemplate;
break;
case ContentPresenter contentPresenter:
contentPresenter.Content = item;
contentPresenter.ContentTemplate = ItemTemplate;
break;
SetValue(HasItemsPropertyKey, ValueBoxes.TrueBox);
}
}
OnItemsChanged(s, e);
};
Items = items;
}
private static void OnItemTemplateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
=> (d as SimpleItemsControl)?.OnItemTemplateChanged(e);
public IEnumerable ItemsSource
{
get => (IEnumerable) GetValue(ItemsSourceProperty);
set => SetValue(ItemsSourceProperty, value);
}
protected virtual void OnItemTemplateChanged(DependencyPropertyChangedEventArgs e) => Refresh();
[Bindable(true)]
[Category("Content")]
public Style ItemContainerStyle
{
get => (Style) GetValue(ItemContainerStyleProperty);
set => SetValue(ItemContainerStyleProperty, value);
}
private static void OnItemContainerStyleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
=> (d as SimpleItemsControl)?.OnItemContainerStyleChanged(e);
[Bindable(true)]
public DataTemplate ItemTemplate
{
get => (DataTemplate) GetValue(ItemTemplateProperty);
set => SetValue(ItemTemplateProperty, value);
}
protected virtual void OnItemContainerStyleChanged(DependencyPropertyChangedEventArgs e) => Refresh();
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
[Bindable(true)]
public Collection<object> Items { get; }
internal static readonly DependencyPropertyKey HasItemsPropertyKey =
DependencyProperty.RegisterReadOnly(nameof(HasItems), typeof(bool), typeof(SimpleItemsControl),
new PropertyMetadata(ValueBoxes.FalseBox));
internal Panel ItemsHost { get; set; }
public static readonly DependencyProperty HasItemsProperty = HasItemsPropertyKey.DependencyProperty;
private static void OnItemsSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((SimpleItemsControl) d).OnItemsSourceChanged((IEnumerable) e.OldValue, (IEnumerable) e.NewValue);
}
public bool HasItems => (bool) GetValue(HasItemsProperty);
protected virtual void OnItemsSourceChanged(IEnumerable oldValue, IEnumerable newValue)
{
}
protected virtual void Refresh()
{
if (ItemsHost == null) return;
public override void OnApplyTemplate()
{
ItemsHost?.Children.Clear();
ItemsHost.Children.Clear();
foreach (var item in Items)
{
DependencyObject container;
if (IsItemItsOwnContainerOverride(item))
{
container = item as DependencyObject;
}
else
{
container = GetContainerForItemOverride();
PrepareContainerForItemOverride(container, item);
}
base.OnApplyTemplate();
if (container is FrameworkElement element)
{
element.Style = ItemContainerStyle;
ItemsHost.Children.Add(element);
}
}
}
ItemsHost = GetTemplateChild(ElementPanel) as Panel;
Refresh();
}
protected virtual void UpdateItems()
protected virtual void OnItemsChanged(object sender, NotifyCollectionChangedEventArgs e)
{
Refresh();
UpdateItems();
}
protected virtual DependencyObject GetContainerForItemOverride() => new ContentPresenter();
protected virtual bool IsItemItsOwnContainerOverride(object item) => item is ContentPresenter;
protected virtual void PrepareContainerForItemOverride(DependencyObject element, object item)
{
switch (element)
{
case ContentControl contentControl:
contentControl.Content = item;
contentControl.ContentTemplate = ItemTemplate;
break;
case ContentPresenter contentPresenter:
contentPresenter.Content = item;
contentPresenter.ContentTemplate = ItemTemplate;
break;
}
}
private static void OnItemTemplateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
=> (d as SimpleItemsControl)?.OnItemTemplateChanged(e);
protected virtual void OnItemTemplateChanged(DependencyPropertyChangedEventArgs e) => Refresh();
private static void OnItemContainerStyleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
=> (d as SimpleItemsControl)?.OnItemContainerStyleChanged(e);
protected virtual void OnItemContainerStyleChanged(DependencyPropertyChangedEventArgs e) => Refresh();
internal static readonly DependencyPropertyKey HasItemsPropertyKey =
DependencyProperty.RegisterReadOnly(nameof(HasItems), typeof(bool), typeof(SimpleItemsControl),
new PropertyMetadata(ValueBoxes.FalseBox));
public static readonly DependencyProperty HasItemsProperty = HasItemsPropertyKey.DependencyProperty;
public bool HasItems => (bool) GetValue(HasItemsProperty);
protected virtual void Refresh()
{
if (ItemsHost == null) return;
ItemsHost.Children.Clear();
foreach (var item in Items)
{
DependencyObject container;
if (IsItemItsOwnContainerOverride(item))
{
container = item as DependencyObject;
}
else
{
container = GetContainerForItemOverride();
PrepareContainerForItemOverride(container, item);
}
if (container is FrameworkElement element)
{
element.Style = ItemContainerStyle;
ItemsHost.Children.Add(element);
}
}
}
protected virtual void UpdateItems()
{
}
}
@@ -9,187 +9,186 @@ using HandyControl.Data;
using HandyControl.Interactivity;
using HandyControl.Tools;
namespace HandyControl.Controls
namespace HandyControl.Controls;
public class FloatingBlock : Control
{
public class FloatingBlock : Control
public static readonly DependencyProperty ToXProperty = DependencyProperty.RegisterAttached(
"ToX", typeof(double), typeof(FloatingBlock), new PropertyMetadata(ValueBoxes.Double0Box));
public static void SetToX(DependencyObject element, double value)
=> element.SetValue(ToXProperty, value);
public static double GetToX(DependencyObject element)
=> (double) element.GetValue(ToXProperty);
public static readonly DependencyProperty ToYProperty = DependencyProperty.RegisterAttached(
"ToY", typeof(double), typeof(FloatingBlock), new PropertyMetadata(-100.0));
public static void SetToY(DependencyObject element, double value)
=> element.SetValue(ToYProperty, value);
public static double GetToY(DependencyObject element)
=> (double) element.GetValue(ToYProperty);
public static readonly DependencyProperty DurationProperty = DependencyProperty.RegisterAttached(
"Duration", typeof(Duration), typeof(FloatingBlock), new PropertyMetadata(new Duration(TimeSpan.FromSeconds(2))));
public static void SetDuration(DependencyObject element, Duration value)
=> element.SetValue(DurationProperty, value);
public static Duration GetDuration(DependencyObject element)
=> (Duration) element.GetValue(DurationProperty);
public static readonly DependencyProperty HorizontalOffsetProperty = DependencyProperty.RegisterAttached(
"HorizontalOffset", typeof(double), typeof(FloatingBlock), new PropertyMetadata(ValueBoxes.Double0Box));
public static void SetHorizontalOffset(DependencyObject element, double value)
=> element.SetValue(HorizontalOffsetProperty, value);
public static double GetHorizontalOffset(DependencyObject element)
=> (double) element.GetValue(HorizontalOffsetProperty);
public static readonly DependencyProperty VerticalOffsetProperty = DependencyProperty.RegisterAttached(
"VerticalOffset", typeof(double), typeof(FloatingBlock), new PropertyMetadata(ValueBoxes.Double0Box));
public static void SetVerticalOffset(DependencyObject element, double value)
=> element.SetValue(VerticalOffsetProperty, value);
public static double GetVerticalOffset(DependencyObject element)
=> (double) element.GetValue(VerticalOffsetProperty);
public static readonly DependencyProperty ContentTemplateProperty = DependencyProperty.RegisterAttached(
"ContentTemplate", typeof(DataTemplate), typeof(FloatingBlock), new PropertyMetadata(default(DataTemplate), OnDataChanged));
public static void SetContentTemplate(DependencyObject element, DataTemplate value)
=> element.SetValue(ContentTemplateProperty, value);
public static DataTemplate GetContentTemplate(DependencyObject element)
=> (DataTemplate) element.GetValue(ContentTemplateProperty);
public DataTemplate ContentTemplate
{
public static readonly DependencyProperty ToXProperty = DependencyProperty.RegisterAttached(
"ToX", typeof(double), typeof(FloatingBlock), new PropertyMetadata(ValueBoxes.Double0Box));
get => (DataTemplate) GetValue(ContentTemplateProperty);
set => SetValue(ContentTemplateProperty, value);
}
public static void SetToX(DependencyObject element, double value)
=> element.SetValue(ToXProperty, value);
private static readonly DependencyProperty ReadyToFloatProperty = DependencyProperty.RegisterAttached(
"ReadyToFloat", typeof(bool), typeof(FloatingBlock), new PropertyMetadata(ValueBoxes.FalseBox));
public static double GetToX(DependencyObject element)
=> (double) element.GetValue(ToXProperty);
private static void SetReadyToFloat(DependencyObject element, bool value)
=> element.SetValue(ReadyToFloatProperty, ValueBoxes.BooleanBox(value));
public static readonly DependencyProperty ToYProperty = DependencyProperty.RegisterAttached(
"ToY", typeof(double), typeof(FloatingBlock), new PropertyMetadata(-100.0));
private static bool GetReadyToFloat(DependencyObject element)
=> (bool) element.GetValue(ReadyToFloatProperty);
public static void SetToY(DependencyObject element, double value)
=> element.SetValue(ToYProperty, value);
public static readonly DependencyProperty ContentProperty = DependencyProperty.RegisterAttached(
"Content", typeof(object), typeof(FloatingBlock), new PropertyMetadata(default, OnDataChanged));
public static double GetToY(DependencyObject element)
=> (double) element.GetValue(ToYProperty);
public static void SetContent(DependencyObject element, object value)
=> element.SetValue(ContentProperty, value);
public static readonly DependencyProperty DurationProperty = DependencyProperty.RegisterAttached(
"Duration", typeof(Duration), typeof(FloatingBlock), new PropertyMetadata(new Duration(TimeSpan.FromSeconds(2))));
public static object GetContent(DependencyObject element)
=> element.GetValue(ContentProperty);
public static void SetDuration(DependencyObject element, Duration value)
=> element.SetValue(DurationProperty, value);
public object Content
{
get => GetValue(ContentProperty);
set => SetValue(ContentProperty, value);
}
public static Duration GetDuration(DependencyObject element)
=> (Duration) element.GetValue(DurationProperty);
private static void OnDataChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is not UIElement target) return;
public static readonly DependencyProperty HorizontalOffsetProperty = DependencyProperty.RegisterAttached(
"HorizontalOffset", typeof(double), typeof(FloatingBlock), new PropertyMetadata(ValueBoxes.Double0Box));
target.PreviewMouseLeftButtonDown -= Target_PreviewMouseLeftButtonDown;
target.PreviewMouseLeftButtonUp -= Target_PreviewMouseLeftButtonUp;
public static void SetHorizontalOffset(DependencyObject element, double value)
=> element.SetValue(HorizontalOffsetProperty, value);
public static double GetHorizontalOffset(DependencyObject element)
=> (double) element.GetValue(HorizontalOffsetProperty);
public static readonly DependencyProperty VerticalOffsetProperty = DependencyProperty.RegisterAttached(
"VerticalOffset", typeof(double), typeof(FloatingBlock), new PropertyMetadata(ValueBoxes.Double0Box));
public static void SetVerticalOffset(DependencyObject element, double value)
=> element.SetValue(VerticalOffsetProperty, value);
public static double GetVerticalOffset(DependencyObject element)
=> (double) element.GetValue(VerticalOffsetProperty);
public static readonly DependencyProperty ContentTemplateProperty = DependencyProperty.RegisterAttached(
"ContentTemplate", typeof(DataTemplate), typeof(FloatingBlock), new PropertyMetadata(default(DataTemplate), OnDataChanged));
public static void SetContentTemplate(DependencyObject element, DataTemplate value)
=> element.SetValue(ContentTemplateProperty, value);
public static DataTemplate GetContentTemplate(DependencyObject element)
=> (DataTemplate) element.GetValue(ContentTemplateProperty);
public DataTemplate ContentTemplate
if (e.NewValue != null)
{
get => (DataTemplate) GetValue(ContentTemplateProperty);
set => SetValue(ContentTemplateProperty, value);
}
private static readonly DependencyProperty ReadyToFloatProperty = DependencyProperty.RegisterAttached(
"ReadyToFloat", typeof(bool), typeof(FloatingBlock), new PropertyMetadata(ValueBoxes.FalseBox));
private static void SetReadyToFloat(DependencyObject element, bool value)
=> element.SetValue(ReadyToFloatProperty, ValueBoxes.BooleanBox(value));
private static bool GetReadyToFloat(DependencyObject element)
=> (bool) element.GetValue(ReadyToFloatProperty);
public static readonly DependencyProperty ContentProperty = DependencyProperty.RegisterAttached(
"Content", typeof(object), typeof(FloatingBlock), new PropertyMetadata(default, OnDataChanged));
public static void SetContent(DependencyObject element, object value)
=> element.SetValue(ContentProperty, value);
public static object GetContent(DependencyObject element)
=> element.GetValue(ContentProperty);
public object Content
{
get => GetValue(ContentProperty);
set => SetValue(ContentProperty, value);
}
private static void OnDataChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (!(d is UIElement target)) return;
target.PreviewMouseLeftButtonDown -= Target_PreviewMouseLeftButtonDown;
target.PreviewMouseLeftButtonUp -= Target_PreviewMouseLeftButtonUp;
if (e.NewValue != null)
{
target.PreviewMouseLeftButtonDown += Target_PreviewMouseLeftButtonDown;
target.PreviewMouseLeftButtonUp += Target_PreviewMouseLeftButtonUp;
}
}
private static void Target_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e) => SetReadyToFloat(sender as DependencyObject, true);
private static void Target_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if (sender is UIElement element && GetReadyToFloat(element))
{
var layer = AdornerLayer.GetAdornerLayer(element);
if (layer != null)
{
var adorner = new AdornerContainer(layer)
{
IsHitTestVisible = false
};
var child = CreateBlock(element, adorner);
adorner.Child = child;
layer.Add(adorner);
}
SetReadyToFloat(element, false);
}
}
private static FloatingBlock CreateBlock(Visual element, AdornerContainer adorner)
{
var p = Mouse.GetPosition(adorner.AdornedElement);
var transform = new TranslateTransform
{
X = p.X + GetHorizontalOffset(element),
Y = p.Y + GetVerticalOffset(element)
};
var block = new FloatingBlock
{
Content = GetContent(element),
ContentTemplate = GetContentTemplate(element),
RenderTransform = new TransformGroup
{
Children =
{
transform
}
}
};
var milliseconds = GetDuration(element).TimeSpan.TotalMilliseconds;
var animationX = AnimationHelper.CreateAnimation(GetToX(element) + transform.X, milliseconds);
Storyboard.SetTargetProperty(animationX, new PropertyPath("(UIElement.RenderTransform).(TransformGroup.Children)[0].(TranslateTransform.X)"));
Storyboard.SetTarget(animationX, block);
var animationY = AnimationHelper.CreateAnimation(GetToY(element) + transform.Y, milliseconds);
Storyboard.SetTargetProperty(animationY, new PropertyPath("(UIElement.RenderTransform).(TransformGroup.Children)[0].(TranslateTransform.Y)"));
Storyboard.SetTarget(animationY, block);
var animationOpacity = AnimationHelper.CreateAnimation(0, milliseconds);
Storyboard.SetTargetProperty(animationOpacity, new PropertyPath("Opacity"));
Storyboard.SetTarget(animationOpacity, block);
var storyboard = new Storyboard();
storyboard.Completed += (s, e) =>
{
var layer = AdornerLayer.GetAdornerLayer(element);
if (layer != null)
{
layer.Remove(adorner);
}
else if (adorner.Parent is AdornerLayer parent)
{
parent.Remove(adorner);
}
adorner.Child = null;
};
storyboard.Children.Add(animationX);
storyboard.Children.Add(animationY);
storyboard.Children.Add(animationOpacity);
storyboard.Begin();
return block;
target.PreviewMouseLeftButtonDown += Target_PreviewMouseLeftButtonDown;
target.PreviewMouseLeftButtonUp += Target_PreviewMouseLeftButtonUp;
}
}
private static void Target_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e) => SetReadyToFloat(sender as DependencyObject, true);
private static void Target_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if (sender is UIElement element && GetReadyToFloat(element))
{
var layer = AdornerLayer.GetAdornerLayer(element);
if (layer != null)
{
var adorner = new AdornerContainer(layer)
{
IsHitTestVisible = false
};
var child = CreateBlock(element, adorner);
adorner.Child = child;
layer.Add(adorner);
}
SetReadyToFloat(element, false);
}
}
private static FloatingBlock CreateBlock(Visual element, AdornerContainer adorner)
{
var p = Mouse.GetPosition(adorner.AdornedElement);
var transform = new TranslateTransform
{
X = p.X + GetHorizontalOffset(element),
Y = p.Y + GetVerticalOffset(element)
};
var block = new FloatingBlock
{
Content = GetContent(element),
ContentTemplate = GetContentTemplate(element),
RenderTransform = new TransformGroup
{
Children =
{
transform
}
}
};
var milliseconds = GetDuration(element).TimeSpan.TotalMilliseconds;
var animationX = AnimationHelper.CreateAnimation(GetToX(element) + transform.X, milliseconds);
Storyboard.SetTargetProperty(animationX, new PropertyPath("(UIElement.RenderTransform).(TransformGroup.Children)[0].(TranslateTransform.X)"));
Storyboard.SetTarget(animationX, block);
var animationY = AnimationHelper.CreateAnimation(GetToY(element) + transform.Y, milliseconds);
Storyboard.SetTargetProperty(animationY, new PropertyPath("(UIElement.RenderTransform).(TransformGroup.Children)[0].(TranslateTransform.Y)"));
Storyboard.SetTarget(animationY, block);
var animationOpacity = AnimationHelper.CreateAnimation(0, milliseconds);
Storyboard.SetTargetProperty(animationOpacity, new PropertyPath("Opacity"));
Storyboard.SetTarget(animationOpacity, block);
var storyboard = new Storyboard();
storyboard.Completed += (s, e) =>
{
var layer = AdornerLayer.GetAdornerLayer(element);
if (layer != null)
{
layer.Remove(adorner);
}
else if (adorner.Parent is AdornerLayer parent)
{
parent.Remove(adorner);
}
adorner.Child = null;
};
storyboard.Children.Add(animationX);
storyboard.Children.Add(animationY);
storyboard.Children.Add(animationOpacity);
storyboard.Begin();
return block;
}
}
@@ -5,189 +5,203 @@ using System.Windows.Media.Animation;
using HandyControl.Data;
using HandyControl.Expression.Drawing;
namespace HandyControl.Controls
namespace HandyControl.Controls;
[TemplatePart(Name = ElementContent, Type = typeof(FrameworkElement))]
[TemplatePart(Name = ElementPanel, Type = typeof(Panel))]
public class RunningBlock : ContentControl
{
[TemplatePart(Name = ElementContent, Type = typeof(FrameworkElement))]
[TemplatePart(Name = ElementPanel, Type = typeof(Panel))]
public class RunningBlock : ContentControl
private const string ElementContent = "PART_ContentElement";
private const string ElementPanel = "PART_Panel";
protected Storyboard _storyboard;
private FrameworkElement _elementContent;
private FrameworkElement _elementPanel;
public override void OnApplyTemplate()
{
private const string ElementContent = "PART_ContentElement";
private const string ElementPanel = "PART_Panel";
protected Storyboard _storyboard;
private FrameworkElement _elementContent;
private FrameworkElement _elementPanel;
public override void OnApplyTemplate()
if (_elementPanel != null)
{
if (_elementPanel != null)
_elementPanel.SizeChanged -= ElementPanel_SizeChanged;
}
base.OnApplyTemplate();
_elementContent = GetTemplateChild(ElementContent) as FrameworkElement;
_elementPanel = GetTemplateChild(ElementPanel) as Panel;
if (_elementPanel != null)
{
_elementPanel.SizeChanged += ElementPanel_SizeChanged;
}
UpdateContent();
}
protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
{
base.OnRenderSizeChanged(sizeInfo);
UpdateContent();
}
private void ElementPanel_SizeChanged(object sender, SizeChangedEventArgs e) => UpdateContent();
public static readonly DependencyProperty RunawayProperty = DependencyProperty.Register(
nameof(Runaway), typeof(bool), typeof(RunningBlock), new FrameworkPropertyMetadata(ValueBoxes.TrueBox, FrameworkPropertyMetadataOptions.AffectsRender));
public bool Runaway
{
get => (bool) GetValue(RunawayProperty);
set => SetValue(RunawayProperty, ValueBoxes.BooleanBox(value));
}
public static readonly DependencyProperty AutoRunProperty = DependencyProperty.Register(
nameof(AutoRun), typeof(bool), typeof(RunningBlock), new FrameworkPropertyMetadata(ValueBoxes.FalseBox, FrameworkPropertyMetadataOptions.AffectsRender));
public bool AutoRun
{
get => (bool) GetValue(AutoRunProperty);
set => SetValue(AutoRunProperty, ValueBoxes.BooleanBox(value));
}
public static readonly DependencyProperty OrientationProperty = DependencyProperty.Register(
nameof(Orientation), typeof(Orientation), typeof(RunningBlock), new FrameworkPropertyMetadata(default(Orientation), FrameworkPropertyMetadataOptions.AffectsRender));
public Orientation Orientation
{
get => (Orientation) GetValue(OrientationProperty);
set => SetValue(OrientationProperty, value);
}
public static readonly DependencyProperty DurationProperty = DependencyProperty.Register(
nameof(Duration), typeof(Duration), typeof(RunningBlock), new FrameworkPropertyMetadata(new Duration(TimeSpan.FromSeconds(5)), FrameworkPropertyMetadataOptions.AffectsRender));
public Duration Duration
{
get => (Duration) GetValue(DurationProperty);
set => SetValue(DurationProperty, value);
}
public static readonly DependencyProperty SpeedProperty = DependencyProperty.Register(
nameof(Speed), typeof(double), typeof(RunningBlock), new FrameworkPropertyMetadata(double.NaN, FrameworkPropertyMetadataOptions.AffectsRender));
public double Speed
{
get => (double) GetValue(SpeedProperty);
set => SetValue(SpeedProperty, value);
}
public static readonly DependencyProperty IsRunningProperty = DependencyProperty.Register(
nameof(IsRunning), typeof(bool), typeof(RunningBlock), new PropertyMetadata(ValueBoxes.TrueBox, (o, args) =>
{
var ctl = (RunningBlock) o;
var v = (bool) args.NewValue;
if (v)
{
_elementPanel.SizeChanged -= ElementPanel_SizeChanged;
}
base.OnApplyTemplate();
_elementContent = GetTemplateChild(ElementContent) as FrameworkElement;
_elementPanel = GetTemplateChild(ElementPanel) as Panel;
if (_elementPanel != null)
{
_elementPanel.SizeChanged += ElementPanel_SizeChanged;
}
UpdateContent();
}
private void ElementPanel_SizeChanged(object sender, SizeChangedEventArgs e) => UpdateContent();
public static readonly DependencyProperty RunawayProperty = DependencyProperty.Register(
"Runaway", typeof(bool), typeof(RunningBlock), new FrameworkPropertyMetadata(ValueBoxes.TrueBox, FrameworkPropertyMetadataOptions.AffectsRender));
public bool Runaway
{
get => (bool) GetValue(RunawayProperty);
set => SetValue(RunawayProperty, ValueBoxes.BooleanBox(value));
}
public static readonly DependencyProperty AutoRunProperty = DependencyProperty.Register(
"AutoRun", typeof(bool), typeof(RunningBlock), new FrameworkPropertyMetadata(ValueBoxes.FalseBox, FrameworkPropertyMetadataOptions.AffectsRender));
public bool AutoRun
{
get => (bool) GetValue(AutoRunProperty);
set => SetValue(AutoRunProperty, ValueBoxes.BooleanBox(value));
}
public static readonly DependencyProperty OrientationProperty = DependencyProperty.Register(
"Orientation", typeof(Orientation), typeof(RunningBlock), new FrameworkPropertyMetadata(default(Orientation), FrameworkPropertyMetadataOptions.AffectsRender));
public Orientation Orientation
{
get => (Orientation) GetValue(OrientationProperty);
set => SetValue(OrientationProperty, value);
}
public static readonly DependencyProperty DurationProperty = DependencyProperty.Register(
"Duration", typeof(Duration), typeof(RunningBlock), new FrameworkPropertyMetadata(new Duration(TimeSpan.FromSeconds(5)), FrameworkPropertyMetadataOptions.AffectsRender));
public Duration Duration
{
get => (Duration) GetValue(DurationProperty);
set => SetValue(DurationProperty, value);
}
public static readonly DependencyProperty SpeedProperty = DependencyProperty.Register(
"Speed", typeof(double), typeof(RunningBlock), new FrameworkPropertyMetadata(double.NaN, FrameworkPropertyMetadataOptions.AffectsRender));
public double Speed
{
get => (double) GetValue(SpeedProperty);
set => SetValue(SpeedProperty, value);
}
public static readonly DependencyProperty IsRunningProperty = DependencyProperty.Register(
"IsRunning", typeof(bool), typeof(RunningBlock), new PropertyMetadata(ValueBoxes.TrueBox, (o, args) =>
{
var ctl = (RunningBlock) o;
var v = (bool) args.NewValue;
if (v)
{
ctl._storyboard?.Resume();
}
else
{
ctl._storyboard?.Pause();
}
}));
public bool IsRunning
{
get => (bool) GetValue(IsRunningProperty);
set => SetValue(IsRunningProperty, ValueBoxes.BooleanBox(value));
}
public static readonly DependencyProperty AutoReverseProperty = DependencyProperty.Register(
"AutoReverse", typeof(bool), typeof(RunningBlock), new FrameworkPropertyMetadata(ValueBoxes.FalseBox, FrameworkPropertyMetadataOptions.AffectsRender));
public bool AutoReverse
{
get => (bool) GetValue(AutoReverseProperty);
set => SetValue(AutoReverseProperty, ValueBoxes.BooleanBox(value));
}
private void UpdateContent()
{
if (_elementContent == null || _elementPanel == null) return;
if (MathHelper.IsZero(_elementPanel.ActualWidth) || MathHelper.IsZero(_elementPanel.ActualHeight)) return;
_storyboard?.Stop();
double from;
double to;
PropertyPath propertyPath;
if (Orientation == Orientation.Horizontal)
{
if (AutoRun && _elementPanel.ActualWidth < ActualWidth)
{
return;
}
if (Runaway)
{
from = -_elementPanel.ActualWidth;
to = ActualWidth;
}
else
{
from = 0;
to = ActualWidth - _elementPanel.ActualWidth;
SetCurrentValue(AutoReverseProperty, ValueBoxes.TrueBox);
}
propertyPath = new PropertyPath("(UIElement.RenderTransform).(TransformGroup.Children)[0].(TranslateTransform.X)");
ctl._storyboard?.Resume();
}
else
{
if (AutoRun && _elementPanel.ActualHeight < ActualHeight)
{
return;
}
ctl._storyboard?.Pause();
}
}));
if (Runaway)
{
from = -_elementPanel.ActualHeight;
to = ActualHeight;
}
else
{
from = 0;
to = ActualHeight - _elementPanel.ActualHeight;
SetCurrentValue(AutoReverseProperty, ValueBoxes.TrueBox);
}
propertyPath = new PropertyPath("(UIElement.RenderTransform).(TransformGroup.Children)[0].(TranslateTransform.Y)");
public bool IsRunning
{
get => (bool) GetValue(IsRunningProperty);
set => SetValue(IsRunningProperty, ValueBoxes.BooleanBox(value));
}
public static readonly DependencyProperty AutoReverseProperty = DependencyProperty.Register(
nameof(AutoReverse), typeof(bool), typeof(RunningBlock), new FrameworkPropertyMetadata(ValueBoxes.FalseBox, FrameworkPropertyMetadataOptions.AffectsRender));
public bool AutoReverse
{
get => (bool) GetValue(AutoReverseProperty);
set => SetValue(AutoReverseProperty, ValueBoxes.BooleanBox(value));
}
public static readonly DependencyProperty RunningDirectionProperty = DependencyProperty.Register(
nameof(RunningDirection), typeof(RunningDirection), typeof(RunningBlock), new PropertyMetadata(RunningDirection.EndToStart));
public RunningDirection RunningDirection
{
get => (RunningDirection) GetValue(RunningDirectionProperty);
set => SetValue(RunningDirectionProperty, value);
}
private void UpdateContent()
{
if (_elementContent == null || _elementPanel == null) return;
if (MathHelper.IsZero(_elementPanel.ActualWidth) || MathHelper.IsZero(_elementPanel.ActualHeight)) return;
_storyboard?.Stop();
double from;
double to;
PropertyPath propertyPath;
if (Orientation == Orientation.Horizontal)
{
if (AutoRun && _elementPanel.ActualWidth < ActualWidth)
{
return;
}
var duration = double.IsNaN(Speed)
? Duration
: !MathHelper.IsVerySmall(Speed)
? TimeSpan.FromSeconds((to - from) / Speed)
: Duration;
var animation = new DoubleAnimation(from, to, duration)
if (Runaway)
{
RepeatBehavior = RepeatBehavior.Forever,
AutoReverse = AutoReverse
};
Storyboard.SetTargetProperty(animation, propertyPath);
Storyboard.SetTarget(animation, _elementContent);
_storyboard = new Storyboard();
_storyboard.Children.Add(animation);
_storyboard.Begin();
from = -_elementPanel.ActualWidth;
to = ActualWidth;
}
else
{
from = 0;
to = ActualWidth - _elementPanel.ActualWidth;
SetCurrentValue(AutoReverseProperty, ValueBoxes.TrueBox);
}
propertyPath = new PropertyPath("(UIElement.RenderTransform).(TransformGroup.Children)[0].(TranslateTransform.X)");
}
else
{
if (AutoRun && _elementPanel.ActualHeight < ActualHeight)
{
return;
}
if (Runaway)
{
from = -_elementPanel.ActualHeight;
to = ActualHeight;
}
else
{
from = 0;
to = ActualHeight - _elementPanel.ActualHeight;
SetCurrentValue(AutoReverseProperty, ValueBoxes.TrueBox);
}
propertyPath = new PropertyPath("(UIElement.RenderTransform).(TransformGroup.Children)[0].(TranslateTransform.Y)");
}
var duration = double.IsNaN(Speed)
? Duration
: !MathHelper.IsVerySmall(Speed)
? TimeSpan.FromSeconds(Math.Abs(to - from) / Speed)
: Duration;
var animation = RunningDirection is RunningDirection.EndToStart
? new DoubleAnimation(to, from, duration)
: new DoubleAnimation(from, to, duration);
animation.RepeatBehavior = RepeatBehavior.Forever;
animation.AutoReverse = AutoReverse;
Storyboard.SetTargetProperty(animation, propertyPath);
Storyboard.SetTarget(animation, _elementContent);
_storyboard = new Storyboard();
_storyboard.Children.Add(animation);
_storyboard.Begin();
}
}
@@ -3,63 +3,62 @@ using System.Windows;
using System.Windows.Controls;
using HandyControl.Tools;
namespace HandyControl.Controls
namespace HandyControl.Controls;
public class RunningBorder : Border
{
public class RunningBorder : Border
private bool _test;
protected override Size MeasureOverride(Size constraint)
{
private bool _test;
protected override Size MeasureOverride(Size constraint)
if (_test)
{
if (_test)
{
_test = false;
return constraint;
}
var child = Child;
var borderThickness = BorderThickness;
var padding = Padding;
if (UseLayoutRounding)
{
var dpiScaleX = DpiHelper.DeviceDpiX;
var dpiScaleY = DpiHelper.DeviceDpiY;
borderThickness = new Thickness(
DpiHelper.RoundLayoutValue(borderThickness.Left, dpiScaleX),
DpiHelper.RoundLayoutValue(borderThickness.Top, dpiScaleY),
DpiHelper.RoundLayoutValue(borderThickness.Right, dpiScaleX),
DpiHelper.RoundLayoutValue(borderThickness.Bottom, dpiScaleY));
}
var borderSize = ConvertThickness2Size(borderThickness);
var paddingSize = ConvertThickness2Size(padding);
var mySize = new Size();
if (child != null)
{
var combined = new Size(borderSize.Width + paddingSize.Width, borderSize.Height + paddingSize.Height);
var borderConstraint = new Size(Math.Max(0.0, constraint.Width - combined.Width), Math.Max(0.0, constraint.Height - combined.Height));
var childConstraint = new Size(Math.Max(0.0, double.PositiveInfinity - combined.Width), Math.Max(0.0, double.PositiveInfinity - combined.Height));
child.Measure(borderConstraint);
var childSize = child.DesiredSize;
mySize.Width = childSize.Width + combined.Width;
mySize.Height = childSize.Height + combined.Height;
child.Measure(childConstraint);
}
else
{
mySize = new Size(borderSize.Width + paddingSize.Width, borderSize.Height + paddingSize.Height);
}
return mySize;
_test = false;
return constraint;
}
private static Size ConvertThickness2Size(Thickness th) => new Size(th.Left + th.Right, th.Top + th.Bottom);
var child = Child;
var borderThickness = BorderThickness;
var padding = Padding;
if (UseLayoutRounding)
{
var dpiScaleX = DpiHelper.DeviceDpiX;
var dpiScaleY = DpiHelper.DeviceDpiY;
borderThickness = new Thickness(
DpiHelper.RoundLayoutValue(borderThickness.Left, dpiScaleX),
DpiHelper.RoundLayoutValue(borderThickness.Top, dpiScaleY),
DpiHelper.RoundLayoutValue(borderThickness.Right, dpiScaleX),
DpiHelper.RoundLayoutValue(borderThickness.Bottom, dpiScaleY));
}
var borderSize = ConvertThickness2Size(borderThickness);
var paddingSize = ConvertThickness2Size(padding);
var mySize = new Size();
if (child != null)
{
var combined = new Size(borderSize.Width + paddingSize.Width, borderSize.Height + paddingSize.Height);
var borderConstraint = new Size(Math.Max(0.0, constraint.Width - combined.Width), Math.Max(0.0, constraint.Height - combined.Height));
var childConstraint = new Size(Math.Max(0.0, double.PositiveInfinity - combined.Width), Math.Max(0.0, double.PositiveInfinity - combined.Height));
child.Measure(borderConstraint);
var childSize = child.DesiredSize;
mySize.Width = childSize.Width + combined.Width;
mySize.Height = childSize.Height + combined.Height;
child.Measure(childConstraint);
}
else
{
mySize = new Size(borderSize.Width + paddingSize.Width, borderSize.Height + paddingSize.Height);
}
return mySize;
}
private static Size ConvertThickness2Size(Thickness th) => new(th.Left + th.Right, th.Top + th.Bottom);
}
@@ -1,59 +1,90 @@
using System.ComponentModel;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Markup;
using HandyControl.Data;
using HandyControl.Input;
using HandyControl.Interactivity;
namespace HandyControl.Controls
namespace HandyControl.Controls;
/// <summary>
/// 切换块
/// </summary>
public class ToggleBlock : Control
{
/// <summary>
/// 切换块
/// </summary>
public class ToggleBlock : Control
public static readonly DependencyProperty IsCheckedProperty = DependencyProperty.Register(nameof(IsChecked), typeof(bool?), typeof(ToggleBlock), new FrameworkPropertyMetadata(ValueBoxes.FalseBox, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault | FrameworkPropertyMetadataOptions.Journal));
public static readonly DependencyProperty CheckedContentProperty = DependencyProperty.Register(nameof(CheckedContent), typeof(object), typeof(ToggleBlock), new PropertyMetadata(default(object)));
public static readonly DependencyProperty UnCheckedContentProperty = DependencyProperty.Register(nameof(UnCheckedContent), typeof(object), typeof(ToggleBlock), new PropertyMetadata(default(object)));
public static readonly DependencyProperty IndeterminateContentProperty = DependencyProperty.Register(nameof(IndeterminateContent), typeof(object), typeof(ToggleBlock), new PropertyMetadata(default(object)));
public static readonly DependencyProperty ToggleGestureProperty = DependencyProperty.Register(nameof(ToggleGesture), typeof(MouseGesture), typeof(ToggleBlock), new UIPropertyMetadata(new MouseGesture(MouseAction.None), OnToggleGestureChanged));
[Category("Appearance")]
[TypeConverter(typeof(NullableBoolConverter))]
[Localizability(LocalizationCategory.None, Readability = Readability.Unreadable)]
public bool? IsChecked
{
public static readonly DependencyProperty IsCheckedProperty = DependencyProperty.Register(
"IsChecked", typeof(bool?), typeof(ToggleBlock), new FrameworkPropertyMetadata(ValueBoxes.FalseBox,
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault | FrameworkPropertyMetadataOptions.Journal));
[Category("Appearance")]
[TypeConverter(typeof(NullableBoolConverter))]
[Localizability(LocalizationCategory.None, Readability = Readability.Unreadable)]
public bool? IsChecked
get
{
get
{
// Because Nullable<bool> unboxing is very slow (uses reflection) first we cast to bool
var value = GetValue(IsCheckedProperty);
// ReSharper disable once RedundantExplicitNullableCreation
return value == null ? new bool?() : new bool?((bool) value);
}
set => SetValue(IsCheckedProperty, value.HasValue ? ValueBoxes.BooleanBox(value.Value) : null);
// Because Nullable<bool> unboxing is very slow (uses reflection) first we cast to bool
var value = GetValue(IsCheckedProperty);
// ReSharper disable once RedundantExplicitNullableCreation
return value == null ? new bool?() : new bool?((bool) value);
}
set => SetValue(IsCheckedProperty, value.HasValue ? ValueBoxes.BooleanBox(value.Value) : null);
}
public object CheckedContent
{
get => GetValue(CheckedContentProperty);
set => SetValue(CheckedContentProperty, value);
}
public object UnCheckedContent
{
get => GetValue(UnCheckedContentProperty);
set => SetValue(UnCheckedContentProperty, value);
}
public object IndeterminateContent
{
get => GetValue(IndeterminateContentProperty);
set => SetValue(IndeterminateContentProperty, value);
}
[ValueSerializer(typeof(MouseGestureValueSerializer))]
[TypeConverter(typeof(MouseGestureConverter))]
public MouseGesture ToggleGesture
{
get => (MouseGesture) GetValue(ToggleGestureProperty);
set => SetValue(ToggleGestureProperty, value);
}
public ToggleBlock()
{
CommandBindings.Add(new CommandBinding(ControlCommands.Toggle, OnToggled));
OnToggleGestureChanged(ToggleGesture);
}
private static void OnToggleGestureChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((ToggleBlock) d).OnToggleGestureChanged((MouseGesture) e.NewValue);
}
private void OnToggleGestureChanged(MouseGesture newValue)
{
foreach (var binding in InputBindings.OfType<SimpleMouseBinding>().ToList())
{
InputBindings.Remove(binding);
}
public static readonly DependencyProperty CheckedContentProperty = DependencyProperty.Register(
"CheckedContent", typeof(object), typeof(ToggleBlock), new PropertyMetadata(default(object)));
InputBindings.Add(new SimpleMouseBinding(ControlCommands.Toggle, newValue));
}
public object CheckedContent
{
get => GetValue(CheckedContentProperty);
set => SetValue(CheckedContentProperty, value);
}
public static readonly DependencyProperty UnCheckedContentProperty = DependencyProperty.Register(
"UnCheckedContent", typeof(object), typeof(ToggleBlock), new PropertyMetadata(default(object)));
public object UnCheckedContent
{
get => GetValue(UnCheckedContentProperty);
set => SetValue(UnCheckedContentProperty, value);
}
public static readonly DependencyProperty IndeterminateContentProperty = DependencyProperty.Register(
"IndeterminateContent", typeof(object), typeof(ToggleBlock), new PropertyMetadata(default(object)));
public object IndeterminateContent
{
get => GetValue(IndeterminateContentProperty);
set => SetValue(IndeterminateContentProperty, value);
}
private void OnToggled(object sender, ExecutedRoutedEventArgs e)
{
SetCurrentValue(IsCheckedProperty, IsChecked == true ? ValueBoxes.FalseBox : ValueBoxes.TrueBox);
}
}
@@ -1,32 +1,40 @@
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Media;
using HandyControl.Data;
namespace HandyControl.Controls
namespace HandyControl.Controls;
public class ButtonGroup : ItemsControl
{
public class ButtonGroup : ItemsControl
protected override bool IsItemItsOwnContainerOverride(object item) => item is Button or RadioButton or ToggleButton;
public static readonly DependencyProperty OrientationProperty = DependencyProperty.Register(
nameof(Orientation), typeof(Orientation), typeof(ButtonGroup), new PropertyMetadata(default(Orientation)));
public Orientation Orientation
{
protected override bool IsItemItsOwnContainerOverride(object item) => item is Button || item is RadioButton || item is ToggleButton;
get => (Orientation) GetValue(OrientationProperty);
set => SetValue(OrientationProperty, value);
}
public static readonly DependencyProperty OrientationProperty = DependencyProperty.Register(
"Orientation", typeof(Orientation), typeof(ButtonGroup), new PropertyMetadata(default(Orientation)));
public static readonly DependencyProperty LayoutProperty = DependencyProperty.Register(
nameof(Layout), typeof(LinearLayout), typeof(ButtonGroup), new PropertyMetadata(LinearLayout.Uniform));
public Orientation Orientation
public LinearLayout Layout
{
get => (LinearLayout) GetValue(LayoutProperty);
set => SetValue(LayoutProperty, value);
}
protected override void OnRender(DrawingContext drawingContext)
{
var count = Items.Count;
for (var i = 0; i < count; i++)
{
get => (Orientation) GetValue(OrientationProperty);
set => SetValue(OrientationProperty, value);
}
protected override void OnVisualChildrenChanged(DependencyObject visualAdded, DependencyObject visualRemoved)
{
base.OnVisualChildrenChanged(visualAdded, visualRemoved);
var count = Items.Count;
for (var i = 0; i < count; i++)
{
var item = (ButtonBase) Items[i];
item.Style = ItemContainerStyleSelector?.SelectStyle(item, this);
}
var item = (ButtonBase) Items[i];
item.Style = ItemContainerStyleSelector?.SelectStyle(item, this);
}
}
}
@@ -1,23 +1,22 @@
using System.Windows.Controls;
namespace HandyControl.Controls
{
/// <summary>
/// 带上下文菜单的按钮
/// </summary>
public class ContextMenuButton : Button
{
public ContextMenu Menu { get; set; }
namespace HandyControl.Controls;
protected override void OnClick()
/// <summary>
/// 带上下文菜单的按钮
/// </summary>
public class ContextMenuButton : Button
{
public ContextMenu Menu { get; set; }
protected override void OnClick()
{
base.OnClick();
if (Menu != null)
{
base.OnClick();
if (Menu != null)
{
Menu.PlacementTarget = this;
Menu.IsOpen = true;
}
Menu.PlacementTarget = this;
Menu.IsOpen = true;
}
}
}
@@ -2,29 +2,28 @@
using System.Windows.Controls.Primitives;
namespace HandyControl.Controls
{
/// <summary>
/// 带上下文菜单的切换按钮
/// </summary>
public class ContextMenuToggleButton : ToggleButton
{
public ContextMenu Menu { get; set; }
namespace HandyControl.Controls;
protected override void OnClick()
/// <summary>
/// 带上下文菜单的切换按钮
/// </summary>
public class ContextMenuToggleButton : ToggleButton
{
public ContextMenu Menu { get; set; }
protected override void OnClick()
{
base.OnClick();
if (Menu != null)
{
base.OnClick();
if (Menu != null)
if (IsChecked == true)
{
if (IsChecked == true)
{
Menu.PlacementTarget = this;
Menu.IsOpen = true;
}
else
{
Menu.IsOpen = false;
}
Menu.PlacementTarget = this;
Menu.IsOpen = true;
}
else
{
Menu.IsOpen = false;
}
}
}
@@ -2,26 +2,25 @@
using System.Windows.Controls.Primitives;
using HandyControl.Data;
namespace HandyControl.Controls
namespace HandyControl.Controls;
public class ProgressButton : ToggleButton
{
public class ProgressButton : ToggleButton
public static readonly DependencyProperty ProgressStyleProperty = DependencyProperty.Register(
nameof(ProgressStyle), typeof(Style), typeof(ProgressButton), new PropertyMetadata(default(Style)));
public Style ProgressStyle
{
public static readonly DependencyProperty ProgressStyleProperty = DependencyProperty.Register(
"ProgressStyle", typeof(Style), typeof(ProgressButton), new PropertyMetadata(default(Style)));
get => (Style) GetValue(ProgressStyleProperty);
set => SetValue(ProgressStyleProperty, value);
}
public Style ProgressStyle
{
get => (Style) GetValue(ProgressStyleProperty);
set => SetValue(ProgressStyleProperty, value);
}
public static readonly DependencyProperty ProgressProperty = DependencyProperty.Register(
nameof(Progress), typeof(double), typeof(ProgressButton), new PropertyMetadata(ValueBoxes.Double0Box));
public static readonly DependencyProperty ProgressProperty = DependencyProperty.Register(
"Progress", typeof(double), typeof(ProgressButton), new PropertyMetadata(ValueBoxes.Double0Box));
public double Progress
{
get => (double) GetValue(ProgressProperty);
set => SetValue(ProgressProperty, value);
}
public double Progress
{
get => (double) GetValue(ProgressProperty);
set => SetValue(ProgressProperty, value);
}
}
@@ -5,77 +5,76 @@ using System.Windows.Input;
using HandyControl.Data;
using HandyControl.Data.Enum;
namespace HandyControl.Controls
namespace HandyControl.Controls;
public class SplitButton : ButtonBase
{
public class SplitButton : ButtonBase
public static readonly DependencyProperty HitModeProperty = DependencyProperty.Register(
nameof(HitMode), typeof(HitMode), typeof(SplitButton), new PropertyMetadata(default(HitMode)));
public HitMode HitMode
{
public static readonly DependencyProperty HitModeProperty = DependencyProperty.Register(
"HitMode", typeof(HitMode), typeof(SplitButton), new PropertyMetadata(default(HitMode)));
get => (HitMode) GetValue(HitModeProperty);
set => SetValue(HitModeProperty, value);
}
public HitMode HitMode
public static readonly DependencyProperty MaxDropDownHeightProperty = DependencyProperty.Register(
nameof(MaxDropDownHeight), typeof(double), typeof(SplitButton), new PropertyMetadata(SystemParameters.PrimaryScreenHeight / 3.0));
public double MaxDropDownHeight
{
get => (double) GetValue(MaxDropDownHeightProperty);
set => SetValue(MaxDropDownHeightProperty, value);
}
public static readonly DependencyProperty IsDropDownOpenProperty = DependencyProperty.Register(
nameof(IsDropDownOpen), typeof(bool), typeof(SplitButton), new PropertyMetadata(ValueBoxes.FalseBox));
public bool IsDropDownOpen
{
get => (bool) GetValue(IsDropDownOpenProperty);
set => SetValue(IsDropDownOpenProperty, ValueBoxes.BooleanBox(value));
}
public static readonly DependencyProperty DropDownContentProperty = DependencyProperty.Register(
nameof(DropDownContent), typeof(object), typeof(SplitButton), new PropertyMetadata(default(object)));
public object DropDownContent
{
get => GetValue(DropDownContentProperty);
set => SetValue(DropDownContentProperty, value);
}
public SplitButton()
{
AddHandler(MenuItem.ClickEvent, new RoutedEventHandler(ItemsOnClick));
}
private void ItemsOnClick(object sender, RoutedEventArgs e)
{
if (e.OriginalSource is MenuItem)
{
get => (HitMode) GetValue(HitModeProperty);
set => SetValue(HitModeProperty, value);
SetCurrentValue(IsDropDownOpenProperty, ValueBoxes.FalseBox);
}
}
public static readonly DependencyProperty MaxDropDownHeightProperty = DependencyProperty.Register(
"MaxDropDownHeight", typeof(double), typeof(SplitButton), new PropertyMetadata(SystemParameters.PrimaryScreenHeight / 3.0));
protected override void OnPreviewMouseLeftButtonDown(MouseButtonEventArgs e)
{
base.OnPreviewMouseLeftButtonDown(e);
public double MaxDropDownHeight
if (HitMode == HitMode.Hover)
{
get => (double) GetValue(MaxDropDownHeightProperty);
set => SetValue(MaxDropDownHeightProperty, value);
e.Handled = true;
}
}
public static readonly DependencyProperty IsDropDownOpenProperty = DependencyProperty.Register(
"IsDropDownOpen", typeof(bool), typeof(SplitButton), new PropertyMetadata(ValueBoxes.FalseBox));
protected override void OnMouseEnter(MouseEventArgs e)
{
base.OnMouseEnter(e);
public bool IsDropDownOpen
if (HitMode == HitMode.Hover)
{
get => (bool) GetValue(IsDropDownOpenProperty);
set => SetValue(IsDropDownOpenProperty, ValueBoxes.BooleanBox(value));
}
public static readonly DependencyProperty DropDownContentProperty = DependencyProperty.Register(
"DropDownContent", typeof(object), typeof(SplitButton), new PropertyMetadata(default(object)));
public object DropDownContent
{
get => GetValue(DropDownContentProperty);
set => SetValue(DropDownContentProperty, value);
}
public SplitButton()
{
AddHandler(MenuItem.ClickEvent, new RoutedEventHandler(ItemsOnClick));
}
private void ItemsOnClick(object sender, RoutedEventArgs e)
{
if (e.OriginalSource is MenuItem)
{
SetCurrentValue(IsDropDownOpenProperty, ValueBoxes.FalseBox);
}
}
protected override void OnPreviewMouseLeftButtonDown(MouseButtonEventArgs e)
{
base.OnPreviewMouseLeftButtonDown(e);
if (HitMode == HitMode.Hover)
{
e.Handled = true;
}
}
protected override void OnMouseEnter(MouseEventArgs e)
{
base.OnMouseEnter(e);
if (HitMode == HitMode.Hover)
{
SetCurrentValue(IsDropDownOpenProperty, ValueBoxes.TrueBox);
}
SetCurrentValue(IsDropDownOpenProperty, ValueBoxes.TrueBox);
}
}
}
@@ -14,443 +14,442 @@ using HandyControl.Data;
using HandyControl.Interactivity;
using HandyControl.Tools;
namespace HandyControl.Controls
namespace HandyControl.Controls;
/// <summary>
/// 轮播控件
/// </summary>
[DefaultProperty("Items")]
[ContentProperty("Items")]
[TemplatePart(Name = ElementPanelPage, Type = typeof(Panel))]
public class Carousel : SimpleItemsControl, IDisposable
{
/// <summary>
/// 轮播控件
/// </summary>
[DefaultProperty("Items")]
[ContentProperty("Items")]
[TemplatePart(Name = ElementPanelPage, Type = typeof(Panel))]
public class Carousel : SimpleItemsControl, IDisposable
#region Constants
private const string ElementPanelPage = "PART_PanelPage";
#endregion Constants
#region Data
private bool _isDisposed;
private Panel _panelPage;
private bool _appliedTemplate;
private int _pageIndex = -1;
private RadioButton _selectedButton;
private DispatcherTimer _updateTimer;
private readonly List<double> _widthList = new();
private readonly Dictionary<object, CarouselItem> _entryDic = new();
private bool _isRefresh;
private IEnumerable _itemsSourceInternal;
#endregion Data
public override void OnApplyTemplate()
{
#region Constants
_appliedTemplate = false;
private const string ElementPanelPage = "PART_PanelPage";
_panelPage?.RemoveHandler(ButtonBase.ClickEvent, new RoutedEventHandler(ButtonPages_OnClick));
#endregion Constants
base.OnApplyTemplate();
#region Data
_panelPage = GetTemplateChild(ElementPanelPage) as Panel;
private bool _isDisposed;
if (!CheckNull()) return;
private Panel _panelPage;
_panelPage.AddHandler(ButtonBase.ClickEvent, new RoutedEventHandler(ButtonPages_OnClick));
_appliedTemplate = true;
Update();
}
private bool _appliedTemplate;
private void Update()
{
TimerSwitch(AutoRun);
UpdatePageButtons(_pageIndex);
}
private int _pageIndex = -1;
private bool CheckNull() => _panelPage != null;
private RadioButton _selectedButton;
private DispatcherTimer _updateTimer;
private readonly List<double> _widthList = new List<double>();
private readonly Dictionary<object, CarouselItem> _entryDic = new Dictionary<object, CarouselItem>();
private bool _isRefresh;
private IEnumerable _itemsSourceInternal;
#endregion Data
public override void OnApplyTemplate()
public static readonly DependencyProperty AutoRunProperty = DependencyProperty.Register(
nameof(AutoRun), typeof(bool), typeof(Carousel), new PropertyMetadata(ValueBoxes.FalseBox, (o, args) =>
{
_appliedTemplate = false;
var ctl = (Carousel) o;
ctl.TimerSwitch((bool) args.NewValue);
}));
_panelPage?.RemoveHandler(ButtonBase.ClickEvent, new RoutedEventHandler(ButtonPages_OnClick));
public static readonly DependencyProperty IntervalProperty = DependencyProperty.Register(
nameof(Interval), typeof(TimeSpan), typeof(Carousel), new PropertyMetadata(TimeSpan.FromSeconds(2)));
base.OnApplyTemplate();
public static readonly DependencyProperty ExtendWidthProperty = DependencyProperty.Register(
nameof(ExtendWidth), typeof(double), typeof(Carousel), new PropertyMetadata(ValueBoxes.Double0Box));
_panelPage = GetTemplateChild(ElementPanelPage) as Panel;
public double ExtendWidth
{
get => (double) GetValue(ExtendWidthProperty);
set => SetValue(ExtendWidthProperty, value);
}
if (!CheckNull()) return;
public static readonly DependencyProperty IsCenterProperty = DependencyProperty.Register(
nameof(IsCenter), typeof(bool), typeof(Carousel), new PropertyMetadata(ValueBoxes.FalseBox));
_panelPage.AddHandler(ButtonBase.ClickEvent, new RoutedEventHandler(ButtonPages_OnClick));
_appliedTemplate = true;
Update();
}
public bool IsCenter
{
get => (bool) GetValue(IsCenterProperty);
set => SetValue(IsCenterProperty, ValueBoxes.BooleanBox(value));
}
private void Update()
public static readonly DependencyProperty PageButtonStyleProperty = DependencyProperty.Register(
nameof(PageButtonStyle), typeof(Style), typeof(Carousel), new PropertyMetadata(default(Style)));
public Style PageButtonStyle
{
get => (Style) GetValue(PageButtonStyleProperty);
set => SetValue(PageButtonStyleProperty, value);
}
public Carousel()
{
CommandBindings.Add(new CommandBinding(ControlCommands.Prev, ButtonPrev_OnClick));
CommandBindings.Add(new CommandBinding(ControlCommands.Next, ButtonNext_OnClick));
CommandBindings.Add(new CommandBinding(ControlCommands.Selected, ButtonPages_OnClick));
Loaded += (s, e) => UpdatePageButtons();
IsVisibleChanged += Carousel_IsVisibleChanged;
}
~Carousel() => Dispose();
public void Dispose()
{
if (_isDisposed) return;
IsVisibleChanged -= Carousel_IsVisibleChanged;
_updateTimer?.Stop();
_isDisposed = true;
GC.SuppressFinalize(this);
}
private void Carousel_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if (_updateTimer == null) return;
if (IsVisible)
{
TimerSwitch(AutoRun);
UpdatePageButtons(_pageIndex);
}
private bool CheckNull() => _panelPage != null;
public static readonly DependencyProperty AutoRunProperty = DependencyProperty.Register(
"AutoRun", typeof(bool), typeof(Carousel), new PropertyMetadata(ValueBoxes.FalseBox, (o, args) =>
{
var ctl = (Carousel) o;
ctl.TimerSwitch((bool) args.NewValue);
}));
public static readonly DependencyProperty IntervalProperty = DependencyProperty.Register(
"Interval", typeof(TimeSpan), typeof(Carousel), new PropertyMetadata(TimeSpan.FromSeconds(2)));
public static readonly DependencyProperty ExtendWidthProperty = DependencyProperty.Register(
"ExtendWidth", typeof(double), typeof(Carousel), new PropertyMetadata(ValueBoxes.Double0Box));
public double ExtendWidth
{
get => (double) GetValue(ExtendWidthProperty);
set => SetValue(ExtendWidthProperty, value);
}
public static readonly DependencyProperty IsCenterProperty = DependencyProperty.Register(
"IsCenter", typeof(bool), typeof(Carousel), new PropertyMetadata(ValueBoxes.FalseBox));
public bool IsCenter
{
get => (bool) GetValue(IsCenterProperty);
set => SetValue(IsCenterProperty, ValueBoxes.BooleanBox(value));
}
public static readonly DependencyProperty PageButtonStyleProperty = DependencyProperty.Register(
"PageButtonStyle", typeof(Style), typeof(Carousel), new PropertyMetadata(default(Style)));
public Style PageButtonStyle
{
get => (Style) GetValue(PageButtonStyleProperty);
set => SetValue(PageButtonStyleProperty, value);
}
public Carousel()
{
CommandBindings.Add(new CommandBinding(ControlCommands.Prev, ButtonPrev_OnClick));
CommandBindings.Add(new CommandBinding(ControlCommands.Next, ButtonNext_OnClick));
CommandBindings.Add(new CommandBinding(ControlCommands.Selected, ButtonPages_OnClick));
Loaded += (s, e) => UpdatePageButtons();
IsVisibleChanged += Carousel_IsVisibleChanged;
}
~Carousel() => Dispose();
public void Dispose()
{
if (_isDisposed) return;
IsVisibleChanged -= Carousel_IsVisibleChanged;
_updateTimer?.Stop();
_isDisposed = true;
GC.SuppressFinalize(this);
}
private void Carousel_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if (_updateTimer == null) return;
if (IsVisible)
{
_updateTimer.Tick += UpdateTimer_Tick;
_updateTimer.Start();
}
else
{
_updateTimer.Stop();
_updateTimer.Tick -= UpdateTimer_Tick;
}
}
/// <summary>
/// 是否自动跳转
/// </summary>
public bool AutoRun
{
get => (bool) GetValue(AutoRunProperty);
set => SetValue(AutoRunProperty, ValueBoxes.BooleanBox(value));
}
/// <summary>
/// 跳转时间间隔
/// </summary>
public TimeSpan Interval
{
get => (TimeSpan) GetValue(IntervalProperty);
set => SetValue(IntervalProperty, value);
}
/// <summary>
/// 页码
/// </summary>
public int PageIndex
{
get => _pageIndex;
set
{
if (Items.Count == 0) return;
if (_pageIndex == value) return;
if (value < 0)
_pageIndex = Items.Count - 1;
else if (value >= Items.Count)
_pageIndex = 0;
else
_pageIndex = value;
UpdatePageButtons(_pageIndex);
}
}
/// <summary>
/// 计时器开关
/// </summary>
private void TimerSwitch(bool run)
{
if (!_appliedTemplate) return;
if (_updateTimer != null)
{
_updateTimer.Tick -= UpdateTimer_Tick;
_updateTimer.Stop();
_updateTimer = null;
}
if (!run) return;
_updateTimer = new DispatcherTimer
{
Interval = Interval
};
_updateTimer.Tick += UpdateTimer_Tick;
_updateTimer.Start();
}
private void UpdateTimer_Tick(object sender, EventArgs e)
else
{
if (IsMouseOver) return;
PageIndex++;
_updateTimer.Stop();
_updateTimer.Tick -= UpdateTimer_Tick;
}
}
/// <summary>
/// 更新页按钮
/// </summary>
public void UpdatePageButtons(int index = -1)
/// <summary>
/// 是否自动跳转
/// </summary>
public bool AutoRun
{
get => (bool) GetValue(AutoRunProperty);
set => SetValue(AutoRunProperty, ValueBoxes.BooleanBox(value));
}
/// <summary>
/// 跳转时间间隔
/// </summary>
public TimeSpan Interval
{
get => (TimeSpan) GetValue(IntervalProperty);
set => SetValue(IntervalProperty, value);
}
/// <summary>
/// 页码
/// </summary>
public int PageIndex
{
get => _pageIndex;
set
{
if (!CheckNull()) return;
if (!_appliedTemplate) return;
var count = Items.Count;
_widthList.Clear();
_widthList.Add(0);
var width = .0;
foreach (FrameworkElement item in ItemsHost.Children)
{
item.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
width += item.DesiredSize.Width;
_widthList.Add(width);
}
ItemsHost.Width = _widthList.Last() + ExtendWidth;
_panelPage.Children.Clear();
for (var i = 0; i < count; i++)
{
_panelPage.Children.Add(new RadioButton
{
Style = PageButtonStyle
});
}
if (index == -1 && count > 0) index = 0;
if (index >= 0 && index < count)
{
if (_panelPage.Children[index] is RadioButton button)
{
button.IsChecked = true;
button.RaiseEvent(new RoutedEventArgs(ButtonBase.ClickEvent, button));
UpdateItemsPosition();
}
}
}
/// <summary>
/// 更新项的位置
/// </summary>
private void UpdateItemsPosition()
{
if (!CheckNull()) return;
if (!_appliedTemplate) return;
if (Items.Count == 0) return;
if (!IsCenter)
if (_pageIndex == value) return;
if (value < 0)
_pageIndex = Items.Count - 1;
else if (value >= Items.Count)
_pageIndex = 0;
else
_pageIndex = value;
UpdatePageButtons(_pageIndex);
}
}
/// <summary>
/// 计时器开关
/// </summary>
private void TimerSwitch(bool run)
{
if (!_appliedTemplate) return;
if (_updateTimer != null)
{
_updateTimer.Tick -= UpdateTimer_Tick;
_updateTimer.Stop();
_updateTimer = null;
}
if (!run) return;
_updateTimer = new DispatcherTimer
{
Interval = Interval
};
_updateTimer.Tick += UpdateTimer_Tick;
_updateTimer.Start();
}
private void UpdateTimer_Tick(object sender, EventArgs e)
{
if (IsMouseOver) return;
PageIndex++;
}
/// <summary>
/// 更新页按钮
/// </summary>
public void UpdatePageButtons(int index = -1)
{
if (!CheckNull()) return;
if (!_appliedTemplate) return;
var count = Items.Count;
_widthList.Clear();
_widthList.Add(0);
var width = .0;
foreach (FrameworkElement item in ItemsHost.Children)
{
item.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
width += item.DesiredSize.Width;
_widthList.Add(width);
}
ItemsHost.Width = _widthList.Last() + ExtendWidth;
_panelPage.Children.Clear();
for (var i = 0; i < count; i++)
{
_panelPage.Children.Add(new RadioButton
{
ItemsHost.BeginAnimation(MarginProperty,
AnimationHelper.CreateAnimation(new Thickness(-_widthList[PageIndex], 0, 0, 0)));
Style = PageButtonStyle
});
}
if (index == -1 && count > 0) index = 0;
if (index >= 0 && index < count)
{
if (_panelPage.Children[index] is RadioButton button)
{
button.IsChecked = true;
button.RaiseEvent(new RoutedEventArgs(ButtonBase.ClickEvent, button));
UpdateItemsPosition();
}
}
}
/// <summary>
/// 更新项的位置
/// </summary>
private void UpdateItemsPosition()
{
if (!CheckNull()) return;
if (!_appliedTemplate) return;
if (Items.Count == 0) return;
if (!IsCenter)
{
ItemsHost.BeginAnimation(MarginProperty,
AnimationHelper.CreateAnimation(new Thickness(-_widthList[PageIndex], 0, 0, 0)));
}
else
{
var ctl = (FrameworkElement) ItemsHost.Children[PageIndex];
var ctlWidth = ctl.DesiredSize.Width;
ItemsHost.BeginAnimation(MarginProperty,
AnimationHelper.CreateAnimation(
new Thickness(-_widthList[PageIndex] + (ActualWidth - ctlWidth) / 2, 0, 0, 0)));
}
}
protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
{
base.OnRenderSizeChanged(sizeInfo);
UpdateItemsPosition();
}
private void ButtonPages_OnClick(object sender, RoutedEventArgs e)
{
if (!CheckNull()) return;
_selectedButton = e.OriginalSource as RadioButton;
var index = _panelPage.Children.IndexOf(_selectedButton);
if (index != -1)
{
PageIndex = index;
}
}
private void ButtonPrev_OnClick(object sender, RoutedEventArgs e) => PageIndex--;
private void ButtonNext_OnClick(object sender, RoutedEventArgs e) => PageIndex++;
protected override DependencyObject GetContainerForItemOverride() => new CarouselItem();
protected override bool IsItemItsOwnContainerOverride(object item) => item is CarouselItem;
private void ClearItems()
{
ItemsHost?.Children.Clear();
_entryDic.Clear();
}
private void RemoveItem(object item)
{
if (_entryDic.TryGetValue(item, out var entry))
{
ItemsHost.Children.Remove(entry);
Items.Remove(item);
_entryDic.Remove(item);
}
}
protected override void Refresh()
{
if (ItemsHost == null) return;
_entryDic.Clear();
_isRefresh = true;
foreach (var item in Items)
{
AddItem(item);
}
_isRefresh = false;
}
private void AddItem(object item) => InsertItem(_entryDic.Count, item);
private void InsertItem(int index, object item)
{
if (ItemsHost == null)
{
Items.Insert(index, item);
_entryDic.Add(item, null);
}
else
{
DependencyObject container;
if (IsItemItsOwnContainerOverride(item))
{
container = item as DependencyObject;
}
else
{
var ctl = (FrameworkElement) ItemsHost.Children[PageIndex];
var ctlWidth = ctl.DesiredSize.Width;
ItemsHost.BeginAnimation(MarginProperty,
AnimationHelper.CreateAnimation(
new Thickness(-_widthList[PageIndex] + (ActualWidth - ctlWidth) / 2, 0, 0, 0)));
container = GetContainerForItemOverride();
PrepareContainerForItemOverride(container, item);
}
}
protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
{
base.OnRenderSizeChanged(sizeInfo);
UpdateItemsPosition();
}
private void ButtonPages_OnClick(object sender, RoutedEventArgs e)
{
if (!CheckNull()) return;
_selectedButton = e.OriginalSource as RadioButton;
var index = _panelPage.Children.IndexOf(_selectedButton);
if (index != -1)
if (container is CarouselItem element)
{
PageIndex = index;
element.Style = ItemContainerStyle;
_entryDic[item] = element;
ItemsHost.Children.Insert(index, element);
if (IsLoaded && !_isRefresh && _itemsSourceInternal != null)
{
Items.Insert(index, item);
}
}
}
}
private void ButtonPrev_OnClick(object sender, RoutedEventArgs e) => PageIndex--;
private void ButtonNext_OnClick(object sender, RoutedEventArgs e) => PageIndex++;
protected override DependencyObject GetContainerForItemOverride() => new CarouselItem();
protected override bool IsItemItsOwnContainerOverride(object item) => item is CarouselItem;
private void ClearItems()
protected override void OnItemsSourceChanged(IEnumerable oldValue, IEnumerable newValue)
{
if (_itemsSourceInternal != null)
{
ItemsHost?.Children.Clear();
_entryDic.Clear();
}
private void RemoveItem(object item)
{
if (_entryDic.TryGetValue(item, out var entry))
if (_itemsSourceInternal is INotifyCollectionChanged s)
{
ItemsHost.Children.Remove(entry);
Items.Remove(item);
_entryDic.Remove(item);
s.CollectionChanged -= InternalCollectionChanged;
}
Items.Clear();
ClearItems();
}
protected override void Refresh()
_itemsSourceInternal = newValue;
if (_itemsSourceInternal != null)
{
if (ItemsHost == null) return;
_entryDic.Clear();
_isRefresh = true;
foreach (var item in Items)
if (_itemsSourceInternal is INotifyCollectionChanged s)
{
s.CollectionChanged += InternalCollectionChanged;
}
foreach (var item in _itemsSourceInternal)
{
AddItem(item);
}
_isRefresh = false;
}
private void AddItem(object item) => InsertItem(_entryDic.Count, item);
private void InsertItem(int index, object item)
{
if (ItemsHost == null)
{
Items.Insert(index, item);
_entryDic.Add(item, null);
}
else
{
DependencyObject container;
if (IsItemItsOwnContainerOverride(item))
{
container = item as DependencyObject;
}
else
{
container = GetContainerForItemOverride();
PrepareContainerForItemOverride(container, item);
}
if (container is CarouselItem element)
{
element.Style = ItemContainerStyle;
_entryDic[item] = element;
ItemsHost.Children.Insert(index, element);
if (IsLoaded && !_isRefresh && _itemsSourceInternal != null)
{
Items.Insert(index, item);
}
}
}
}
protected override void OnItemsSourceChanged(IEnumerable oldValue, IEnumerable newValue)
{
if (_itemsSourceInternal != null)
{
if (_itemsSourceInternal is INotifyCollectionChanged s)
{
s.CollectionChanged -= InternalCollectionChanged;
}
Items.Clear();
ClearItems();
}
_itemsSourceInternal = newValue;
if (_itemsSourceInternal != null)
{
if (_itemsSourceInternal is INotifyCollectionChanged s)
{
s.CollectionChanged += InternalCollectionChanged;
}
foreach (var item in _itemsSourceInternal)
{
AddItem(item);
}
}
}
private void InternalCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (ItemsHost == null) return;
if (e.Action == NotifyCollectionChangedAction.Reset)
{
if (_entryDic.Count == 0) return;
ClearItems();
Items.Clear();
return;
}
if (e.OldItems != null)
{
foreach (var item in e.OldItems)
{
RemoveItem(item);
}
}
if (e.NewItems != null)
{
var count = 0;
foreach (var item in e.NewItems)
{
var index = e.NewStartingIndex + count++;
InsertItem(index, item);
}
}
}
protected override void OnItemsChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (_itemsSourceInternal != null) return;
InternalCollectionChanged(sender, e);
}
protected override void OnItemTemplateChanged(DependencyPropertyChangedEventArgs e)
{
}
protected override void OnItemContainerStyleChanged(DependencyPropertyChangedEventArgs e)
{
}
}
private void InternalCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (ItemsHost == null) return;
if (e.Action == NotifyCollectionChangedAction.Reset)
{
if (_entryDic.Count == 0) return;
ClearItems();
Items.Clear();
return;
}
if (e.OldItems != null)
{
foreach (var item in e.OldItems)
{
RemoveItem(item);
}
}
if (e.NewItems != null)
{
var count = 0;
foreach (var item in e.NewItems)
{
var index = e.NewStartingIndex + count++;
InsertItem(index, item);
}
}
}
protected override void OnItemsChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (_itemsSourceInternal != null) return;
InternalCollectionChanged(sender, e);
}
protected override void OnItemTemplateChanged(DependencyPropertyChangedEventArgs e)
{
}
protected override void OnItemContainerStyleChanged(DependencyPropertyChangedEventArgs e)
{
}
}
@@ -1,9 +1,8 @@
using System.Windows.Controls;
namespace HandyControl.Controls
namespace HandyControl.Controls;
public class CarouselItem : ContentControl
{
public class CarouselItem : ContentControl
{
}
}
@@ -6,101 +6,100 @@ using HandyControl.Data;
using HandyControl.Tools;
using HandyControl.Tools.Interop;
namespace HandyControl.Controls
namespace HandyControl.Controls;
internal class ColorDropper
{
internal class ColorDropper
private bool _cursorIsSetted;
private Cursor _dropperCursor;
private readonly ColorPicker _colorPicker;
public ColorDropper(ColorPicker colorPicker)
{
private bool _cursorIsSetted;
_colorPicker = colorPicker;
}
private Cursor _dropperCursor;
private readonly ColorPicker _colorPicker;
public ColorDropper(ColorPicker colorPicker)
public void Update(bool isShow)
{
if (isShow)
{
_colorPicker = colorPicker;
}
public void Update(bool isShow)
{
if (isShow)
if (_dropperCursor == null)
{
if (_dropperCursor == null)
var info = Application.GetResourceStream(new Uri("pack://application:,,,/HandyControl;Component/Resources/dropper.cur"));
if (info != null)
{
var info = Application.GetResourceStream(new Uri("pack://application:,,,/HandyControl;Component/Resources/dropper.cur"));
if (info != null)
{
_dropperCursor = new Cursor(info.Stream);
}
}
if (_dropperCursor == null) return;
MouseHook.Start();
MouseHook.StatusChanged += MouseHook_StatusChanged;
}
else
{
Mouse.OverrideCursor = Cursors.Arrow;
MouseHook.Stop();
MouseHook.StatusChanged -= MouseHook_StatusChanged;
}
}
private void MouseHook_StatusChanged(object sender, MouseHookEventArgs e)
{
var window = System.Windows.Window.GetWindow(_colorPicker);
if (window == null)
{
UpdateCursor(false);
return;
}
if (!_colorPicker.IsMouseOver && window.IsMouseOver)
{
UpdateCursor(true);
if (e.MessageType == MouseHookMessageType.LeftButtonDown)
{
var brush = new SolidColorBrush(GetColorAt(e.Point.X, e.Point.Y));
_colorPicker.SelectedBrush = brush;
_dropperCursor = new Cursor(info.Stream);
}
}
else
{
UpdateCursor(false);
}
if (_dropperCursor == null) return;
MouseHook.Start();
MouseHook.StatusChanged += MouseHook_StatusChanged;
}
private void UpdateCursor(bool isDropper)
else
{
if (isDropper)
{
Mouse.Captured?.ReleaseMouseCapture();
if (!_cursorIsSetted)
{
Mouse.OverrideCursor = _dropperCursor;
_cursorIsSetted = true;
}
}
else
{
if (_cursorIsSetted)
{
Mouse.OverrideCursor = Cursors.Arrow;
_cursorIsSetted = false;
}
}
}
public static Color GetColorAt(int x, int y)
{
var desk = InteropMethods.GetDesktopWindow();
var dc = InteropMethods.GetWindowDC(desk);
var a = (int) InteropMethods.GetPixel(dc, x, y);
InteropMethods.ReleaseDC(desk, dc);
return Color.FromArgb(255, (byte) ((a >> 0) & 0xff), (byte) ((a >> 8) & 0xff), (byte) ((a >> 16) & 0xff));
Mouse.OverrideCursor = Cursors.Arrow;
MouseHook.Stop();
MouseHook.StatusChanged -= MouseHook_StatusChanged;
}
}
private void MouseHook_StatusChanged(object sender, MouseHookEventArgs e)
{
var window = System.Windows.Window.GetWindow(_colorPicker);
if (window == null)
{
UpdateCursor(false);
return;
}
if (!_colorPicker.IsMouseOver && window.IsMouseOver)
{
UpdateCursor(true);
if (e.MessageType == MouseHookMessageType.LeftButtonDown)
{
var brush = new SolidColorBrush(GetColorAt(e.Point.X, e.Point.Y));
_colorPicker.SelectedBrush = brush;
}
}
else
{
UpdateCursor(false);
}
}
private void UpdateCursor(bool isDropper)
{
if (isDropper)
{
Mouse.Captured?.ReleaseMouseCapture();
if (!_cursorIsSetted)
{
Mouse.OverrideCursor = _dropperCursor;
_cursorIsSetted = true;
}
}
else
{
if (_cursorIsSetted)
{
Mouse.OverrideCursor = Cursors.Arrow;
_cursorIsSetted = false;
}
}
}
public static Color GetColorAt(int x, int y)
{
var desk = InteropMethods.GetDesktopWindow();
var dc = InteropMethods.GetWindowDC(desk);
var a = (int) InteropMethods.GetPixel(dc, x, y);
InteropMethods.ReleaseDC(desk, dc);
return Color.FromArgb(255, (byte) ((a >> 0) & 0xff), (byte) ((a >> 8) & 0xff), (byte) ((a >> 16) & 0xff));
}
}
File diff suppressed because it is too large. Load diff
@@ -10,295 +10,288 @@ using System.Windows.Media.Media3D;
using HandyControl.Data;
using HandyControl.Tools.Extension;
namespace HandyControl.Controls
namespace HandyControl.Controls;
/// <summary>
/// 封面流
/// </summary>
[TemplatePart(Name = ElementViewport3D, Type = typeof(Viewport3D))]
[TemplatePart(Name = ElementCamera, Type = typeof(ProjectionCamera))]
[TemplatePart(Name = ElementVisualParent, Type = typeof(ModelVisual3D))]
public class CoverFlow : Control
{
private const string ElementViewport3D = "PART_Viewport3D";
private const string ElementCamera = "PART_Camera";
private const string ElementVisualParent = "PART_VisualParent";
/// <summary>
/// 封面流
/// 最大显示数量的一半
/// </summary>
[TemplatePart(Name = ElementViewport3D, Type = typeof(Viewport3D))]
[TemplatePart(Name = ElementCamera, Type = typeof(ProjectionCamera))]
[TemplatePart(Name = ElementVisualParent, Type = typeof(ModelVisual3D))]
public class CoverFlow : Control
private const int MaxShowCountHalf = 3;
/// <summary>
/// 页码
/// </summary>
public static readonly DependencyProperty PageIndexProperty = DependencyProperty.Register(
nameof(PageIndex), typeof(int), typeof(CoverFlow),
new PropertyMetadata(ValueBoxes.Int0Box, OnPageIndexChanged, CoercePageIndex));
private static object CoercePageIndex(DependencyObject d, object baseValue)
{
private const string ElementViewport3D = "PART_Viewport3D";
var ctl = (CoverFlow) d;
var v = (int) baseValue;
private const string ElementCamera = "PART_Camera";
private const string ElementVisualParent = "PART_VisualParent";
/// <summary>
/// 最大显示数量的一半
/// </summary>
private const int MaxShowCountHalf = 3;
/// <summary>
/// 页码
/// </summary>
public static readonly DependencyProperty PageIndexProperty = DependencyProperty.Register(
"PageIndex", typeof(int), typeof(CoverFlow),
new PropertyMetadata(ValueBoxes.Int0Box, OnPageIndexChanged, CoercePageIndex));
private static object CoercePageIndex(DependencyObject d, object baseValue)
if (v < 0)
{
var ctl = (CoverFlow) d;
var v = (int) baseValue;
return 0;
}
if (v >= ctl._contentDic.Count)
{
return ctl._contentDic.Count - 1;
}
return v;
}
if (v < 0)
{
return 0;
}
if (v >= ctl._contentDic.Count)
{
return ctl._contentDic.Count - 1;
}
return v;
private static void OnPageIndexChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var ctl = (CoverFlow) d;
ctl.UpdateIndex((int) e.NewValue);
}
/// <summary>
/// 是否循环
/// </summary>
public static readonly DependencyProperty LoopProperty = DependencyProperty.Register(
nameof(Loop), typeof(bool), typeof(CoverFlow), new PropertyMetadata(ValueBoxes.FalseBox));
/// <summary>
/// 存储所有的内容
/// </summary>
private readonly Dictionary<int, object> _contentDic = new();
/// <summary>
/// 当前在显示范围内的项
/// </summary>
private readonly Dictionary<int, CoverFlowItem> _itemShowDic = new();
/// <summary>
/// 相机
/// </summary>
private ProjectionCamera _camera;
private Point3DAnimation _point3DAnimation;
/// <summary>
/// 3d画布
/// </summary>
private Viewport3D _viewport3D;
/// <summary>
/// 项容器
/// </summary>
private ModelVisual3D _visualParent;
/// <summary>
/// 显示范围内第一个项的编号
/// </summary>
private int _firstShowIndex;
/// <summary>
/// 显示范围内最后一个项的编号
/// </summary>
private int _lastShowIndex;
/// <summary>
/// 页码
/// </summary>
public int PageIndex
{
get => (int) GetValue(PageIndexProperty);
set => SetValue(PageIndexProperty, value);
}
/// <summary>
/// 是否循环
/// </summary>
public bool Loop
{
get => (bool) GetValue(LoopProperty);
set => SetValue(LoopProperty, ValueBoxes.BooleanBox(value));
}
public override void OnApplyTemplate()
{
if (_viewport3D != null)
{
_viewport3D.Children.Clear();
_itemShowDic.Clear();
_viewport3D.MouseLeftButtonDown -= Viewport3D_MouseLeftButtonDown;
}
private static void OnPageIndexChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
base.OnApplyTemplate();
_viewport3D = GetTemplateChild(ElementViewport3D) as Viewport3D;
if (_viewport3D != null)
{
var ctl = (CoverFlow) d;
ctl.UpdateIndex((int) e.NewValue);
_viewport3D.MouseLeftButtonDown += Viewport3D_MouseLeftButtonDown;
}
/// <summary>
/// 是否循环
/// </summary>
public static readonly DependencyProperty LoopProperty = DependencyProperty.Register(
"Loop", typeof(bool), typeof(CoverFlow), new PropertyMetadata(ValueBoxes.FalseBox));
_camera = GetTemplateChild(ElementCamera) as ProjectionCamera;
_visualParent = GetTemplateChild(ElementVisualParent) as ModelVisual3D;
/// <summary>
/// 存储所有的内容
/// </summary>
private readonly Dictionary<int, object> _contentDic = new Dictionary<int, object>();
UpdateShowRange();
/// <summary>
/// 当前在显示范围内的项
/// </summary>
private readonly Dictionary<int, CoverFlowItem> _itemShowDic = new Dictionary<int, CoverFlowItem>();
_point3DAnimation = new Point3DAnimation(new Point3D(CoverFlowItem.Interval * PageIndex, _camera.Position.Y, _camera.Position.Z), new Duration(TimeSpan.FromMilliseconds(200)));
_camera.BeginAnimation(ProjectionCamera.PositionProperty, _point3DAnimation);
}
/// <summary>
/// 相机
/// </summary>
private ProjectionCamera _camera;
private Point3DAnimation _point3DAnimation;
/// <summary>
/// 3d画布
/// </summary>
private Viewport3D _viewport3D;
/// <summary>
/// 项容器
/// </summary>
private ModelVisual3D _visualParent;
/// <summary>
/// 显示范围内第一个项的编号
/// </summary>
private int _firstShowIndex;
/// <summary>
/// 显示范围内最后一个项的编号
/// </summary>
private int _lastShowIndex;
/// <summary>
/// 跳转编号
/// </summary>
private int _jumpToIndex = -1;
/// <summary>
/// 页码
/// </summary>
public int PageIndex
/// <summary>
/// 批量添加资源
/// </summary>
/// <param name="contentList"></param>
public void AddRange(IEnumerable<object> contentList)
{
foreach (var content in contentList)
{
get => (int) GetValue(PageIndexProperty);
internal set => SetValue(PageIndexProperty, value);
_contentDic.Add(_contentDic.Count, content);
}
}
/// <summary>
/// 添加一项资源
/// </summary>
/// <param name="uriString"></param>
public void Add(string uriString) => _contentDic.Add(_contentDic.Count, new Uri(uriString));
/// <summary>
/// 添加一项资源
/// </summary>
/// <param name="uri"></param>
public void Add(Uri uri) => _contentDic.Add(_contentDic.Count, uri);
public void Next() => PageIndex++;
public void Prev() => PageIndex--;
protected override void OnMouseWheel(MouseWheelEventArgs e)
{
base.OnMouseWheel(e);
if (e.Delta < 0)
{
var index = PageIndex + 1;
PageIndex = index >= _contentDic.Count ? Loop ? 0 : _contentDic.Count - 1 : index;
}
else
{
var index = PageIndex - 1;
PageIndex = index < 0 ? Loop ? _contentDic.Count - 1 : 0 : index;
}
/// <summary>
/// 是否循环
/// </summary>
public bool Loop
e.Handled = true;
}
/// <summary>
/// 删除指定位置的项
/// </summary>
/// <param name="pos"></param>
private void Remove(int pos)
{
if (!_itemShowDic.TryGetValue(pos, out var item)) return;
_visualParent.Children.Remove(item);
_itemShowDic.Remove(pos);
}
private void Viewport3D_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
var result = (RayMeshGeometry3DHitTestResult) VisualTreeHelper.HitTest(_viewport3D, e.GetPosition(_viewport3D));
if (result != null)
{
get => (bool) GetValue(LoopProperty);
set => SetValue(LoopProperty, ValueBoxes.BooleanBox(value));
}
public override void OnApplyTemplate()
{
if (_viewport3D != null)
foreach (var item in _itemShowDic.Values)
{
_viewport3D.Children.Clear();
_itemShowDic.Clear();
_viewport3D.MouseLeftButtonDown -= Viewport3D_MouseLeftButtonDown;
}
base.OnApplyTemplate();
_viewport3D = GetTemplateChild(ElementViewport3D) as Viewport3D;
if (_viewport3D != null)
{
_viewport3D.MouseLeftButtonDown += Viewport3D_MouseLeftButtonDown;
}
_camera = GetTemplateChild(ElementCamera) as ProjectionCamera;
_visualParent = GetTemplateChild(ElementVisualParent) as ModelVisual3D;
UpdateShowRange();
if (_jumpToIndex > 0)
{
PageIndex = _jumpToIndex;
_jumpToIndex = -1;
}
_point3DAnimation = new Point3DAnimation(new Point3D(CoverFlowItem.Interval * PageIndex, _camera.Position.Y, _camera.Position.Z), new Duration(TimeSpan.FromMilliseconds(200)));
_camera.BeginAnimation(ProjectionCamera.PositionProperty, _point3DAnimation);
}
/// <summary>
/// 批量添加资源
/// </summary>
/// <param name="contentList"></param>
public void AddRange(IEnumerable<object> contentList)
{
foreach (var content in contentList)
{
_contentDic.Add(_contentDic.Count, content);
if (item.HitTest(result.MeshHit))
{
PageIndex = item.Index;
break;
}
}
}
}
/// <summary>
/// 添加一项资源
/// </summary>
/// <param name="uriString"></param>
public void Add(string uriString) => _contentDic.Add(_contentDic.Count, new Uri(uriString));
/// <summary>
/// 添加一项资源
/// </summary>
/// <param name="uri"></param>
public void Add(Uri uri) => _contentDic.Add(_contentDic.Count, uri);
/// <summary>
/// 跳转
/// </summary>
public void JumpTo(int index) => _jumpToIndex = index;
protected override void OnMouseWheel(MouseWheelEventArgs e)
/// <summary>
/// 更新项的位置
/// </summary>
/// <param name="newIndex"></param>
private void UpdateIndex(int newIndex)
{
if (!IsLoaded)
{
base.OnMouseWheel(e);
return;
}
if (e.Delta < 0)
UpdateShowRange();
_itemShowDic.Do(item =>
{
if (item.Value.Index < _firstShowIndex || item.Value.Index > _lastShowIndex)
{
var index = PageIndex + 1;
PageIndex = index >= _contentDic.Count ? Loop ? 0 : _contentDic.Count - 1 : index;
Remove(item.Value.Index);
}
else
{
var index = PageIndex - 1;
PageIndex = index < 0 ? Loop ? _contentDic.Count - 1 : 0 : index;
item.Value.Move(PageIndex);
}
});
e.Handled = true;
}
_point3DAnimation = new Point3DAnimation(new Point3D(CoverFlowItem.Interval * newIndex, _camera.Position.Y,
_camera.Position.Z), new Duration(TimeSpan.FromMilliseconds(200)));
/// <summary>
/// 删除指定位置的项
/// </summary>
/// <param name="pos"></param>
private void Remove(int pos)
_camera.BeginAnimation(ProjectionCamera.PositionProperty, _point3DAnimation);
}
/// <summary>
/// 更新显示范围
/// </summary>
private void UpdateShowRange()
{
var newFirstShowIndex = Math.Max(PageIndex - MaxShowCountHalf, 0);
var newLastShowIndex = Math.Min(PageIndex + MaxShowCountHalf, _contentDic.Count - 1);
for (var i = newFirstShowIndex; i <= newLastShowIndex; i++)
{
if (!_itemShowDic.TryGetValue(pos, out var item)) return;
_visualParent.Children.Remove(item);
_itemShowDic.Remove(pos);
}
private void Viewport3D_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
var result = (RayMeshGeometry3DHitTestResult) VisualTreeHelper.HitTest(_viewport3D, e.GetPosition(_viewport3D));
if (result != null)
if (!_itemShowDic.ContainsKey(i))
{
foreach (var item in _itemShowDic.Values)
{
if (item.HitTest(result.MeshHit))
{
PageIndex = item.Index;
break;
}
}
var cover = CreateCoverFlowItem(i, _contentDic[i]);
_itemShowDic[i] = cover;
_visualParent.Children.Add(cover);
}
}
/// <summary>
/// 更新项的位置
/// </summary>
/// <param name="newIndex"></param>
private void UpdateIndex(int newIndex)
_firstShowIndex = newFirstShowIndex;
_lastShowIndex = newLastShowIndex;
}
private CoverFlowItem CreateCoverFlowItem(int index, object content)
{
if (content is Uri uri)
{
UpdateShowRange();
_itemShowDic.Do(item =>
try
{
if (item.Value.Index < _firstShowIndex || item.Value.Index > _lastShowIndex)
return new CoverFlowItem(index, PageIndex, new Image
{
Remove(item.Value.Index);
}
else
{
item.Value.Move(PageIndex);
}
});
_point3DAnimation = new Point3DAnimation(new Point3D(CoverFlowItem.Interval * newIndex, _camera.Position.Y,
_camera.Position.Z), new Duration(TimeSpan.FromMilliseconds(200)));
_camera.BeginAnimation(ProjectionCamera.PositionProperty, _point3DAnimation);
}
/// <summary>
/// 更新显示范围
/// </summary>
private void UpdateShowRange()
{
var newFirstShowIndex = Math.Max(PageIndex - MaxShowCountHalf, 0);
var newLastShowIndex = Math.Min(PageIndex + MaxShowCountHalf, _contentDic.Count - 1);
for (var i = newFirstShowIndex; i <= newLastShowIndex; i++)
{
if (!_itemShowDic.ContainsKey(i))
{
var cover = CreateCoverFlowItem(i, _contentDic[i]);
_itemShowDic[i] = cover;
_visualParent.Children.Add(cover);
}
Source = BitmapFrame.Create(uri, BitmapCreateOptions.DelayCreation, BitmapCacheOption.OnDemand)
});
}
_firstShowIndex = newFirstShowIndex;
_lastShowIndex = newLastShowIndex;
}
private CoverFlowItem CreateCoverFlowItem(int index, object content)
{
if (content is Uri uri)
catch
{
try
{
return new CoverFlowItem(index, PageIndex, new Image
{
Source = BitmapFrame.Create(uri, BitmapCreateOptions.DelayCreation, BitmapCacheOption.OnDemand)
});
}
catch
{
return new CoverFlowItem(index, PageIndex, new ContentControl());
}
return new CoverFlowItem(index, PageIndex, new ContentControl());
}
return new CoverFlowItem(index, PageIndex, new ContentControl
{
Content = content
});
}
return new CoverFlowItem(index, PageIndex, new ContentControl
{
Content = content
});
}
}
@@ -4,154 +4,153 @@ using System.Windows.Media;
using System.Windows.Media.Media3D;
using HandyControl.Tools;
namespace HandyControl.Controls
namespace HandyControl.Controls;
public class CoverFlowItem : ModelVisual3D
{
public class CoverFlowItem : ModelVisual3D
internal static readonly double Interval = .2;
internal static readonly double AnimationSpeed = 400;
private readonly AxisAngleRotation3D _rotation3D;
private readonly TranslateTransform3D _transform3D;
private readonly Model3DGroup _model3DGroup;
private readonly UIElement _uiElement;
internal int Index { get; set; }
public CoverFlowItem(int itemIndex, int currentIndex, UIElement element)
{
internal static readonly double Interval = .2;
Index = itemIndex;
_uiElement = element;
_uiElement.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
internal static readonly double AnimationSpeed = 400;
_rotation3D = new AxisAngleRotation3D(new Vector3D(0, 1, 0), GetAngleByPos(currentIndex));
_transform3D = new TranslateTransform3D(GetXByPos(currentIndex), 0, GetZByPos(currentIndex));
private readonly AxisAngleRotation3D _rotation3D;
var transformGroup = new Transform3DGroup();
transformGroup.Children.Add(new RotateTransform3D(_rotation3D));
transformGroup.Children.Add(_transform3D);
private readonly TranslateTransform3D _transform3D;
_model3DGroup = new Model3DGroup();
_model3DGroup.Children.Add(new GeometryModel3D(CreateItemGeometry(), new DiffuseMaterial(new VisualBrush(element))));
_model3DGroup.Transform = transformGroup;
private readonly Model3DGroup _model3DGroup;
Content = _model3DGroup;
}
private readonly UIElement _uiElement;
internal int Index { get; set; }
public CoverFlowItem(int itemIndex, int currentIndex, UIElement element)
/// <summary>
/// 创建网格形状
/// </summary>
/// <param name="p0"></param>
/// <param name="p1"></param>
/// <param name="p2"></param>
/// <param name="p3"></param>
/// <returns></returns>
private static MeshGeometry3D CreateMeshGeometry(Point3D p0, Point3D p1, Point3D p2, Point3D p3)
{
var positions = new Point3DCollection
{
Index = itemIndex;
_uiElement = element;
_uiElement.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
p0,
p1,
p2,
p3
};
_rotation3D = new AxisAngleRotation3D(new Vector3D(0, 1, 0), GetAngleByPos(currentIndex));
_transform3D = new TranslateTransform3D(GetXByPos(currentIndex), 0, GetZByPos(currentIndex));
var transformGroup = new Transform3DGroup();
transformGroup.Children.Add(new RotateTransform3D(_rotation3D));
transformGroup.Children.Add(_transform3D);
_model3DGroup = new Model3DGroup();
_model3DGroup.Children.Add(new GeometryModel3D(CreateItemGeometry(), new DiffuseMaterial(new VisualBrush(element))));
_model3DGroup.Transform = transformGroup;
Content = _model3DGroup;
}
/// <summary>
/// 创建网格形状
/// </summary>
/// <param name="p0"></param>
/// <param name="p1"></param>
/// <param name="p2"></param>
/// <param name="p3"></param>
/// <returns></returns>
private static MeshGeometry3D CreateMeshGeometry(Point3D p0, Point3D p1, Point3D p2, Point3D p3)
var triangleIndices = new Int32Collection
{
var positions = new Point3DCollection
{
p0,
p1,
p2,
p3
};
0,
1,
2,
2,
3,
0
};
var triangleIndices = new Int32Collection
{
0,
1,
2,
2,
3,
0
};
var textureCoordinates = new PointCollection
{
new Point(0, 1),
new Point(1, 1),
new Point(1, 0),
new Point(0, 0),
new Point(1, 0),
new Point(1, 1)
};
var normals = new Vector3DCollection
{
ArithmeticHelper.CalNormal(p0, p1, p2),
ArithmeticHelper.CalNormal(p2, p3, p0)
};
var geometry3D = new MeshGeometry3D
{
Positions = positions,
TriangleIndices = triangleIndices,
TextureCoordinates = textureCoordinates,
Normals = normals
};
geometry3D.Freeze();
return geometry3D;
}
/// <summary>
/// 创建内容形状
/// </summary>
/// <returns></returns>
private Geometry3D CreateItemGeometry()
var textureCoordinates = new PointCollection
{
var sx = _uiElement.DesiredSize.Width > _uiElement.DesiredSize.Height ? 0 : 1 - _uiElement.DesiredSize.Width / _uiElement.DesiredSize.Height;
var sy = _uiElement.DesiredSize.Width < _uiElement.DesiredSize.Height ? 0 : 1 - _uiElement.DesiredSize.Height / _uiElement.DesiredSize.Width;
new(0, 1),
new(1, 1),
new(1, 0),
new(0, 0),
new(1, 0),
new(1, 1)
};
var p0 = new Point3D(-1 + sx, -1 + sy, 0);
var p1 = new Point3D(1 - sx, -1 + sy, 0);
var p2 = new Point3D(1 - sx, 1 - sy, 0);
var p3 = new Point3D(-1 + sx, 1 - sy, 0);
return CreateMeshGeometry(p0, p1, p2, p3);
}
private double GetAngleByPos(int index) => Math.Sign(Index - index) * -90;
private double GetXByPos(int index) => Index * Interval + Math.Sign(Index - index) * 1.5;
private double GetZByPos(int index) => Index == index ? 1 : 0;
/// <summary>
/// 移动
/// </summary>
internal void Move(int currentIndex)
var normals = new Vector3DCollection
{
_rotation3D.BeginAnimation(AxisAngleRotation3D.AngleProperty,
AnimationHelper.CreateAnimation(GetAngleByPos(currentIndex), AnimationSpeed));
_transform3D.BeginAnimation(TranslateTransform3D.OffsetXProperty,
AnimationHelper.CreateAnimation(GetXByPos(currentIndex), AnimationSpeed));
_transform3D.BeginAnimation(TranslateTransform3D.OffsetZProperty,
AnimationHelper.CreateAnimation(GetZByPos(currentIndex), AnimationSpeed));
}
ArithmeticHelper.CalNormal(p0, p1, p2),
ArithmeticHelper.CalNormal(p2, p3, p0)
};
/// <summary>
/// 命中测试
/// </summary>
/// <param name="mesh"></param>
/// <returns></returns>
internal bool HitTest(MeshGeometry3D mesh)
var geometry3D = new MeshGeometry3D
{
foreach (var item in _model3DGroup.Children)
Positions = positions,
TriangleIndices = triangleIndices,
TextureCoordinates = textureCoordinates,
Normals = normals
};
geometry3D.Freeze();
return geometry3D;
}
/// <summary>
/// 创建内容形状
/// </summary>
/// <returns></returns>
private Geometry3D CreateItemGeometry()
{
var sx = _uiElement.DesiredSize.Width > _uiElement.DesiredSize.Height ? 0 : 1 - _uiElement.DesiredSize.Width / _uiElement.DesiredSize.Height;
var sy = _uiElement.DesiredSize.Width < _uiElement.DesiredSize.Height ? 0 : 1 - _uiElement.DesiredSize.Height / _uiElement.DesiredSize.Width;
var p0 = new Point3D(-1 + sx, -1 + sy, 0);
var p1 = new Point3D(1 - sx, -1 + sy, 0);
var p2 = new Point3D(1 - sx, 1 - sy, 0);
var p3 = new Point3D(-1 + sx, 1 - sy, 0);
return CreateMeshGeometry(p0, p1, p2, p3);
}
private double GetAngleByPos(int index) => Math.Sign(Index - index) * -90;
private double GetXByPos(int index) => Index * Interval + Math.Sign(Index - index) * 1.5;
private double GetZByPos(int index) => Index == index ? 1 : 0;
/// <summary>
/// 移动
/// </summary>
internal void Move(int currentIndex)
{
_rotation3D.BeginAnimation(AxisAngleRotation3D.AngleProperty,
AnimationHelper.CreateAnimation(GetAngleByPos(currentIndex), AnimationSpeed));
_transform3D.BeginAnimation(TranslateTransform3D.OffsetXProperty,
AnimationHelper.CreateAnimation(GetXByPos(currentIndex), AnimationSpeed));
_transform3D.BeginAnimation(TranslateTransform3D.OffsetZProperty,
AnimationHelper.CreateAnimation(GetZByPos(currentIndex), AnimationSpeed));
}
/// <summary>
/// 命中测试
/// </summary>
/// <param name="mesh"></param>
/// <returns></returns>
internal bool HitTest(MeshGeometry3D mesh)
{
foreach (var item in _model3DGroup.Children)
{
if (item is GeometryModel3D geometryModel3D)
{
if (item is GeometryModel3D geometryModel3D)
if (Equals(geometryModel3D.Geometry, mesh))
{
if (Equals(geometryModel3D.Geometry, mesh))
{
return true;
}
return true;
}
}
return false;
}
return false;
}
}
@@ -6,413 +6,417 @@ using System.Windows.Data;
using HandyControl.Data;
using HandyControl.Tools;
namespace HandyControl.Controls
namespace HandyControl.Controls;
public class CoverView : RegularItemsControl
{
public class CoverView : RegularItemsControl
private readonly CoverViewContent _viewContent;
private CoverViewItem _selectedItem;
private IEnumerable _itemsSourceInternal;
private readonly Dictionary<object, CoverViewItem> _entryDic = new();
private bool _isRefresh;
public CoverView()
{
private readonly CoverViewContent _viewContent;
_viewContent = new CoverViewContent();
private CoverViewItem _selectedItem;
AddHandler(SelectableItem.SelectedEvent, new RoutedEventHandler(CoverViewItem_OnSelected));
_viewContent.SetBinding(CoverViewContent.ContentHeightProperty, new Binding(ItemContentHeightProperty.Name) { Source = this });
_viewContent.SetBinding(WidthProperty, new Binding(ActualWidthProperty.Name) { Source = this });
}
private IEnumerable _itemsSourceInternal;
private readonly Dictionary<object, CoverViewItem> _entryDic = new Dictionary<object, CoverViewItem>();
private bool _isRefresh;
public CoverView()
private void CoverViewItem_OnSelected(object sender, RoutedEventArgs e)
{
if (e.OriginalSource is CoverViewItem item)
{
_viewContent = new CoverViewContent();
AddHandler(SelectableItem.SelectedEvent, new RoutedEventHandler(CoverViewItem_OnSelected));
_viewContent.SetBinding(CoverViewContent.ContentHeightProperty, new Binding(ItemContentHeightProperty.Name) { Source = this });
_viewContent.SetBinding(WidthProperty, new Binding(ActualWidthProperty.Name) { Source = this });
}
private void CoverViewItem_OnSelected(object sender, RoutedEventArgs e)
{
if (e.OriginalSource is CoverViewItem item)
if (_selectedItem == null)
{
if (_selectedItem == null)
{
item.IsSelected = true;
_selectedItem = item;
if (_viewContent != null)
{
_viewContent.Content = item.Content;
_viewContent.ContentTemplate = ItemTemplate;
UpdateCoverViewContent(true);
}
return;
}
if (!Equals(_selectedItem, item))
{
_selectedItem.IsSelected = false;
item.IsSelected = true;
_selectedItem = item;
if (_viewContent != null)
{
_viewContent.Content = item.Content;
UpdateCoverViewContent(true);
}
return;
}
item.SetCurrentValue(SelectableItem.IsSelectedProperty, ValueBoxes.TrueBox);
_selectedItem = item;
if (_viewContent != null)
{
_viewContent.Content = null;
_viewContent.ContentTemplate = null;
UpdateCoverViewContent(false);
_viewContent.Content = item.Content;
_viewContent.ContentTemplate = ItemTemplate;
UpdateCoverViewContent(true);
}
_selectedItem.IsSelected = false;
_selectedItem = null;
}
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
_viewContent.Style ??= CoverViewContentStyle;
if (_selectedItem != null)
{
UpdateCoverViewContent(_selectedItem != null);
}
}
protected override DependencyObject GetContainerForItemOverride() => new CoverViewItem();
private void SetBindingForItem(FrameworkElement item)
{
item.SetBinding(MarginProperty, new Binding(ItemMarginProperty.Name) { Source = this });
item.SetBinding(WidthProperty, new Binding(ItemWidthProperty.Name) { Source = this });
item.SetBinding(HeightProperty, new Binding(ItemHeightProperty.Name) { Source = this });
item.SetBinding(HeaderedSelectableItem.HeaderTemplateProperty, new Binding(ItemHeaderTemplateProperty.Name) { Source = this });
}
protected override bool IsItemItsOwnContainerOverride(object item) => item is CoverViewItem;
public static readonly DependencyProperty CoverViewContentStyleProperty = DependencyProperty.Register(
"CoverViewContentStyle", typeof(Style), typeof(CoverView), new PropertyMetadata(default(Style)));
public Style CoverViewContentStyle
{
get => (Style) GetValue(CoverViewContentStyleProperty);
set => SetValue(CoverViewContentStyleProperty, value);
}
internal static readonly DependencyProperty GroupsProperty = DependencyProperty.Register(
"Groups", typeof(int), typeof(CoverView),
new FrameworkPropertyMetadata(ValueBoxes.Int5Box, FrameworkPropertyMetadataOptions.AffectsMeasure,
OnGroupsChanged), IsGroupsValid);
internal static void OnGroupsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is CoverView ctl)
{
ctl.UpdateCoverViewContent(ctl._viewContent.IsOpen);
}
}
private static bool IsGroupsValid(object value)
{
var v = (int) value;
return v >= 1;
}
public int Groups
{
get => (int) GetValue(GroupsProperty);
set => SetValue(GroupsProperty, value);
}
public static readonly DependencyProperty ItemContentHeightProperty = DependencyProperty.Register(
"ItemContentHeight", typeof(double), typeof(CoverView), new PropertyMetadata(ValueBoxes.Double300Box),
ValidateHelper.IsInRangeOfPosDoubleIncludeZero);
public double ItemContentHeight
{
get => (double) GetValue(ItemContentHeightProperty);
set => SetValue(ItemContentHeightProperty, value);
}
public static readonly DependencyProperty ItemContentHeightFixedProperty = DependencyProperty.Register(
"ItemContentHeightFixed", typeof(bool), typeof(CoverView), new PropertyMetadata(ValueBoxes.TrueBox));
public bool ItemContentHeightFixed
{
get => (bool) GetValue(ItemContentHeightFixedProperty);
set => SetValue(ItemContentHeightFixedProperty, ValueBoxes.BooleanBox(value));
}
public static readonly DependencyProperty ItemHeaderTemplateProperty = DependencyProperty.Register(
"ItemHeaderTemplate", typeof(DataTemplate), typeof(CoverView), new PropertyMetadata(default(DataTemplate)));
public DataTemplate ItemHeaderTemplate
{
get => (DataTemplate) GetValue(ItemHeaderTemplateProperty);
set => SetValue(ItemHeaderTemplateProperty, value);
}
protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
{
base.OnRenderSizeChanged(sizeInfo);
Groups = (int) (sizeInfo.NewSize.Width / (ItemWidth + ItemMargin.Left + ItemMargin.Right));
}
protected override void Refresh()
{
if (ItemsHost == null) return;
_entryDic.Clear();
_isRefresh = true;
foreach (var item in Items)
{
AddItem(item);
return;
}
_isRefresh = false;
GenerateIndex();
UpdateCoverViewContent(_viewContent.IsOpen);
}
/// <summary>
/// 更新内容视图
/// </summary>
private void UpdateCoverViewContent(bool isOpen)
{
if (_selectedItem == null) return;
ItemsHost.Children.Remove(_viewContent);
if (!ItemContentHeightFixed)
if (!Equals(_selectedItem, item))
{
_viewContent.ManualHeight = 0;
if (_viewContent.Content is FrameworkElement element)
_selectedItem.SetCurrentValue(SelectableItem.IsSelectedProperty, ValueBoxes.FalseBox);
item.SetCurrentValue(SelectableItem.IsSelectedProperty, ValueBoxes.TrueBox);
_selectedItem = item;
if (_viewContent != null)
{
element.VerticalAlignment = VerticalAlignment.Top;
element.Arrange(new Rect(new Size(double.MaxValue, double.MaxValue)));
_viewContent.ManualHeight = element.ActualHeight;
_viewContent.Content = item.Content;
UpdateCoverViewContent(true);
}
return;
}
UpdateCoverViewContentPosition();
if (_viewContent.CanSwitch)
if (_viewContent != null)
{
_viewContent.IsOpen = isOpen;
_viewContent.Content = null;
_viewContent.ContentTemplate = null;
UpdateCoverViewContent(false);
}
_selectedItem.SetCurrentValue(SelectableItem.IsSelectedProperty, ValueBoxes.FalseBox);
_selectedItem = null;
}
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
_viewContent.Style ??= CoverViewContentStyle;
if (_selectedItem != null)
{
UpdateCoverViewContent(_selectedItem != null);
}
}
protected override DependencyObject GetContainerForItemOverride() => new CoverViewItem();
private void SetBindingForItem(FrameworkElement item)
{
item.SetBinding(MarginProperty, new Binding(ItemMarginProperty.Name) { Source = this });
item.SetBinding(WidthProperty, new Binding(ItemWidthProperty.Name) { Source = this });
item.SetBinding(HeightProperty, new Binding(ItemHeightProperty.Name) { Source = this });
item.SetBinding(HeaderedSelectableItem.HeaderTemplateProperty, new Binding(ItemHeaderTemplateProperty.Name) { Source = this });
}
protected override bool IsItemItsOwnContainerOverride(object item) => item is CoverViewItem;
public static readonly DependencyProperty CoverViewContentStyleProperty = DependencyProperty.Register(
nameof(CoverViewContentStyle), typeof(Style), typeof(CoverView), new PropertyMetadata(default(Style)));
public Style CoverViewContentStyle
{
get => (Style) GetValue(CoverViewContentStyleProperty);
set => SetValue(CoverViewContentStyleProperty, value);
}
internal static readonly DependencyProperty GroupsProperty = DependencyProperty.Register(
nameof(Groups), typeof(int), typeof(CoverView),
new FrameworkPropertyMetadata(ValueBoxes.Int5Box, FrameworkPropertyMetadataOptions.AffectsMeasure,
OnGroupsChanged, CoerceGroups));
private static object CoerceGroups(DependencyObject d, object basevalue)
{
return (int) basevalue < 1 ? 1 : basevalue;
}
internal static void OnGroupsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is CoverView ctl)
{
ctl.UpdateCoverViewContent(ctl._viewContent.IsOpen);
}
}
public int Groups
{
get => (int) GetValue(GroupsProperty);
set => SetValue(GroupsProperty, value);
}
public static readonly DependencyProperty ItemContentHeightProperty = DependencyProperty.Register(
nameof(ItemContentHeight), typeof(double), typeof(CoverView), new PropertyMetadata(ValueBoxes.Double300Box),
ValidateHelper.IsInRangeOfPosDoubleIncludeZero);
public double ItemContentHeight
{
get => (double) GetValue(ItemContentHeightProperty);
set => SetValue(ItemContentHeightProperty, value);
}
public static readonly DependencyProperty ItemContentHeightFixedProperty = DependencyProperty.Register(
nameof(ItemContentHeightFixed), typeof(bool), typeof(CoverView), new PropertyMetadata(ValueBoxes.TrueBox));
public bool ItemContentHeightFixed
{
get => (bool) GetValue(ItemContentHeightFixedProperty);
set => SetValue(ItemContentHeightFixedProperty, ValueBoxes.BooleanBox(value));
}
public static readonly DependencyProperty ItemHeaderTemplateProperty = DependencyProperty.Register(
nameof(ItemHeaderTemplate), typeof(DataTemplate), typeof(CoverView), new PropertyMetadata(default(DataTemplate)));
public DataTemplate ItemHeaderTemplate
{
get => (DataTemplate) GetValue(ItemHeaderTemplateProperty);
set => SetValue(ItemHeaderTemplateProperty, value);
}
protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
{
base.OnRenderSizeChanged(sizeInfo);
Groups = (int) (sizeInfo.NewSize.Width / (ItemWidth + ItemMargin.Left + ItemMargin.Right));
}
protected override void Refresh()
{
if (ItemsHost == null) return;
_entryDic.Clear();
_isRefresh = true;
foreach (var item in Items)
{
AddItem(item);
}
_isRefresh = false;
GenerateIndex();
UpdateCoverViewContent(_viewContent.IsOpen);
}
/// <summary>
/// 更新内容视图
/// </summary>
private void UpdateCoverViewContent(bool isOpen)
{
if (_selectedItem == null) return;
ItemsHost.Children.Remove(_viewContent);
if (!ItemContentHeightFixed)
{
_viewContent.ManualHeight = 0;
if (_viewContent.Content is FrameworkElement element)
{
element.VerticalAlignment = VerticalAlignment.Top;
element.Arrange(new Rect(new Size(double.MaxValue, double.MaxValue)));
_viewContent.ManualHeight = element.ActualHeight;
}
}
/// <summary>
/// 更新内容视图位置
/// </summary>
private void UpdateCoverViewContentPosition()
UpdateCoverViewContentPosition();
if (_viewContent.CanSwitch)
{
if (_viewContent.Parent != null)
{
ItemsHost.Children.Remove(_viewContent);
}
var total = _entryDic.Count + 1;
var totalRow = total / Groups + (total % Groups > 0 ? 1 : 0);
if (total <= Groups)
_viewContent.IsOpen = isOpen;
}
}
/// <summary>
/// 更新内容视图位置
/// </summary>
private void UpdateCoverViewContentPosition()
{
if (_viewContent.Parent != null)
{
ItemsHost.Children.Remove(_viewContent);
}
if (_selectedItem == null)
{
return;
}
var total = _entryDic.Count + 1;
var totalRow = total / Groups + (total % Groups > 0 ? 1 : 0);
if (total <= Groups)
{
ItemsHost.Children.Add(_viewContent);
}
else
{
var row = _selectedItem.Index / Groups + 1;
if (row == totalRow)
{
ItemsHost.Children.Add(_viewContent);
}
else
{
var row = _selectedItem.Index / Groups + 1;
if (row == totalRow)
{
ItemsHost.Children.Add(_viewContent);
}
else
{
ItemsHost.Children.Insert(row * Groups, _viewContent);
}
}
_viewContent.UpdatePosition(_selectedItem.Index, Groups, _selectedItem.DesiredSize.Width);
}
protected override void OnItemsSourceChanged(IEnumerable oldValue, IEnumerable newValue)
{
if (_itemsSourceInternal != null)
{
if (_itemsSourceInternal is INotifyCollectionChanged s)
{
s.CollectionChanged -= InternalCollectionChanged;
}
Items.Clear();
ClearItems();
}
_itemsSourceInternal = newValue;
if (_itemsSourceInternal != null)
{
if (_itemsSourceInternal is INotifyCollectionChanged s)
{
s.CollectionChanged += InternalCollectionChanged;
}
foreach (var item in _itemsSourceInternal)
{
AddItem(item);
}
}
if (ItemsHost != null)
{
GenerateIndex();
ItemsHost.Children.Insert(row * Groups, _viewContent);
}
}
private void ClearItems()
{
_selectedItem = null;
_viewContent.Content = null;
_viewContent.ContentTemplate = null;
_viewContent.UpdatePosition(_selectedItem.Index, Groups, _selectedItem.DesiredSize.Width);
}
if (ItemsHost != null)
protected override void OnItemsSourceChanged(IEnumerable oldValue, IEnumerable newValue)
{
if (_itemsSourceInternal != null)
{
if (_itemsSourceInternal is INotifyCollectionChanged s)
{
ItemsHost.Children.Remove(_viewContent);
ItemsHost.Children.Clear();
s.CollectionChanged -= InternalCollectionChanged;
}
_entryDic.Clear();
Items.Clear();
ClearItems();
}
_itemsSourceInternal = newValue;
if (_itemsSourceInternal != null)
{
if (_itemsSourceInternal is INotifyCollectionChanged s)
{
s.CollectionChanged += InternalCollectionChanged;
}
foreach (var item in _itemsSourceInternal)
{
AddItem(item);
}
}
private void RemoveItem(object item)
if (ItemsHost != null)
{
if (_entryDic.TryGetValue(item, out var entry))
GenerateIndex();
}
}
private void ClearItems()
{
_selectedItem = null;
_viewContent.Content = null;
_viewContent.ContentTemplate = null;
if (ItemsHost != null)
{
ItemsHost.Children.Remove(_viewContent);
ItemsHost.Children.Clear();
}
_entryDic.Clear();
}
private void RemoveItem(object item)
{
if (_entryDic.TryGetValue(item, out var entry))
{
if (ReferenceEquals(entry, _selectedItem))
{
if (ReferenceEquals(entry, _selectedItem))
_selectedItem = null;
if (_viewContent != null)
{
_selectedItem = null;
if (_viewContent != null)
{
_viewContent.Content = null;
_viewContent.IsOpen = false;
ItemsHost.Children.Remove(_viewContent);
}
_viewContent.Content = null;
_viewContent.IsOpen = false;
ItemsHost.Children.Remove(_viewContent);
}
ItemsHost.Children.Remove(entry);
Items.Remove(item);
_entryDic.Remove(item);
}
ItemsHost.Children.Remove(entry);
Items.Remove(item);
_entryDic.Remove(item);
}
}
private void AddItem(object item) => InsertItem(_entryDic.Count, item);
private void AddItem(object item) => InsertItem(_entryDic.Count, item);
private void InsertItem(int index, object item)
private void InsertItem(int index, object item)
{
if (ItemsHost == null)
{
if (ItemsHost == null)
Items.Insert(index, item);
_entryDic.Add(item, null);
}
else
{
DependencyObject container;
if (IsItemItsOwnContainerOverride(item))
{
Items.Insert(index, item);
_entryDic.Add(item, null);
container = item as DependencyObject;
}
else
{
DependencyObject container;
if (IsItemItsOwnContainerOverride(item))
{
container = item as DependencyObject;
}
else
{
container = GetContainerForItemOverride();
PrepareContainerForItemOverride(container, item);
}
if (container is CoverViewItem element)
{
SetBindingForItem(element);
element.Style = ItemContainerStyle;
element.Header ??= item;
_entryDic[item] = element;
ItemsHost.Children.Insert(index, element);
if (IsLoaded && !_isRefresh && _itemsSourceInternal != null)
{
Items.Insert(index, item);
}
}
container = GetContainerForItemOverride();
PrepareContainerForItemOverride(container, item);
}
}
/// <summary>
/// 生成序号
/// </summary>
private void GenerateIndex()
{
var index = 0;
foreach (var item in _entryDic.Values)
if (container is CoverViewItem element)
{
item.Index = index++;
}
}
SetBindingForItem(element);
element.Style = ItemContainerStyle;
element.Header ??= item;
_entryDic[item] = element;
ItemsHost.Children.Insert(index, element);
private void InternalCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (ItemsHost == null) return;
if (e.Action == NotifyCollectionChangedAction.Reset)
{
if (_entryDic.Count == 0) return;
ClearItems();
Items.Clear();
return;
}
if (e.OldItems != null)
{
foreach (var item in e.OldItems)
if (IsLoaded && !_isRefresh && _itemsSourceInternal != null)
{
RemoveItem(item);
Items.Insert(index, item);
}
}
if (e.NewItems != null)
{
if (_viewContent.IsOpen)
{
ItemsHost.Children.Remove(_viewContent);
}
var count = 0;
foreach (var item in e.NewItems)
{
var index = e.NewStartingIndex + count++;
InsertItem(index, item);
}
}
GenerateIndex();
if (_viewContent.IsOpen)
{
UpdateCoverViewContentPosition();
}
}
protected override void OnItemsChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (_itemsSourceInternal != null) return;
InternalCollectionChanged(sender, e);
}
protected override void OnItemTemplateChanged(DependencyPropertyChangedEventArgs e)
{
}
protected override void OnItemContainerStyleChanged(DependencyPropertyChangedEventArgs e)
{
}
}
/// <summary>
/// 生成序号
/// </summary>
private void GenerateIndex()
{
var index = 0;
foreach (var item in _entryDic.Values)
{
item.Index = index++;
}
}
private void InternalCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (ItemsHost == null) return;
if (e.Action == NotifyCollectionChangedAction.Reset)
{
if (_entryDic.Count == 0) return;
ClearItems();
Items.Clear();
return;
}
if (e.OldItems != null)
{
foreach (var item in e.OldItems)
{
RemoveItem(item);
}
}
if (e.NewItems != null)
{
if (_viewContent.IsOpen)
{
ItemsHost.Children.Remove(_viewContent);
}
var count = 0;
foreach (var item in e.NewItems)
{
var index = e.NewStartingIndex + count++;
InsertItem(index, item);
}
}
GenerateIndex();
if (_viewContent.IsOpen)
{
UpdateCoverViewContentPosition();
}
}
protected override void OnItemsChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (_itemsSourceInternal != null) return;
InternalCollectionChanged(sender, e);
}
protected override void OnItemTemplateChanged(DependencyPropertyChangedEventArgs e)
{
}
protected override void OnItemContainerStyleChanged(DependencyPropertyChangedEventArgs e)
{
}
}
@@ -5,118 +5,117 @@ using HandyControl.Expression.Drawing;
using HandyControl.Tools;
using HandyControl.Tools.Extension;
namespace HandyControl.Controls
namespace HandyControl.Controls;
[TemplatePart(Name = ElementTriangle, Type = typeof(FrameworkElement))]
[TemplatePart(Name = ElementContent, Type = typeof(FrameworkElement))]
public class CoverViewContent : ContentControl
{
[TemplatePart(Name = ElementTriangle, Type = typeof(FrameworkElement))]
[TemplatePart(Name = ElementContent, Type = typeof(FrameworkElement))]
internal class CoverViewContent : ContentControl
private const string ElementTriangle = "PART_Triangle";
private const string ElementContent = "PART_Content";
private FrameworkElement _triangle;
private FrameworkElement _content;
internal bool WaitForUpdate { get; set; }
private int _index;
private int _groups;
private double _itemWidth;
internal bool CanSwitch { get; set; } = true;
private bool _isOpen;
internal bool IsOpen
{
private const string ElementTriangle = "PART_Triangle";
private const string ElementContent = "PART_Content";
private FrameworkElement _triangle;
private FrameworkElement _content;
internal bool WaitForUpdate { get; set; }
private int _index;
private int _groups;
private double _itemWidth;
internal bool CanSwitch { get; set; } = true;
private bool _isOpen;
internal bool IsOpen
get => _isOpen;
set
{
get => _isOpen;
set
{
if (_isOpen == value) return;
_isOpen = value;
OpenSwitch(value);
}
}
internal static readonly DependencyProperty ManualHeightProperty = DependencyProperty.Register(
"ManualHeight", typeof(double), typeof(CoverViewContent), new PropertyMetadata(ValueBoxes.Double0Box), ValidateHelper.IsInRangeOfPosDoubleIncludeZero);
internal double ManualHeight
{
get => (double) GetValue(ManualHeightProperty);
set => SetValue(ManualHeightProperty, value);
}
public static readonly DependencyProperty ContentHeightProperty = DependencyProperty.Register(
"ContentHeight", typeof(double), typeof(CoverViewContent), new PropertyMetadata(ValueBoxes.Double300Box),
ValidateHelper.IsInRangeOfPosDoubleIncludeZero);
public double ContentHeight
{
get => (double) GetValue(ContentHeightProperty);
set => SetValue(ContentHeightProperty, value);
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
_triangle = GetTemplateChild(ElementTriangle) as FrameworkElement;
_content = GetTemplateChild(ElementContent) as FrameworkElement;
if (WaitForUpdate)
{
_triangle.BeginAnimation(MarginProperty, AnimationHelper.CreateAnimation(new Thickness((_index % _groups + .5) * _itemWidth - _triangle.Width / 2, 0, 0, 0)));
OpenSwitch(_isOpen);
WaitForUpdate = false;
}
}
internal void UpdatePosition(int index, int groups, double itemWidth)
{
if (_triangle == null)
{
_index = index;
_groups = groups;
_itemWidth = itemWidth;
WaitForUpdate = true;
return;
}
_triangle.BeginAnimation(MarginProperty, AnimationHelper.CreateAnimation(new Thickness((index % groups + .5) * itemWidth - _triangle.Width / 2, 0, 0, 0)));
if (IsOpen)
{
if (ManualHeight > 0 && !MathHelper.AreClose(ManualHeight, ContentHeight))
{
_content.BeginAnimation(HeightProperty, AnimationHelper.CreateAnimation(ManualHeight));
}
else
{
_content.BeginAnimation(HeightProperty, AnimationHelper.CreateAnimation(ContentHeight));
}
}
}
private void OpenSwitch(bool isOpen)
{
if (_content == null) return;
var animation = AnimationHelper.CreateAnimation(isOpen ? ManualHeight > 0 ? ManualHeight : ContentHeight : 0);
_triangle.Show(false);
this.Show(true);
animation.Completed += (s, e) =>
{
CanSwitch = true;
this.Show(IsOpen);
if (IsOpen)
{
_triangle.Show(true);
}
};
CanSwitch = false;
_content.BeginAnimation(HeightProperty, animation);
if (_isOpen == value) return;
_isOpen = value;
OpenSwitch(value);
}
}
internal static readonly DependencyProperty ManualHeightProperty = DependencyProperty.Register(
nameof(ManualHeight), typeof(double), typeof(CoverViewContent), new PropertyMetadata(ValueBoxes.Double0Box), ValidateHelper.IsInRangeOfPosDoubleIncludeZero);
internal double ManualHeight
{
get => (double) GetValue(ManualHeightProperty);
set => SetValue(ManualHeightProperty, value);
}
public static readonly DependencyProperty ContentHeightProperty = DependencyProperty.Register(
nameof(ContentHeight), typeof(double), typeof(CoverViewContent), new PropertyMetadata(ValueBoxes.Double300Box),
ValidateHelper.IsInRangeOfPosDoubleIncludeZero);
public double ContentHeight
{
get => (double) GetValue(ContentHeightProperty);
set => SetValue(ContentHeightProperty, value);
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
_triangle = GetTemplateChild(ElementTriangle) as FrameworkElement;
_content = GetTemplateChild(ElementContent) as FrameworkElement;
if (WaitForUpdate)
{
_triangle.BeginAnimation(MarginProperty, AnimationHelper.CreateAnimation(new Thickness((_index % _groups + .5) * _itemWidth - _triangle.Width / 2, 0, 0, 0)));
OpenSwitch(_isOpen);
WaitForUpdate = false;
}
}
internal void UpdatePosition(int index, int groups, double itemWidth)
{
if (_triangle == null)
{
_index = index;
_groups = groups;
_itemWidth = itemWidth;
WaitForUpdate = true;
return;
}
_triangle.BeginAnimation(MarginProperty, AnimationHelper.CreateAnimation(new Thickness((index % groups + .5) * itemWidth - _triangle.Width / 2, 0, 0, 0)));
if (IsOpen)
{
if (ManualHeight > 0 && !MathHelper.AreClose(ManualHeight, ContentHeight))
{
_content.BeginAnimation(HeightProperty, AnimationHelper.CreateAnimation(ManualHeight));
}
else
{
_content.BeginAnimation(HeightProperty, AnimationHelper.CreateAnimation(ContentHeight));
}
}
}
private void OpenSwitch(bool isOpen)
{
if (_content == null) return;
var animation = AnimationHelper.CreateAnimation(isOpen ? ManualHeight > 0 ? ManualHeight : ContentHeight : 0);
_triangle.Show(false);
this.Show(true);
animation.Completed += (s, e) =>
{
CanSwitch = true;
this.Show(IsOpen);
if (IsOpen)
{
_triangle.Show(true);
}
};
CanSwitch = false;
_content.BeginAnimation(HeightProperty, animation);
}
}
@@ -1,7 +1,6 @@
namespace HandyControl.Controls
namespace HandyControl.Controls;
public class CoverViewItem : HeaderedSelectableItem
{
public class CoverViewItem : HeaderedSelectableItem
{
internal int Index { get; set; }
}
internal int Index { get; set; }
}
@@ -4,172 +4,221 @@ using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using HandyControl.Data;
using HandyControl.Interactivity;
using HandyControl.Tools;
namespace HandyControl.Controls
namespace HandyControl.Controls;
[TemplatePart(Name = BackElement, Type = typeof(Border))]
public class Dialog : ContentControl
{
public class Dialog : ContentControl
private const string BackElement = "PART_BackElement";
private string _token;
private Border _backElement;
private AdornerContainer _container;
private static readonly Dictionary<string, FrameworkElement> ContainerDict = new();
private static readonly Dictionary<string, Dialog> DialogDict = new();
public static readonly DependencyProperty IsClosedProperty = DependencyProperty.Register(
nameof(IsClosed), typeof(bool), typeof(Dialog), new PropertyMetadata(ValueBoxes.FalseBox));
public bool IsClosed
{
private string _token;
get => (bool) GetValue(IsClosedProperty);
internal set => SetValue(IsClosedProperty, ValueBoxes.BooleanBox(value));
}
private AdornerContainer _container;
public static readonly DependencyProperty MaskCanCloseProperty = DependencyProperty.RegisterAttached(
"MaskCanClose", typeof(bool), typeof(Dialog), new FrameworkPropertyMetadata(ValueBoxes.FalseBox, FrameworkPropertyMetadataOptions.Inherits));
private static readonly Dictionary<string, FrameworkElement> ContainerDic = new Dictionary<string, FrameworkElement>();
public static void SetMaskCanClose(DependencyObject element, bool value)
=> element.SetValue(MaskCanCloseProperty, ValueBoxes.BooleanBox(value));
public static readonly DependencyProperty IsClosedProperty = DependencyProperty.Register(
"IsClosed", typeof(bool), typeof(Dialog), new PropertyMetadata(ValueBoxes.FalseBox));
public static bool GetMaskCanClose(DependencyObject element)
=> (bool) element.GetValue(MaskCanCloseProperty);
public bool IsClosed
public static readonly DependencyProperty MaskBrushProperty = DependencyProperty.Register(
nameof(MaskBrush), typeof(Brush), typeof(Dialog), new PropertyMetadata(default(Brush)));
public Brush MaskBrush
{
get => (Brush) GetValue(MaskBrushProperty);
set => SetValue(MaskBrushProperty, value);
}
public static readonly DependencyProperty TokenProperty = DependencyProperty.RegisterAttached(
"Token", typeof(string), typeof(Dialog), new PropertyMetadata(default(string), OnTokenChanged));
private static void OnTokenChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is FrameworkElement element)
{
get => (bool) GetValue(IsClosedProperty);
internal set => SetValue(IsClosedProperty, ValueBoxes.BooleanBox(value));
}
public static readonly DependencyProperty TokenProperty = DependencyProperty.RegisterAttached(
"Token", typeof(string), typeof(Dialog), new PropertyMetadata(default(string), OnTokenChanged));
private static void OnTokenChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is FrameworkElement element)
if (e.NewValue == null)
{
if (e.NewValue == null)
{
Unregister(element);
}
else
{
Register(e.NewValue.ToString(), element);
}
}
}
public static void SetToken(DependencyObject element, string value)
=> element.SetValue(TokenProperty, value);
public static string GetToken(DependencyObject element)
=> (string) element.GetValue(TokenProperty);
public Dialog()
{
CommandBindings.Add(new CommandBinding(ControlCommands.Close, (s, e) => Close()));
}
public static void Register(string token, FrameworkElement element)
{
if (string.IsNullOrEmpty(token) || element == null) return;
ContainerDic[token] = element;
}
public static void Unregister(string token, FrameworkElement element)
{
if (string.IsNullOrEmpty(token) || element == null) return;
if (ContainerDic.ContainsKey(token))
{
if (ReferenceEquals(ContainerDic[token], element))
{
ContainerDic.Remove(token);
}
}
}
public static void Unregister(FrameworkElement element)
{
if (element == null) return;
var first = ContainerDic.FirstOrDefault(item => ReferenceEquals(element, item.Value));
if (!string.IsNullOrEmpty(first.Key))
{
ContainerDic.Remove(first.Key);
}
}
public static void Unregister(string token)
{
if (string.IsNullOrEmpty(token)) return;
if (ContainerDic.ContainsKey(token))
{
ContainerDic.Remove(token);
}
}
public static Dialog Show<T>(string token = "") where T : new() => Show(new T(), token);
public static Dialog Show(object content, string token = "")
{
var dialog = new Dialog
{
_token = token,
Content = content
};
FrameworkElement element;
AdornerDecorator decorator;
if (string.IsNullOrEmpty(token))
{
element = WindowHelper.GetActiveWindow();
decorator = VisualHelper.GetChild<AdornerDecorator>(element);
Unregister(element);
}
else
{
ContainerDic.TryGetValue(token, out element);
decorator = element is System.Windows.Window ?
VisualHelper.GetChild<AdornerDecorator>(element) :
VisualHelper.GetChild<DialogContainer>(element);
Register(e.NewValue.ToString(), element);
}
}
}
public static void SetToken(DependencyObject element, string value)
=> element.SetValue(TokenProperty, value);
public static string GetToken(DependencyObject element)
=> (string) element.GetValue(TokenProperty);
public Dialog()
{
CommandBindings.Add(new CommandBinding(ControlCommands.Close, (s, e) => Close()));
}
public static void Register(string token, FrameworkElement element)
{
if (string.IsNullOrEmpty(token) || element == null) return;
ContainerDict[token] = element;
}
public static void Unregister(string token, FrameworkElement element)
{
if (string.IsNullOrEmpty(token) || element == null) return;
if (ContainerDict.ContainsKey(token))
{
if (ReferenceEquals(ContainerDict[token], element))
{
ContainerDict.Remove(token);
}
}
}
public static void Unregister(FrameworkElement element)
{
if (element == null) return;
var first = ContainerDict.FirstOrDefault(item => ReferenceEquals(element, item.Value));
if (!string.IsNullOrEmpty(first.Key))
{
ContainerDict.Remove(first.Key);
}
}
public static void Unregister(string token)
{
if (string.IsNullOrEmpty(token)) return;
if (ContainerDict.ContainsKey(token))
{
ContainerDict.Remove(token);
}
}
public static Dialog Show<T>(string token = "") where T : new() => Show(new T(), token);
public static Dialog Show(object content, string token = "")
{
var dialog = new Dialog
{
_token = token,
Content = content
};
FrameworkElement element;
AdornerDecorator decorator;
if (string.IsNullOrEmpty(token))
{
element = WindowHelper.GetActiveWindow();
decorator = VisualHelper.GetChild<AdornerDecorator>(element);
}
else
{
Close(token);
DialogDict[token] = dialog;
ContainerDict.TryGetValue(token, out element);
decorator = element is System.Windows.Window ?
VisualHelper.GetChild<AdornerDecorator>(element) :
VisualHelper.GetChild<DialogContainer>(element);
}
if (decorator != null)
{
if (decorator.Child != null)
{
decorator.Child.IsEnabled = false;
}
var layer = decorator.AdornerLayer;
if (layer != null)
{
var container = new AdornerContainer(layer)
{
Child = dialog
};
dialog._container = container;
dialog.IsClosed = false;
layer.Add(container);
}
}
return dialog;
}
public static void Close(string token)
{
if (DialogDict.TryGetValue(token, out Dialog dialog))
{
dialog.Close();
}
}
public void Close()
{
if (string.IsNullOrEmpty(_token))
{
Close(WindowHelper.GetActiveWindow());
}
else if (ContainerDict.TryGetValue(_token, out var element))
{
Close(element);
DialogDict.Remove(_token);
}
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
_backElement = GetTemplateChild(BackElement) as Border;
}
protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
{
if (GetMaskCanClose(this) && _backElement is { IsMouseDirectlyOver: true })
{
Close();
}
}
private void Close(DependencyObject element)
{
if (element != null && _container != null)
{
var decorator = VisualHelper.GetChild<AdornerDecorator>(element);
if (decorator != null)
{
if (decorator.Child != null)
{
decorator.Child.IsEnabled = false;
decorator.Child.IsEnabled = true;
}
var layer = decorator.AdornerLayer;
if (layer != null)
{
var container = new AdornerContainer(layer)
{
Child = dialog
};
dialog._container = container;
dialog.IsClosed = false;
layer.Add(container);
}
}
return dialog;
}
public void Close()
{
if (string.IsNullOrEmpty(_token))
{
Close(WindowHelper.GetActiveWindow());
}
else if (ContainerDic.TryGetValue(_token, out var element))
{
Close(element);
}
}
private void Close(DependencyObject element)
{
if (element != null && _container != null)
{
var decorator = VisualHelper.GetChild<AdornerDecorator>(element);
if (decorator != null)
{
if (decorator.Child != null)
{
decorator.Child.IsEnabled = true;
}
var layer = decorator.AdornerLayer;
layer?.Remove(_container);
IsClosed = true;
}
layer?.Remove(_container);
IsClosed = true;
}
}
}
@@ -1,9 +1,8 @@
using System.Windows.Documents;
namespace HandyControl.Controls
namespace HandyControl.Controls;
public class DialogContainer : AdornerDecorator
{
public class DialogContainer : AdornerDecorator
{
}
}
@@ -0,0 +1,412 @@
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Markup;
using System.Windows.Media;
using System.Windows.Media.Animation;
using HandyControl.Data;
using HandyControl.Interactivity;
using HandyControl.Tools;
namespace HandyControl.Controls;
[ContentProperty("Content")]
public class Drawer : FrameworkElement
{
private Storyboard _storyboard;
private AdornerContainer _container;
private ContentControl _animationControl;
private TranslateTransform _translateTransform;
private double _animationLength;
private string _animationPropertyName;
private FrameworkElement _maskElement;
private AdornerLayer _layer;
private UIElement _contentElement;
private Point _contentRenderTransformOrigin;
static Drawer()
{
DataContextProperty.OverrideMetadata(typeof(Drawer), new FrameworkPropertyMetadata(DataContextPropertyChanged));
}
public Drawer()
{
Loaded += Drawer_Loaded;
}
private void Drawer_Loaded(object sender, RoutedEventArgs e)
{
if (IsOpen)
{
OnIsOpenChanged(true);
}
Loaded -= Drawer_Loaded;
}
private static void DataContextPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) =>
((Drawer) d).OnDataContextPropertyChanged(e);
private void OnDataContextPropertyChanged(DependencyPropertyChangedEventArgs e) => UpdateDataContext(_animationControl, e.OldValue, e.NewValue);
public static readonly RoutedEvent OpenedEvent =
EventManager.RegisterRoutedEvent("Opened", RoutingStrategy.Bubble,
typeof(RoutedEventHandler), typeof(Drawer));
public event RoutedEventHandler Opened
{
add => AddHandler(OpenedEvent, value);
remove => RemoveHandler(OpenedEvent, value);
}
public static readonly RoutedEvent ClosedEvent =
EventManager.RegisterRoutedEvent("Closed", RoutingStrategy.Bubble,
typeof(RoutedEventHandler), typeof(Drawer));
public event RoutedEventHandler Closed
{
add => AddHandler(ClosedEvent, value);
remove => RemoveHandler(ClosedEvent, value);
}
public static readonly DependencyProperty IsOpenProperty = DependencyProperty.Register(
nameof(IsOpen), typeof(bool), typeof(Drawer), new FrameworkPropertyMetadata(ValueBoxes.FalseBox, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnIsOpenChanged));
private static void OnIsOpenChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var ctl = (Drawer) d;
ctl.OnIsOpenChanged((bool) e.NewValue);
}
public bool IsOpen
{
get => (bool) GetValue(IsOpenProperty);
set => SetValue(IsOpenProperty, ValueBoxes.BooleanBox(value));
}
public static readonly DependencyProperty MaskCanCloseProperty = DependencyProperty.Register(
nameof(MaskCanClose), typeof(bool), typeof(Drawer), new PropertyMetadata(ValueBoxes.TrueBox));
public bool MaskCanClose
{
get => (bool) GetValue(MaskCanCloseProperty);
set => SetValue(MaskCanCloseProperty, ValueBoxes.BooleanBox(value));
}
public static readonly DependencyProperty ShowMaskProperty = DependencyProperty.Register(
nameof(ShowMask), typeof(bool), typeof(Drawer), new PropertyMetadata(ValueBoxes.TrueBox));
public bool ShowMask
{
get => (bool) GetValue(ShowMaskProperty);
set => SetValue(ShowMaskProperty, ValueBoxes.BooleanBox(value));
}
public static readonly DependencyProperty DockProperty = DependencyProperty.Register(
nameof(Dock), typeof(Dock), typeof(Drawer), new PropertyMetadata(default(Dock)));
public Dock Dock
{
get => (Dock) GetValue(DockProperty);
set => SetValue(DockProperty, value);
}
public static readonly DependencyProperty ShowModeProperty = DependencyProperty.Register(
nameof(ShowMode), typeof(DrawerShowMode), typeof(Drawer), new PropertyMetadata(default(DrawerShowMode)));
public DrawerShowMode ShowMode
{
get => (DrawerShowMode) GetValue(ShowModeProperty);
set => SetValue(ShowModeProperty, value);
}
public static readonly DependencyProperty MaskBrushProperty = DependencyProperty.Register(
nameof(MaskBrush), typeof(Brush), typeof(Drawer), new PropertyMetadata(default(Brush)));
public Brush MaskBrush
{
get => (Brush) GetValue(MaskBrushProperty);
set => SetValue(MaskBrushProperty, value);
}
public static readonly DependencyProperty ContentProperty = DependencyProperty.Register(
nameof(Content), typeof(object), typeof(Drawer), new PropertyMetadata(default(object)));
public object Content
{
get => GetValue(ContentProperty);
set => SetValue(ContentProperty, value);
}
private void CreateContainer()
{
_storyboard = new Storyboard();
_storyboard.Completed += Storyboard_Completed;
_translateTransform = new TranslateTransform();
_animationControl = new ContentControl
{
Content = Content,
RenderTransform = _translateTransform,
DataContext = this
};
var panel = new SimplePanel
{
ClipToBounds = true
};
if (ShowMask)
{
_maskElement = new Border
{
Background = MaskBrush,
Opacity = 0
};
_maskElement.PreviewMouseLeftButtonDown += MaskElement_PreviewMouseLeftButtonDown;
panel.Children.Add(_maskElement);
}
_animationControl.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
var size = _animationControl.DesiredSize;
switch (Dock)
{
case Dock.Left:
_animationControl.HorizontalAlignment = HorizontalAlignment.Left;
_animationControl.VerticalAlignment = VerticalAlignment.Stretch;
_translateTransform.X = -size.Width;
_animationLength = -size.Width;
_animationPropertyName = "(UIElement.RenderTransform).(TranslateTransform.X)";
break;
case Dock.Top:
_animationControl.HorizontalAlignment = HorizontalAlignment.Stretch;
_animationControl.VerticalAlignment = VerticalAlignment.Top;
_translateTransform.Y = -size.Height;
_animationLength = -size.Height;
_animationPropertyName = "(UIElement.RenderTransform).(TranslateTransform.Y)";
break;
case Dock.Right:
_animationControl.HorizontalAlignment = HorizontalAlignment.Right;
_animationControl.VerticalAlignment = VerticalAlignment.Stretch;
_translateTransform.X = size.Width;
_animationLength = size.Width;
_animationPropertyName = "(UIElement.RenderTransform).(TranslateTransform.X)";
break;
case Dock.Bottom:
_animationControl.HorizontalAlignment = HorizontalAlignment.Stretch;
_animationControl.VerticalAlignment = VerticalAlignment.Bottom;
_translateTransform.Y = size.Height;
_animationLength = size.Height;
_animationPropertyName = "(UIElement.RenderTransform).(TranslateTransform.Y)";
break;
default:
throw new ArgumentOutOfRangeException();
}
_animationControl.DataContext = DataContext;
_animationControl.CommandBindings.Clear();
_animationControl.CommandBindings.Add(new CommandBinding(ControlCommands.Close, (s, e) => SetCurrentValue(IsOpenProperty, ValueBoxes.FalseBox)));
panel.Children.Add(_animationControl);
_container = new AdornerContainer(_layer)
{
Child = panel,
ClipToBounds = true
};
}
private void Storyboard_Completed(object sender, EventArgs e)
{
if (!IsOpen)
{
_contentElement.SetCurrentValue(RenderTransformOriginProperty, _contentRenderTransformOrigin);
_layer.Remove(_container);
RaiseEvent(new RoutedEventArgs(ClosedEvent, this));
}
else
{
RaiseEvent(new RoutedEventArgs(OpenedEvent, this));
}
}
private void OnIsOpenChanged(bool isOpen)
{
if (Content == null || DesignerHelper.IsInDesignMode)
{
return;
}
AdornerDecorator decorator;
var parent = VisualHelper.GetParent<DrawerContainer>(this);
if (parent != null)
{
_contentElement = parent.Child;
decorator = parent;
}
else
{
var window = WindowHelper.GetActiveWindow();
if (window == null)
{
return;
}
decorator = VisualHelper.GetChild<AdornerDecorator>(window);
_contentElement = window.Content as UIElement;
}
if (_contentElement == null)
{
return;
}
_layer = decorator?.AdornerLayer;
if (_layer == null)
{
return;
}
_contentRenderTransformOrigin = _contentElement.RenderTransformOrigin;
if (_container == null)
{
CreateContainer();
}
switch (ShowMode)
{
case DrawerShowMode.Push:
ShowByPush(isOpen);
break;
case DrawerShowMode.Press:
_contentElement.SetCurrentValue(RenderTransformOriginProperty, new Point(0.5, 0.5));
ShowByPress(isOpen);
break;
}
if (isOpen)
{
if (_maskElement != null)
{
var maskAnimation = AnimationHelper.CreateAnimation(1);
Storyboard.SetTarget(maskAnimation, _maskElement);
Storyboard.SetTargetProperty(maskAnimation, new PropertyPath(OpacityProperty.Name));
_storyboard.Children.Add(maskAnimation);
}
var drawerAnimation = AnimationHelper.CreateAnimation(0);
Storyboard.SetTarget(drawerAnimation, _animationControl);
Storyboard.SetTargetProperty(drawerAnimation, new PropertyPath(_animationPropertyName));
_storyboard.Children.Add(drawerAnimation);
_layer.Remove(_container);
_layer.Add(_container);
}
else
{
if (_maskElement != null)
{
var maskAnimation = AnimationHelper.CreateAnimation(0);
Storyboard.SetTarget(maskAnimation, _maskElement);
Storyboard.SetTargetProperty(maskAnimation, new PropertyPath(OpacityProperty.Name));
_storyboard.Children.Add(maskAnimation);
}
var drawerAnimation = AnimationHelper.CreateAnimation(_animationLength);
Storyboard.SetTarget(drawerAnimation, _animationControl);
Storyboard.SetTargetProperty(drawerAnimation, new PropertyPath(_animationPropertyName));
_storyboard.Children.Add(drawerAnimation);
}
_storyboard.Begin();
}
private void ShowByPush(bool isOpen)
{
string animationPropertyName;
switch (Dock)
{
case Dock.Left:
case Dock.Right:
animationPropertyName = "(UIElement.RenderTransform).(TranslateTransform.X)";
_contentElement.RenderTransform = new TranslateTransform
{
X = isOpen ? 0 : -_animationLength
};
break;
case Dock.Top:
case Dock.Bottom:
animationPropertyName = "(UIElement.RenderTransform).(TranslateTransform.Y)";
_contentElement.RenderTransform = new TranslateTransform
{
Y = isOpen ? 0 : -_animationLength
};
break;
default:
throw new ArgumentOutOfRangeException();
}
var animation = isOpen
? AnimationHelper.CreateAnimation(-_animationLength)
: AnimationHelper.CreateAnimation(0);
Storyboard.SetTarget(animation, _contentElement);
Storyboard.SetTargetProperty(animation, new PropertyPath(animationPropertyName));
_storyboard.Children.Add(animation);
}
private void ShowByPress(bool isOpen)
{
_contentElement.RenderTransform = isOpen
? new ScaleTransform()
: new ScaleTransform
{
ScaleX = 0.9,
ScaleY = 0.9
};
var animationX = isOpen
? AnimationHelper.CreateAnimation(.9)
: AnimationHelper.CreateAnimation(1);
Storyboard.SetTarget(animationX, _contentElement);
Storyboard.SetTargetProperty(animationX, new PropertyPath("(UIElement.RenderTransform).(ScaleTransform.ScaleX)"));
_storyboard.Children.Add(animationX);
var animationY = isOpen
? AnimationHelper.CreateAnimation(.9)
: AnimationHelper.CreateAnimation(1);
Storyboard.SetTarget(animationY, _contentElement);
Storyboard.SetTargetProperty(animationY, new PropertyPath("(UIElement.RenderTransform).(ScaleTransform.ScaleY)"));
_storyboard.Children.Add(animationY);
}
private void MaskElement_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (MaskCanClose)
{
SetCurrentValue(IsOpenProperty, ValueBoxes.FalseBox);
}
}
private void UpdateDataContext(FrameworkElement target, object oldValue, object newValue)
{
if (target == null || BindingOperations.GetBindingExpression(target, DataContextProperty) != null) return;
if (ReferenceEquals(this, target.DataContext) || Equals(oldValue, target.DataContext))
{
target.DataContext = newValue ?? this;
}
}
}
@@ -0,0 +1,8 @@
using System.Windows.Documents;
namespace HandyControl.Controls;
public class DrawerContainer : AdornerDecorator
{
}
File diff suppressed because it is too large. Load diff
@@ -1,37 +1,41 @@
using System.Windows;
using System;
using System.Windows;
using System.Windows.Controls;
using HandyControl.Tools;
using HandyControl.Tools.Interop;
namespace HandyControl.Controls
namespace HandyControl.Controls;
public sealed class GrowlWindow : Window
{
public sealed class GrowlWindow : Window
internal Panel GrowlPanel { get; set; }
internal GrowlWindow()
{
internal Panel GrowlPanel { get; set; }
WindowStyle = WindowStyle.None;
AllowsTransparency = true;
internal GrowlWindow()
GrowlPanel = new StackPanel
{
WindowStyle = WindowStyle.None;
AllowsTransparency = true;
VerticalAlignment = VerticalAlignment.Top
};
GrowlPanel = new StackPanel
{
VerticalAlignment = VerticalAlignment.Top,
Margin = new Thickness(0, 10, 10, 10)
};
Content = new ScrollViewer
{
VerticalScrollBarVisibility = ScrollBarVisibility.Hidden,
IsInertiaEnabled = true,
Content = GrowlPanel
};
}
internal void Init()
Content = new ScrollViewer
{
var desktopWorkingArea = SystemParameters.WorkArea;
Height = desktopWorkingArea.Height;
Left = desktopWorkingArea.Right - Width;
Top = 0;
}
VerticalScrollBarVisibility = ScrollBarVisibility.Hidden,
IsInertiaEnabled = true,
Content = GrowlPanel
};
}
internal void Init()
{
var desktopWorkingArea = SystemParameters.WorkArea;
Height = desktopWorkingArea.Height;
Left = desktopWorkingArea.Right - Width;
Top = 0;
}
protected override void OnSourceInitialized(EventArgs e)
=> InteropMethods.IntDestroyMenu(this.GetHwndSource().CreateHandleRef());
}
@@ -15,480 +15,503 @@ using HandyControl.Data;
using HandyControl.Tools;
using HandyControl.Tools.Interop;
namespace HandyControl.Controls
namespace HandyControl.Controls;
public class GifImage : Image, IDisposable
{
public class GifImage : Image, IDisposable
private static readonly Guid GifGuid = new("{b96b3caa-0728-11d3-9d7b-0000f81ef32e}");
private static readonly Guid GifSingleFrameGuid = new("{b96b3cb0-0728-11d3-9d7b-0000f81ef32e}");
internal IntPtr NativeImage;
private byte[] _rawData;
private bool _isStart;
private bool _isLoaded;
static GifImage()
{
private static readonly Guid GifGuid = new Guid("{b96b3caa-0728-11d3-9d7b-0000f81ef32e}");
VisibilityProperty.OverrideMetadata(typeof(GifImage), new PropertyMetadata(default(Visibility), OnVisibilityChanged));
}
internal IntPtr NativeImage;
public static readonly DependencyProperty UriProperty = DependencyProperty.Register(
nameof(Uri), typeof(Uri), typeof(GifImage), new PropertyMetadata(default(Uri), OnUriChanged));
private byte[] _rawData;
private static void OnUriChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var ctl = (GifImage) d;
ctl.StopAnimate();
private bool _isStart;
private bool _isLoaded;
static GifImage()
if (e.NewValue != null)
{
VisibilityProperty.OverrideMetadata(typeof(GifImage), new PropertyMetadata(default(Visibility), OnVisibilityChanged));
var v = (Uri) e.NewValue;
ctl.GetGifStreamFromPack(v);
ctl.StartAnimate();
}
else
{
ctl.Source = null;
}
}
public Uri Uri
{
get => (Uri) GetValue(UriProperty);
set => SetValue(UriProperty, value);
}
private static void OnVisibilityChanged(DependencyObject s, DependencyPropertyChangedEventArgs e)
{
var ctl = (GifImage) s;
if (ctl.NativeImage == IntPtr.Zero) return;
var v = (Visibility) e.NewValue;
if (v != Visibility.Visible)
{
ctl.StopAnimate();
}
else if (!ctl._isStart)
{
ctl.StartAnimate();
}
}
~GifImage() => Dispose(false);
public GifImage()
{
Loaded += (s, e) =>
{
if (DesignerProperties.GetIsInDesignMode(this)) return;
if (_isLoaded) return;
_isLoaded = true;
if (Uri != null)
{
GetGifStreamFromPack(Uri);
StartAnimate();
}
else if (Source is BitmapImage image)
{
GetGifStreamFromPack(image.UriSource);
StartAnimate();
}
};
Unloaded += (s, e) => Dispose();
}
public GifImage(string filename)
{
Source = null;
CreateSourceFromFile(filename);
StartAnimate();
}
public GifImage(Stream stream)
{
Source = null;
CreateSourceFromStream(stream);
StartAnimate();
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (NativeImage == IntPtr.Zero) return;
try
{
StopAnimate();
InteropMethods.Gdip.GdipDisposeImage(new HandleRef(this, NativeImage));
}
catch
{
// ignored
}
finally
{
NativeImage = IntPtr.Zero;
}
}
private void CreateSourceFromFile(string filename)
{
filename = Path.GetFullPath(filename);
var status = InteropMethods.Gdip.GdipCreateBitmapFromFile(filename, out var bitmap);
if (status != InteropMethods.Gdip.Ok)
throw InteropMethods.Gdip.StatusException(status);
status = InteropMethods.Gdip.GdipImageForceValidation(new HandleRef(null, bitmap));
if (status != InteropMethods.Gdip.Ok)
{
InteropMethods.Gdip.GdipDisposeImage(new HandleRef(null, bitmap));
throw InteropMethods.Gdip.StatusException(status);
}
public static readonly DependencyProperty UriProperty = DependencyProperty.Register(
"Uri", typeof(Uri), typeof(GifImage), new PropertyMetadata(default(Uri), OnUriChanged));
SetNativeImage(bitmap);
private static void OnUriChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
EnsureSave(this, filename, null);
}
private void CreateSourceFromStream(Stream stream)
{
if (stream == null)
throw new ArgumentException("stream null");
var status = InteropMethods.Gdip.GdipCreateBitmapFromStream(new GPStream(stream), out var bitmap);
if (status != InteropMethods.Gdip.Ok)
throw InteropMethods.Gdip.StatusException(status);
status = InteropMethods.Gdip.GdipImageForceValidation(new HandleRef(null, bitmap));
if (status != InteropMethods.Gdip.Ok)
{
var ctl = (GifImage) d;
ctl.StopAnimate();
InteropMethods.Gdip.GdipDisposeImage(new HandleRef(null, bitmap));
throw InteropMethods.Gdip.StatusException(status);
}
if (e.NewValue != null)
SetNativeImage(bitmap);
EnsureSave(this, null, stream);
}
private void GetGifStreamFromPack(Uri uri)
{
try
{
StreamResourceInfo streamInfo;
if (!uri.IsAbsoluteUri)
{
var v = (Uri) e.NewValue;
ctl.GetGifStreamFromPack(v);
ctl.StartAnimate();
streamInfo = Application.GetContentStream(uri) ?? Application.GetResourceStream(uri);
}
else
{
ctl.Source = null;
}
}
public Uri Uri
{
get => (Uri) GetValue(UriProperty);
set => SetValue(UriProperty, value);
}
private static void OnVisibilityChanged(DependencyObject s, DependencyPropertyChangedEventArgs e)
{
var ctl = (GifImage) s;
var v = (Visibility) e.NewValue;
if (v != Visibility.Visible)
{
ctl.StopAnimate();
}
else if (!ctl._isStart)
{
ctl.StartAnimate();
}
}
~GifImage() => Dispose(false);
public GifImage()
{
Loaded += (s, e) =>
{
if (DesignerProperties.GetIsInDesignMode(this)) return;
if (_isLoaded) return;
_isLoaded = true;
if (Uri != null)
if (uri.GetLeftPart(UriPartial.Authority).Contains("siteoforigin"))
{
GetGifStreamFromPack(Uri);
StartAnimate();
}
else if (Source is BitmapImage image)
{
GetGifStreamFromPack(image.UriSource);
StartAnimate();
}
};
Unloaded += (s, e) => Dispose();
}
public GifImage(string filename)
{
Source = null;
CreateSourceFromFile(filename);
StartAnimate();
}
public GifImage(Stream stream)
{
Source = null;
CreateSourceFromStream(stream);
StartAnimate();
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (NativeImage == IntPtr.Zero) return;
try
{
StopAnimate();
InteropMethods.Gdip.GdipDisposeImage(new HandleRef(this, NativeImage));
}
catch
{
// ignored
}
finally
{
NativeImage = IntPtr.Zero;
}
}
private void CreateSourceFromFile(string filename)
{
filename = Path.GetFullPath(filename);
var status = InteropMethods.Gdip.GdipCreateBitmapFromFile(filename, out var bitmap);
if (status != InteropMethods.Gdip.Ok)
throw InteropMethods.Gdip.StatusException(status);
status = InteropMethods.Gdip.GdipImageForceValidation(new HandleRef(null, bitmap));
if (status != InteropMethods.Gdip.Ok)
{
InteropMethods.Gdip.GdipDisposeImage(new HandleRef(null, bitmap));
throw InteropMethods.Gdip.StatusException(status);
}
SetNativeImage(bitmap);
EnsureSave(this, filename, null);
}
private void CreateSourceFromStream(Stream stream)
{
if (stream == null)
throw new ArgumentException("stream null");
var status = InteropMethods.Gdip.GdipCreateBitmapFromStream(new GPStream(stream), out var bitmap);
if (status != InteropMethods.Gdip.Ok)
throw InteropMethods.Gdip.StatusException(status);
status = InteropMethods.Gdip.GdipImageForceValidation(new HandleRef(null, bitmap));
if (status != InteropMethods.Gdip.Ok)
{
InteropMethods.Gdip.GdipDisposeImage(new HandleRef(null, bitmap));
throw InteropMethods.Gdip.StatusException(status);
}
SetNativeImage(bitmap);
EnsureSave(this, null, stream);
}
private void GetGifStreamFromPack(Uri uri)
{
try
{
StreamResourceInfo streamInfo;
if (!uri.IsAbsoluteUri)
{
streamInfo = Application.GetContentStream(uri) ?? Application.GetResourceStream(uri);
streamInfo = Application.GetRemoteStream(uri);
}
else
{
if (uri.GetLeftPart(UriPartial.Authority).Contains("siteoforigin"))
{
streamInfo = Application.GetRemoteStream(uri);
}
else
{
streamInfo = Application.GetContentStream(uri) ?? Application.GetResourceStream(uri);
}
}
if (streamInfo == null)
throw new FileNotFoundException("Resource not found.", uri.ToString());
CreateSourceFromStream(streamInfo.Stream);
}
catch
{
// ignored
}
}
[ResourceExposure(ResourceScope.None)]
[ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
internal static void EnsureSave(GifImage image, string filename, Stream dataStream)
{
if (!image.RawGuid.Equals(GifGuid)) return;
var animatedGif = false;
var dimensions = image.FrameDimensionsList;
if (dimensions.Select(guid => new GifFrameDimension(guid)).Contains(GifFrameDimension.Time))
{
animatedGif = image.GetFrameCount(GifFrameDimension.Time) > 1;
}
if (!animatedGif) return;
try
{
Stream created = null;
long lastPos = 0;
if (dataStream != null)
{
lastPos = dataStream.Position;
dataStream.Position = 0;
}
try
{
if (dataStream == null)
{
created = dataStream = File.OpenRead(filename);
}
image._rawData = new byte[(int) dataStream.Length];
dataStream.Read(image._rawData, 0, (int) dataStream.Length);
}
finally
{
if (created != null)
{
created.Close();
}
else
{
// ReSharper disable once PossibleNullReferenceException
dataStream.Position = lastPos;
}
streamInfo = Application.GetContentStream(uri) ?? Application.GetResourceStream(uri);
}
}
// possible exceptions for reading the filename
catch (UnauthorizedAccessException)
{
}
catch (DirectoryNotFoundException)
{
}
catch (IOException)
{
}
// possible exceptions for setting/getting the position inside dataStream
catch (NotSupportedException)
{
}
catch (ObjectDisposedException)
{
}
// possible exception when reading stuff into dataStream
catch (ArgumentException)
if (streamInfo == null)
{
throw new FileNotFoundException("Resource not found.", uri.ToString());
}
CreateSourceFromStream(streamInfo.Stream);
}
internal void SetNativeImage(IntPtr handle)
catch
{
if (handle == IntPtr.Zero)
throw new ArgumentException("NativeHandle0");
NativeImage = handle;
}
internal Guid RawGuid
{
get
{
var guid = new Guid();
var status = InteropMethods.Gdip.GdipGetImageRawFormat(new HandleRef(this, NativeImage), ref guid);
if (status != InteropMethods.Gdip.Ok)
throw InteropMethods.Gdip.StatusException(status);
return guid;
}
}
internal void StartAnimate()
{
ImageAnimator.Animate(this, OnFrameChanged);
_isStart = true;
}
internal void StopAnimate()
{
ImageAnimator.StopAnimate(this, OnFrameChanged);
_isStart = false;
}
internal Guid[] FrameDimensionsList
{
get
{
var status = InteropMethods.Gdip.GdipImageGetFrameDimensionsCount(new HandleRef(this, NativeImage), out var count);
if (status != InteropMethods.Gdip.Ok)
{
throw InteropMethods.Gdip.StatusException(status);
}
if (count <= 0)
{
return new Guid[0];
}
var size = Marshal.SizeOf(typeof(Guid));
var buffer = Marshal.AllocHGlobal(checked(size * count));
if (buffer == IntPtr.Zero)
{
throw InteropMethods.Gdip.StatusException(InteropMethods.Gdip.OutOfMemory);
}
status = InteropMethods.Gdip.GdipImageGetFrameDimensionsList(new HandleRef(this, NativeImage), buffer, count);
if (status != InteropMethods.Gdip.Ok)
{
Marshal.FreeHGlobal(buffer);
throw InteropMethods.Gdip.StatusException(status);
}
var guids = new Guid[count];
try
{
for (var i = 0; i < count; i++)
{
guids[i] = (Guid) InteropMethods.PtrToStructure((IntPtr) ((long) buffer + size * i), typeof(Guid));
}
}
finally
{
Marshal.FreeHGlobal(buffer);
}
return guids;
}
}
internal int GetFrameCount(GifFrameDimension dimension)
{
var count = new[] { 0 };
var dimensionId = dimension.Guid;
var status = InteropMethods.Gdip.GdipImageGetFrameCount(new HandleRef(this, NativeImage), ref dimensionId, count);
if (status != InteropMethods.Gdip.Ok)
throw InteropMethods.Gdip.StatusException(status);
return count[0];
}
internal GifPropertyItem GetPropertyItem(int propid)
{
GifPropertyItem propitem;
var status = InteropMethods.Gdip.GdipGetPropertyItemSize(new HandleRef(this, NativeImage), propid, out var size);
if (status != InteropMethods.Gdip.Ok)
throw InteropMethods.Gdip.StatusException(status);
if (size == 0)
return null;
var propdata = Marshal.AllocHGlobal(size);
if (propdata == IntPtr.Zero)
throw InteropMethods.Gdip.StatusException(InteropMethods.Gdip.OutOfMemory);
status = InteropMethods.Gdip.GdipGetPropertyItem(new HandleRef(this, NativeImage), propid, size, propdata);
try
{
if (status != InteropMethods.Gdip.Ok)
{
throw InteropMethods.Gdip.StatusException(status);
}
propitem = GifPropertyItemInternal.ConvertFromMemory(propdata, 1)[0];
}
finally
{
Marshal.FreeHGlobal(propdata);
}
return propitem;
}
internal int SelectActiveFrame(GifFrameDimension dimension, int frameIndex)
{
var count = new[] { 0 };
var dimensionId = dimension.Guid;
var status = InteropMethods.Gdip.GdipImageSelectActiveFrame(new HandleRef(this, NativeImage), ref dimensionId, frameIndex);
if (status != InteropMethods.Gdip.Ok)
throw InteropMethods.Gdip.StatusException(status);
return count[0];
}
[SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
[EditorBrowsable(EditorBrowsableState.Advanced)]
[ResourceExposure(ResourceScope.Machine)]
[ResourceConsumption(ResourceScope.Machine)]
internal IntPtr GetHbitmap() => GetHbitmap(Colors.LightGray);
[SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
[EditorBrowsable(EditorBrowsableState.Advanced)]
[ResourceExposure(ResourceScope.Machine)]
[ResourceConsumption(ResourceScope.Machine)]
internal IntPtr GetHbitmap(Color background)
{
var status = InteropMethods.Gdip.GdipCreateHBITMAPFromBitmap(new HandleRef(this, NativeImage), out var hBitmap,
ColorHelper.ToWin32(background));
if (status == 2 && (Width >= short.MaxValue || Height >= short.MaxValue))
{
throw new ArgumentException("GdiplusInvalidSize");
}
if (status != InteropMethods.Gdip.Ok && NativeImage != IntPtr.Zero)
{
throw InteropMethods.Gdip.StatusException(status);
}
return hBitmap;
}
private void GetBitmapSource()
{
var handle = IntPtr.Zero;
try
{
handle = GetHbitmap();
Source = Imaging.CreateBitmapSourceFromHBitmap(handle, IntPtr.Zero, Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());
}
catch
{
// ignored
}
finally
{
if (handle != IntPtr.Zero)
{
InteropMethods.DeleteObject(handle);
}
}
}
private void OnFrameChanged(object sender, EventArgs e)
{
Dispatcher.BeginInvoke(new Action(() =>
{
ImageAnimator.UpdateFrames();
Source?.Freeze();
GetBitmapSource();
InvalidateVisual();
}));
// ignored
}
}
private void SwitchToCommonImage()
{
if (Source == null && Uri != null)
{
SetCurrentValue(SourceProperty, new BitmapImage(Uri));
}
}
[ResourceExposure(ResourceScope.None)]
[ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
internal static void EnsureSave(GifImage image, string filename, Stream dataStream)
{
if (!image.RawGuid.Equals(GifGuid) && !image.RawGuid.Equals(GifSingleFrameGuid))
{
image.SwitchToCommonImage();
return;
}
var animatedGif = false;
var dimensions = image.FrameDimensionsList;
if (dimensions.Select(guid => new GifFrameDimension(guid)).Contains(GifFrameDimension.Time))
{
animatedGif = image.GetFrameCount(GifFrameDimension.Time) > 1;
}
if (!animatedGif)
{
image.SwitchToCommonImage();
return;
}
try
{
Stream created = null;
long lastPos = 0;
if (dataStream != null)
{
lastPos = dataStream.Position;
dataStream.Position = 0;
}
try
{
if (dataStream == null)
{
created = dataStream = File.OpenRead(filename);
}
image._rawData = new byte[(int) dataStream.Length];
dataStream.Read(image._rawData, 0, (int) dataStream.Length);
}
finally
{
if (created != null)
{
created.Close();
}
else
{
// ReSharper disable once PossibleNullReferenceException
dataStream.Position = lastPos;
}
}
}
// possible exceptions for reading the filename
catch (UnauthorizedAccessException)
{
}
catch (DirectoryNotFoundException)
{
}
catch (IOException)
{
}
// possible exceptions for setting/getting the position inside dataStream
catch (NotSupportedException)
{
}
catch (ObjectDisposedException)
{
}
// possible exception when reading stuff into dataStream
catch (ArgumentException)
{
}
}
internal void SetNativeImage(IntPtr handle)
{
if (handle == IntPtr.Zero)
throw new ArgumentException("NativeHandle0");
NativeImage = handle;
}
internal Guid RawGuid
{
get
{
var guid = new Guid();
var status = InteropMethods.Gdip.GdipGetImageRawFormat(new HandleRef(this, NativeImage), ref guid);
if (status != InteropMethods.Gdip.Ok)
throw InteropMethods.Gdip.StatusException(status);
return guid;
}
}
internal void StartAnimate()
{
ImageAnimator.Animate(this, OnFrameChanged);
_isStart = true;
}
internal void StopAnimate()
{
ImageAnimator.StopAnimate(this, OnFrameChanged);
_isStart = false;
}
internal Guid[] FrameDimensionsList
{
get
{
var status = InteropMethods.Gdip.GdipImageGetFrameDimensionsCount(new HandleRef(this, NativeImage), out var count);
if (status != InteropMethods.Gdip.Ok)
{
throw InteropMethods.Gdip.StatusException(status);
}
if (count <= 0)
{
return new Guid[0];
}
var size = Marshal.SizeOf(typeof(Guid));
var buffer = Marshal.AllocHGlobal(checked(size * count));
if (buffer == IntPtr.Zero)
{
throw InteropMethods.Gdip.StatusException(InteropMethods.Gdip.OutOfMemory);
}
status = InteropMethods.Gdip.GdipImageGetFrameDimensionsList(new HandleRef(this, NativeImage), buffer, count);
if (status != InteropMethods.Gdip.Ok)
{
Marshal.FreeHGlobal(buffer);
throw InteropMethods.Gdip.StatusException(status);
}
var guids = new Guid[count];
try
{
for (var i = 0; i < count; i++)
{
guids[i] = (Guid) InteropMethods.PtrToStructure((IntPtr) ((long) buffer + size * i), typeof(Guid));
}
}
finally
{
Marshal.FreeHGlobal(buffer);
}
return guids;
}
}
internal int GetFrameCount(GifFrameDimension dimension)
{
var count = new[] { 0 };
var dimensionId = dimension.Guid;
var status = InteropMethods.Gdip.GdipImageGetFrameCount(new HandleRef(this, NativeImage), ref dimensionId, count);
if (status != InteropMethods.Gdip.Ok)
throw InteropMethods.Gdip.StatusException(status);
return count[0];
}
internal GifPropertyItem GetPropertyItem(int propid)
{
GifPropertyItem propitem;
var status = InteropMethods.Gdip.GdipGetPropertyItemSize(new HandleRef(this, NativeImage), propid, out var size);
if (status != InteropMethods.Gdip.Ok)
throw InteropMethods.Gdip.StatusException(status);
if (size == 0)
return null;
var propdata = Marshal.AllocHGlobal(size);
if (propdata == IntPtr.Zero)
throw InteropMethods.Gdip.StatusException(InteropMethods.Gdip.OutOfMemory);
status = InteropMethods.Gdip.GdipGetPropertyItem(new HandleRef(this, NativeImage), propid, size, propdata);
try
{
if (status != InteropMethods.Gdip.Ok)
{
throw InteropMethods.Gdip.StatusException(status);
}
propitem = GifPropertyItemInternal.ConvertFromMemory(propdata, 1)[0];
}
finally
{
Marshal.FreeHGlobal(propdata);
}
return propitem;
}
internal int SelectActiveFrame(GifFrameDimension dimension, int frameIndex)
{
var count = new[] { 0 };
var dimensionId = dimension.Guid;
var status = InteropMethods.Gdip.GdipImageSelectActiveFrame(new HandleRef(this, NativeImage), ref dimensionId, frameIndex);
if (status != InteropMethods.Gdip.Ok)
throw InteropMethods.Gdip.StatusException(status);
return count[0];
}
[SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
[EditorBrowsable(EditorBrowsableState.Advanced)]
[ResourceExposure(ResourceScope.Machine)]
[ResourceConsumption(ResourceScope.Machine)]
internal IntPtr GetHbitmap() => GetHbitmap(Colors.LightGray);
[SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
[EditorBrowsable(EditorBrowsableState.Advanced)]
[ResourceExposure(ResourceScope.Machine)]
[ResourceConsumption(ResourceScope.Machine)]
internal IntPtr GetHbitmap(Color background)
{
var status = InteropMethods.Gdip.GdipCreateHBITMAPFromBitmap(new HandleRef(this, NativeImage), out var hBitmap,
ColorHelper.ToWin32(background));
if (status == 2 && (Width >= short.MaxValue || Height >= short.MaxValue))
{
throw new ArgumentException("GdiplusInvalidSize");
}
if (status != InteropMethods.Gdip.Ok && NativeImage != IntPtr.Zero)
{
throw InteropMethods.Gdip.StatusException(status);
}
return hBitmap;
}
private void GetBitmapSource()
{
var handle = IntPtr.Zero;
try
{
handle = GetHbitmap();
Source = Imaging.CreateBitmapSourceFromHBitmap(handle, IntPtr.Zero, Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());
}
catch
{
// ignored
}
finally
{
if (handle != IntPtr.Zero)
{
InteropMethods.DeleteObject(handle);
}
}
}
private void OnFrameChanged(object sender, EventArgs e)
{
Dispatcher.BeginInvoke(new Action(() =>
{
ImageAnimator.UpdateFrames();
Source?.Freeze();
GetBitmapSource();
InvalidateVisual();
}));
}
}
@@ -5,228 +5,227 @@ using System.Windows.Media.Imaging;
using System.Windows.Threading;
using HandyControl.Data;
namespace HandyControl.Controls
namespace HandyControl.Controls;
public class ImageBlock : FrameworkElement
{
public class ImageBlock : FrameworkElement
private readonly DispatcherTimer _dispatcherTimer;
private BitmapSource _source;
private int _indexMax;
private int _indexMin;
private int _currentIndex;
private int _blockWidth;
private int _blockHeight;
private bool _isDisposed;
private int _columns = 1;
public ImageBlock()
{
private readonly DispatcherTimer _dispatcherTimer;
private BitmapSource _source;
private int _indexMax;
private int _indexMin;
private int _currentIndex;
private int _blockWidth;
private int _blockHeight;
private bool _isDisposed;
private int _columns = 1;
public ImageBlock()
_dispatcherTimer = new DispatcherTimer(DispatcherPriority.Render)
{
_dispatcherTimer = new DispatcherTimer(DispatcherPriority.Render)
Interval = Interval
};
IsVisibleChanged += ImageBlock_IsVisibleChanged;
}
~ImageBlock() => Dispose();
public void Dispose()
{
if (_isDisposed) return;
IsVisibleChanged -= ImageBlock_IsVisibleChanged;
_dispatcherTimer.Stop();
_isDisposed = true;
GC.SuppressFinalize(this);
}
private void ImageBlock_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if (IsVisible)
{
_dispatcherTimer.Tick += DispatcherTimer_Tick;
if (IsPlaying)
{
Interval = Interval
};
IsVisibleChanged += ImageBlock_IsVisibleChanged;
_dispatcherTimer.Start();
}
}
~ImageBlock() => Dispose();
public void Dispose()
else
{
if (_isDisposed) return;
IsVisibleChanged -= ImageBlock_IsVisibleChanged;
_dispatcherTimer.Stop();
_isDisposed = true;
GC.SuppressFinalize(this);
}
private void ImageBlock_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if (IsVisible)
{
_dispatcherTimer.Tick += DispatcherTimer_Tick;
if (IsPlaying)
{
_dispatcherTimer.Start();
}
}
else
{
_dispatcherTimer.Stop();
_dispatcherTimer.Tick -= DispatcherTimer_Tick;
}
}
private void UpdateDatas()
{
if (_source == null) return;
_indexMin = StartRow * _columns + StartColumn;
_indexMax = EndRow * _columns + EndColumn;
_currentIndex = _indexMin;
_blockWidth = _source.PixelWidth / _columns;
_blockHeight = _source.PixelHeight / Rows;
}
private static void OnPositionsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var ctl = (ImageBlock) d;
if (e.Property == ColumnsProperty)
{
ctl._columns = (int) e.NewValue;
}
ctl.UpdateDatas();
}
private void DispatcherTimer_Tick(object sender, EventArgs e) => InvalidateVisual();
public static readonly DependencyProperty StartColumnProperty = DependencyProperty.Register(
"StartColumn", typeof(int), typeof(ImageBlock), new FrameworkPropertyMetadata(ValueBoxes.Int0Box,
FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender, OnPositionsChanged));
public int StartColumn
{
get => (int) GetValue(StartColumnProperty);
set => SetValue(StartColumnProperty, value);
}
public static readonly DependencyProperty StartRowProperty = DependencyProperty.Register(
"StartRow", typeof(int), typeof(ImageBlock), new FrameworkPropertyMetadata(ValueBoxes.Int0Box,
FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender, OnPositionsChanged));
public int StartRow
{
get => (int) GetValue(StartRowProperty);
set => SetValue(StartRowProperty, value);
}
public static readonly DependencyProperty EndColumnProperty = DependencyProperty.Register(
"EndColumn", typeof(int), typeof(ImageBlock), new FrameworkPropertyMetadata(ValueBoxes.Int0Box,
FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender, OnPositionsChanged));
public int EndColumn
{
get => (int) GetValue(EndColumnProperty);
set => SetValue(EndColumnProperty, value);
}
public static readonly DependencyProperty EndRowProperty = DependencyProperty.Register(
"EndRow", typeof(int), typeof(ImageBlock), new FrameworkPropertyMetadata(ValueBoxes.Int0Box,
FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender, OnPositionsChanged));
public int EndRow
{
get => (int) GetValue(EndRowProperty);
set => SetValue(EndRowProperty, value);
}
public static readonly DependencyProperty IsPlayingProperty = DependencyProperty.Register(
"IsPlaying", typeof(bool), typeof(ImageBlock), new FrameworkPropertyMetadata(ValueBoxes.FalseBox,
FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender, OnIsPlayingChanged));
private static void OnIsPlayingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var ctl = (ImageBlock) d;
if ((bool) e.NewValue)
{
ctl._dispatcherTimer.Start();
}
else
{
ctl._dispatcherTimer.Stop();
}
}
public bool IsPlaying
{
get => (bool) GetValue(IsPlayingProperty);
set => SetValue(IsPlayingProperty, ValueBoxes.BooleanBox(value));
}
public static readonly DependencyProperty ColumnsProperty = DependencyProperty.Register(
"Columns", typeof(int), typeof(ImageBlock), new FrameworkPropertyMetadata(ValueBoxes.Int1Box,
FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender, OnPositionsChanged), obj => (int) obj >= 1);
public int Columns
{
get => (int) GetValue(ColumnsProperty);
set => SetValue(ColumnsProperty, value);
}
public static readonly DependencyProperty RowsProperty = DependencyProperty.Register(
"Rows", typeof(int), typeof(ImageBlock), new FrameworkPropertyMetadata(ValueBoxes.Int1Box,
FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender, OnPositionsChanged), obj => (int) obj >= 1);
public int Rows
{
get => (int) GetValue(RowsProperty);
set => SetValue(RowsProperty, value);
}
public static readonly DependencyProperty IntervalProperty = DependencyProperty.Register(
"Interval", typeof(TimeSpan), typeof(ImageBlock), new PropertyMetadata(TimeSpan.FromSeconds(1), OnIntervalChanged));
private static void OnIntervalChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var ctl = (ImageBlock) d;
ctl._dispatcherTimer.Interval = (TimeSpan) e.NewValue;
}
public TimeSpan Interval
{
get => (TimeSpan) GetValue(IntervalProperty);
set => SetValue(IntervalProperty, value);
}
public static readonly DependencyProperty SourceProperty = DependencyProperty.Register(
"Source", typeof(ImageSource), typeof(ImageBlock), new FrameworkPropertyMetadata(default(ImageSource),
FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender, OnSourceChanged));
private static void OnSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var ctl = (ImageBlock) d;
ctl._source = e.NewValue as BitmapSource;
ctl.UpdateDatas();
}
public ImageSource Source
{
get => (ImageSource) GetValue(SourceProperty);
set => SetValue(SourceProperty, value);
}
protected override void OnRender(DrawingContext dc)
{
if (_source == null) return;
var croppedBitmap = new CroppedBitmap(_source, CalDisplayRect());
dc.DrawImage(croppedBitmap, new Rect(0, 0, RenderSize.Width, RenderSize.Height));
}
private Int32Rect CalDisplayRect()
{
if (_currentIndex > _indexMax)
{
_currentIndex = _indexMin;
}
var x = _currentIndex % _columns * _blockWidth;
var y = _currentIndex / _columns * _blockHeight;
var rect = new Int32Rect(x, y, _blockWidth, _blockHeight);
_currentIndex++;
return rect;
_dispatcherTimer.Tick -= DispatcherTimer_Tick;
}
}
private void UpdateDatas()
{
if (_source == null) return;
_indexMin = StartRow * _columns + StartColumn;
_indexMax = EndRow * _columns + EndColumn;
_currentIndex = _indexMin;
_blockWidth = _source.PixelWidth / _columns;
_blockHeight = _source.PixelHeight / Rows;
}
private static void OnPositionsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var ctl = (ImageBlock) d;
if (e.Property == ColumnsProperty)
{
ctl._columns = (int) e.NewValue;
}
ctl.UpdateDatas();
}
private void DispatcherTimer_Tick(object sender, EventArgs e) => InvalidateVisual();
public static readonly DependencyProperty StartColumnProperty = DependencyProperty.Register(
nameof(StartColumn), typeof(int), typeof(ImageBlock), new FrameworkPropertyMetadata(ValueBoxes.Int0Box,
FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender, OnPositionsChanged));
public int StartColumn
{
get => (int) GetValue(StartColumnProperty);
set => SetValue(StartColumnProperty, value);
}
public static readonly DependencyProperty StartRowProperty = DependencyProperty.Register(
nameof(StartRow), typeof(int), typeof(ImageBlock), new FrameworkPropertyMetadata(ValueBoxes.Int0Box,
FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender, OnPositionsChanged));
public int StartRow
{
get => (int) GetValue(StartRowProperty);
set => SetValue(StartRowProperty, value);
}
public static readonly DependencyProperty EndColumnProperty = DependencyProperty.Register(
nameof(EndColumn), typeof(int), typeof(ImageBlock), new FrameworkPropertyMetadata(ValueBoxes.Int0Box,
FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender, OnPositionsChanged));
public int EndColumn
{
get => (int) GetValue(EndColumnProperty);
set => SetValue(EndColumnProperty, value);
}
public static readonly DependencyProperty EndRowProperty = DependencyProperty.Register(
nameof(EndRow), typeof(int), typeof(ImageBlock), new FrameworkPropertyMetadata(ValueBoxes.Int0Box,
FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender, OnPositionsChanged));
public int EndRow
{
get => (int) GetValue(EndRowProperty);
set => SetValue(EndRowProperty, value);
}
public static readonly DependencyProperty IsPlayingProperty = DependencyProperty.Register(
nameof(IsPlaying), typeof(bool), typeof(ImageBlock), new FrameworkPropertyMetadata(ValueBoxes.FalseBox,
FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender, OnIsPlayingChanged));
private static void OnIsPlayingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var ctl = (ImageBlock) d;
if ((bool) e.NewValue)
{
ctl._dispatcherTimer.Start();
}
else
{
ctl._dispatcherTimer.Stop();
}
}
public bool IsPlaying
{
get => (bool) GetValue(IsPlayingProperty);
set => SetValue(IsPlayingProperty, ValueBoxes.BooleanBox(value));
}
public static readonly DependencyProperty ColumnsProperty = DependencyProperty.Register(
nameof(Columns), typeof(int), typeof(ImageBlock), new FrameworkPropertyMetadata(ValueBoxes.Int1Box,
FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender, OnPositionsChanged), obj => (int) obj >= 1);
public int Columns
{
get => (int) GetValue(ColumnsProperty);
set => SetValue(ColumnsProperty, value);
}
public static readonly DependencyProperty RowsProperty = DependencyProperty.Register(
nameof(Rows), typeof(int), typeof(ImageBlock), new FrameworkPropertyMetadata(ValueBoxes.Int1Box,
FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender, OnPositionsChanged), obj => (int) obj >= 1);
public int Rows
{
get => (int) GetValue(RowsProperty);
set => SetValue(RowsProperty, value);
}
public static readonly DependencyProperty IntervalProperty = DependencyProperty.Register(
nameof(Interval), typeof(TimeSpan), typeof(ImageBlock), new PropertyMetadata(TimeSpan.FromSeconds(1), OnIntervalChanged));
private static void OnIntervalChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var ctl = (ImageBlock) d;
ctl._dispatcherTimer.Interval = (TimeSpan) e.NewValue;
}
public TimeSpan Interval
{
get => (TimeSpan) GetValue(IntervalProperty);
set => SetValue(IntervalProperty, value);
}
public static readonly DependencyProperty SourceProperty = DependencyProperty.Register(
nameof(Source), typeof(ImageSource), typeof(ImageBlock), new FrameworkPropertyMetadata(default(ImageSource),
FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender, OnSourceChanged));
private static void OnSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var ctl = (ImageBlock) d;
ctl._source = e.NewValue as BitmapSource;
ctl.UpdateDatas();
}
public ImageSource Source
{
get => (ImageSource) GetValue(SourceProperty);
set => SetValue(SourceProperty, value);
}
protected override void OnRender(DrawingContext dc)
{
if (_source == null) return;
var croppedBitmap = new CroppedBitmap(_source, CalDisplayRect());
dc.DrawImage(croppedBitmap, new Rect(0, 0, RenderSize.Width, RenderSize.Height));
}
private Int32Rect CalDisplayRect()
{
if (_currentIndex > _indexMax)
{
_currentIndex = _indexMin;
}
var x = _currentIndex % _columns * _blockWidth;
var y = _currentIndex / _columns * _blockHeight;
var rect = new Int32Rect(x, y, _blockWidth, _blockHeight);
_currentIndex++;
return rect;
}
}
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,158 @@
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using HandyControl.Data;
using HandyControl.Tools.Helper;
namespace HandyControl.Controls;
[TemplatePart(Name = SearchTextBox, Type = typeof(System.Windows.Controls.TextBox))]
public class AutoCompleteTextBox : ComboBox
{
private const string SearchTextBox = "PART_SearchTextBox";
private bool ignoreTextChanging;
private System.Windows.Controls.TextBox _searchTextBox;
private object _selectedItem;
static AutoCompleteTextBox()
{
TextProperty.OverrideMetadata(typeof(AutoCompleteTextBox), new FrameworkPropertyMetadata(default(string), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
}
public override void OnApplyTemplate()
{
if (_searchTextBox != null)
{
_searchTextBox.GotFocus -= SearchTextBoxGotFocus;
_searchTextBox.KeyDown -= SearchTextBoxKeyDown;
_searchTextBox.TextChanged -= SearchTextBoxTextChanged;
}
base.OnApplyTemplate();
_searchTextBox = GetTemplateChild(SearchTextBox) as System.Windows.Controls.TextBox;
if (_searchTextBox != null)
{
_searchTextBox.GotFocus += SearchTextBoxGotFocus;
_searchTextBox.PreviewKeyDown += SearchTextBoxKeyDown;
_searchTextBox.TextChanged += SearchTextBoxTextChanged;
}
UpdateTextBoxBySelectedItem(_selectedItem);
}
protected override void OnSelectionChanged(SelectionChangedEventArgs e)
{
base.OnSelectionChanged(e);
if (e.AddedItems.Count > 0)
{
_selectedItem = e.AddedItems[0];
UpdateTextBoxBySelectedItem(_selectedItem);
}
}
protected override bool IsItemItsOwnContainerOverride(object item) => item is AutoCompleteTextBoxItem;
protected override DependencyObject GetContainerForItemOverride() => new AutoCompleteTextBoxItem();
private void SearchTextBoxTextChanged(object sender, TextChangedEventArgs e)
{
_selectedItem = null;
SelectedIndex = -1;
if (ignoreTextChanging)
{
ignoreTextChanging = false;
return;
}
Text = _searchTextBox.Text;
if (string.IsNullOrEmpty(Text))
{
SetCurrentValue(IsDropDownOpenProperty, ValueBoxes.FalseBox);
_searchTextBox.Focus();
}
else if (_searchTextBox.IsFocused)
{
SetCurrentValue(IsDropDownOpenProperty, ValueBoxes.TrueBox);
}
}
private void SearchTextBoxKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Up)
{
var index = SelectedIndex - 1;
if (index < 0)
{
index = Items.Count - 1;
}
UpdateTextBoxBySelectedIndex(index);
}
else if (e.Key == Key.Down)
{
var index = SelectedIndex + 1;
if (index >= Items.Count)
{
index = 0;
}
UpdateTextBoxBySelectedIndex(index);
}
else if (e.Key == Key.Enter)
{
SetCurrentValue(IsDropDownOpenProperty, ValueBoxes.FalseBox);
e.Handled = true;
}
}
private void UpdateTextBoxBySelectedIndex(int selectedIndex)
{
if (_searchTextBox == null)
{
return;
}
ignoreTextChanging = true;
if (ItemContainerGenerator.ContainerFromIndex(selectedIndex) is AutoCompleteTextBoxItem boxItem)
{
_searchTextBox.Text = BindingHelper.GetString(boxItem.Content, DisplayMemberPath);
_searchTextBox.CaretIndex = _searchTextBox.Text.Length;
SelectedIndex = selectedIndex;
}
}
private void UpdateTextBoxBySelectedItem(object selectedItem)
{
if (_searchTextBox == null)
{
return;
}
ignoreTextChanging = true;
_searchTextBox.Text = BindingHelper.GetString(selectedItem, DisplayMemberPath);
_searchTextBox.CaretIndex = _searchTextBox.Text.Length;
ignoreTextChanging = true;
Text = _searchTextBox.Text;
ignoreTextChanging = false;
}
private void SearchTextBoxGotFocus(object sender, RoutedEventArgs e)
{
if (!string.IsNullOrEmpty(Text))
{
SetCurrentValue(IsDropDownOpenProperty, ValueBoxes.TrueBox);
}
}
}
@@ -0,0 +1,8 @@
using System.Windows.Controls;
namespace HandyControl.Controls;
public class AutoCompleteTextBoxItem : ComboBoxItem
{
}
@@ -2,245 +2,198 @@
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Threading;
using HandyControl.Data;
using HandyControl.Interactivity;
namespace HandyControl.Controls
namespace HandyControl.Controls;
[TemplatePart(Name = ElementPanel, Type = typeof(Panel))]
[TemplatePart(Name = ElementSelectAll, Type = typeof(CheckComboBoxItem))]
public class CheckComboBox : ListBox
{
[TemplatePart(Name = ElementPanel, Type = typeof(Panel))]
[TemplatePart(Name = ElementSelectAll, Type = typeof(CheckComboBoxItem))]
public class CheckComboBox : ListBox, IDataInput
private const string ElementPanel = "PART_Panel";
private const string ElementSelectAll = "PART_SelectAll";
private Panel _panel;
private CheckComboBoxItem _selectAllItem;
private bool _isInternalAction;
public static readonly DependencyProperty MaxDropDownHeightProperty =
System.Windows.Controls.ComboBox.MaxDropDownHeightProperty.AddOwner(typeof(CheckComboBox),
new FrameworkPropertyMetadata(SystemParameters.PrimaryScreenHeight / 3));
[Bindable(true), Category("Layout")]
[TypeConverter(typeof(LengthConverter))]
public double MaxDropDownHeight
{
private const string ElementPanel = "PART_Panel";
get => (double) GetValue(MaxDropDownHeightProperty);
set => SetValue(MaxDropDownHeightProperty, value);
}
private const string ElementSelectAll = "PART_SelectAll";
public static readonly DependencyProperty IsDropDownOpenProperty = DependencyProperty.Register(
nameof(IsDropDownOpen), typeof(bool), typeof(CheckComboBox), new PropertyMetadata(ValueBoxes.FalseBox, OnIsDropDownOpenChanged));
private Panel _panel;
private static void OnIsDropDownOpenChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var ctl = (CheckComboBox) d;
private CheckComboBoxItem _selectAllItem;
private bool _isInternalAction;
public static readonly DependencyProperty IsErrorProperty = DependencyProperty.Register(
"IsError", typeof(bool), typeof(CheckComboBox), new PropertyMetadata(ValueBoxes.FalseBox));
public bool IsError
if (!(bool) e.NewValue)
{
get => (bool) GetValue(IsErrorProperty);
set => SetValue(IsErrorProperty, ValueBoxes.BooleanBox(value));
}
public static readonly DependencyProperty ErrorStrProperty = DependencyProperty.Register(
"ErrorStr", typeof(string), typeof(CheckComboBox), new PropertyMetadata(default(string)));
public string ErrorStr
{
get => (string) GetValue(ErrorStrProperty);
set => SetValue(ErrorStrProperty, value);
}
public static readonly DependencyProperty TextTypeProperty = DependencyProperty.Register(
"TextType", typeof(TextType), typeof(CheckComboBox), new PropertyMetadata(default(TextType)));
public TextType TextType
{
get => (TextType) GetValue(TextTypeProperty);
set => SetValue(TextTypeProperty, value);
}
public static readonly DependencyProperty ShowClearButtonProperty = DependencyProperty.Register(
"ShowClearButton", typeof(bool), typeof(CheckComboBox), new PropertyMetadata(ValueBoxes.FalseBox));
public bool ShowClearButton
{
get => (bool) GetValue(ShowClearButtonProperty);
set => SetValue(ShowClearButtonProperty, ValueBoxes.BooleanBox(value));
}
public static readonly DependencyProperty MaxDropDownHeightProperty =
System.Windows.Controls.ComboBox.MaxDropDownHeightProperty.AddOwner(typeof(CheckComboBox),
new FrameworkPropertyMetadata(SystemParameters.PrimaryScreenHeight / 3));
[Bindable(true), Category("Layout")]
[TypeConverter(typeof(LengthConverter))]
public double MaxDropDownHeight
{
get => (double) GetValue(MaxDropDownHeightProperty);
set => SetValue(MaxDropDownHeightProperty, value);
}
public static readonly DependencyProperty IsDropDownOpenProperty = DependencyProperty.Register(
"IsDropDownOpen", typeof(bool), typeof(CheckComboBox), new PropertyMetadata(ValueBoxes.FalseBox));
public bool IsDropDownOpen
{
get => (bool) GetValue(IsDropDownOpenProperty);
set => SetValue(IsDropDownOpenProperty, ValueBoxes.BooleanBox(value));
}
public static readonly DependencyProperty TagStyleProperty = DependencyProperty.Register(
"TagStyle", typeof(Style), typeof(CheckComboBox), new PropertyMetadata(default(Style)));
public Style TagStyle
{
get => (Style) GetValue(TagStyleProperty);
set => SetValue(TagStyleProperty, value);
}
public static readonly DependencyProperty ShowSelectAllButtonProperty = DependencyProperty.Register(
"ShowSelectAllButton", typeof(bool), typeof(CheckComboBox), new PropertyMetadata(ValueBoxes.FalseBox));
public bool ShowSelectAllButton
{
get => (bool) GetValue(ShowSelectAllButtonProperty);
set => SetValue(ShowSelectAllButtonProperty, ValueBoxes.BooleanBox(value));
}
public CheckComboBox()
{
AddHandler(Controls.Tag.ClosedEvent, new RoutedEventHandler(Tags_OnClosed));
CommandBindings.Add(new CommandBinding(ControlCommands.Clear, (s, e) => SelectedItems.Clear()));
ItemContainerGenerator.StatusChanged += ItemContainerGenerator_StatusChanged;
}
private void ItemContainerGenerator_StatusChanged(object sender, EventArgs e)
{
if (ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated)
ctl.Dispatcher.BeginInvoke(new Action(() =>
{
UpdateTags();
}
Mouse.Capture(null);
}), DispatcherPriority.Send);
}
}
public bool IsDropDownOpen
{
get => (bool) GetValue(IsDropDownOpenProperty);
set => SetValue(IsDropDownOpenProperty, ValueBoxes.BooleanBox(value));
}
public static readonly DependencyProperty TagStyleProperty = DependencyProperty.Register(
nameof(TagStyle), typeof(Style), typeof(CheckComboBox), new PropertyMetadata(default(Style)));
public Style TagStyle
{
get => (Style) GetValue(TagStyleProperty);
set => SetValue(TagStyleProperty, value);
}
public static readonly DependencyProperty TagSpacingProperty = DependencyProperty.Register(
nameof(TagSpacing), typeof(double), typeof(CheckComboBox), new PropertyMetadata(ValueBoxes.Double0Box));
public double TagSpacing
{
get => (double) GetValue(TagSpacingProperty);
set => SetValue(TagSpacingProperty, value);
}
public static readonly DependencyProperty ShowSelectAllButtonProperty = DependencyProperty.Register(
nameof(ShowSelectAllButton), typeof(bool), typeof(CheckComboBox), new PropertyMetadata(ValueBoxes.FalseBox));
public bool ShowSelectAllButton
{
get => (bool) GetValue(ShowSelectAllButtonProperty);
set => SetValue(ShowSelectAllButtonProperty, ValueBoxes.BooleanBox(value));
}
public CheckComboBox()
{
AddHandler(Controls.Tag.ClosedEvent, new RoutedEventHandler(Tags_OnClosed));
CommandBindings.Add(new CommandBinding(ControlCommands.Clear, (s, e) =>
{
SetCurrentValue(SelectedValueProperty, null);
SetCurrentValue(SelectedItemProperty, null);
SetCurrentValue(SelectedIndexProperty, -1);
SelectedItems.Clear();
}));
}
public override void OnApplyTemplate()
{
if (_selectAllItem != null)
{
_selectAllItem.Selected -= SelectAllItem_Selected;
_selectAllItem.Unselected -= SelectAllItem_Unselected;
}
public override void OnApplyTemplate()
base.OnApplyTemplate();
_panel = GetTemplateChild(ElementPanel) as Panel;
_selectAllItem = GetTemplateChild(ElementSelectAll) as CheckComboBoxItem;
if (_selectAllItem != null)
{
if (_selectAllItem != null)
{
_selectAllItem.Selected -= SelectAllItem_Selected;
_selectAllItem.Unselected -= SelectAllItem_Unselected;
}
base.OnApplyTemplate();
_panel = GetTemplateChild(ElementPanel) as Panel;
_selectAllItem = GetTemplateChild(ElementSelectAll) as CheckComboBoxItem;
if (_selectAllItem != null)
{
_selectAllItem.Selected += SelectAllItem_Selected;
_selectAllItem.Unselected += SelectAllItem_Unselected;
}
Dispatcher.BeginInvoke(new Action(() =>
{
IsDropDownOpen = true;
IsDropDownOpen = false;
}), DispatcherPriority.DataBind);
_selectAllItem.Selected += SelectAllItem_Selected;
_selectAllItem.Unselected += SelectAllItem_Unselected;
}
public bool VerifyData()
{
OperationResult<bool> result;
UpdateTags();
}
if (VerifyFunc != null)
protected override void OnSelectionChanged(SelectionChangedEventArgs e)
{
UpdateTags();
base.OnSelectionChanged(e);
}
protected override bool IsItemItsOwnContainerOverride(object item) => item is CheckComboBoxItem;
protected override DependencyObject GetContainerForItemOverride() => new CheckComboBoxItem();
protected override void OnDisplayMemberPathChanged(string oldDisplayMemberPath, string newDisplayMemberPath) => UpdateTags();
private void Tags_OnClosed(object sender, RoutedEventArgs e)
{
if (e.OriginalSource is Tag tag)
{
SelectedItems.Remove(tag.Tag);
_panel.Children.Remove(tag);
}
}
private void SwitchAllItems(bool selected)
{
if (_isInternalAction) return;
_isInternalAction = true;
if (!selected)
{
UnselectAll();
}
else
{
SelectAll();
}
_isInternalAction = false;
UpdateTags();
}
private void SelectAllItem_Selected(object sender, RoutedEventArgs e) => SwitchAllItems(true);
private void SelectAllItem_Unselected(object sender, RoutedEventArgs e) => SwitchAllItems(false);
private void UpdateTags()
{
if (_panel == null || _isInternalAction) return;
if (_selectAllItem != null)
{
_isInternalAction = true;
_selectAllItem.SetCurrentValue(IsSelectedProperty, Items.Count > 0 && SelectedItems.Count == Items.Count);
_isInternalAction = false;
}
_panel.Children.Clear();
foreach (var item in SelectedItems)
{
var tag = new Tag
{
result = VerifyFunc.Invoke(null);
Style = TagStyle,
Tag = item
};
if (ItemsSource != null)
{
tag.SetBinding(ContentControl.ContentProperty, new Binding(DisplayMemberPath) { Source = item });
}
else
{
if (SelectedItems.Count > 0)
{
result = OperationResult.Success();
}
else if (InfoElement.GetNecessary(this))
{
result = OperationResult.Failed(Properties.Langs.Lang.IsNecessary);
}
else
{
result = OperationResult.Success();
}
tag.Content = IsItemItsOwnContainerOverride(item) ? ((CheckComboBoxItem) item).Content : item;
}
IsError = !result.Data;
ErrorStr = result.Message;
return result.Data;
}
public Func<string, OperationResult<bool>> VerifyFunc { get; set; }
protected override void OnSelectionChanged(SelectionChangedEventArgs e)
{
UpdateTags();
VerifyData();
base.OnSelectionChanged(e);
}
protected override bool IsItemItsOwnContainerOverride(object item) => item is CheckComboBoxItem;
protected override DependencyObject GetContainerForItemOverride() => new CheckComboBoxItem();
protected override void OnDisplayMemberPathChanged(string oldDisplayMemberPath, string newDisplayMemberPath) => UpdateTags();
private void Tags_OnClosed(object sender, RoutedEventArgs e)
{
if (e.OriginalSource is Tag tag && tag.Tag is CheckComboBoxItem checkComboBoxItem)
{
checkComboBoxItem.SetCurrentValue(IsSelectedProperty, false);
}
}
private void SwitchAllItems(bool selected)
{
if (_isInternalAction) return;
_isInternalAction = true;
foreach (var item in Items)
{
if (ItemContainerGenerator.ContainerFromItem(item) is CheckComboBoxItem checkComboBoxItem)
{
checkComboBoxItem.SetCurrentValue(ListBoxItem.IsSelectedProperty, selected);
}
}
_isInternalAction = false;
UpdateTags();
}
private void SelectAllItem_Selected(object sender, RoutedEventArgs e) => SwitchAllItems(true);
private void SelectAllItem_Unselected(object sender, RoutedEventArgs e) => SwitchAllItems(false);
private void UpdateTags()
{
if (_panel == null || _isInternalAction) return;
if (_selectAllItem != null)
{
_isInternalAction = true;
_selectAllItem.SetCurrentValue(IsSelectedProperty, SelectedItems.Count == Items.Count);
_isInternalAction = false;
}
_panel.Children.Clear();
foreach (var item in SelectedItems)
{
if (ItemContainerGenerator.ContainerFromItem(item) is CheckComboBoxItem checkComboBoxItem)
{
var tag = new Tag
{
Style = TagStyle,
Tag = checkComboBoxItem
};
tag.SetBinding(ContentControl.ContentProperty, new Binding(DisplayMemberPath) { Source = item });
_panel.Children.Add(tag);
}
}
_panel.Children.Add(tag);
}
}
}
@@ -1,9 +1,8 @@
using System.Windows.Controls;
namespace HandyControl.Controls
namespace HandyControl.Controls;
public class CheckComboBoxItem : ListBoxItem
{
public class CheckComboBoxItem : ListBoxItem
{
}
}
@@ -12,417 +12,307 @@ using System.Windows.Threading;
using HandyControl.Data;
using HandyControl.Interactivity;
namespace HandyControl.Controls
namespace HandyControl.Controls;
[TemplatePart(Name = AutoCompletePanel, Type = typeof(Panel))]
[TemplatePart(Name = EditableTextBox, Type = typeof(System.Windows.Controls.TextBox))]
[TemplatePart(Name = AutoPopupAutoComplete, Type = typeof(Popup))]
public class ComboBox : System.Windows.Controls.ComboBox
{
[TemplatePart(Name = AutoCompletePanel, Type = typeof(Panel))]
[TemplatePart(Name = EditableTextBox, Type = typeof(System.Windows.Controls.TextBox))]
[TemplatePart(Name = AutoPopupAutoComplete, Type = typeof(Popup))]
public class ComboBox : System.Windows.Controls.ComboBox, IDataInput
{
#if NET40
private string _searchText;
private string _searchText;
private DispatcherTimer _autoCompleteTimer;
private DispatcherTimer _autoCompleteTimer;
#endif
private bool _isAutoCompleteAction = true;
private bool _isAutoCompleteAction = true;
private Panel _autoCompletePanel;
private Panel _autoCompletePanel;
private System.Windows.Controls.TextBox _editableTextBox;
private System.Windows.Controls.TextBox _editableTextBox;
private Popup _autoPopupAutoComplete;
private Popup _autoPopupAutoComplete;
private const string AutoCompletePanel = "PART_AutoCompletePanel";
private const string AutoCompletePanel = "PART_AutoCompletePanel";
private const string AutoPopupAutoComplete = "PART_Popup_AutoComplete";
private const string AutoPopupAutoComplete = "PART_Popup_AutoComplete";
private const string EditableTextBox = "PART_EditableTextBox";
private const string EditableTextBox = "PART_EditableTextBox";
public ComboBox()
public ComboBox()
{
CommandBindings.Add(new CommandBinding(ControlCommands.Clear, (s, e) =>
{
CommandBindings.Add(new CommandBinding(ControlCommands.Clear, (s, e) =>
if (IsReadOnly)
{
SetCurrentValue(SelectedValueProperty, null);
SetCurrentValue(SelectedItemProperty, null);
SetCurrentValue(SelectedIndexProperty, -1);
SetCurrentValue(TextProperty, "");
}));
}
public override void OnApplyTemplate()
{
if (_editableTextBox != null)
{
BindingOperations.ClearBinding(_editableTextBox, System.Windows.Controls.TextBox.TextProperty);
_editableTextBox.GotFocus -= EditableTextBox_GotFocus;
_editableTextBox.LostFocus -= EditableTextBox_LostFocus;
return;
}
base.OnApplyTemplate();
SetCurrentValue(SelectedValueProperty, null);
SetCurrentValue(SelectedItemProperty, null);
SetCurrentValue(SelectedIndexProperty, -1);
SetCurrentValue(TextProperty, "");
}));
}
if (IsEditable)
public override void OnApplyTemplate()
{
if (_editableTextBox != null)
{
BindingOperations.ClearBinding(_editableTextBox, System.Windows.Controls.TextBox.TextProperty);
_editableTextBox.GotFocus -= EditableTextBox_GotFocus;
_editableTextBox.LostFocus -= EditableTextBox_LostFocus;
}
base.OnApplyTemplate();
if (IsEditable)
{
_editableTextBox = GetTemplateChild(EditableTextBox) as System.Windows.Controls.TextBox;
if (_editableTextBox != null)
{
_editableTextBox = GetTemplateChild(EditableTextBox) as System.Windows.Controls.TextBox;
if (_editableTextBox != null)
{
_editableTextBox.TextChanged += EditableTextBox_TextChanged;
_editableTextBox.SetBinding(SelectionBrushProperty, new Binding(SelectionBrushProperty.Name) { Source = this });
_editableTextBox.SetBinding(SelectionBrushProperty, new Binding(SelectionBrushProperty.Name) { Source = this });
#if !(NET40 || NET45 || NET451 || NET452 || NET46 || NET461 || NET462 || NET47 || NET471 || NET472)
_editableTextBox.SetBinding(SelectionTextBrushProperty, new Binding(SelectionTextBrushProperty.Name) { Source = this });
_editableTextBox.SetBinding(SelectionTextBrushProperty, new Binding(SelectionTextBrushProperty.Name) { Source = this });
#endif
_editableTextBox.SetBinding(SelectionOpacityProperty, new Binding(SelectionOpacityProperty.Name) { Source = this });
_editableTextBox.SetBinding(CaretBrushProperty, new Binding(CaretBrushProperty.Name) { Source = this });
_editableTextBox.SetBinding(SelectionOpacityProperty, new Binding(SelectionOpacityProperty.Name) { Source = this });
_editableTextBox.SetBinding(CaretBrushProperty, new Binding(CaretBrushProperty.Name) { Source = this });
if (AutoComplete)
{
if (AutoComplete)
{
#if NET40
_autoCompleteTimer = new DispatcherTimer
{
Interval = TimeSpan.FromMilliseconds(500)
};
_autoCompleteTimer.Tick += AutoCompleteTimer_Tick;
_autoCompleteTimer = new DispatcherTimer
{
Interval = TimeSpan.FromMilliseconds(500)
};
_autoCompleteTimer.Tick += AutoCompleteTimer_Tick;
#endif
_autoPopupAutoComplete = GetTemplateChild(AutoPopupAutoComplete) as Popup;
_autoCompletePanel = GetTemplateChild(AutoCompletePanel) as Panel;
_editableTextBox.SetBinding(System.Windows.Controls.TextBox.TextProperty, new Binding(SearchTextProperty.Name)
{
UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged,
Mode = BindingMode.OneWayToSource,
_autoPopupAutoComplete = GetTemplateChild(AutoPopupAutoComplete) as Popup;
_autoCompletePanel = GetTemplateChild(AutoCompletePanel) as Panel;
_editableTextBox.SetBinding(System.Windows.Controls.TextBox.TextProperty, new Binding(SearchTextProperty.Name)
{
UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged,
Mode = BindingMode.OneWayToSource,
#if !NET40
Delay = 500,
Delay = 500,
#endif
Source = this
});
_editableTextBox.GotFocus += EditableTextBox_GotFocus;
_editableTextBox.LostFocus += EditableTextBox_LostFocus;
}
Source = this
});
_editableTextBox.GotFocus += EditableTextBox_GotFocus;
_editableTextBox.LostFocus += EditableTextBox_LostFocus;
}
}
}
}
#if NET40
private void AutoCompleteTimer_Tick(object sender, EventArgs e)
{
UpdateSearchItems(_searchText);
_autoCompleteTimer.Stop();
}
private void AutoCompleteTimer_Tick(object sender, EventArgs e)
{
UpdateSearchItems(_searchText);
_autoCompleteTimer.Stop();
}
#endif
private void EditableTextBox_TextChanged(object sender, TextChangedEventArgs e)
private void EditableTextBox_LostFocus(object sender, RoutedEventArgs e)
{
if (_autoPopupAutoComplete != null)
{
VerifyData();
_autoPopupAutoComplete.IsOpen = false;
}
}
#if !NET40
protected override void OnDropDownClosed(EventArgs e)
{
base.OnDropDownClosed(e);
_isAutoCompleteAction = false;
}
#endif
private void EditableTextBox_GotFocus(object sender, RoutedEventArgs e)
{
if (_autoPopupAutoComplete != null && _editableTextBox != null &&
!string.IsNullOrEmpty(_editableTextBox.Text))
{
_autoPopupAutoComplete.IsOpen = true;
}
}
protected override void OnSelectionChanged(SelectionChangedEventArgs e)
{
_isAutoCompleteAction = false;
base.OnSelectionChanged(e);
#if NET40
_isAutoCompleteAction = true;
#endif
}
/// <summary>
/// 数据搜索委托
/// </summary>
public Func<ItemCollection, object, IEnumerable<object>> SearchFunc { get; set; }
/// <summary>
/// 是否自动完成输入
/// </summary>
public static readonly DependencyProperty AutoCompleteProperty = DependencyProperty.Register(
nameof(AutoComplete), typeof(bool), typeof(ComboBox), new PropertyMetadata(ValueBoxes.FalseBox, OnAutoCompleteChanged));
private static void OnAutoCompleteChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var ctl = (ComboBox) d;
if (ctl._editableTextBox != null)
{
ctl.UpdateSearchItems(ctl._editableTextBox.Text);
}
}
/// <summary>
/// 是否自动完成输入
/// </summary>
public bool AutoComplete
{
get => (bool) GetValue(AutoCompleteProperty);
set => SetValue(AutoCompleteProperty, ValueBoxes.BooleanBox(value));
}
/// <summary>
/// 搜索文本
/// </summary>
internal static readonly DependencyProperty SearchTextProperty = DependencyProperty.Register(
nameof(SearchText), typeof(string), typeof(ComboBox), new PropertyMetadata(default(string), OnSearchTextChanged));
private static void OnSearchTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var ctl = (ComboBox) d;
if (ctl._isAutoCompleteAction)
{
#if NET40
ctl._searchText = e.NewValue as string;
ctl._autoCompleteTimer.Stop();
ctl._autoCompleteTimer.Start();
#else
ctl.UpdateSearchItems(e.NewValue as string);
#endif
}
private void EditableTextBox_LostFocus(object sender, RoutedEventArgs e)
ctl._isAutoCompleteAction = true;
}
/// <summary>
/// 搜索文本
/// </summary>
internal string SearchText
{
get => (string) GetValue(SearchTextProperty);
set => SetValue(SearchTextProperty, value);
}
public static readonly DependencyProperty SelectionBrushProperty =
TextBoxBase.SelectionBrushProperty.AddOwner(typeof(ComboBox));
public Brush SelectionBrush
{
get => (Brush) GetValue(SelectionBrushProperty);
set => SetValue(SelectionBrushProperty, value);
}
#if !(NET40 || NET45 || NET451 || NET452 || NET46 || NET461 || NET462 || NET47 || NET471 || NET472)
public static readonly DependencyProperty SelectionTextBrushProperty =
TextBoxBase.SelectionTextBrushProperty.AddOwner(typeof(ComboBox));
public Brush SelectionTextBrush
{
get => (Brush) GetValue(SelectionTextBrushProperty);
set => SetValue(SelectionTextBrushProperty, value);
}
#endif
public static readonly DependencyProperty SelectionOpacityProperty =
TextBoxBase.SelectionOpacityProperty.AddOwner(typeof(ComboBox));
public double SelectionOpacity
{
get => (double) GetValue(SelectionOpacityProperty);
set => SetValue(SelectionOpacityProperty, value);
}
public static readonly DependencyProperty CaretBrushProperty =
TextBoxBase.CaretBrushProperty.AddOwner(typeof(ComboBox));
public Brush CaretBrush
{
get => (Brush) GetValue(CaretBrushProperty);
set => SetValue(CaretBrushProperty, value);
}
/// <summary>
/// 更新搜索的项目
/// </summary>
/// <param name="key"></param>
private void UpdateSearchItems(string key)
{
if (_editableTextBox != null && _autoPopupAutoComplete != null)
{
_autoPopupAutoComplete.IsOpen = !string.IsNullOrEmpty(key);
_autoCompletePanel.Children.Clear();
if (SearchFunc == null)
{
if (!string.IsNullOrEmpty(key))
{
foreach (var item in Items)
{
var content = item?.ToString();
if (content == null) continue;
if (!content.Contains(key)) continue;
_autoCompletePanel.Children.Add(CreateSearchItem(item));
}
}
}
else
{
foreach (var item in SearchFunc.Invoke(Items, key))
{
_autoCompletePanel.Children.Add(CreateSearchItem(item));
}
}
}
}
private ComboBoxItem CreateSearchItem(object content)
{
var item = new ComboBoxItem
{
Content = content,
Style = ItemContainerStyle,
ContentTemplate = ItemTemplate
};
item.PreviewMouseLeftButtonDown += AutoCompleteItem_PreviewMouseLeftButtonDown;
return item;
}
private void AutoCompleteItem_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (sender is ComboBoxItem comboBoxItem)
{
if (_autoPopupAutoComplete != null)
{
_autoPopupAutoComplete.IsOpen = false;
}
}
#if !NET40
protected override void OnDropDownClosed(EventArgs e)
{
base.OnDropDownClosed(e);
_isAutoCompleteAction = false;
}
#endif
private void EditableTextBox_GotFocus(object sender, RoutedEventArgs e)
{
if (_autoPopupAutoComplete != null && _editableTextBox != null &&
!string.IsNullOrEmpty(_editableTextBox.Text))
{
_autoPopupAutoComplete.IsOpen = true;
}
}
protected override void OnSelectionChanged(SelectionChangedEventArgs e)
{
_isAutoCompleteAction = false;
base.OnSelectionChanged(e);
VerifyData();
#if NET40
_isAutoCompleteAction = true;
#endif
}
/// <summary>
/// 数据验证委托
/// </summary>
public Func<string, OperationResult<bool>> VerifyFunc { get; set; }
/// <summary>
/// 数据搜索委托
/// </summary>
public Func<ItemCollection, object, IEnumerable<object>> SearchFunc { get; set; }
/// <summary>
/// 数据是否错误
/// </summary>
public static readonly DependencyProperty IsErrorProperty = DependencyProperty.Register(
"IsError", typeof(bool), typeof(ComboBox), new PropertyMetadata(ValueBoxes.FalseBox));
/// <summary>
/// 数据是否错误
/// </summary>
public bool IsError
{
get => (bool) GetValue(IsErrorProperty);
set => SetValue(IsErrorProperty, ValueBoxes.BooleanBox(value));
}
/// <summary>
/// 错误提示
/// </summary>
public static readonly DependencyProperty ErrorStrProperty = DependencyProperty.Register(
"ErrorStr", typeof(string), typeof(ComboBox), new PropertyMetadata(default(string)));
/// <summary>
/// 错误提示
/// </summary>
public string ErrorStr
{
get => (string) GetValue(ErrorStrProperty);
set => SetValue(ErrorStrProperty, value);
}
/// <summary>
/// 文本类型
/// </summary>
public static readonly DependencyPropertyKey TextTypePropertyKey =
DependencyProperty.RegisterReadOnly("TextType", typeof(TextType), typeof(ComboBox),
new PropertyMetadata(default(TextType)));
/// <summary>
/// 文本类型
/// </summary>
public static readonly DependencyProperty TextTypeProperty = TextTypePropertyKey.DependencyProperty;
/// <summary>
/// 文本类型
/// </summary>
public TextType TextType
{
get => (TextType) GetValue(TextTypeProperty);
set => SetValue(TextTypeProperty, value);
}
/// <summary>
/// 是否显示清除按钮
/// </summary>
public static readonly DependencyProperty ShowClearButtonProperty = DependencyProperty.Register(
"ShowClearButton", typeof(bool), typeof(ComboBox), new PropertyMetadata(ValueBoxes.FalseBox));
/// <summary>
/// 是否显示清除按钮
/// </summary>
public bool ShowClearButton
{
get => (bool) GetValue(ShowClearButtonProperty);
set => SetValue(ShowClearButtonProperty, ValueBoxes.BooleanBox(value));
}
/// <summary>
/// 是否自动完成输入
/// </summary>
public static readonly DependencyProperty AutoCompleteProperty = DependencyProperty.Register(
"AutoComplete", typeof(bool), typeof(ComboBox), new PropertyMetadata(ValueBoxes.FalseBox, OnAutoCompleteChanged));
private static void OnAutoCompleteChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var ctl = (ComboBox) d;
if (ctl._editableTextBox != null)
{
ctl.UpdateSearchItems(ctl._editableTextBox.Text);
}
}
/// <summary>
/// 是否自动完成输入
/// </summary>
public bool AutoComplete
{
get => (bool) GetValue(AutoCompleteProperty);
set => SetValue(AutoCompleteProperty, ValueBoxes.BooleanBox(value));
}
/// <summary>
/// 搜索文本
/// </summary>
internal static readonly DependencyProperty SearchTextProperty = DependencyProperty.Register(
"SearchText", typeof(string), typeof(ComboBox), new PropertyMetadata(default(string), OnSearchTextChanged));
private static void OnSearchTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var ctl = (ComboBox) d;
if (ctl._isAutoCompleteAction)
{
#if NET40
ctl._searchText = e.NewValue as string;
ctl._autoCompleteTimer.Stop();
ctl._autoCompleteTimer.Start();
#else
ctl.UpdateSearchItems(e.NewValue as string);
#endif
}
ctl._isAutoCompleteAction = true;
}
/// <summary>
/// 搜索文本
/// </summary>
internal string SearchText
{
get => (string) GetValue(SearchTextProperty);
set => SetValue(SearchTextProperty, value);
}
public static readonly DependencyProperty SelectionBrushProperty =
TextBoxBase.SelectionBrushProperty.AddOwner(typeof(ComboBox));
public Brush SelectionBrush
{
get => (Brush) GetValue(SelectionBrushProperty);
set => SetValue(SelectionBrushProperty, value);
}
#if !(NET40 || NET45 || NET451 || NET452 || NET46 || NET461 || NET462 || NET47 || NET471 || NET472)
public static readonly DependencyProperty SelectionTextBrushProperty =
TextBoxBase.SelectionTextBrushProperty.AddOwner(typeof(ComboBox));
public Brush SelectionTextBrush
{
get => (Brush) GetValue(SelectionTextBrushProperty);
set => SetValue(SelectionTextBrushProperty, value);
}
#endif
public static readonly DependencyProperty SelectionOpacityProperty =
TextBoxBase.SelectionOpacityProperty.AddOwner(typeof(ComboBox));
public double SelectionOpacity
{
get => (double) GetValue(SelectionOpacityProperty);
set => SetValue(SelectionOpacityProperty, value);
}
public static readonly DependencyProperty CaretBrushProperty =
TextBoxBase.CaretBrushProperty.AddOwner(typeof(ComboBox));
public Brush CaretBrush
{
get => (Brush) GetValue(CaretBrushProperty);
set => SetValue(CaretBrushProperty, value);
}
/// <summary>
/// 验证数据
/// </summary>
/// <returns></returns>
public virtual bool VerifyData()
{
OperationResult<bool> result;
var value = _editableTextBox == null ? Text : _editableTextBox.Text;
if (VerifyFunc != null)
{
result = VerifyFunc.Invoke(value);
}
else
{
if (!string.IsNullOrEmpty(value))
{
result = OperationResult.Success();
}
else if (InfoElement.GetNecessary(this))
{
result = OperationResult.Failed(Properties.Langs.Lang.IsNecessary);
}
else
{
result = OperationResult.Success();
}
}
IsError = !result.Data;
ErrorStr = result.Message;
return result.Data;
}
/// <summary>
/// 更新搜索的项目
/// </summary>
/// <param name="key"></param>
private void UpdateSearchItems(string key)
{
if (_editableTextBox != null && _autoPopupAutoComplete != null)
{
_autoPopupAutoComplete.IsOpen = !string.IsNullOrEmpty(key);
_autoCompletePanel.Children.Clear();
if (SearchFunc == null)
{
if (!string.IsNullOrEmpty(key))
{
foreach (var item in Items)
{
var content = item?.ToString();
if (content == null) continue;
if (!content.Contains(key)) continue;
_autoCompletePanel.Children.Add(CreateSearchItem(item));
}
}
}
else
{
foreach (var item in SearchFunc.Invoke(Items, key))
{
_autoCompletePanel.Children.Add(CreateSearchItem(item));
}
}
}
}
private ComboBoxItem CreateSearchItem(object content)
{
var item = new ComboBoxItem
{
Content = content,
Style = ItemContainerStyle,
ContentTemplate = ItemTemplate
};
item.PreviewMouseLeftButtonDown += AutoCompleteItem_PreviewMouseLeftButtonDown;
return item;
}
private void AutoCompleteItem_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (sender is ComboBoxItem comboBoxItem)
{
if (_autoPopupAutoComplete != null)
{
_autoPopupAutoComplete.IsOpen = false;
}
_isAutoCompleteAction = false;
SelectedValue = comboBoxItem.Content;
}
SelectedValue = comboBoxItem.Content;
}
}
}
@@ -1,161 +1,81 @@
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media;
using HandyControl.Data;
using HandyControl.Interactivity;
namespace HandyControl.Controls
namespace HandyControl.Controls;
[TemplatePart(Name = ElementTextBox, Type = typeof(DatePickerTextBox))]
public class DatePicker : System.Windows.Controls.DatePicker
{
[TemplatePart(Name = ElementTextBox, Type = typeof(DatePickerTextBox))]
public class DatePicker : System.Windows.Controls.DatePicker, IDataInput
private const string ElementTextBox = "PART_TextBox";
private System.Windows.Controls.TextBox _textBox;
public DatePicker()
{
private const string ElementTextBox = "PART_TextBox";
private System.Windows.Controls.TextBox _textBox;
public DatePicker()
CommandBindings.Add(new CommandBinding(ControlCommands.Clear, (s, e) =>
{
CommandBindings.Add(new CommandBinding(ControlCommands.Clear, (s, e) =>
{
SetCurrentValue(SelectedDateProperty, null);
SetCurrentValue(TextProperty, "");
}));
}
SetCurrentValue(SelectedDateProperty, null);
SetCurrentValue(TextProperty, "");
}));
}
public override void OnApplyTemplate()
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
_textBox = GetTemplateChild(ElementTextBox) as System.Windows.Controls.TextBox;
if (_textBox != null)
{
if (_textBox != null)
{
_textBox.TextChanged -= TextBox_TextChanged;
}
base.OnApplyTemplate();
_textBox = GetTemplateChild(ElementTextBox) as System.Windows.Controls.TextBox;
if (_textBox != null)
{
_textBox.SetBinding(SelectionBrushProperty, new Binding(SelectionBrushProperty.Name) { Source = this });
_textBox.SetBinding(SelectionBrushProperty, new Binding(SelectionBrushProperty.Name) { Source = this });
#if !(NET40 || NET45 || NET451 || NET452 || NET46 || NET461 || NET462 || NET47 || NET471 || NET472)
_textBox.SetBinding(SelectionTextBrushProperty, new Binding(SelectionTextBrushProperty.Name) { Source = this });
_textBox.SetBinding(SelectionTextBrushProperty, new Binding(SelectionTextBrushProperty.Name) { Source = this });
#endif
_textBox.SetBinding(SelectionOpacityProperty, new Binding(SelectionOpacityProperty.Name) { Source = this });
_textBox.SetBinding(CaretBrushProperty, new Binding(CaretBrushProperty.Name) { Source = this });
_textBox.TextChanged += TextBox_TextChanged;
}
}
private void TextBox_TextChanged(object sender, TextChangedEventArgs e) => VerifyData();
public Func<string, OperationResult<bool>> VerifyFunc { get; set; }
public static readonly DependencyProperty IsErrorProperty = DependencyProperty.Register(
"IsError", typeof(bool), typeof(DatePicker), new PropertyMetadata(ValueBoxes.FalseBox));
public bool IsError
{
get => (bool) GetValue(IsErrorProperty);
set => SetValue(IsErrorProperty, ValueBoxes.BooleanBox(value));
}
public static readonly DependencyProperty ErrorStrProperty = DependencyProperty.Register(
"ErrorStr", typeof(string), typeof(DatePicker), new PropertyMetadata(default(string)));
public string ErrorStr
{
get => (string) GetValue(ErrorStrProperty);
set => SetValue(ErrorStrProperty, value);
}
public static readonly DependencyProperty TextTypeProperty = DependencyProperty.Register(
"TextType", typeof(TextType), typeof(DatePicker), new PropertyMetadata(default(TextType)));
public TextType TextType
{
get => (TextType) GetValue(TextTypeProperty);
set => SetValue(TextTypeProperty, value);
}
public static readonly DependencyProperty ShowClearButtonProperty = DependencyProperty.Register(
"ShowClearButton", typeof(bool), typeof(DatePicker), new PropertyMetadata(ValueBoxes.FalseBox));
public bool ShowClearButton
{
get => (bool) GetValue(ShowClearButtonProperty);
set => SetValue(ShowClearButtonProperty, ValueBoxes.BooleanBox(value));
}
public static readonly DependencyProperty SelectionBrushProperty =
TextBoxBase.SelectionBrushProperty.AddOwner(typeof(DatePicker));
public Brush SelectionBrush
{
get => (Brush) GetValue(SelectionBrushProperty);
set => SetValue(SelectionBrushProperty, value);
}
#if !(NET40 || NET45 || NET451 || NET452 || NET46 || NET461 || NET462 || NET47 || NET471 || NET472)
public static readonly DependencyProperty SelectionTextBrushProperty =
TextBoxBase.SelectionTextBrushProperty.AddOwner(typeof(DatePicker));
public Brush SelectionTextBrush
{
get => (Brush) GetValue(SelectionTextBrushProperty);
set => SetValue(SelectionTextBrushProperty, value);
}
#endif
public static readonly DependencyProperty SelectionOpacityProperty =
TextBoxBase.SelectionOpacityProperty.AddOwner(typeof(DatePicker));
public double SelectionOpacity
{
get => (double) GetValue(SelectionOpacityProperty);
set => SetValue(SelectionOpacityProperty, value);
}
public static readonly DependencyProperty CaretBrushProperty =
TextBoxBase.CaretBrushProperty.AddOwner(typeof(DatePicker));
public Brush CaretBrush
{
get => (Brush) GetValue(CaretBrushProperty);
set => SetValue(CaretBrushProperty, value);
}
public virtual bool VerifyData()
{
OperationResult<bool> result;
if (VerifyFunc != null)
{
result = VerifyFunc.Invoke(Text);
}
else
{
if (!string.IsNullOrEmpty(Text))
{
result = OperationResult.Success();
}
else if (InfoElement.GetNecessary(this))
{
result = OperationResult.Failed(Properties.Langs.Lang.IsNecessary);
}
else
{
result = OperationResult.Success();
}
}
IsError = !result.Data;
ErrorStr = result.Message;
return result.Data;
_textBox.SetBinding(SelectionOpacityProperty, new Binding(SelectionOpacityProperty.Name) { Source = this });
_textBox.SetBinding(CaretBrushProperty, new Binding(CaretBrushProperty.Name) { Source = this });
}
}
public static readonly DependencyProperty SelectionBrushProperty =
TextBoxBase.SelectionBrushProperty.AddOwner(typeof(DatePicker));
public Brush SelectionBrush
{
get => (Brush) GetValue(SelectionBrushProperty);
set => SetValue(SelectionBrushProperty, value);
}
#if !(NET40 || NET45 || NET451 || NET452 || NET46 || NET461 || NET462 || NET47 || NET471 || NET472)
public static readonly DependencyProperty SelectionTextBrushProperty =
TextBoxBase.SelectionTextBrushProperty.AddOwner(typeof(DatePicker));
public Brush SelectionTextBrush
{
get => (Brush) GetValue(SelectionTextBrushProperty);
set => SetValue(SelectionTextBrushProperty, value);
}
#endif
public static readonly DependencyProperty SelectionOpacityProperty =
TextBoxBase.SelectionOpacityProperty.AddOwner(typeof(DatePicker));
public double SelectionOpacity
{
get => (double) GetValue(SelectionOpacityProperty);
set => SetValue(SelectionOpacityProperty, value);
}
public static readonly DependencyProperty CaretBrushProperty =
TextBoxBase.CaretBrushProperty.AddOwner(typeof(DatePicker));
public Brush CaretBrush
{
get => (Brush) GetValue(CaretBrushProperty);
set => SetValue(CaretBrushProperty, value);
}
}
File diff suppressed because it is too large. Load diff
@@ -1,43 +0,0 @@
using System;
using HandyControl.Data;
namespace HandyControl.Controls
{
/// <summary>
/// 表示可以成为一个数据输入控件
/// </summary>
public interface IDataInput
{
/// <summary>
/// 验证数据
/// </summary>
/// <returns></returns>
bool VerifyData();
/// <summary>
/// 数据验证委托
/// </summary>
Func<string, OperationResult<bool>> VerifyFunc { get; set; }
/// <summary>
/// 数据是否错误
/// </summary>
bool IsError { get; set; }
/// <summary>
/// 错误提示
/// </summary>
string ErrorStr { get; set; }
/// <summary>
/// 文本类型
/// </summary>
TextType TextType { get; set; }
/// <summary>
/// 是否显示清除按钮
/// </summary>
bool ShowClearButton { get; set; }
}
}
@@ -8,119 +8,140 @@ using HandyControl.Data;
using HandyControl.Interactivity;
using Microsoft.Win32;
namespace HandyControl.Controls
namespace HandyControl.Controls;
public class ImageSelector : Control
{
public class ImageSelector : Control
public static readonly RoutedEvent ImageSelectedEvent =
EventManager.RegisterRoutedEvent("ImageSelected", RoutingStrategy.Bubble,
typeof(RoutedEventHandler), typeof(ImageSelector));
public event RoutedEventHandler ImageSelected
{
public ImageSelector() => CommandBindings.Add(new CommandBinding(ControlCommands.Switch, SwitchImage));
add => AddHandler(ImageSelectedEvent, value);
remove => RemoveHandler(ImageSelectedEvent, value);
}
private void SwitchImage(object sender, ExecutedRoutedEventArgs e)
public static readonly RoutedEvent ImageUnselectedEvent =
EventManager.RegisterRoutedEvent("ImageUnselected", RoutingStrategy.Bubble,
typeof(RoutedEventHandler), typeof(ImageSelector));
public event RoutedEventHandler ImageUnselected
{
add => AddHandler(ImageUnselectedEvent, value);
remove => RemoveHandler(ImageUnselectedEvent, value);
}
public ImageSelector() => CommandBindings.Add(new CommandBinding(ControlCommands.Switch, SwitchImage));
private void SwitchImage(object sender, ExecutedRoutedEventArgs e)
{
if (!HasValue)
{
if (!HasValue)
var dialog = new OpenFileDialog
{
var dialog = new OpenFileDialog
{
RestoreDirectory = true,
Filter = Filter,
DefaultExt = DefaultExt
};
RestoreDirectory = true,
Filter = Filter,
DefaultExt = DefaultExt
};
if (dialog.ShowDialog() == true)
{
SetValue(UriPropertyKey, new Uri(dialog.FileName, UriKind.RelativeOrAbsolute));
SetValue(PreviewBrushPropertyKey, new ImageBrush(BitmapFrame.Create(Uri, BitmapCreateOptions.IgnoreImageCache, BitmapCacheOption.None))
{
Stretch = Stretch
});
SetValue(HasValuePropertyKey, ValueBoxes.TrueBox);
SetCurrentValue(ToolTipProperty, dialog.FileName);
}
}
else
if (dialog.ShowDialog() == true)
{
SetValue(UriPropertyKey, default(Uri));
SetValue(PreviewBrushPropertyKey, default(Brush));
SetValue(HasValuePropertyKey, ValueBoxes.FalseBox);
SetCurrentValue(ToolTipProperty, default);
SetValue(UriPropertyKey, new Uri(dialog.FileName, UriKind.RelativeOrAbsolute));
SetValue(PreviewBrushPropertyKey, new ImageBrush(BitmapFrame.Create(Uri, BitmapCreateOptions.IgnoreImageCache, BitmapCacheOption.None))
{
Stretch = Stretch
});
SetValue(HasValuePropertyKey, ValueBoxes.TrueBox);
SetCurrentValue(ToolTipProperty, dialog.FileName);
RaiseEvent(new RoutedEventArgs(ImageSelectedEvent, this));
}
}
public static readonly DependencyProperty StretchProperty = DependencyProperty.Register(
"Stretch", typeof(Stretch), typeof(ImageSelector), new PropertyMetadata(default(Stretch)));
public Stretch Stretch
else
{
get => (Stretch) GetValue(StretchProperty);
set => SetValue(StretchProperty, value);
}
public static readonly DependencyPropertyKey UriPropertyKey = DependencyProperty.RegisterReadOnly(
"Uri", typeof(Uri), typeof(ImageSelector), new PropertyMetadata(default(Uri)));
public static readonly DependencyProperty UriProperty = UriPropertyKey.DependencyProperty;
public Uri Uri
{
get => (Uri) GetValue(UriProperty);
set => SetValue(UriProperty, value);
}
public static readonly DependencyPropertyKey PreviewBrushPropertyKey = DependencyProperty.RegisterReadOnly(
"PreviewBrush", typeof(Brush), typeof(ImageSelector), new PropertyMetadata(default(Brush)));
public static readonly DependencyProperty PreviewBrushProperty = PreviewBrushPropertyKey.DependencyProperty;
public Brush PreviewBrush
{
get => (Brush) GetValue(PreviewBrushProperty);
set => SetValue(PreviewBrushProperty, value);
}
public static readonly DependencyProperty StrokeThicknessProperty = DependencyProperty.Register(
"StrokeThickness", typeof(double), typeof(ImageSelector), new FrameworkPropertyMetadata(ValueBoxes.Double1Box, FrameworkPropertyMetadataOptions.AffectsRender));
public double StrokeThickness
{
get => (double) GetValue(StrokeThicknessProperty);
set => SetValue(StrokeThicknessProperty, value);
}
public static readonly DependencyProperty StrokeDashArrayProperty = DependencyProperty.Register(
"StrokeDashArray", typeof(DoubleCollection), typeof(ImageSelector), new FrameworkPropertyMetadata(default(DoubleCollection), FrameworkPropertyMetadataOptions.AffectsRender));
public DoubleCollection StrokeDashArray
{
get => (DoubleCollection) GetValue(StrokeDashArrayProperty);
set => SetValue(StrokeDashArrayProperty, value);
}
public static readonly DependencyProperty DefaultExtProperty = DependencyProperty.Register(
"DefaultExt", typeof(string), typeof(ImageSelector), new PropertyMetadata(".jpg"));
public string DefaultExt
{
get => (string) GetValue(DefaultExtProperty);
set => SetValue(DefaultExtProperty, value);
}
public static readonly DependencyProperty FilterProperty = DependencyProperty.Register(
"Filter", typeof(string), typeof(ImageSelector), new PropertyMetadata("(.jpg)|*.jpg|(.png)|*.png"));
public string Filter
{
get => (string) GetValue(FilterProperty);
set => SetValue(FilterProperty, value);
}
public static readonly DependencyPropertyKey HasValuePropertyKey = DependencyProperty.RegisterReadOnly(
"HasValue", typeof(bool), typeof(ImageSelector), new PropertyMetadata(ValueBoxes.FalseBox));
public static readonly DependencyProperty HasValueProperty = HasValuePropertyKey.DependencyProperty;
public bool HasValue
{
get => (bool) GetValue(HasValueProperty);
set => SetValue(HasValueProperty, ValueBoxes.BooleanBox(value));
SetValue(UriPropertyKey, default(Uri));
SetValue(PreviewBrushPropertyKey, default(Brush));
SetValue(HasValuePropertyKey, ValueBoxes.FalseBox);
SetCurrentValue(ToolTipProperty, default);
RaiseEvent(new RoutedEventArgs(ImageUnselectedEvent, this));
}
}
public static readonly DependencyProperty StretchProperty = DependencyProperty.Register(
nameof(Stretch), typeof(Stretch), typeof(ImageSelector), new PropertyMetadata(default(Stretch)));
public Stretch Stretch
{
get => (Stretch) GetValue(StretchProperty);
set => SetValue(StretchProperty, value);
}
public static readonly DependencyPropertyKey UriPropertyKey = DependencyProperty.RegisterReadOnly(
"Uri", typeof(Uri), typeof(ImageSelector), new PropertyMetadata(default(Uri)));
public static readonly DependencyProperty UriProperty = UriPropertyKey.DependencyProperty;
public Uri Uri
{
get => (Uri) GetValue(UriProperty);
set => SetValue(UriProperty, value);
}
public static readonly DependencyPropertyKey PreviewBrushPropertyKey = DependencyProperty.RegisterReadOnly(
"PreviewBrush", typeof(Brush), typeof(ImageSelector), new PropertyMetadata(default(Brush)));
public static readonly DependencyProperty PreviewBrushProperty = PreviewBrushPropertyKey.DependencyProperty;
public Brush PreviewBrush
{
get => (Brush) GetValue(PreviewBrushProperty);
set => SetValue(PreviewBrushProperty, value);
}
public static readonly DependencyProperty StrokeThicknessProperty = DependencyProperty.Register(
nameof(StrokeThickness), typeof(double), typeof(ImageSelector), new FrameworkPropertyMetadata(ValueBoxes.Double1Box, FrameworkPropertyMetadataOptions.AffectsRender));
public double StrokeThickness
{
get => (double) GetValue(StrokeThicknessProperty);
set => SetValue(StrokeThicknessProperty, value);
}
public static readonly DependencyProperty StrokeDashArrayProperty = DependencyProperty.Register(
nameof(StrokeDashArray), typeof(DoubleCollection), typeof(ImageSelector), new FrameworkPropertyMetadata(default(DoubleCollection), FrameworkPropertyMetadataOptions.AffectsRender));
public DoubleCollection StrokeDashArray
{
get => (DoubleCollection) GetValue(StrokeDashArrayProperty);
set => SetValue(StrokeDashArrayProperty, value);
}
public static readonly DependencyProperty DefaultExtProperty = DependencyProperty.Register(
nameof(DefaultExt), typeof(string), typeof(ImageSelector), new PropertyMetadata(".jpg"));
public string DefaultExt
{
get => (string) GetValue(DefaultExtProperty);
set => SetValue(DefaultExtProperty, value);
}
public static readonly DependencyProperty FilterProperty = DependencyProperty.Register(
nameof(Filter), typeof(string), typeof(ImageSelector), new PropertyMetadata("(.jpg)|*.jpg|(.png)|*.png"));
public string Filter
{
get => (string) GetValue(FilterProperty);
set => SetValue(FilterProperty, value);
}
public static readonly DependencyPropertyKey HasValuePropertyKey = DependencyProperty.RegisterReadOnly(
"HasValue", typeof(bool), typeof(ImageSelector), new PropertyMetadata(ValueBoxes.FalseBox));
public static readonly DependencyProperty HasValueProperty = HasValuePropertyKey.DependencyProperty;
public bool HasValue
{
get => (bool) GetValue(HasValueProperty);
set => SetValue(HasValueProperty, ValueBoxes.BooleanBox(value));
}
}
@@ -9,496 +9,371 @@ using HandyControl.Data;
using HandyControl.Interactivity;
using HandyControl.Tools;
namespace HandyControl.Controls
namespace HandyControl.Controls;
/// <summary>
/// 数值选择控件
/// </summary>
[TemplatePart(Name = ElementTextBox, Type = typeof(TextBox))]
public class NumericUpDown : Control
{
/// <summary>
/// 数值选择控件
/// </summary>
[TemplatePart(Name = ElementTextBox, Type = typeof(TextBox))]
[TemplatePart(Name = ElementErrorTip, Type = typeof(UIElement))]
public class NumericUpDown : Control, IDataInput
private const string ElementTextBox = "PART_TextBox";
private TextBox _textBox;
public NumericUpDown()
{
#region Constants
private const string ElementTextBox = "PART_TextBox";
private const string ElementErrorTip = "PART_ErrorTip";
#endregion Constants
#region Data
private TextBox _textBox;
private UIElement _errorTip;
private bool _updateText;
#endregion Data
public NumericUpDown()
{
CommandBindings.Add(new CommandBinding(ControlCommands.Prev, (s, e) =>
{
if (IsReadOnly) return;
Value += Increment;
}));
CommandBindings.Add(new CommandBinding(ControlCommands.Next, (s, e) =>
{
if (IsReadOnly) return;
Value -= Increment;
}));
CommandBindings.Add(new CommandBinding(ControlCommands.Clear, (s, e) =>
{
if (IsReadOnly) return;
SetCurrentValue(ValueProperty, ValueBoxes.Double0Box);
}));
Loaded += (s, e) => OnApplyTemplate();
}
protected override void OnGotFocus(RoutedEventArgs e)
{
base.OnGotFocus(e);
if (_textBox != null)
{
_textBox?.Focus();
_textBox.Select(_textBox.Text.Length, 0);
}
}
public override void OnApplyTemplate()
{
if (_textBox != null)
{
TextCompositionManager.RemovePreviewTextInputHandler(_textBox, PreviewTextInputHandler);
_textBox.TextChanged -= TextBox_TextChanged;
_textBox.PreviewKeyDown -= TextBox_PreviewKeyDown;
_textBox.LostFocus -= TextBox_LostFocus;
}
base.OnApplyTemplate();
_textBox = GetTemplateChild(ElementTextBox) as TextBox;
_errorTip = GetTemplateChild(ElementErrorTip) as UIElement;
if (_textBox != null)
{
_textBox.SetBinding(SelectionBrushProperty, new Binding(SelectionBrushProperty.Name) { Source = this });
#if !(NET40 || NET45 || NET451 || NET452 || NET46 || NET461 || NET462 || NET47 || NET471 || NET472)
_textBox.SetBinding(SelectionTextBrushProperty, new Binding(SelectionTextBrushProperty.Name) { Source = this });
#endif
_textBox.SetBinding(SelectionOpacityProperty, new Binding(SelectionOpacityProperty.Name) { Source = this });
_textBox.SetBinding(CaretBrushProperty, new Binding(CaretBrushProperty.Name) { Source = this });
TextCompositionManager.AddPreviewTextInputHandler(_textBox, PreviewTextInputHandler);
_textBox.TextChanged += TextBox_TextChanged;
_textBox.PreviewKeyDown += TextBox_PreviewKeyDown;
_textBox.LostFocus += TextBox_LostFocus;
_textBox.Text = CurrentText;
}
}
private void TextBox_TextChanged(object sender, TextChangedEventArgs e) => UpdateData();
private void UpdateData()
{
if (!VerifyData())
{
_updateText = true;
return;
}
if (double.TryParse(_textBox.Text, out var value))
{
_updateText = false;
Value = value;
_updateText = true;
}
}
private void PreviewTextInputHandler(object sender, TextCompositionEventArgs e) => UpdateData();
private void TextBox_LostFocus(object sender, RoutedEventArgs e)
{
if (IsError && _errorTip != null) return;
_textBox.Text = CurrentText;
}
private void TextBox_PreviewKeyDown(object sender, KeyEventArgs e)
CommandBindings.Add(new CommandBinding(ControlCommands.Prev, (s, e) =>
{
if (IsReadOnly) return;
if (e.Key == Key.Up)
SetCurrentValue(ValueProperty, Value + Increment);
}));
CommandBindings.Add(new CommandBinding(ControlCommands.Next, (s, e) =>
{
if (IsReadOnly) return;
SetCurrentValue(ValueProperty, Value - Increment);
}));
CommandBindings.Add(new CommandBinding(ControlCommands.Clear, (s, e) =>
{
if (IsReadOnly) return;
SetCurrentValue(ValueProperty, ValueBoxes.Double0Box);
}));
}
public override void OnApplyTemplate()
{
if (_textBox != null)
{
_textBox.PreviewKeyDown -= TextBox_PreviewKeyDown;
_textBox.TextChanged -= TextBox_TextChanged;
_textBox.LostFocus -= TextBox_LostFocus;
}
base.OnApplyTemplate();
_textBox = GetTemplateChild(ElementTextBox) as TextBox;
if (_textBox != null)
{
_textBox.SetBinding(SelectionBrushProperty, new Binding(SelectionBrushProperty.Name) { Source = this });
#if NET48_OR_GREATER
_textBox.SetBinding(SelectionTextBrushProperty, new Binding(SelectionTextBrushProperty.Name) { Source = this });
#endif
_textBox.SetBinding(SelectionOpacityProperty, new Binding(SelectionOpacityProperty.Name) { Source = this });
_textBox.SetBinding(CaretBrushProperty, new Binding(CaretBrushProperty.Name) { Source = this });
_textBox.PreviewKeyDown += TextBox_PreviewKeyDown;
_textBox.TextChanged += TextBox_TextChanged;
_textBox.LostFocus += TextBox_LostFocus;
_textBox.Text = CurrentText;
}
}
private void TextBox_LostFocus(object sender, RoutedEventArgs e)
{
if (string.IsNullOrWhiteSpace(_textBox.Text))
{
SetCurrentValue(ValueProperty, ValueBoxes.Double0Box);
}
else if (double.TryParse(_textBox.Text, out double value))
{
SetCurrentValue(ValueProperty, value);
}
else
{
SetText(true);
}
}
private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
{
if (double.TryParse(_textBox.Text, out double value))
{
if (value >= Minimum && value <= Maximum)
{
Value += Increment;
}
else if (e.Key == Key.Down)
{
Value -= Increment;
SetCurrentValue(ValueProperty, value);
}
}
}
protected override void OnMouseWheel(MouseWheelEventArgs e)
private void TextBox_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (IsReadOnly) return;
if (e.Key == Key.Up)
{
base.OnMouseWheel(e);
if (_textBox.IsFocused && !IsReadOnly)
{
Value += e.Delta > 0 ? Increment : -Increment;
e.Handled = true;
}
Value += Increment;
}
private string CurrentText => string.IsNullOrWhiteSpace(ValueFormat)
? DecimalPlaces.HasValue
? Value.ToString($"#0.{new string('0', DecimalPlaces.Value)}")
: Value.ToString()
: Value.ToString(ValueFormat);
protected virtual void OnValueChanged(FunctionEventArgs<double> e) => RaiseEvent(e);
/// <summary>
/// 值改变事件
/// </summary>
public static readonly RoutedEvent ValueChangedEvent =
EventManager.RegisterRoutedEvent("ValueChanged", RoutingStrategy.Bubble,
typeof(EventHandler<FunctionEventArgs<double>>), typeof(NumericUpDown));
/// <summary>
/// 值改变事件
/// </summary>
public event EventHandler<FunctionEventArgs<double>> ValueChanged
else if (e.Key == Key.Down)
{
add => AddHandler(ValueChangedEvent, value);
remove => RemoveHandler(ValueChangedEvent, value);
Value -= Increment;
}
}
/// <summary>
/// 当前值
/// </summary>
public static readonly DependencyProperty ValueProperty = DependencyProperty.Register(
"Value", typeof(double), typeof(NumericUpDown),
new FrameworkPropertyMetadata(ValueBoxes.Double0Box, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
OnValueChanged, CoerceValue), ValidateHelper.IsInRangeOfDouble);
protected override void OnMouseWheel(MouseWheelEventArgs e)
{
base.OnMouseWheel(e);
private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
if (_textBox.IsFocused && !IsReadOnly)
{
var ctl = (NumericUpDown) d;
var v = (double) e.NewValue;
ctl.SetText();
ctl.OnValueChanged(new FunctionEventArgs<double>(ValueChangedEvent, ctl)
{
Info = v
});
Value += e.Delta > 0 ? Increment : -Increment;
SetText(true);
e.Handled = true;
}
}
private void SetText()
private string CurrentText => string.IsNullOrWhiteSpace(ValueFormat)
? DecimalPlaces.HasValue
? Value.ToString($"#0.{new string('0', DecimalPlaces.Value)}")
: Value.ToString()
: Value.ToString(ValueFormat);
protected virtual void OnValueChanged(FunctionEventArgs<double> e) => RaiseEvent(e);
/// <summary>
/// 值改变事件
/// </summary>
public static readonly RoutedEvent ValueChangedEvent =
EventManager.RegisterRoutedEvent("ValueChanged", RoutingStrategy.Bubble,
typeof(EventHandler<FunctionEventArgs<double>>), typeof(NumericUpDown));
/// <summary>
/// 值改变事件
/// </summary>
public event EventHandler<FunctionEventArgs<double>> ValueChanged
{
add => AddHandler(ValueChangedEvent, value);
remove => RemoveHandler(ValueChangedEvent, value);
}
/// <summary>
/// 当前值
/// </summary>
public static readonly DependencyProperty ValueProperty = DependencyProperty.Register(
nameof(Value), typeof(double), typeof(NumericUpDown),
new FrameworkPropertyMetadata(ValueBoxes.Double0Box, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
OnValueChanged, CoerceValue), ValidateHelper.IsInRangeOfDouble);
private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var ctl = (NumericUpDown) d;
var v = (double) e.NewValue;
ctl.SetText();
ctl.OnValueChanged(new FunctionEventArgs<double>(ValueChangedEvent, ctl)
{
if (_updateText && _textBox != null)
{
_textBox.Text = CurrentText;
_textBox.Select(_textBox.Text.Length, 0);
}
}
Info = v
});
}
private static object CoerceValue(DependencyObject d, object basevalue)
private void SetText(bool force = false)
{
if (_textBox != null && (!_textBox.IsFocused || force))
{
var ctl = (NumericUpDown) d;
var minimum = ctl.Minimum;
var num = (double) basevalue;
if (num < minimum)
{
ctl.Value = minimum;
return minimum;
}
var maximum = ctl.Maximum;
if (num > maximum)
{
ctl.Value = maximum;
}
ctl.SetText();
return num > maximum ? maximum : num;
_textBox.Text = CurrentText;
_textBox.Select(_textBox.Text.Length, 0);
}
}
/// <summary>
/// 当前值
/// </summary>
public double Value
private static object CoerceValue(DependencyObject d, object basevalue)
{
var ctl = (NumericUpDown) d;
var minimum = ctl.Minimum;
var num = (double) basevalue;
if (num < minimum)
{
get => (double) GetValue(ValueProperty);
set => SetValue(ValueProperty, value);
ctl.Value = minimum;
return minimum;
}
/// <summary>
/// 最大值
/// </summary>
public static readonly DependencyProperty MaximumProperty = DependencyProperty.Register(
"Maximum", typeof(double), typeof(NumericUpDown), new PropertyMetadata(double.MaxValue, OnMaximumChanged, CoerceMaximum), ValidateHelper.IsInRangeOfDouble);
private static void OnMaximumChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
var maximum = ctl.Maximum;
if (num > maximum)
{
var ctl = (NumericUpDown) d;
ctl.CoerceValue(MinimumProperty);
ctl.CoerceValue(ValueProperty);
ctl.Value = maximum;
}
ctl.SetText();
return num > maximum ? maximum : num;
}
private static object CoerceMaximum(DependencyObject d, object basevalue)
{
var minimum = ((NumericUpDown) d).Minimum;
return (double) basevalue < minimum ? minimum : basevalue;
}
/// <summary>
/// 当前值
/// </summary>
public double Value
{
get => (double) GetValue(ValueProperty);
set => SetValue(ValueProperty, value);
}
/// <summary>
/// 最大值
/// </summary>
public double Maximum
{
get => (double) GetValue(MaximumProperty);
set => SetValue(MaximumProperty, value);
}
/// <summary>
/// 最大值
/// </summary>
public static readonly DependencyProperty MaximumProperty = DependencyProperty.Register(
nameof(Maximum), typeof(double), typeof(NumericUpDown), new PropertyMetadata(double.MaxValue, OnMaximumChanged, CoerceMaximum), ValidateHelper.IsInRangeOfDouble);
/// <summary>
/// 最小值
/// </summary>
public static readonly DependencyProperty MinimumProperty = DependencyProperty.Register(
"Minimum", typeof(double), typeof(NumericUpDown), new PropertyMetadata(double.MinValue, OnMinimumChanged, CoerceMinimum), ValidateHelper.IsInRangeOfDouble);
private static void OnMaximumChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var ctl = (NumericUpDown) d;
ctl.CoerceValue(MinimumProperty);
ctl.CoerceValue(ValueProperty);
}
private static void OnMinimumChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var ctl = (NumericUpDown) d;
ctl.CoerceValue(MaximumProperty);
ctl.CoerceValue(ValueProperty);
}
private static object CoerceMaximum(DependencyObject d, object basevalue)
{
var minimum = ((NumericUpDown) d).Minimum;
return (double) basevalue < minimum ? minimum : basevalue;
}
private static object CoerceMinimum(DependencyObject d, object basevalue)
{
var maximum = ((NumericUpDown) d).Maximum;
return (double) basevalue > maximum ? maximum : basevalue;
}
/// <summary>
/// 最大值
/// </summary>
public double Maximum
{
get => (double) GetValue(MaximumProperty);
set => SetValue(MaximumProperty, value);
}
/// <summary>
/// 最小值
/// </summary>
public double Minimum
{
get => (double) GetValue(MinimumProperty);
set => SetValue(MinimumProperty, value);
}
/// <summary>
/// 最小值
/// </summary>
public static readonly DependencyProperty MinimumProperty = DependencyProperty.Register(
nameof(Minimum), typeof(double), typeof(NumericUpDown), new PropertyMetadata(double.MinValue, OnMinimumChanged, CoerceMinimum), ValidateHelper.IsInRangeOfDouble);
/// <summary>
/// 指示每单击一下按钮时增加或减少的数量
/// </summary>
public static readonly DependencyProperty IncrementProperty = DependencyProperty.Register(
"Increment", typeof(double), typeof(NumericUpDown), new PropertyMetadata(ValueBoxes.Double1Box));
private static void OnMinimumChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var ctl = (NumericUpDown) d;
ctl.CoerceValue(MaximumProperty);
ctl.CoerceValue(ValueProperty);
}
/// <summary>
/// 指示每单击一下按钮时增加或减少的数量
/// </summary>
public double Increment
{
get => (double) GetValue(IncrementProperty);
set => SetValue(IncrementProperty, value);
}
private static object CoerceMinimum(DependencyObject d, object basevalue)
{
var maximum = ((NumericUpDown) d).Maximum;
return (double) basevalue > maximum ? maximum : basevalue;
}
/// <summary>
/// 指示要显示的小数位数
/// </summary>
public static readonly DependencyProperty DecimalPlacesProperty = DependencyProperty.Register(
"DecimalPlaces", typeof(int?), typeof(NumericUpDown), new PropertyMetadata(default(int?)));
/// <summary>
/// 最小值
/// </summary>
public double Minimum
{
get => (double) GetValue(MinimumProperty);
set => SetValue(MinimumProperty, value);
}
/// <summary>
/// 指示要显示的小数位数
/// </summary>
public int? DecimalPlaces
{
get => (int?) GetValue(DecimalPlacesProperty);
set => SetValue(DecimalPlacesProperty, value);
}
/// <summary>
/// 指示每单击一下按钮时增加或减少的数量
/// </summary>
public static readonly DependencyProperty IncrementProperty = DependencyProperty.Register(
nameof(Increment), typeof(double), typeof(NumericUpDown), new PropertyMetadata(ValueBoxes.Double1Box));
/// <summary>
/// 指示要显示的数字的格式
/// </summary>
public static readonly DependencyProperty ValueFormatProperty = DependencyProperty.Register(
"ValueFormat", typeof(string), typeof(NumericUpDown), new PropertyMetadata(default(string)));
/// <summary>
/// 指示每单击一下按钮时增加或减少的数量
/// </summary>
public double Increment
{
get => (double) GetValue(IncrementProperty);
set => SetValue(IncrementProperty, value);
}
/// <summary>
/// 指示要显示的数字的格式,这将会覆盖 <see cref="DecimalPlaces"/> 属性
/// </summary>
public string ValueFormat
{
get => (string) GetValue(ValueFormatProperty);
set => SetValue(ValueFormatProperty, value);
}
/// <summary>
/// 指示要显示的小数位数
/// </summary>
public static readonly DependencyProperty DecimalPlacesProperty = DependencyProperty.Register(
nameof(DecimalPlaces), typeof(int?), typeof(NumericUpDown), new PropertyMetadata(default(int?)));
/// <summary>
/// 是否显示上下调值按钮
/// </summary>
internal static readonly DependencyProperty ShowUpDownButtonProperty = DependencyProperty.Register(
"ShowUpDownButton", typeof(bool), typeof(NumericUpDown), new PropertyMetadata(ValueBoxes.TrueBox));
/// <summary>
/// 指示要显示的小数位数
/// </summary>
public int? DecimalPlaces
{
get => (int?) GetValue(DecimalPlacesProperty);
set => SetValue(DecimalPlacesProperty, value);
}
/// <summary>
/// 是否显示上下调值按钮
/// </summary>
internal bool ShowUpDownButton
{
get => (bool) GetValue(ShowUpDownButtonProperty);
set => SetValue(ShowUpDownButtonProperty, ValueBoxes.BooleanBox(value));
}
/// <summary>
/// 指示要显示的数字的格式
/// </summary>
public static readonly DependencyProperty ValueFormatProperty = DependencyProperty.Register(
nameof(ValueFormat), typeof(string), typeof(NumericUpDown), new PropertyMetadata(default(string)));
/// <summary>
/// 数据是否错误
/// </summary>
public static readonly DependencyProperty IsErrorProperty = DependencyProperty.Register(
"IsError", typeof(bool), typeof(NumericUpDown), new PropertyMetadata(ValueBoxes.FalseBox));
/// <summary>
/// 指示要显示的数字的格式,这将会覆盖 <see cref="DecimalPlaces"/> 属性
/// </summary>
public string ValueFormat
{
get => (string) GetValue(ValueFormatProperty);
set => SetValue(ValueFormatProperty, value);
}
public bool IsError
{
get => (bool) GetValue(IsErrorProperty);
set => SetValue(IsErrorProperty, ValueBoxes.BooleanBox(value));
}
/// <summary>
/// 是否显示上下调值按钮
/// </summary>
internal static readonly DependencyProperty ShowUpDownButtonProperty = DependencyProperty.Register(
nameof(ShowUpDownButton), typeof(bool), typeof(NumericUpDown), new PropertyMetadata(ValueBoxes.TrueBox));
/// <summary>
/// 错误提示
/// </summary>
public static readonly DependencyProperty ErrorStrProperty = DependencyProperty.Register(
"ErrorStr", typeof(string), typeof(NumericUpDown), new PropertyMetadata(default(string)));
/// <summary>
/// 是否显示上下调值按钮
/// </summary>
internal bool ShowUpDownButton
{
get => (bool) GetValue(ShowUpDownButtonProperty);
set => SetValue(ShowUpDownButtonProperty, ValueBoxes.BooleanBox(value));
}
public string ErrorStr
{
get => (string) GetValue(ErrorStrProperty);
set => SetValue(ErrorStrProperty, value);
}
/// <summary>
/// 标识 IsReadOnly 依赖属性。
/// </summary>
public static readonly DependencyProperty IsReadOnlyProperty = DependencyProperty.Register(
nameof(IsReadOnly), typeof(bool), typeof(NumericUpDown), new PropertyMetadata(ValueBoxes.FalseBox));
public static readonly DependencyPropertyKey TextTypePropertyKey =
DependencyProperty.RegisterReadOnly("TextType", typeof(TextType), typeof(NumericUpDown),
new PropertyMetadata(default(TextType)));
/// <summary>
/// 获取或设置一个值,该值指示NumericUpDown是否只读。
/// </summary>
public bool IsReadOnly
{
get => (bool) GetValue(IsReadOnlyProperty);
set => SetValue(IsReadOnlyProperty, ValueBoxes.BooleanBox(value));
}
/// <summary>
/// 文本类型
/// </summary>
public static readonly DependencyProperty TextTypeProperty = TextTypePropertyKey.DependencyProperty;
public static readonly DependencyProperty SelectionBrushProperty =
TextBoxBase.SelectionBrushProperty.AddOwner(typeof(NumericUpDown));
public TextType TextType
{
get => (TextType) GetValue(TextTypeProperty);
set => SetValue(TextTypeProperty, value);
}
public Brush SelectionBrush
{
get => (Brush) GetValue(SelectionBrushProperty);
set => SetValue(SelectionBrushProperty, value);
}
/// <summary>
/// 是否显示清除按钮
/// </summary>
public static readonly DependencyProperty ShowClearButtonProperty = DependencyProperty.Register(
"ShowClearButton", typeof(bool), typeof(NumericUpDown), new PropertyMetadata(ValueBoxes.FalseBox));
#if NET48_OR_GREATER
public bool ShowClearButton
{
get => (bool) GetValue(ShowClearButtonProperty);
set => SetValue(ShowClearButtonProperty, ValueBoxes.BooleanBox(value));
}
public static readonly DependencyProperty SelectionTextBrushProperty =
TextBoxBase.SelectionTextBrushProperty.AddOwner(typeof(NumericUpDown));
/// <summary>
/// 标识 IsReadOnly 依赖属性。
/// </summary>
public static readonly DependencyProperty IsReadOnlyProperty = DependencyProperty.Register(
"IsReadOnly", typeof(bool), typeof(NumericUpDown), new PropertyMetadata(ValueBoxes.FalseBox));
/// <summary>
/// 获取或设置一个值,该值指示NumericUpDown是否只读。
/// </summary>
public bool IsReadOnly
{
get => (bool) GetValue(IsReadOnlyProperty);
set => SetValue(IsReadOnlyProperty, ValueBoxes.BooleanBox(value));
}
public static readonly DependencyProperty SelectionBrushProperty =
TextBoxBase.SelectionBrushProperty.AddOwner(typeof(NumericUpDown));
public Brush SelectionBrush
{
get => (Brush) GetValue(SelectionBrushProperty);
set => SetValue(SelectionBrushProperty, value);
}
#if !(NET40 || NET45 || NET451 || NET452 || NET46 || NET461 || NET462 || NET47 || NET471 || NET472)
public static readonly DependencyProperty SelectionTextBrushProperty =
TextBoxBase.SelectionTextBrushProperty.AddOwner(typeof(NumericUpDown));
public Brush SelectionTextBrush
{
get => (Brush) GetValue(SelectionTextBrushProperty);
set => SetValue(SelectionTextBrushProperty, value);
}
public Brush SelectionTextBrush
{
get => (Brush) GetValue(SelectionTextBrushProperty);
set => SetValue(SelectionTextBrushProperty, value);
}
#endif
public static readonly DependencyProperty SelectionOpacityProperty =
TextBoxBase.SelectionOpacityProperty.AddOwner(typeof(NumericUpDown));
public static readonly DependencyProperty SelectionOpacityProperty =
TextBoxBase.SelectionOpacityProperty.AddOwner(typeof(NumericUpDown));
public double SelectionOpacity
{
get => (double) GetValue(SelectionOpacityProperty);
set => SetValue(SelectionOpacityProperty, value);
}
public double SelectionOpacity
{
get => (double) GetValue(SelectionOpacityProperty);
set => SetValue(SelectionOpacityProperty, value);
}
public static readonly DependencyProperty CaretBrushProperty =
TextBoxBase.CaretBrushProperty.AddOwner(typeof(NumericUpDown));
public static readonly DependencyProperty CaretBrushProperty =
TextBoxBase.CaretBrushProperty.AddOwner(typeof(NumericUpDown));
public Brush CaretBrush
{
get => (Brush) GetValue(CaretBrushProperty);
set => SetValue(CaretBrushProperty, value);
}
public Func<string, OperationResult<bool>> VerifyFunc { get; set; }
public virtual bool VerifyData()
{
OperationResult<bool> result;
if (VerifyFunc != null)
{
result = VerifyFunc.Invoke(_textBox.Text);
}
else
{
if (!string.IsNullOrEmpty(_textBox.Text))
{
if (double.TryParse(_textBox.Text, out var value))
{
if (value < Minimum || value > Maximum)
{
result = OperationResult.Failed(Properties.Langs.Lang.OutOfRange);
}
else
{
result = OperationResult.Success();
}
}
else
{
result = OperationResult.Failed(Properties.Langs.Lang.FormatError);
}
}
else if (InfoElement.GetNecessary(this))
{
result = OperationResult.Failed(Properties.Langs.Lang.IsNecessary);
}
else
{
result = OperationResult.Success();
}
}
IsError = !result.Data;
ErrorStr = result.Message;
return result.Data;
}
public Brush CaretBrush
{
get => (Brush) GetValue(CaretBrushProperty);
set => SetValue(CaretBrushProperty, value);
}
}
@@ -10,364 +10,285 @@ using System.Windows.Input;
using System.Windows.Media;
using HandyControl.Data;
using HandyControl.Interactivity;
using HandyControl.Properties.Langs;
namespace HandyControl.Controls
namespace HandyControl.Controls;
[TemplatePart(Name = ElementPasswordBox, Type = typeof(System.Windows.Controls.PasswordBox))]
[TemplatePart(Name = ElementTextBox, Type = typeof(System.Windows.Controls.TextBox))]
public class PasswordBox : Control
{
[TemplatePart(Name = ElementPasswordBox, Type = typeof(System.Windows.Controls.PasswordBox))]
[TemplatePart(Name = ElementTextBox, Type = typeof(System.Windows.Controls.TextBox))]
public class PasswordBox : Control, IDataInput
private const string ElementPasswordBox = "PART_PasswordBox";
private const string ElementTextBox = "PART_TextBox";
private SecureString _password;
private System.Windows.Controls.TextBox _textBox;
/// <summary>
/// 掩码字符
/// </summary>
public static readonly DependencyProperty PasswordCharProperty =
System.Windows.Controls.PasswordBox.PasswordCharProperty.AddOwner(typeof(PasswordBox),
new FrameworkPropertyMetadata('●'));
public char PasswordChar
{
private const string ElementPasswordBox = "PART_PasswordBox";
get => (char) GetValue(PasswordCharProperty);
set => SetValue(PasswordCharProperty, value);
}
private const string ElementTextBox = "PART_TextBox";
public static readonly DependencyProperty ShowEyeButtonProperty = DependencyProperty.Register(
nameof(ShowEyeButton), typeof(bool), typeof(PasswordBox), new PropertyMetadata(ValueBoxes.FalseBox));
private SecureString _password;
public bool ShowEyeButton
{
get => (bool) GetValue(ShowEyeButtonProperty);
set => SetValue(ShowEyeButtonProperty, ValueBoxes.BooleanBox(value));
}
private System.Windows.Controls.TextBox _textBox;
public static readonly DependencyProperty ShowPasswordProperty = DependencyProperty.Register(
nameof(ShowPassword), typeof(bool), typeof(PasswordBox),
new PropertyMetadata(ValueBoxes.FalseBox, OnShowPasswordChanged));
/// <summary>
/// 掩码字符
/// </summary>
public static readonly DependencyProperty PasswordCharProperty =
System.Windows.Controls.PasswordBox.PasswordCharProperty.AddOwner(typeof(PasswordBox),
new FrameworkPropertyMetadata('●'));
public bool ShowPassword
{
get => (bool) GetValue(ShowPasswordProperty);
set => SetValue(ShowPasswordProperty, ValueBoxes.BooleanBox(value));
}
public char PasswordChar
public static readonly DependencyProperty IsSafeEnabledProperty = DependencyProperty.Register(
nameof(IsSafeEnabled), typeof(bool), typeof(PasswordBox), new PropertyMetadata(ValueBoxes.TrueBox, OnIsSafeEnabledChanged));
private static void OnIsSafeEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var p = (PasswordBox) d;
p.SetCurrentValue(UnsafePasswordProperty, !(bool) e.NewValue ? p.Password : string.Empty);
}
public bool IsSafeEnabled
{
get => (bool) GetValue(IsSafeEnabledProperty);
set => SetValue(IsSafeEnabledProperty, ValueBoxes.BooleanBox(value));
}
public static readonly DependencyProperty UnsafePasswordProperty = DependencyProperty.Register(
nameof(UnsafePassword), typeof(string), typeof(PasswordBox), new FrameworkPropertyMetadata(default(string),
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnUnsafePasswordChanged));
private static void OnUnsafePasswordChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var p = (PasswordBox) d;
if (!p.IsSafeEnabled)
{
get => (char) GetValue(PasswordCharProperty);
set => SetValue(PasswordCharProperty, value);
p.Password = e.NewValue != null ? e.NewValue.ToString() : string.Empty;
}
}
/// <summary>
/// 数据是否错误
/// </summary>
public static readonly DependencyProperty IsErrorProperty = DependencyProperty.Register(
"IsError", typeof(bool), typeof(PasswordBox), new PropertyMetadata(ValueBoxes.FalseBox));
public string UnsafePassword
{
get => (string) GetValue(UnsafePasswordProperty);
set => SetValue(UnsafePasswordProperty, value);
}
public bool IsError
{
get => (bool) GetValue(IsErrorProperty);
set => SetValue(IsErrorProperty, ValueBoxes.BooleanBox(value));
}
public static readonly DependencyProperty MaxLengthProperty =
System.Windows.Controls.TextBox.MaxLengthProperty.AddOwner(typeof(PasswordBox));
/// <summary>
/// 错误提示
/// </summary>
public static readonly DependencyProperty ErrorStrProperty = DependencyProperty.Register(
"ErrorStr", typeof(string), typeof(PasswordBox), new PropertyMetadata(default(string)));
public int MaxLength
{
get => (int) GetValue(MaxLengthProperty);
set => SetValue(MaxLengthProperty, value);
}
public string ErrorStr
{
get => (string) GetValue(ErrorStrProperty);
set => SetValue(ErrorStrProperty, value);
}
public static readonly DependencyProperty SelectionBrushProperty =
TextBoxBase.SelectionBrushProperty.AddOwner(typeof(PasswordBox));
/// <summary>
/// 文本类型
/// </summary>
public static readonly DependencyProperty TextTypeProperty = DependencyProperty.Register(
"TextType", typeof(TextType), typeof(PasswordBox), new PropertyMetadata(default(TextType)));
public TextType TextType
{
get => (TextType) GetValue(TextTypeProperty);
set => SetValue(TextTypeProperty, value);
}
/// <summary>
/// 是否显示清除按钮
/// </summary>
public static readonly DependencyProperty ShowClearButtonProperty = DependencyProperty.Register(
"ShowClearButton", typeof(bool), typeof(PasswordBox), new PropertyMetadata(ValueBoxes.FalseBox));
public bool ShowClearButton
{
get => (bool) GetValue(ShowClearButtonProperty);
set => SetValue(ShowClearButtonProperty, ValueBoxes.BooleanBox(value));
}
public static readonly DependencyProperty ShowEyeButtonProperty = DependencyProperty.Register(
"ShowEyeButton", typeof(bool), typeof(PasswordBox), new PropertyMetadata(ValueBoxes.FalseBox));
public bool ShowEyeButton
{
get => (bool) GetValue(ShowEyeButtonProperty);
set => SetValue(ShowEyeButtonProperty, ValueBoxes.BooleanBox(value));
}
public static readonly DependencyProperty ShowPasswordProperty = DependencyProperty.Register(
"ShowPassword", typeof(bool), typeof(PasswordBox),
new PropertyMetadata(ValueBoxes.FalseBox, OnShowPasswordChanged));
public bool ShowPassword
{
get => (bool) GetValue(ShowPasswordProperty);
set => SetValue(ShowPasswordProperty, ValueBoxes.BooleanBox(value));
}
public static readonly DependencyProperty IsSafeEnabledProperty = DependencyProperty.Register(
"IsSafeEnabled", typeof(bool), typeof(PasswordBox), new PropertyMetadata(ValueBoxes.TrueBox, OnIsSafeEnabledChanged));
private static void OnIsSafeEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var p = (PasswordBox) d;
p.SetCurrentValue(UnsafePasswordProperty, !(bool) e.NewValue ? p.Password : string.Empty);
}
public bool IsSafeEnabled
{
get => (bool) GetValue(IsSafeEnabledProperty);
set => SetValue(IsSafeEnabledProperty, ValueBoxes.BooleanBox(value));
}
public static readonly DependencyProperty UnsafePasswordProperty = DependencyProperty.Register(
"UnsafePassword", typeof(string), typeof(PasswordBox), new FrameworkPropertyMetadata(default(string),
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnUnsafePasswordChanged));
private static void OnUnsafePasswordChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var p = (PasswordBox) d;
if (!p.IsSafeEnabled)
{
p.Password = e.NewValue != null ? e.NewValue.ToString() : string.Empty;
}
}
public string UnsafePassword
{
get => (string) GetValue(UnsafePasswordProperty);
set => SetValue(UnsafePasswordProperty, value);
}
public static readonly DependencyProperty MaxLengthProperty =
System.Windows.Controls.TextBox.MaxLengthProperty.AddOwner(typeof(PasswordBox));
public int MaxLength
{
get => (int) GetValue(MaxLengthProperty);
set => SetValue(MaxLengthProperty, value);
}
public static readonly DependencyProperty SelectionBrushProperty =
TextBoxBase.SelectionBrushProperty.AddOwner(typeof(PasswordBox));
public Brush SelectionBrush
{
get => (Brush) GetValue(SelectionBrushProperty);
set => SetValue(SelectionBrushProperty, value);
}
public Brush SelectionBrush
{
get => (Brush) GetValue(SelectionBrushProperty);
set => SetValue(SelectionBrushProperty, value);
}
#if !(NET40 || NET45 || NET451 || NET452 || NET46 || NET461 || NET462 || NET47 || NET471 || NET472)
public static readonly DependencyProperty SelectionTextBrushProperty =
TextBoxBase.SelectionTextBrushProperty.AddOwner(typeof(PasswordBox));
public static readonly DependencyProperty SelectionTextBrushProperty =
TextBoxBase.SelectionTextBrushProperty.AddOwner(typeof(PasswordBox));
public Brush SelectionTextBrush
{
get => (Brush) GetValue(SelectionTextBrushProperty);
set => SetValue(SelectionTextBrushProperty, value);
}
public Brush SelectionTextBrush
{
get => (Brush) GetValue(SelectionTextBrushProperty);
set => SetValue(SelectionTextBrushProperty, value);
}
#endif
public static readonly DependencyProperty SelectionOpacityProperty =
TextBoxBase.SelectionOpacityProperty.AddOwner(typeof(PasswordBox));
public static readonly DependencyProperty SelectionOpacityProperty =
TextBoxBase.SelectionOpacityProperty.AddOwner(typeof(PasswordBox));
public double SelectionOpacity
{
get => (double) GetValue(SelectionOpacityProperty);
set => SetValue(SelectionOpacityProperty, value);
}
public double SelectionOpacity
{
get => (double) GetValue(SelectionOpacityProperty);
set => SetValue(SelectionOpacityProperty, value);
}
public static readonly DependencyProperty CaretBrushProperty =
TextBoxBase.CaretBrushProperty.AddOwner(typeof(PasswordBox));
public static readonly DependencyProperty CaretBrushProperty =
TextBoxBase.CaretBrushProperty.AddOwner(typeof(PasswordBox));
public Brush CaretBrush
{
get => (Brush) GetValue(CaretBrushProperty);
set => SetValue(CaretBrushProperty, value);
}
public Brush CaretBrush
{
get => (Brush) GetValue(CaretBrushProperty);
set => SetValue(CaretBrushProperty, value);
}
#if !NET40
public static readonly DependencyProperty IsSelectionActiveProperty =
TextBoxBase.IsSelectionActiveProperty.AddOwner(typeof(PasswordBox));
public static readonly DependencyProperty IsSelectionActiveProperty =
TextBoxBase.IsSelectionActiveProperty.AddOwner(typeof(PasswordBox));
public bool IsSelectionActive => ActualPasswordBox != null && (bool) ActualPasswordBox.GetValue(IsSelectionActiveProperty);
public bool IsSelectionActive => ActualPasswordBox != null && (bool) ActualPasswordBox.GetValue(IsSelectionActiveProperty);
#endif
public PasswordBox() => CommandBindings.Add(new CommandBinding(ControlCommands.Clear, (s, e) => Clear()));
public PasswordBox() => CommandBindings.Add(new CommandBinding(ControlCommands.Clear, (s, e) => Clear()));
public System.Windows.Controls.PasswordBox ActualPasswordBox { get; set; }
public System.Windows.Controls.PasswordBox ActualPasswordBox { get; set; }
[DefaultValue("")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public string Password
[DefaultValue("")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public string Password
{
get
{
get
{
if (ShowEyeButton && ShowPassword)
{
return _textBox.Text;
}
return ActualPasswordBox?.Password;
}
set
{
if (ActualPasswordBox == null)
{
_password = new SecureString();
value ??= string.Empty;
foreach (var item in value)
_password.AppendChar(item);
return;
}
if (Equals(ActualPasswordBox.Password, value)) return;
ActualPasswordBox.Password = value;
}
}
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public SecureString SecurePassword => ActualPasswordBox?.SecurePassword;
public Func<string, OperationResult<bool>> VerifyFunc { get; set; }
public virtual bool VerifyData()
{
OperationResult<bool> result;
if (VerifyFunc != null)
{
result = VerifyFunc.Invoke(ShowEyeButton && ShowPassword
? _textBox.Text
: ActualPasswordBox.Password);
}
else
{
if (!string.IsNullOrEmpty(ShowEyeButton && ShowPassword
? _textBox.Text
: ActualPasswordBox.Password))
result = OperationResult.Success();
else if (InfoElement.GetNecessary(this))
result = OperationResult.Failed(Lang.IsNecessary);
else
result = OperationResult.Success();
}
IsError = !result.Data;
ErrorStr = result.Message;
return result.Data;
}
private static void OnShowPasswordChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var ctl = (PasswordBox) d;
if (!ctl.ShowEyeButton) return;
if ((bool) e.NewValue)
{
ctl._textBox.Text = ctl.ActualPasswordBox.Password;
ctl._textBox.Select(string.IsNullOrEmpty(ctl._textBox.Text) ? 0 : ctl._textBox.Text.Length, 0);
}
else
{
ctl.ActualPasswordBox.Password = ctl._textBox.Text;
ctl._textBox.Clear();
}
}
public override void OnApplyTemplate()
{
if (ActualPasswordBox != null)
ActualPasswordBox.PasswordChanged -= PasswordBox_PasswordChanged;
if (_textBox != null)
_textBox.TextChanged -= TextBox_TextChanged;
base.OnApplyTemplate();
ActualPasswordBox = GetTemplateChild(ElementPasswordBox) as System.Windows.Controls.PasswordBox;
_textBox = GetTemplateChild(ElementTextBox) as System.Windows.Controls.TextBox;
if (ActualPasswordBox != null)
{
ActualPasswordBox.PasswordChanged += PasswordBox_PasswordChanged;
ActualPasswordBox.SetBinding(System.Windows.Controls.PasswordBox.MaxLengthProperty, new Binding(MaxLengthProperty.Name) { Source = this });
ActualPasswordBox.SetBinding(System.Windows.Controls.PasswordBox.SelectionBrushProperty, new Binding(SelectionBrushProperty.Name) { Source = this });
#if !(NET40 || NET45 || NET451 || NET452 || NET46 || NET461 || NET462 || NET47 || NET471 || NET472)
ActualPasswordBox.SetBinding(System.Windows.Controls.PasswordBox.SelectionTextBrushProperty, new Binding(SelectionTextBrushProperty.Name) { Source = this });
#endif
ActualPasswordBox.SetBinding(System.Windows.Controls.PasswordBox.SelectionOpacityProperty, new Binding(SelectionOpacityProperty.Name) { Source = this });
ActualPasswordBox.SetBinding(System.Windows.Controls.PasswordBox.CaretBrushProperty, new Binding(CaretBrushProperty.Name) { Source = this });
if (_password != null)
{
if (_password.Length > 0)
{
var valuePtr = IntPtr.Zero;
try
{
valuePtr = Marshal.SecureStringToGlobalAllocUnicode(_password);
ActualPasswordBox.Password = Marshal.PtrToStringUni(valuePtr) ?? throw new InvalidOperationException();
}
finally
{
Marshal.ZeroFreeGlobalAllocUnicode(valuePtr);
_password.Clear();
}
}
}
}
if (_textBox != null)
{
_textBox.TextChanged += TextBox_TextChanged;
}
}
public void Paste()
{
ActualPasswordBox.Paste();
if (ShowEyeButton && ShowPassword)
{
return _textBox.Text;
}
return ActualPasswordBox?.Password;
}
set
{
if (ActualPasswordBox == null)
{
_password = new SecureString();
value ??= string.Empty;
foreach (var item in value)
_password.AppendChar(item);
return;
}
if (Equals(ActualPasswordBox.Password, value)) return;
ActualPasswordBox.Password = value;
}
}
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public SecureString SecurePassword => ActualPasswordBox?.SecurePassword;
private static void OnShowPasswordChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var ctl = (PasswordBox) d;
if (!ctl.ShowEyeButton) return;
if ((bool) e.NewValue)
{
ctl._textBox.Text = ctl.ActualPasswordBox.Password;
ctl._textBox.Select(string.IsNullOrEmpty(ctl._textBox.Text) ? 0 : ctl._textBox.Text.Length, 0);
}
else
{
ctl.ActualPasswordBox.Password = ctl._textBox.Text;
ctl._textBox.Clear();
}
}
public override void OnApplyTemplate()
{
if (ActualPasswordBox != null)
ActualPasswordBox.PasswordChanged -= PasswordBox_PasswordChanged;
if (_textBox != null)
_textBox.TextChanged -= TextBox_TextChanged;
base.OnApplyTemplate();
ActualPasswordBox = GetTemplateChild(ElementPasswordBox) as System.Windows.Controls.PasswordBox;
_textBox = GetTemplateChild(ElementTextBox) as System.Windows.Controls.TextBox;
if (ActualPasswordBox != null)
{
ActualPasswordBox.PasswordChanged += PasswordBox_PasswordChanged;
ActualPasswordBox.SetBinding(System.Windows.Controls.PasswordBox.MaxLengthProperty, new Binding(MaxLengthProperty.Name) { Source = this });
ActualPasswordBox.SetBinding(System.Windows.Controls.PasswordBox.SelectionBrushProperty, new Binding(SelectionBrushProperty.Name) { Source = this });
#if !(NET40 || NET45 || NET451 || NET452 || NET46 || NET461 || NET462 || NET47 || NET471 || NET472)
ActualPasswordBox.SetBinding(System.Windows.Controls.PasswordBox.SelectionTextBrushProperty, new Binding(SelectionTextBrushProperty.Name) { Source = this });
#endif
ActualPasswordBox.SetBinding(System.Windows.Controls.PasswordBox.SelectionOpacityProperty, new Binding(SelectionOpacityProperty.Name) { Source = this });
ActualPasswordBox.SetBinding(System.Windows.Controls.PasswordBox.CaretBrushProperty, new Binding(CaretBrushProperty.Name) { Source = this });
if (_password is { Length: > 0 })
{
var valuePtr = IntPtr.Zero;
try
{
valuePtr = Marshal.SecureStringToGlobalAllocUnicode(_password);
ActualPasswordBox.Password = Marshal.PtrToStringUni(valuePtr) ?? throw new InvalidOperationException();
}
finally
{
Marshal.ZeroFreeGlobalAllocUnicode(valuePtr);
_password.Clear();
}
}
}
if (_textBox != null)
{
_textBox.TextChanged += TextBox_TextChanged;
}
}
public void Paste()
{
ActualPasswordBox.Paste();
if (ShowEyeButton && ShowPassword)
{
_textBox.Text = ActualPasswordBox.Password;
}
}
public void SelectAll()
{
ActualPasswordBox.SelectAll();
if (ShowEyeButton && ShowPassword)
{
_textBox.SelectAll();
}
}
public void Clear()
{
ActualPasswordBox.Clear();
_textBox.Clear();
}
private void PasswordBox_PasswordChanged(object sender, RoutedEventArgs e)
{
if (!IsSafeEnabled)
{
SetCurrentValue(UnsafePasswordProperty, ActualPasswordBox.Password);
if (ShowPassword)
{
_textBox.Text = ActualPasswordBox.Password;
}
}
}
public void SelectAll()
private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
{
if (!IsSafeEnabled && ShowPassword)
{
ActualPasswordBox.SelectAll();
if (ShowEyeButton && ShowPassword)
{
_textBox.SelectAll();
}
}
public void Clear()
{
ActualPasswordBox.Clear();
_textBox.Clear();
}
private void PasswordBox_PasswordChanged(object sender, RoutedEventArgs e)
{
if (VerifyData() && !IsSafeEnabled)
{
SetCurrentValue(UnsafePasswordProperty, ActualPasswordBox.Password);
}
}
private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
{
VerifyData();
if (!IsSafeEnabled && ShowPassword)
{
Password = _textBox.Text;
SetCurrentValue(UnsafePasswordProperty, Password);
}
Password = _textBox.Text;
SetCurrentValue(UnsafePasswordProperty, Password);
}
}
}
@@ -13,377 +13,468 @@ using HandyControl.Data;
using HandyControl.Tools;
using HandyControl.Tools.Extension;
namespace HandyControl.Controls
namespace HandyControl.Controls;
[TemplatePart(Name = ElementPanel, Type = typeof(Panel))]
public class PinBox : Control
{
[TemplatePart(Name = ElementPanel, Type = typeof(Panel))]
public class PinBox : Control
private const string ElementPanel = "PART_Panel";
private static readonly object MinLength = 4;
private Panel _panel;
private int _inputIndex;
private bool _changed;
private bool _isInternalAction;
private List<SecureString> _passwordList;
private RoutedEventHandler _passwordBoxsGotFocusEventHandler;
private RoutedEventHandler _passwordBoxsPasswordChangedEventHandler;
public PinBox()
{
private const string ElementPanel = "PART_Panel";
Loaded += PinBox_Loaded;
Unloaded += PinBox_Unloaded;
}
private static readonly object MinLength = 4;
private void PinBox_Unloaded(object sender, RoutedEventArgs e)
{
RemoveHandler(System.Windows.Controls.PasswordBox.PasswordChangedEvent, _passwordBoxsPasswordChangedEventHandler);
RemoveHandler(GotFocusEvent, _passwordBoxsGotFocusEventHandler);
private Panel _panel;
Loaded -= PinBox_Loaded;
Unloaded -= PinBox_Unloaded;
}
private int _inputIndex;
private void PinBox_Loaded(object sender, RoutedEventArgs e)
{
_passwordBoxsPasswordChangedEventHandler = PasswordBoxsPasswordChanged;
AddHandler(System.Windows.Controls.PasswordBox.PasswordChangedEvent, _passwordBoxsPasswordChangedEventHandler);
private bool _changed;
_passwordBoxsGotFocusEventHandler = PasswordBoxsGotFocus;
AddHandler(GotFocusEvent, _passwordBoxsGotFocusEventHandler);
private bool _isInternalAction;
FocusPasswordBox();
}
private List<SecureString> _passwordList;
private RoutedEventHandler _passwordBoxsGotFocusEventHandler;
private RoutedEventHandler _passwordBoxsPasswordChangedEventHandler;
public PinBox()
private void FocusPasswordBox()
{
if (!IsFocused)
{
Loaded += PinBox_Loaded;
Unloaded += PinBox_Unloaded;
return;
}
private void PinBox_Unloaded(object sender, RoutedEventArgs e)
if (_panel.Children.Count == 0)
{
RemoveHandler(System.Windows.Controls.PasswordBox.PasswordChangedEvent, _passwordBoxsPasswordChangedEventHandler);
RemoveHandler(GotFocusEvent, _passwordBoxsGotFocusEventHandler);
Loaded -= PinBox_Loaded;
Unloaded -= PinBox_Unloaded;
return;
}
private void PinBox_Loaded(object sender, RoutedEventArgs e)
if (_panel.Children.OfType<System.Windows.Controls.PasswordBox>().Any(box => box.IsFocused))
{
_passwordBoxsPasswordChangedEventHandler = PasswordBoxsPasswordChanged;
AddHandler(System.Windows.Controls.PasswordBox.PasswordChangedEvent, _passwordBoxsPasswordChangedEventHandler);
_passwordBoxsGotFocusEventHandler = PasswordBoxsGotFocus;
AddHandler(GotFocusEvent, _passwordBoxsGotFocusEventHandler);
return;
}
private void PasswordBoxsPasswordChanged(object sender, RoutedEventArgs e)
{
if (_isInternalAction) return;
FocusManager.SetFocusedElement(this, _panel.Children[0]);
}
if (e.OriginalSource is System.Windows.Controls.PasswordBox passwordBox)
private void PasswordBoxsPasswordChanged(object sender, RoutedEventArgs e)
{
if (_isInternalAction) return;
if (e.OriginalSource is System.Windows.Controls.PasswordBox passwordBox)
{
if (!IsSafeEnabled)
{
if (passwordBox.Password.Length > 0)
{
if (++_inputIndex >= Length)
{
_inputIndex = Length - 1;
if (_panel.Children.OfType<System.Windows.Controls.PasswordBox>()
.All(item => item.Password.Any()))
{
FocusManager.SetFocusedElement(this, null);
Keyboard.ClearFocus();
RaiseEvent(new RoutedEventArgs(CompletedEvent, this));
}
return;
}
}
else
{
if (--_inputIndex < 0)
{
_inputIndex = 0;
return;
}
}
_changed = true;
FocusManager.SetFocusedElement(this, _panel.Children[_inputIndex]);
SetCurrentValue(UnsafePasswordProperty, Password);
}
}
private void PasswordBoxsGotFocus(object sender, RoutedEventArgs e)
{
if (e.OriginalSource is System.Windows.Controls.PasswordBox passwordBox)
{
_inputIndex = _panel.Children.IndexOf(passwordBox);
passwordBox.SelectAll();
}
}
protected override void OnPreviewKeyDown(KeyEventArgs e)
{
base.OnPreviewKeyUp(e);
if (e.Key == Key.Left)
{
if (--_inputIndex < 0)
{
_inputIndex = 0;
return;
}
var passwordBox = _panel.Children[_inputIndex] as System.Windows.Controls.PasswordBox;
passwordBox?.SelectAll();
FocusManager.SetFocusedElement(this, passwordBox);
}
else if (e.Key == Key.Right)
if (passwordBox.Password.Length > 0)
{
if (++_inputIndex >= Length)
{
_inputIndex = Length - 1;
if (_panel.Children.OfType<System.Windows.Controls.PasswordBox>()
.All(item => item.Password.Any()))
{
FocusManager.SetFocusedElement(this, null);
Keyboard.ClearFocus();
RaiseEvent(new RoutedEventArgs(CompletedEvent, this));
}
return;
}
var passwordBox = _panel.Children[_inputIndex] as System.Windows.Controls.PasswordBox;
passwordBox?.SelectAll();
FocusManager.SetFocusedElement(this, passwordBox);
}
}
protected override void OnPreviewKeyUp(KeyEventArgs e)
{
base.OnPreviewKeyUp(e);
if (_changed)
else
{
_changed = false;
return;
}
if (e.Key == Key.Delete || e.Key == Key.Back)
{
if (_panel.Children[_inputIndex] is System.Windows.Controls.PasswordBox passwordBox)
{
_isInternalAction = true;
passwordBox.Clear();
_isInternalAction = false;
}
if (--_inputIndex < 0)
{
_inputIndex = 0;
return;
}
FocusManager.SetFocusedElement(this, _panel.Children[_inputIndex]);
}
_changed = true;
FocusManager.SetFocusedElement(this, _panel.Children[_inputIndex]);
}
}
private void PasswordBoxsGotFocus(object sender, RoutedEventArgs e)
{
if (e.OriginalSource is System.Windows.Controls.PasswordBox passwordBox)
{
_inputIndex = _panel.Children.IndexOf(passwordBox);
passwordBox.SelectAll();
}
}
protected override void OnPreviewKeyDown(KeyEventArgs e)
{
base.OnPreviewKeyUp(e);
if (e.Key == Key.Left)
{
if (--_inputIndex < 0)
{
_inputIndex = 0;
return;
}
var passwordBox = _panel.Children[_inputIndex] as System.Windows.Controls.PasswordBox;
passwordBox?.SelectAll();
FocusManager.SetFocusedElement(this, passwordBox);
}
else if (e.Key == Key.Right)
{
if (++_inputIndex >= Length)
{
_inputIndex = Length - 1;
return;
}
var passwordBox = _panel.Children[_inputIndex] as System.Windows.Controls.PasswordBox;
passwordBox?.SelectAll();
FocusManager.SetFocusedElement(this, passwordBox);
}
}
protected override void OnPreviewKeyUp(KeyEventArgs e)
{
base.OnPreviewKeyUp(e);
if (_changed)
{
_changed = false;
return;
}
public string Password
if (e.Key == Key.Delete || e.Key == Key.Back)
{
get
if (_panel.Children[_inputIndex] is System.Windows.Controls.PasswordBox passwordBox)
{
return string.Join("", _panel.Children.OfType<System.Windows.Controls.PasswordBox>().Select(item => item.Password));
_isInternalAction = true;
passwordBox.Clear();
_isInternalAction = false;
}
set
if (--_inputIndex < 0)
{
if (_panel == null)
_inputIndex = 0;
return;
}
FocusManager.SetFocusedElement(this, _panel.Children[_inputIndex]);
}
}
public string Password
{
get
{
return _panel == null
? string.Empty
: string.Join(string.Empty, _panel.Children.OfType<System.Windows.Controls.PasswordBox>().Select(item => item.Password));
}
set
{
if (_panel == null)
{
_passwordList = new List<SecureString>();
value ??= string.Empty;
foreach (var item in value)
{
_passwordList = new List<SecureString>();
value ??= string.Empty;
foreach (var item in value)
{
var secureString = new SecureString();
secureString.AppendChar(item);
_passwordList.Add(secureString);
}
return;
var secureString = new SecureString();
secureString.AppendChar(item);
_passwordList.Add(secureString);
}
var length = Length;
if (string.IsNullOrEmpty(value) || value.Length != length || value.Length != _panel.Children.Count)
{
_panel.Children.OfType<System.Windows.Controls.PasswordBox>().Do(item => item.Clear());
}
else
{
_panel.Children.OfType<System.Windows.Controls.PasswordBox>().DoWithIndex((item, index) => item.Password = value[index].ToString());
}
return;
}
}
/// <summary>
/// 掩码字符
/// </summary>
public static readonly DependencyProperty PasswordCharProperty =
System.Windows.Controls.PasswordBox.PasswordCharProperty.AddOwner(typeof(PinBox),
new FrameworkPropertyMetadata('●'));
_isInternalAction = true;
public char PasswordChar
{
get => (char) GetValue(PasswordCharProperty);
set => SetValue(PasswordCharProperty, value);
}
public static readonly DependencyProperty LengthProperty = DependencyProperty.Register(
"Length", typeof(int), typeof(PinBox), new PropertyMetadata(MinLength, OnLengthChanged, CoerceLength), ValidateHelper.IsInRangeOfPosInt);
private static void OnLengthChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var ctl = (PinBox) d;
ctl.UpdateItems();
}
private static object CoerceLength(DependencyObject d, object basevalue) => (int) basevalue < 4 ? MinLength : basevalue;
public int Length
{
get => (int) GetValue(LengthProperty);
set => SetValue(LengthProperty, value);
}
public static readonly DependencyProperty ItemMarginProperty = DependencyProperty.Register(
"ItemMargin", typeof(Thickness), typeof(PinBox), new PropertyMetadata(default(Thickness)));
public Thickness ItemMargin
{
get => (Thickness) GetValue(ItemMarginProperty);
set => SetValue(ItemMarginProperty, value);
}
public static readonly DependencyProperty ItemWidthProperty = DependencyProperty.Register(
"ItemWidth", typeof(double), typeof(PinBox), new PropertyMetadata(ValueBoxes.Double0Box));
public double ItemWidth
{
get => (double) GetValue(ItemWidthProperty);
set => SetValue(ItemWidthProperty, value);
}
public static readonly DependencyProperty ItemHeightProperty = DependencyProperty.Register(
"ItemHeight", typeof(double), typeof(PinBox), new PropertyMetadata(ValueBoxes.Double0Box));
public double ItemHeight
{
get => (double) GetValue(ItemHeightProperty);
set => SetValue(ItemHeightProperty, value);
}
public static readonly DependencyProperty SelectionBrushProperty =
TextBoxBase.SelectionBrushProperty.AddOwner(typeof(PinBox));
public Brush SelectionBrush
{
get => (Brush) GetValue(SelectionBrushProperty);
set => SetValue(SelectionBrushProperty, value);
}
#if !(NET40 || NET45 || NET451 || NET452 || NET46 || NET461 || NET462 || NET47 || NET471 || NET472)
public static readonly DependencyProperty SelectionTextBrushProperty =
TextBoxBase.SelectionTextBrushProperty.AddOwner(typeof(PinBox));
public Brush SelectionTextBrush
{
get => (Brush) GetValue(SelectionTextBrushProperty);
set => SetValue(SelectionTextBrushProperty, value);
}
#endif
public static readonly DependencyProperty SelectionOpacityProperty =
TextBoxBase.SelectionOpacityProperty.AddOwner(typeof(PinBox));
public double SelectionOpacity
{
get => (double) GetValue(SelectionOpacityProperty);
set => SetValue(SelectionOpacityProperty, value);
}
public static readonly DependencyProperty CaretBrushProperty =
TextBoxBase.CaretBrushProperty.AddOwner(typeof(PinBox));
public Brush CaretBrush
{
get => (Brush) GetValue(CaretBrushProperty);
set => SetValue(CaretBrushProperty, value);
}
public static readonly RoutedEvent CompletedEvent =
EventManager.RegisterRoutedEvent("Completed", RoutingStrategy.Bubble,
typeof(RoutedEventHandler), typeof(PinBox));
public event RoutedEventHandler Completed
{
add => AddHandler(CompletedEvent, value);
remove => RemoveHandler(CompletedEvent, value);
}
private void UpdateItems()
{
if (_panel == null) return;
_panel.Children.Clear();
var length = Length;
for (var i = 0; i < length; i++)
if (string.IsNullOrEmpty(value))
{
_panel.Children.Add(CreatePasswordBox());
_panel.Children.OfType<System.Windows.Controls.PasswordBox>().Do(item => item.Clear());
}
}
private System.Windows.Controls.PasswordBox CreatePasswordBox()
{
var passwordBox = new System.Windows.Controls.PasswordBox
else
{
MaxLength = 1,
HorizontalContentAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center,
Margin = ItemMargin,
Width = ItemWidth,
Height = ItemHeight,
Padding = default,
PasswordChar = PasswordChar,
Foreground = Foreground
};
_panel.Children
.OfType<System.Windows.Controls.PasswordBox>()
.Take(Math.Min(Length, value.Length))
.DoWithIndex((item, index) => item.Password = value[index].ToString());
passwordBox.SetBinding(SelectionBrushProperty, new Binding(SelectionBrushProperty.Name) { Source = this });
#if !(NET40 || NET45 || NET451 || NET452 || NET46 || NET461 || NET462 || NET47 || NET471 || NET472)
passwordBox.SetBinding(SelectionTextBrushProperty, new Binding(SelectionTextBrushProperty.Name) { Source = this });
#endif
passwordBox.SetBinding(SelectionOpacityProperty, new Binding(SelectionOpacityProperty.Name) { Source = this });
passwordBox.SetBinding(CaretBrushProperty, new Binding(CaretBrushProperty.Name) { Source = this });
_panel.Children
.OfType<System.Windows.Controls.PasswordBox>()
.Skip(value.Length)
.Take(Length - value.Length)
.Do(item => item.Clear());
}
return passwordBox;
}
_isInternalAction = false;
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
_panel = GetTemplateChild(ElementPanel) as Panel;
if (_panel != null)
if (!IsSafeEnabled)
{
UpdateItems();
var length = Length;
if (_passwordList != null && _passwordList.Count == length && _panel.Children.Count == length)
{
for (var i = 0; i < length; i++)
{
var password = _passwordList[i];
if (password.Length > 0)
{
var valuePtr = IntPtr.Zero;
try
{
valuePtr = Marshal.SecureStringToGlobalAllocUnicode(password);
if (_panel.Children[i] is System.Windows.Controls.PasswordBox passwordBox)
{
passwordBox.Password = Marshal.PtrToStringUni(valuePtr) ?? throw new InvalidOperationException();
}
}
finally
{
Marshal.ZeroFreeGlobalAllocUnicode(valuePtr);
password.Clear();
}
}
}
_passwordList.Clear();
}
SetCurrentValue(UnsafePasswordProperty, Password);
}
}
}
/// <summary>
/// 掩码字符
/// </summary>
public static readonly DependencyProperty PasswordCharProperty =
System.Windows.Controls.PasswordBox.PasswordCharProperty.AddOwner(typeof(PinBox),
new FrameworkPropertyMetadata('●'));
public char PasswordChar
{
get => (char) GetValue(PasswordCharProperty);
set => SetValue(PasswordCharProperty, value);
}
public static readonly DependencyProperty LengthProperty = DependencyProperty.Register(
nameof(Length), typeof(int), typeof(PinBox), new PropertyMetadata(MinLength, OnLengthChanged, CoerceLength), ValidateHelper.IsInRangeOfPosInt);
private static void OnLengthChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var ctl = (PinBox) d;
ctl.UpdateItems();
}
private static object CoerceLength(DependencyObject d, object basevalue) => (int) basevalue < 4 ? MinLength : basevalue;
public int Length
{
get => (int) GetValue(LengthProperty);
set => SetValue(LengthProperty, value);
}
public static readonly DependencyProperty ItemMarginProperty = DependencyProperty.Register(
nameof(ItemMargin), typeof(Thickness), typeof(PinBox), new PropertyMetadata(default(Thickness)));
public Thickness ItemMargin
{
get => (Thickness) GetValue(ItemMarginProperty);
set => SetValue(ItemMarginProperty, value);
}
public static readonly DependencyProperty ItemWidthProperty = DependencyProperty.Register(
nameof(ItemWidth), typeof(double), typeof(PinBox), new PropertyMetadata(ValueBoxes.Double0Box));
public double ItemWidth
{
get => (double) GetValue(ItemWidthProperty);
set => SetValue(ItemWidthProperty, value);
}
public static readonly DependencyProperty ItemHeightProperty = DependencyProperty.Register(
nameof(ItemHeight), typeof(double), typeof(PinBox), new PropertyMetadata(ValueBoxes.Double0Box));
public double ItemHeight
{
get => (double) GetValue(ItemHeightProperty);
set => SetValue(ItemHeightProperty, value);
}
public static readonly DependencyProperty SelectionBrushProperty =
TextBoxBase.SelectionBrushProperty.AddOwner(typeof(PinBox));
public Brush SelectionBrush
{
get => (Brush) GetValue(SelectionBrushProperty);
set => SetValue(SelectionBrushProperty, value);
}
#if !(NET40 || NET45 || NET451 || NET452 || NET46 || NET461 || NET462 || NET47 || NET471 || NET472)
public static readonly DependencyProperty SelectionTextBrushProperty =
TextBoxBase.SelectionTextBrushProperty.AddOwner(typeof(PinBox));
public Brush SelectionTextBrush
{
get => (Brush) GetValue(SelectionTextBrushProperty);
set => SetValue(SelectionTextBrushProperty, value);
}
#endif
public static readonly DependencyProperty SelectionOpacityProperty =
TextBoxBase.SelectionOpacityProperty.AddOwner(typeof(PinBox));
public double SelectionOpacity
{
get => (double) GetValue(SelectionOpacityProperty);
set => SetValue(SelectionOpacityProperty, value);
}
public static readonly DependencyProperty CaretBrushProperty =
TextBoxBase.CaretBrushProperty.AddOwner(typeof(PinBox));
public Brush CaretBrush
{
get => (Brush) GetValue(CaretBrushProperty);
set => SetValue(CaretBrushProperty, value);
}
public static readonly DependencyProperty IsSafeEnabledProperty = PasswordBox.IsSafeEnabledProperty
.AddOwner(typeof(PinBox), new FrameworkPropertyMetadata(ValueBoxes.TrueBox, OnIsSafeEnabledChanged));
private static void OnIsSafeEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var p = (PinBox) d;
p.OnIsSafeEnabledChanged((bool) e.NewValue);
}
private void OnIsSafeEnabledChanged(bool newValue)
{
if (_panel == null)
{
return;
}
SetCurrentValue(UnsafePasswordProperty, !newValue ? Password : string.Empty);
}
public bool IsSafeEnabled
{
get => (bool) GetValue(IsSafeEnabledProperty);
set => SetValue(IsSafeEnabledProperty, ValueBoxes.BooleanBox(value));
}
public static readonly DependencyProperty UnsafePasswordProperty = PasswordBox.UnsafePasswordProperty
.AddOwner(typeof(PinBox), new FrameworkPropertyMetadata(default(string),
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnUnsafePasswordChanged));
private static void OnUnsafePasswordChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var p = (PinBox) d;
if (!p.IsSafeEnabled)
{
p.Password = e.NewValue != null ? e.NewValue.ToString() : string.Empty;
}
}
public string UnsafePassword
{
get => (string) GetValue(UnsafePasswordProperty);
set => SetValue(UnsafePasswordProperty, value);
}
public static readonly RoutedEvent CompletedEvent =
EventManager.RegisterRoutedEvent("Completed", RoutingStrategy.Bubble,
typeof(RoutedEventHandler), typeof(PinBox));
public event RoutedEventHandler Completed
{
add => AddHandler(CompletedEvent, value);
remove => RemoveHandler(CompletedEvent, value);
}
private void UpdateItems()
{
if (_panel == null) return;
_panel.Children.Clear();
var length = Length;
for (var i = 0; i < length; i++)
{
_panel.Children.Add(CreatePasswordBox());
}
}
private System.Windows.Controls.PasswordBox CreatePasswordBox()
{
var passwordBox = new System.Windows.Controls.PasswordBox
{
MaxLength = 1,
HorizontalContentAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center,
Margin = ItemMargin,
Width = ItemWidth,
Height = ItemHeight,
Padding = default,
PasswordChar = PasswordChar,
Foreground = Foreground
};
passwordBox.SetBinding(SelectionBrushProperty, new Binding(SelectionBrushProperty.Name) { Source = this });
#if !(NET40 || NET45 || NET451 || NET452 || NET46 || NET461 || NET462 || NET47 || NET471 || NET472)
passwordBox.SetBinding(SelectionTextBrushProperty, new Binding(SelectionTextBrushProperty.Name) { Source = this });
#endif
passwordBox.SetBinding(SelectionOpacityProperty, new Binding(SelectionOpacityProperty.Name) { Source = this });
passwordBox.SetBinding(CaretBrushProperty, new Binding(CaretBrushProperty.Name) { Source = this });
return passwordBox;
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
_panel = GetTemplateChild(ElementPanel) as Panel;
if (_panel != null)
{
UpdateItems();
var length = Length;
if (_passwordList != null && _passwordList.Count == length && _panel.Children.Count == length)
{
for (var i = 0; i < length; i++)
{
var password = _passwordList[i];
if (password.Length > 0)
{
var valuePtr = IntPtr.Zero;
try
{
valuePtr = Marshal.SecureStringToGlobalAllocUnicode(password);
if (_panel.Children[i] is System.Windows.Controls.PasswordBox passwordBox)
{
passwordBox.Password = Marshal.PtrToStringUni(valuePtr) ?? throw new InvalidOperationException();
}
}
finally
{
Marshal.ZeroFreeGlobalAllocUnicode(valuePtr);
password.Clear();
}
}
}
_passwordList.Clear();
}
OnIsSafeEnabledChanged(IsSafeEnabled);
}
}
}
@@ -5,128 +5,126 @@ using System.Windows.Input;
using HandyControl.Data;
using HandyControl.Interactivity;
namespace HandyControl.Controls
namespace HandyControl.Controls;
public class SearchBar : TextBox, ICommandSource
{
public class SearchBar : TextBox, ICommandSource
public SearchBar()
{
public SearchBar()
CommandBindings.Add(new CommandBinding(ControlCommands.Search, (s, e) => OnSearchStarted()));
}
public static readonly RoutedEvent SearchStartedEvent =
EventManager.RegisterRoutedEvent("SearchStarted", RoutingStrategy.Bubble,
typeof(EventHandler<FunctionEventArgs<string>>), typeof(SearchBar));
public event EventHandler<FunctionEventArgs<string>> SearchStarted
{
add => AddHandler(SearchStartedEvent, value);
remove => RemoveHandler(SearchStartedEvent, value);
}
protected override void OnKeyDown(KeyEventArgs e)
{
base.OnKeyDown(e);
if (e.Key == Key.Enter)
{
CommandBindings.Add(new CommandBinding(ControlCommands.Search, (s, e) => OnSearchStarted()));
}
public static readonly RoutedEvent SearchStartedEvent =
EventManager.RegisterRoutedEvent("SearchStarted", RoutingStrategy.Bubble,
typeof(EventHandler<FunctionEventArgs<string>>), typeof(SearchBar));
public event EventHandler<FunctionEventArgs<string>> SearchStarted
{
add => AddHandler(SearchStartedEvent, value);
remove => RemoveHandler(SearchStartedEvent, value);
}
protected override void OnKeyDown(KeyEventArgs e)
{
base.OnKeyDown(e);
if (e.Key == Key.Enter)
{
OnSearchStarted();
}
}
protected override void OnTextChanged(TextChangedEventArgs e)
{
base.OnTextChanged(e);
if (IsRealTime)
{
OnSearchStarted();
}
VerifyData();
}
private void OnSearchStarted()
{
RaiseEvent(new FunctionEventArgs<string>(SearchStartedEvent, this)
{
Info = Text
});
switch (Command)
{
case null:
return;
case RoutedCommand command:
command.Execute(CommandParameter, CommandTarget);
break;
default:
Command.Execute(CommandParameter);
break;
}
}
/// <summary>
/// 是否实时搜索
/// </summary>
public static readonly DependencyProperty IsRealTimeProperty = DependencyProperty.Register(
"IsRealTime", typeof(bool), typeof(SearchBar), new PropertyMetadata(ValueBoxes.FalseBox));
/// <summary>
/// 是否实时搜索
/// </summary>
public bool IsRealTime
{
get => (bool) GetValue(IsRealTimeProperty);
set => SetValue(IsRealTimeProperty, ValueBoxes.BooleanBox(value));
}
public static readonly DependencyProperty CommandProperty = DependencyProperty.Register(
"Command", typeof(ICommand), typeof(SearchBar), new PropertyMetadata(default(ICommand), OnCommandChanged));
private static void OnCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var ctl = (SearchBar) d;
if (e.OldValue is ICommand oldCommand)
{
oldCommand.CanExecuteChanged -= ctl.CanExecuteChanged;
}
if (e.NewValue is ICommand newCommand)
{
newCommand.CanExecuteChanged += ctl.CanExecuteChanged;
}
}
public ICommand Command
{
get => (ICommand) GetValue(CommandProperty);
set => SetValue(CommandProperty, value);
}
public static readonly DependencyProperty CommandParameterProperty = DependencyProperty.Register(
"CommandParameter", typeof(object), typeof(SearchBar), new PropertyMetadata(default(object)));
public object CommandParameter
{
get => GetValue(CommandParameterProperty);
set => SetValue(CommandParameterProperty, value);
}
public static readonly DependencyProperty CommandTargetProperty = DependencyProperty.Register(
"CommandTarget", typeof(IInputElement), typeof(SearchBar), new PropertyMetadata(default(IInputElement)));
public IInputElement CommandTarget
{
get => (IInputElement) GetValue(CommandTargetProperty);
set => SetValue(CommandTargetProperty, value);
}
private void CanExecuteChanged(object sender, EventArgs e)
{
if (Command == null) return;
IsEnabled = Command is RoutedCommand command
? command.CanExecute(CommandParameter, CommandTarget)
: Command.CanExecute(CommandParameter);
OnSearchStarted();
}
}
protected override void OnTextChanged(TextChangedEventArgs e)
{
base.OnTextChanged(e);
if (IsRealTime)
{
OnSearchStarted();
}
}
private void OnSearchStarted()
{
RaiseEvent(new FunctionEventArgs<string>(SearchStartedEvent, this)
{
Info = Text
});
switch (Command)
{
case null:
return;
case RoutedCommand command:
command.Execute(CommandParameter, CommandTarget);
break;
default:
Command.Execute(CommandParameter);
break;
}
}
/// <summary>
/// 是否实时搜索
/// </summary>
public static readonly DependencyProperty IsRealTimeProperty = DependencyProperty.Register(
nameof(IsRealTime), typeof(bool), typeof(SearchBar), new PropertyMetadata(ValueBoxes.FalseBox));
/// <summary>
/// 是否实时搜索
/// </summary>
public bool IsRealTime
{
get => (bool) GetValue(IsRealTimeProperty);
set => SetValue(IsRealTimeProperty, ValueBoxes.BooleanBox(value));
}
public static readonly DependencyProperty CommandProperty = DependencyProperty.Register(
nameof(Command), typeof(ICommand), typeof(SearchBar), new PropertyMetadata(default(ICommand), OnCommandChanged));
private static void OnCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var ctl = (SearchBar) d;
if (e.OldValue is ICommand oldCommand)
{
oldCommand.CanExecuteChanged -= ctl.CanExecuteChanged;
}
if (e.NewValue is ICommand newCommand)
{
newCommand.CanExecuteChanged += ctl.CanExecuteChanged;
}
}
public ICommand Command
{
get => (ICommand) GetValue(CommandProperty);
set => SetValue(CommandProperty, value);
}
public static readonly DependencyProperty CommandParameterProperty = DependencyProperty.Register(
nameof(CommandParameter), typeof(object), typeof(SearchBar), new PropertyMetadata(default(object)));
public object CommandParameter
{
get => GetValue(CommandParameterProperty);
set => SetValue(CommandParameterProperty, value);
}
public static readonly DependencyProperty CommandTargetProperty = DependencyProperty.Register(
nameof(CommandTarget), typeof(IInputElement), typeof(SearchBar), new PropertyMetadata(default(IInputElement)));
public IInputElement CommandTarget
{
get => (IInputElement) GetValue(CommandTargetProperty);
set => SetValue(CommandTargetProperty, value);
}
private void CanExecuteChanged(object sender, EventArgs e)
{
if (Command == null) return;
IsEnabled = Command is RoutedCommand command
? command.CanExecute(CommandParameter, CommandTarget)
: Command.CanExecute(CommandParameter);
}
}
@@ -1,120 +1,20 @@
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using HandyControl.Data;
using System.Windows.Input;
using HandyControl.Interactivity;
using HandyControl.Tools;
namespace HandyControl.Controls
namespace HandyControl.Controls;
public class TextBox : System.Windows.Controls.TextBox
{
public class TextBox : System.Windows.Controls.TextBox, IDataInput
public TextBox()
{
public TextBox()
CommandBindings.Add(new CommandBinding(ControlCommands.Clear, (s, e) =>
{
CommandBindings.Add(new CommandBinding(ControlCommands.Clear, (s, e) =>
if (IsReadOnly)
{
SetCurrentValue(TextProperty, "");
}));
}
protected override void OnTextChanged(TextChangedEventArgs e)
{
base.OnTextChanged(e);
VerifyData();
}
public Func<string, OperationResult<bool>> VerifyFunc { get; set; }
/// <summary>
/// 数据是否错误
/// </summary>
public static readonly DependencyProperty IsErrorProperty = DependencyProperty.Register(
"IsError", typeof(bool), typeof(TextBox), new PropertyMetadata(ValueBoxes.FalseBox));
public bool IsError
{
get => (bool) GetValue(IsErrorProperty);
set => SetValue(IsErrorProperty, ValueBoxes.BooleanBox(value));
}
/// <summary>
/// 错误提示
/// </summary>
public static readonly DependencyProperty ErrorStrProperty = DependencyProperty.Register(
"ErrorStr", typeof(string), typeof(TextBox), new PropertyMetadata(default(string)));
public string ErrorStr
{
get => (string) GetValue(ErrorStrProperty);
set => SetValue(ErrorStrProperty, value);
}
/// <summary>
/// 文本类型
/// </summary>
public static readonly DependencyProperty TextTypeProperty = DependencyProperty.Register(
"TextType", typeof(TextType), typeof(TextBox), new PropertyMetadata(default(TextType)));
public TextType TextType
{
get => (TextType) GetValue(TextTypeProperty);
set => SetValue(TextTypeProperty, value);
}
/// <summary>
/// 是否显示清除按钮
/// </summary>
public static readonly DependencyProperty ShowClearButtonProperty = DependencyProperty.Register(
"ShowClearButton", typeof(bool), typeof(TextBox), new PropertyMetadata(ValueBoxes.FalseBox));
public bool ShowClearButton
{
get => (bool) GetValue(ShowClearButtonProperty);
set => SetValue(ShowClearButtonProperty, ValueBoxes.BooleanBox(value));
}
public virtual bool VerifyData()
{
OperationResult<bool> result;
if (VerifyFunc != null)
{
result = VerifyFunc.Invoke(Text);
}
else
{
if (!string.IsNullOrEmpty(Text))
{
if (TextType != TextType.Common)
{
var regexPattern = InfoElement.GetRegexPattern(this);
result = string.IsNullOrEmpty(regexPattern)
? Text.IsKindOf(TextType)
? OperationResult.Success()
: OperationResult.Failed(Properties.Langs.Lang.FormatError)
: Text.IsKindOf(regexPattern)
? OperationResult.Success()
: OperationResult.Failed(Properties.Langs.Lang.FormatError);
}
else
{
result = OperationResult.Success();
}
}
else if (InfoElement.GetNecessary(this))
{
result = OperationResult.Failed(Properties.Langs.Lang.IsNecessary);
}
else
{
result = OperationResult.Success();
}
return;
}
IsError = !result.Data;
ErrorStr = result.Message;
return result.Data;
}
SetCurrentValue(TextProperty, string.Empty);
}));
}
}
File diff suppressed because it is too large. Load diff
@@ -1,35 +1,34 @@
using System.Windows;
namespace HandyControl.Controls
namespace HandyControl.Controls;
public class WatermarkTextBox : TextBox
{
public class WatermarkTextBox : TextBox
#region Public Properties
#region Watermark
public static readonly DependencyProperty WatermarkProperty = DependencyProperty.Register(
nameof(Watermark), typeof(object), typeof(WatermarkTextBox), new PropertyMetadata(default(object)));
public object Watermark
{
#region Public Properties
get => GetValue(WatermarkProperty);
set => SetValue(WatermarkProperty, value);
}
#region Watermark
#endregion Watermark
public static readonly DependencyProperty WatermarkProperty = DependencyProperty.Register(
"Watermark", typeof(object), typeof(WatermarkTextBox), new PropertyMetadata(default(object)));
#endregion Public Properties
public object Watermark
protected override void OnGotFocus(RoutedEventArgs e)
{
base.OnGotFocus(e);
if (IsEnabled)
{
get => GetValue(WatermarkProperty);
set => SetValue(WatermarkProperty, value);
}
#endregion Watermark
#endregion Public Properties
protected override void OnGotFocus(RoutedEventArgs e)
{
base.OnGotFocus(e);
if (IsEnabled)
if (!string.IsNullOrEmpty(Text))
{
if (!string.IsNullOrEmpty(Text))
{
Select(0, Text.Length);
}
Select(0, Text.Length);
}
}
}
@@ -1,64 +0,0 @@
using System.Windows;
using System.Windows.Controls;
namespace HandyControl.Controls
{
internal class InternalCanvas : Canvas
{
internal static readonly DependencyProperty OrientationProperty = DependencyProperty.Register(
"Orientation", typeof(Orientation), typeof(InternalCanvas), new PropertyMetadata(default(Orientation)));
internal Orientation Orientation
{
get => (Orientation) GetValue(OrientationProperty);
set => SetValue(OrientationProperty, value);
}
protected override Size ArrangeOverride(Size arrangeSize)
{
foreach (UIElement internalChild in InternalChildren)
{
if (internalChild == null) continue;
var x = 0.0;
var y = 0.0;
if (Orientation == Orientation.Horizontal)
{
x = (arrangeSize.Width - internalChild.DesiredSize.Width) / 2;
var top = GetTop(internalChild);
if (!double.IsNaN(top))
{
y = top;
}
else
{
var bottom = GetBottom(internalChild);
if (!double.IsNaN(bottom))
y = arrangeSize.Height - internalChild.DesiredSize.Height - bottom;
}
}
else
{
y = (arrangeSize.Height - internalChild.DesiredSize.Height) / 2;
var left = GetLeft(internalChild);
if (!double.IsNaN(left))
{
x = left;
}
else
{
var right = GetRight(internalChild);
if (!double.IsNaN(right))
x = arrangeSize.Width - internalChild.DesiredSize.Width - right;
}
}
internalChild.Arrange(new Rect(new Point(x, y), internalChild.DesiredSize));
}
return arrangeSize;
}
}
}
@@ -7,130 +7,129 @@ using System.Windows.Shapes;
using HandyControl.Data;
namespace HandyControl.Controls
namespace HandyControl.Controls;
public abstract class LoadingBase : ContentControl
{
public abstract class LoadingBase : ContentControl
{
protected Storyboard Storyboard;
protected Storyboard Storyboard;
public static readonly DependencyProperty IsRunningProperty = DependencyProperty.Register(
"IsRunning", typeof(bool), typeof(LoadingBase), new PropertyMetadata(ValueBoxes.TrueBox, (o, args) =>
public static readonly DependencyProperty IsRunningProperty = DependencyProperty.Register(
nameof(IsRunning), typeof(bool), typeof(LoadingBase), new PropertyMetadata(ValueBoxes.TrueBox, (o, args) =>
{
var ctl = (LoadingBase) o;
var v = (bool) args.NewValue;
if (v)
{
var ctl = (LoadingBase) o;
var v = (bool) args.NewValue;
if (v)
{
ctl.Storyboard?.Resume();
}
else
{
ctl.Storyboard?.Pause();
}
}));
ctl.Storyboard?.Resume();
}
else
{
ctl.Storyboard?.Pause();
}
}));
public bool IsRunning
{
get => (bool) GetValue(IsRunningProperty);
set => SetValue(IsRunningProperty, ValueBoxes.BooleanBox(value));
}
public bool IsRunning
{
get => (bool) GetValue(IsRunningProperty);
set => SetValue(IsRunningProperty, ValueBoxes.BooleanBox(value));
}
public static readonly DependencyProperty DotCountProperty = DependencyProperty.Register(
"DotCount", typeof(int), typeof(LoadingBase),
new FrameworkPropertyMetadata(ValueBoxes.Int5Box, FrameworkPropertyMetadataOptions.AffectsRender));
public static readonly DependencyProperty DotCountProperty = DependencyProperty.Register(
nameof(DotCount), typeof(int), typeof(LoadingBase),
new FrameworkPropertyMetadata(ValueBoxes.Int5Box, FrameworkPropertyMetadataOptions.AffectsRender));
public static readonly DependencyProperty DotIntervalProperty = DependencyProperty.Register(
"DotInterval", typeof(double), typeof(LoadingBase),
new FrameworkPropertyMetadata(ValueBoxes.Double10Box, FrameworkPropertyMetadataOptions.AffectsRender));
public static readonly DependencyProperty DotIntervalProperty = DependencyProperty.Register(
nameof(DotInterval), typeof(double), typeof(LoadingBase),
new FrameworkPropertyMetadata(ValueBoxes.Double10Box, FrameworkPropertyMetadataOptions.AffectsRender));
public static readonly DependencyProperty DotBorderBrushProperty = DependencyProperty.Register(
"DotBorderBrush", typeof(Brush), typeof(LoadingBase),
new FrameworkPropertyMetadata(default(Brush), FrameworkPropertyMetadataOptions.AffectsRender));
public static readonly DependencyProperty DotBorderBrushProperty = DependencyProperty.Register(
nameof(DotBorderBrush), typeof(Brush), typeof(LoadingBase),
new FrameworkPropertyMetadata(default(Brush), FrameworkPropertyMetadataOptions.AffectsRender));
public static readonly DependencyProperty DotBorderThicknessProperty = DependencyProperty.Register(
"DotBorderThickness", typeof(double), typeof(LoadingBase),
new FrameworkPropertyMetadata(ValueBoxes.Double0Box, FrameworkPropertyMetadataOptions.AffectsRender));
public static readonly DependencyProperty DotBorderThicknessProperty = DependencyProperty.Register(
nameof(DotBorderThickness), typeof(double), typeof(LoadingBase),
new FrameworkPropertyMetadata(ValueBoxes.Double0Box, FrameworkPropertyMetadataOptions.AffectsRender));
public static readonly DependencyProperty DotDiameterProperty = DependencyProperty.Register(
"DotDiameter", typeof(double), typeof(LoadingBase),
new FrameworkPropertyMetadata(6.0, FrameworkPropertyMetadataOptions.AffectsRender));
public static readonly DependencyProperty DotDiameterProperty = DependencyProperty.Register(
nameof(DotDiameter), typeof(double), typeof(LoadingBase),
new FrameworkPropertyMetadata(6.0, FrameworkPropertyMetadataOptions.AffectsRender));
public static readonly DependencyProperty DotSpeedProperty = DependencyProperty.Register(
"DotSpeed", typeof(double), typeof(LoadingBase),
new FrameworkPropertyMetadata(4.0, FrameworkPropertyMetadataOptions.AffectsRender));
public static readonly DependencyProperty DotSpeedProperty = DependencyProperty.Register(
nameof(DotSpeed), typeof(double), typeof(LoadingBase),
new FrameworkPropertyMetadata(4.0, FrameworkPropertyMetadataOptions.AffectsRender));
public static readonly DependencyProperty DotDelayTimeProperty = DependencyProperty.Register(
"DotDelayTime", typeof(double), typeof(LoadingBase),
new FrameworkPropertyMetadata(80.0, FrameworkPropertyMetadataOptions.AffectsRender));
public static readonly DependencyProperty DotDelayTimeProperty = DependencyProperty.Register(
nameof(DotDelayTime), typeof(double), typeof(LoadingBase),
new FrameworkPropertyMetadata(80.0, FrameworkPropertyMetadataOptions.AffectsRender));
protected readonly Canvas PrivateCanvas = new Canvas
{
ClipToBounds = true
};
protected readonly Canvas PrivateCanvas = new()
{
ClipToBounds = true
};
protected LoadingBase()
{
Content = PrivateCanvas;
}
protected LoadingBase()
{
Content = PrivateCanvas;
}
public int DotCount
{
get => (int) GetValue(DotCountProperty);
set => SetValue(DotCountProperty, value);
}
public int DotCount
{
get => (int) GetValue(DotCountProperty);
set => SetValue(DotCountProperty, value);
}
public double DotInterval
{
get => (double) GetValue(DotIntervalProperty);
set => SetValue(DotIntervalProperty, value);
}
public double DotInterval
{
get => (double) GetValue(DotIntervalProperty);
set => SetValue(DotIntervalProperty, value);
}
public Brush DotBorderBrush
{
get => (Brush) GetValue(DotBorderBrushProperty);
set => SetValue(DotBorderBrushProperty, value);
}
public Brush DotBorderBrush
{
get => (Brush) GetValue(DotBorderBrushProperty);
set => SetValue(DotBorderBrushProperty, value);
}
public double DotBorderThickness
{
get => (double) GetValue(DotBorderThicknessProperty);
set => SetValue(DotBorderThicknessProperty, value);
}
public double DotBorderThickness
{
get => (double) GetValue(DotBorderThicknessProperty);
set => SetValue(DotBorderThicknessProperty, value);
}
public double DotDiameter
{
get => (double) GetValue(DotDiameterProperty);
set => SetValue(DotDiameterProperty, value);
}
public double DotDiameter
{
get => (double) GetValue(DotDiameterProperty);
set => SetValue(DotDiameterProperty, value);
}
public double DotSpeed
{
get => (double) GetValue(DotSpeedProperty);
set => SetValue(DotSpeedProperty, value);
}
public double DotSpeed
{
get => (double) GetValue(DotSpeedProperty);
set => SetValue(DotSpeedProperty, value);
}
public double DotDelayTime
{
get => (double) GetValue(DotDelayTimeProperty);
set => SetValue(DotDelayTimeProperty, value);
}
public double DotDelayTime
{
get => (double) GetValue(DotDelayTimeProperty);
set => SetValue(DotDelayTimeProperty, value);
}
protected abstract void UpdateDots();
protected abstract void UpdateDots();
protected override void OnRender(DrawingContext drawingContext)
{
base.OnRender(drawingContext);
UpdateDots();
}
protected override void OnRender(DrawingContext drawingContext)
{
base.OnRender(drawingContext);
UpdateDots();
}
protected virtual Ellipse CreateEllipse(int index)
{
var ellipse = new Ellipse();
ellipse.SetBinding(WidthProperty, new Binding(DotDiameterProperty.Name) { Source = this });
ellipse.SetBinding(HeightProperty, new Binding(DotDiameterProperty.Name) { Source = this });
ellipse.SetBinding(Shape.FillProperty, new Binding(ForegroundProperty.Name) { Source = this });
ellipse.SetBinding(Shape.StrokeThicknessProperty, new Binding(DotBorderThicknessProperty.Name) { Source = this });
ellipse.SetBinding(Shape.StrokeProperty, new Binding(DotBorderBrushProperty.Name) { Source = this });
return ellipse;
}
protected virtual Ellipse CreateEllipse(int index)
{
var ellipse = new Ellipse();
ellipse.SetBinding(WidthProperty, new Binding(DotDiameterProperty.Name) { Source = this });
ellipse.SetBinding(HeightProperty, new Binding(DotDiameterProperty.Name) { Source = this });
ellipse.SetBinding(Shape.FillProperty, new Binding(ForegroundProperty.Name) { Source = this });
ellipse.SetBinding(Shape.StrokeThicknessProperty, new Binding(DotBorderThicknessProperty.Name) { Source = this });
ellipse.SetBinding(Shape.StrokeProperty, new Binding(DotBorderBrushProperty.Name) { Source = this });
return ellipse;
}
}
@@ -7,203 +7,206 @@ using System.Windows.Media.Animation;
using HandyControl.Data;
namespace HandyControl.Controls
namespace HandyControl.Controls;
public class LoadingCircle : LoadingBase
{
public class LoadingCircle : LoadingBase
public static readonly DependencyProperty DotOffSetProperty = DependencyProperty.Register(
nameof(DotOffSet), typeof(double), typeof(LoadingCircle),
new FrameworkPropertyMetadata(20.0, FrameworkPropertyMetadataOptions.AffectsRender));
public double DotOffSet
{
public static readonly DependencyProperty DotOffSetProperty = DependencyProperty.Register(
"DotOffSet", typeof(double), typeof(LoadingCircle),
new FrameworkPropertyMetadata(20.0, FrameworkPropertyMetadataOptions.AffectsRender));
get => (double) GetValue(DotOffSetProperty);
set => SetValue(DotOffSetProperty, value);
}
public double DotOffSet
public static readonly DependencyProperty NeedHiddenProperty = DependencyProperty.Register(
nameof(NeedHidden), typeof(bool), typeof(LoadingCircle),
new FrameworkPropertyMetadata(ValueBoxes.TrueBox, FrameworkPropertyMetadataOptions.AffectsRender));
public bool NeedHidden
{
get => (bool) GetValue(NeedHiddenProperty);
set => SetValue(NeedHiddenProperty, ValueBoxes.BooleanBox(value));
}
static LoadingCircle()
{
DotSpeedProperty.OverrideMetadata(typeof(LoadingCircle),
new FrameworkPropertyMetadata(6.0, FrameworkPropertyMetadataOptions.AffectsRender));
DotDelayTimeProperty.OverrideMetadata(typeof(LoadingCircle),
new FrameworkPropertyMetadata(220.0, FrameworkPropertyMetadataOptions.AffectsRender));
}
protected sealed override void UpdateDots()
{
var dotCount = DotCount;
var dotInterval = DotInterval;
var dotSpeed = DotSpeed;
var dotDelayTime = DotDelayTime;
var needHidden = NeedHidden;
if (dotCount < 1) return;
PrivateCanvas.Children.Clear();
//定义动画
Storyboard = new Storyboard
{
get => (double) GetValue(DotOffSetProperty);
set => SetValue(DotOffSetProperty, value);
}
RepeatBehavior = RepeatBehavior.Forever
};
public static readonly DependencyProperty NeedHiddenProperty = DependencyProperty.Register(
"NeedHidden", typeof(bool), typeof(LoadingCircle),
new FrameworkPropertyMetadata(ValueBoxes.TrueBox, FrameworkPropertyMetadataOptions.AffectsRender));
public bool NeedHidden
//创建圆点
for (var i = 0; i < dotCount; i++)
{
get => (bool) GetValue(NeedHiddenProperty);
set => SetValue(NeedHiddenProperty, ValueBoxes.BooleanBox(value));
}
var border = CreateBorder(i, dotInterval, needHidden);
static LoadingCircle()
{
DotSpeedProperty.OverrideMetadata(typeof(LoadingCircle),
new FrameworkPropertyMetadata(6.0, FrameworkPropertyMetadataOptions.AffectsRender));
DotDelayTimeProperty.OverrideMetadata(typeof(LoadingCircle),
new FrameworkPropertyMetadata(220.0, FrameworkPropertyMetadataOptions.AffectsRender));
}
protected sealed override void UpdateDots()
{
var dotCount = DotCount;
var dotInterval = DotInterval;
var dotSpeed = DotSpeed;
var dotDelayTime = DotDelayTime;
var needHidden = NeedHidden;
if (dotCount < 1) return;
PrivateCanvas.Children.Clear();
//定义动画
Storyboard = new Storyboard
var framesMove = new DoubleAnimationUsingKeyFrames
{
RepeatBehavior = RepeatBehavior.Forever
BeginTime = TimeSpan.FromMilliseconds(dotDelayTime * i)
};
//创建圆点
for (var i = 0; i < dotCount; i++)
var subAngle = -dotInterval * i;
//开始位置
var frame0 = new LinearDoubleKeyFrame
{
var border = CreateBorder(i, dotInterval, needHidden);
Value = 0 + subAngle,
KeyTime = KeyTime.FromTimeSpan(TimeSpan.Zero)
};
var framesMove = new DoubleAnimationUsingKeyFrames
//开始位置到第一次匀速开始
var frame1 = new EasingDoubleKeyFrame
{
EasingFunction = new PowerEase
{
BeginTime = TimeSpan.FromMilliseconds(dotDelayTime * i)
};
EasingMode = EasingMode.EaseOut
},
Value = 180 + subAngle,
KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(dotSpeed * (0.75 / 7)))
};
var subAngle = -dotInterval * i;
//第一次匀速开始到第一次匀速结束
var frame2 = new LinearDoubleKeyFrame
{
Value = 180 + DotOffSet + subAngle,
KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(dotSpeed * (2.75 / 7)))
};
//开始位置
var frame0 = new LinearDoubleKeyFrame
//第一次匀速结束到匀加速结束
var frame3 = new EasingDoubleKeyFrame
{
EasingFunction = new PowerEase
{
Value = 0 + subAngle,
KeyTime = KeyTime.FromTimeSpan(TimeSpan.Zero)
};
EasingMode = EasingMode.EaseIn
},
Value = 360 + subAngle,
KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(dotSpeed * (3.5 / 7)))
};
//开始位置到第一次匀速开始
var frame1 = new EasingDoubleKeyFrame
//匀加速结束到匀减速结束
var frame4 = new EasingDoubleKeyFrame
{
EasingFunction = new PowerEase
{
EasingFunction = new PowerEase
{
EasingMode = EasingMode.EaseOut
},
Value = 180 + subAngle,
KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(dotSpeed * (0.75 / 7)))
};
EasingMode = EasingMode.EaseOut
},
Value = 540 + subAngle,
KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(dotSpeed * (4.25 / 7)))
};
//第一次匀速开始到第一次匀速结束
var frame2 = new LinearDoubleKeyFrame
{
Value = 180 + DotOffSet + subAngle,
KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(dotSpeed * (2.75 / 7)))
};
//匀减速结束到第二次匀速结束
var frame5 = new LinearDoubleKeyFrame
{
Value = 540 + DotOffSet + subAngle,
KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(dotSpeed * (6.25 / 7)))
};
//第一次匀速结束到匀加速结束
var frame3 = new EasingDoubleKeyFrame
//第二次匀速结束到匀加速结束
var frame6 = new EasingDoubleKeyFrame
{
EasingFunction = new PowerEase
{
EasingFunction = new PowerEase
{
EasingMode = EasingMode.EaseIn
},
Value = 360 + subAngle,
KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(dotSpeed * (3.5 / 7)))
};
EasingMode = EasingMode.EaseIn
},
Value = 720 + subAngle,
KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(dotSpeed))
};
//匀加速结束到匀减速结束
var frame4 = new EasingDoubleKeyFrame
{
EasingFunction = new PowerEase
{
EasingMode = EasingMode.EaseOut
},
Value = 540 + subAngle,
KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(dotSpeed * (4.25 / 7)))
};
framesMove.KeyFrames.Add(frame0);
framesMove.KeyFrames.Add(frame1);
framesMove.KeyFrames.Add(frame2);
framesMove.KeyFrames.Add(frame3);
framesMove.KeyFrames.Add(frame4);
framesMove.KeyFrames.Add(frame5);
framesMove.KeyFrames.Add(frame6);
//匀减速结束到第二次匀速结束
var frame5 = new LinearDoubleKeyFrame
{
Value = 540 + DotOffSet + subAngle,
KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(dotSpeed * (6.25 / 7)))
};
Storyboard.SetTarget(framesMove, border);
Storyboard.SetTargetProperty(framesMove, new PropertyPath("(UIElement.RenderTransform).(TransformGroup.Children)[0].(RotateTransform.Angle)"));
Storyboard.Children.Add(framesMove);
//第二次匀速结束到匀加速结束
var frame6 = new EasingDoubleKeyFrame
if (NeedHidden)
{
//隐藏一段时间
var frame7 = new DiscreteObjectKeyFrame
{
EasingFunction = new PowerEase
{
EasingMode = EasingMode.EaseIn
},
Value = 720 + subAngle,
Value = Visibility.Collapsed,
KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(dotSpeed))
};
framesMove.KeyFrames.Add(frame0);
framesMove.KeyFrames.Add(frame1);
framesMove.KeyFrames.Add(frame2);
framesMove.KeyFrames.Add(frame3);
framesMove.KeyFrames.Add(frame4);
framesMove.KeyFrames.Add(frame5);
framesMove.KeyFrames.Add(frame6);
Storyboard.SetTarget(framesMove, border);
Storyboard.SetTargetProperty(framesMove, new PropertyPath("(UIElement.RenderTransform).(TransformGroup.Children)[0].(RotateTransform.Angle)"));
Storyboard.Children.Add(framesMove);
if (NeedHidden)
var frame8 = new DiscreteObjectKeyFrame
{
//隐藏一段时间
var frame7 = new DiscreteObjectKeyFrame
{
Value = Visibility.Collapsed,
KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(dotSpeed))
};
Value = Visibility.Collapsed,
KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(dotSpeed + 0.4))
};
var frame8 = new DiscreteObjectKeyFrame
{
Value = Visibility.Collapsed,
KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(dotSpeed + 0.4))
};
var frame9 = new DiscreteObjectKeyFrame
{
Value = Visibility.Visible,
KeyTime = KeyTime.FromTimeSpan(TimeSpan.Zero)
};
var framesVisibility = new ObjectAnimationUsingKeyFrames
{
BeginTime = TimeSpan.FromMilliseconds(dotDelayTime * i)
};
framesVisibility.KeyFrames.Add(frame9);
framesVisibility.KeyFrames.Add(frame7);
framesVisibility.KeyFrames.Add(frame8);
Storyboard.SetTarget(framesVisibility, border);
Storyboard.SetTargetProperty(framesVisibility, new PropertyPath("(UIElement.Visibility)"));
Storyboard.Children.Add(framesVisibility);
}
PrivateCanvas.Children.Add(border);
var frame9 = new DiscreteObjectKeyFrame
{
Value = Visibility.Visible,
KeyTime = KeyTime.FromTimeSpan(TimeSpan.Zero)
};
var framesVisibility = new ObjectAnimationUsingKeyFrames
{
BeginTime = TimeSpan.FromMilliseconds(dotDelayTime * i)
};
framesVisibility.KeyFrames.Add(frame9);
framesVisibility.KeyFrames.Add(frame7);
framesVisibility.KeyFrames.Add(frame8);
Storyboard.SetTarget(framesVisibility, border);
Storyboard.SetTargetProperty(framesVisibility, new PropertyPath("(UIElement.Visibility)"));
Storyboard.Children.Add(framesVisibility);
}
Storyboard.Begin();
PrivateCanvas.Children.Add(border);
}
private Border CreateBorder(int index, double dotInterval, bool needHidden)
Storyboard.Begin();
if (!IsRunning)
{
var ellipse = CreateEllipse(index);
ellipse.HorizontalAlignment = HorizontalAlignment.Center;
ellipse.VerticalAlignment = VerticalAlignment.Bottom;
var rt = new RotateTransform
{
Angle = -dotInterval * index
};
var myTransGroup = new TransformGroup();
myTransGroup.Children.Add(rt);
var border = new Border
{
RenderTransformOrigin = new Point(0.5, 0.5),
RenderTransform = myTransGroup,
Child = ellipse,
Visibility = needHidden ? Visibility.Collapsed : Visibility.Visible
};
border.SetBinding(WidthProperty, new Binding("Width") { Source = this });
border.SetBinding(HeightProperty, new Binding("Height") { Source = this });
return border;
Storyboard.Pause();
}
}
private Border CreateBorder(int index, double dotInterval, bool needHidden)
{
var ellipse = CreateEllipse(index);
ellipse.HorizontalAlignment = HorizontalAlignment.Center;
ellipse.VerticalAlignment = VerticalAlignment.Bottom;
var rt = new RotateTransform
{
Angle = -dotInterval * index
};
var myTransGroup = new TransformGroup();
myTransGroup.Children.Add(rt);
var border = new Border
{
RenderTransformOrigin = new Point(0.5, 0.5),
RenderTransform = myTransGroup,
Child = ellipse,
Visibility = needHidden ? Visibility.Collapsed : Visibility.Visible
};
border.SetBinding(WidthProperty, new Binding("Width") { Source = this });
border.SetBinding(HeightProperty, new Binding("Height") { Source = this });
return border;
}
}
@@ -5,108 +5,111 @@ using System.Windows.Media.Animation;
using System.Windows.Shapes;
namespace HandyControl.Controls
namespace HandyControl.Controls;
public class LoadingLine : LoadingBase
{
public class LoadingLine : LoadingBase
private const double MoveLength = 80;
private const double UniformScale = .6;
public LoadingLine()
{
private const double MoveLength = 80;
SetBinding(HeightProperty, new Binding("DotDiameter") { Source = this });
}
private const double UniformScale = .6;
protected sealed override void UpdateDots()
{
var dotCount = DotCount;
var dotInterval = DotInterval;
var dotDiameter = DotDiameter;
var dotSpeed = DotSpeed;
var dotDelayTime = DotDelayTime;
public LoadingLine()
if (dotCount < 1) return;
PrivateCanvas.Children.Clear();
//计算相关尺寸
var centerWidth = dotDiameter * dotCount + dotInterval * (dotCount - 1) + MoveLength;
var speedDownLength = (ActualWidth - MoveLength) / 2;
var speedUniformLength = centerWidth / 2;
//定义动画
Storyboard = new Storyboard
{
SetBinding(HeightProperty, new Binding("DotDiameter") { Source = this });
}
RepeatBehavior = RepeatBehavior.Forever
};
protected sealed override void UpdateDots()
//创建圆点
for (var i = 0; i < dotCount; i++)
{
var dotCount = DotCount;
var dotInterval = DotInterval;
var dotDiameter = DotDiameter;
var dotSpeed = DotSpeed;
var dotDelayTime = DotDelayTime;
var ellipse = CreateEllipse(i, dotInterval, dotDiameter);
if (dotCount < 1) return;
PrivateCanvas.Children.Clear();
//计算相关尺寸
var centerWidth = dotDiameter * dotCount + dotInterval * (dotCount - 1) + MoveLength;
var speedDownLength = (ActualWidth - MoveLength) / 2;
var speedUniformLength = centerWidth / 2;
//定义动画
Storyboard = new Storyboard
var frames = new ThicknessAnimationUsingKeyFrames
{
RepeatBehavior = RepeatBehavior.Forever
BeginTime = TimeSpan.FromMilliseconds(dotDelayTime * i)
};
//开始位置
var frame0 = new LinearThicknessKeyFrame
{
Value = new Thickness(ellipse.Margin.Left, 0, 0, 0),
KeyTime = KeyTime.FromTimeSpan(TimeSpan.Zero)
};
//创建圆点
for (var i = 0; i < dotCount; i++)
//开始位置到匀速开始
var frame1 = new EasingThicknessKeyFrame
{
var ellipse = CreateEllipse(i, dotInterval, dotDiameter);
var frames = new ThicknessAnimationUsingKeyFrames
EasingFunction = new PowerEase
{
BeginTime = TimeSpan.FromMilliseconds(dotDelayTime * i)
};
//开始位置
var frame0 = new LinearThicknessKeyFrame
EasingMode = EasingMode.EaseOut
},
Value = new Thickness(speedDownLength + ellipse.Margin.Left, 0, 0, 0),
KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(dotSpeed * (1 - UniformScale) / 2))
};
//匀速开始到匀速结束
var frame2 = new LinearThicknessKeyFrame
{
Value = new Thickness(speedDownLength + speedUniformLength + ellipse.Margin.Left, 0, 0, 0),
KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(dotSpeed * (1 + UniformScale) / 2))
};
//匀速结束到匀加速结束
var frame3 = new EasingThicknessKeyFrame
{
EasingFunction = new PowerEase
{
Value = new Thickness(ellipse.Margin.Left, 0, 0, 0),
KeyTime = KeyTime.FromTimeSpan(TimeSpan.Zero)
};
EasingMode = EasingMode.EaseIn
},
Value = new Thickness(ActualWidth + ellipse.Margin.Left + speedUniformLength, 0, 0, 0),
KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(dotSpeed))
};
//开始位置到匀速开始
var frame1 = new EasingThicknessKeyFrame
{
EasingFunction = new PowerEase
{
EasingMode = EasingMode.EaseOut
},
Value = new Thickness(speedDownLength + ellipse.Margin.Left, 0, 0, 0),
KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(dotSpeed * (1 - UniformScale) / 2))
};
frames.KeyFrames.Add(frame0);
frames.KeyFrames.Add(frame1);
frames.KeyFrames.Add(frame2);
frames.KeyFrames.Add(frame3);
//匀速开始到匀速结束
var frame2 = new LinearThicknessKeyFrame
{
Value = new Thickness(speedDownLength + speedUniformLength + ellipse.Margin.Left, 0, 0, 0),
KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(dotSpeed * (1 + UniformScale) / 2))
};
Storyboard.SetTarget(frames, ellipse);
Storyboard.SetTargetProperty(frames, new PropertyPath(MarginProperty));
Storyboard.Children.Add(frames);
//匀速结束到匀加速结束
var frame3 = new EasingThicknessKeyFrame
{
EasingFunction = new PowerEase
{
EasingMode = EasingMode.EaseIn
},
Value = new Thickness(ActualWidth + ellipse.Margin.Left + speedUniformLength, 0, 0, 0),
KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(dotSpeed))
};
frames.KeyFrames.Add(frame0);
frames.KeyFrames.Add(frame1);
frames.KeyFrames.Add(frame2);
frames.KeyFrames.Add(frame3);
Storyboard.SetTarget(frames, ellipse);
Storyboard.SetTargetProperty(frames, new PropertyPath(MarginProperty));
Storyboard.Children.Add(frames);
PrivateCanvas.Children.Add(ellipse);
}
Storyboard.Begin();
PrivateCanvas.Children.Add(ellipse);
}
private Ellipse CreateEllipse(int index, double dotInterval, double dotDiameter)
Storyboard.Begin();
if (!IsRunning)
{
var ellipse = base.CreateEllipse(index);
ellipse.HorizontalAlignment = HorizontalAlignment.Left;
ellipse.VerticalAlignment = VerticalAlignment.Top;
ellipse.Margin = new Thickness(-(dotInterval + dotDiameter) * index, 0, 0, 0);
return ellipse;
Storyboard.Pause();
}
}
private Ellipse CreateEllipse(int index, double dotInterval, double dotDiameter)
{
var ellipse = base.CreateEllipse(index);
ellipse.HorizontalAlignment = HorizontalAlignment.Left;
ellipse.VerticalAlignment = VerticalAlignment.Top;
ellipse.Margin = new Thickness(-(dotInterval + dotDiameter) * index, 0, 0, 0);
return ellipse;
}
}
@@ -8,193 +8,163 @@ using HandyControl.Data;
using HandyControl.Expression.Drawing;
using HandyControl.Tools.Extension;
namespace HandyControl.Controls
namespace HandyControl.Controls;
public class AnimationPath : Shape
{
public class AnimationPath : Shape
private Storyboard _storyboard;
private double _pathLength;
public static readonly DependencyProperty DataProperty = DependencyProperty.Register(nameof(Data),
typeof(Geometry), typeof(AnimationPath), new FrameworkPropertyMetadata(null,
OnPropertiesChanged));
private static void OnPropertiesChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
/// <summary>
/// 故事板
/// </summary>
private Storyboard _storyboard;
/// <summary>
/// 路径长度
/// </summary>
private double _pathLength;
/// <summary>
/// 路径
/// </summary>
public static readonly DependencyProperty DataProperty = DependencyProperty.Register(nameof(Data),
typeof(Geometry), typeof(AnimationPath), new FrameworkPropertyMetadata(null,
OnPropertiesChanged));
private static void OnPropertiesChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
if (d is AnimationPath path)
{
if (d is AnimationPath path)
{
path.UpdatePath();
}
path.UpdatePath();
}
/// <summary>
/// 路径
/// </summary>
public Geometry Data
{
get => (Geometry) GetValue(DataProperty);
set => SetValue(DataProperty, value);
}
protected override Geometry DefiningGeometry => Data ?? Geometry.Empty;
/// <summary>
/// 路径长度
/// </summary>
public static readonly DependencyProperty PathLengthProperty = DependencyProperty.Register(
"PathLength", typeof(double), typeof(AnimationPath), new FrameworkPropertyMetadata(ValueBoxes.Double0Box, OnPropertiesChanged));
/// <summary>
/// 路径长度
/// </summary>
public double PathLength
{
get => (double) GetValue(PathLengthProperty);
set => SetValue(PathLengthProperty, value);
}
/// <summary>
/// 动画间隔时间
/// </summary>
public static readonly DependencyProperty DurationProperty = DependencyProperty.Register(
"Duration", typeof(Duration), typeof(AnimationPath), new FrameworkPropertyMetadata(new Duration(TimeSpan.FromSeconds(2)),
OnPropertiesChanged));
/// <summary>
/// 动画间隔时间
/// </summary>
public Duration Duration
{
get => (Duration) GetValue(DurationProperty);
set => SetValue(DurationProperty, value);
}
public static readonly DependencyProperty IsPlayingProperty = DependencyProperty.Register(
"IsPlaying", typeof(bool), typeof(AnimationPath), new FrameworkPropertyMetadata(ValueBoxes.TrueBox, (o, args) =>
{
var ctl = (AnimationPath) o;
var v = (bool) args.NewValue;
if (v)
{
ctl.UpdatePath();
}
else
{
ctl._storyboard?.Pause();
}
}));
/// <summary>
/// 是否正在播放动画
/// </summary>
public bool IsPlaying
{
get => (bool) GetValue(IsPlayingProperty);
set => SetValue(IsPlayingProperty, ValueBoxes.BooleanBox(value));
}
public static readonly DependencyProperty RepeatBehaviorProperty = DependencyProperty.Register(
"RepeatBehavior", typeof(RepeatBehavior), typeof(AnimationPath), new PropertyMetadata(RepeatBehavior.Forever));
/// <summary>
/// 动画重复行为
/// </summary>
public RepeatBehavior RepeatBehavior
{
get => (RepeatBehavior) GetValue(RepeatBehaviorProperty);
set => SetValue(RepeatBehaviorProperty, value);
}
static AnimationPath()
{
StretchProperty.AddOwner(typeof(AnimationPath), new FrameworkPropertyMetadata(Stretch.Uniform,
OnPropertiesChanged));
StrokeThicknessProperty.AddOwner(typeof(AnimationPath), new FrameworkPropertyMetadata(ValueBoxes.Double1Box,
OnPropertiesChanged));
}
public AnimationPath() => Loaded += (s, e) => UpdatePath();
/// <summary>
/// 动画完成事件
/// </summary>
public static readonly RoutedEvent CompletedEvent =
EventManager.RegisterRoutedEvent("Completed", RoutingStrategy.Bubble,
typeof(EventHandler), typeof(AnimationPath));
/// <summary>
/// 动画完成事件
/// </summary>
public event EventHandler Completed
{
add => AddHandler(CompletedEvent, value);
remove => RemoveHandler(CompletedEvent, value);
}
/// <summary>
/// 更新路径
/// </summary>
private void UpdatePath()
{
if (!Duration.HasTimeSpan || !IsPlaying) return;
_pathLength = PathLength > 0 ? PathLength : Data.GetTotalLength(new Size(ActualWidth, ActualHeight), StrokeThickness);
if (MathHelper.IsVerySmall(_pathLength)) return;
StrokeDashOffset = _pathLength;
StrokeDashArray = new DoubleCollection(new List<double>
{
_pathLength,
_pathLength
});
//定义动画
if (_storyboard != null)
{
_storyboard.Stop();
_storyboard.Completed -= Storyboard_Completed;
}
_storyboard = new Storyboard
{
RepeatBehavior = RepeatBehavior
};
_storyboard.Completed += Storyboard_Completed;
var frames = new DoubleAnimationUsingKeyFrames();
//开始位置
var frame0 = new LinearDoubleKeyFrame
{
Value = _pathLength,
KeyTime = KeyTime.FromTimeSpan(TimeSpan.Zero)
};
//结束位置
var frame1 = new LinearDoubleKeyFrame
{
Value = -_pathLength,
KeyTime = KeyTime.FromTimeSpan(Duration.TimeSpan)
};
frames.KeyFrames.Add(frame0);
frames.KeyFrames.Add(frame1);
Storyboard.SetTarget(frames, this);
Storyboard.SetTargetProperty(frames, new PropertyPath(StrokeDashOffsetProperty));
_storyboard.Children.Add(frames);
_storyboard.Begin();
}
private void Storyboard_Completed(object sender, EventArgs e) => RaiseEvent(new RoutedEventArgs(CompletedEvent));
}
public Geometry Data
{
get => (Geometry) GetValue(DataProperty);
set => SetValue(DataProperty, value);
}
protected override Geometry DefiningGeometry => Data ?? Geometry.Empty;
public static readonly DependencyProperty PathLengthProperty = DependencyProperty.Register(
nameof(PathLength), typeof(double), typeof(AnimationPath), new FrameworkPropertyMetadata(ValueBoxes.Double0Box, OnPropertiesChanged));
public double PathLength
{
get => (double) GetValue(PathLengthProperty);
set => SetValue(PathLengthProperty, value);
}
public static readonly DependencyProperty DurationProperty = DependencyProperty.Register(
nameof(Duration), typeof(Duration), typeof(AnimationPath), new FrameworkPropertyMetadata(new Duration(TimeSpan.FromSeconds(2)),
OnPropertiesChanged));
public Duration Duration
{
get => (Duration) GetValue(DurationProperty);
set => SetValue(DurationProperty, value);
}
public static readonly DependencyProperty IsPlayingProperty = DependencyProperty.Register(
nameof(IsPlaying), typeof(bool), typeof(AnimationPath), new FrameworkPropertyMetadata(ValueBoxes.TrueBox, (o, args) =>
{
var ctl = (AnimationPath) o;
var v = (bool) args.NewValue;
if (v)
{
ctl.UpdatePath();
}
else
{
ctl._storyboard?.Pause();
}
}));
public bool IsPlaying
{
get => (bool) GetValue(IsPlayingProperty);
set => SetValue(IsPlayingProperty, ValueBoxes.BooleanBox(value));
}
public static readonly DependencyProperty RepeatBehaviorProperty =
Timeline.RepeatBehaviorProperty.AddOwner(typeof(AnimationPath),
new PropertyMetadata(RepeatBehavior.Forever));
public RepeatBehavior RepeatBehavior
{
get => (RepeatBehavior) GetValue(RepeatBehaviorProperty);
set => SetValue(RepeatBehaviorProperty, value);
}
public static readonly DependencyProperty FillBehaviorProperty =
Timeline.FillBehaviorProperty.AddOwner(typeof(AnimationPath), new PropertyMetadata(FillBehavior.Stop));
public FillBehavior FillBehavior
{
get { return (FillBehavior) GetValue(FillBehaviorProperty); }
set { SetValue(FillBehaviorProperty, value); }
}
static AnimationPath()
{
StretchProperty.AddOwner(typeof(AnimationPath), new FrameworkPropertyMetadata(Stretch.Uniform,
OnPropertiesChanged));
StrokeThicknessProperty.AddOwner(typeof(AnimationPath), new FrameworkPropertyMetadata(ValueBoxes.Double1Box,
OnPropertiesChanged));
}
public AnimationPath() => Loaded += (s, e) => UpdatePath();
public static readonly RoutedEvent CompletedEvent =
EventManager.RegisterRoutedEvent("Completed", RoutingStrategy.Bubble,
typeof(EventHandler), typeof(AnimationPath));
public event EventHandler Completed
{
add => AddHandler(CompletedEvent, value);
remove => RemoveHandler(CompletedEvent, value);
}
private void UpdatePath()
{
if (!Duration.HasTimeSpan || !IsPlaying) return;
_pathLength = PathLength > 0 ? PathLength : Data.GetTotalLength(new Size(ActualWidth, ActualHeight), StrokeThickness);
if (MathHelper.IsVerySmall(_pathLength)) return;
StrokeDashOffset = _pathLength;
StrokeDashArray = new DoubleCollection(new List<double>
{
_pathLength,
_pathLength
});
if (_storyboard != null)
{
_storyboard.Stop();
_storyboard.Completed -= Storyboard_Completed;
}
_storyboard = new Storyboard
{
RepeatBehavior = RepeatBehavior,
FillBehavior = FillBehavior
};
_storyboard.Completed += Storyboard_Completed;
var frames = new DoubleAnimationUsingKeyFrames();
var frameIn = new LinearDoubleKeyFrame
{
Value = _pathLength,
KeyTime = KeyTime.FromTimeSpan(TimeSpan.Zero)
};
frames.KeyFrames.Add(frameIn);
var frameOut = new LinearDoubleKeyFrame
{
Value = FillBehavior == FillBehavior.Stop ? -_pathLength : 0,
KeyTime = KeyTime.FromTimeSpan(Duration.TimeSpan)
};
frames.KeyFrames.Add(frameOut);
Storyboard.SetTarget(frames, this);
Storyboard.SetTargetProperty(frames, new PropertyPath(StrokeDashOffsetProperty));
_storyboard.Children.Add(frames);
_storyboard.Begin();
}
private void Storyboard_Completed(object sender, EventArgs e) => RaiseEvent(new RoutedEventArgs(CompletedEvent));
}
@@ -4,91 +4,118 @@ using System.Windows.Controls;
using System.Windows.Media;
using HandyControl.Data;
namespace HandyControl.Controls
namespace HandyControl.Controls;
/// <summary>
/// 标记
/// </summary>
public class Badge : ContentControl
{
/// <summary>
/// 标记
/// </summary>
public class Badge : ContentControl
private int? _originalValue;
public static readonly RoutedEvent ValueChangedEvent =
EventManager.RegisterRoutedEvent("ValueChanged", RoutingStrategy.Bubble,
typeof(EventHandler<FunctionEventArgs<int>>), typeof(Badge));
public event EventHandler<FunctionEventArgs<int>> ValueChanged
{
public static readonly RoutedEvent ValueChangedEvent =
EventManager.RegisterRoutedEvent("ValueChanged", RoutingStrategy.Bubble,
typeof(EventHandler<FunctionEventArgs<int>>), typeof(Badge));
add => AddHandler(ValueChangedEvent, value);
remove => RemoveHandler(ValueChangedEvent, value);
}
public event EventHandler<FunctionEventArgs<int>> ValueChanged
public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
nameof(Text), typeof(string), typeof(Badge), new PropertyMetadata("0"));
public string Text
{
get => (string) GetValue(TextProperty);
set => SetValue(TextProperty, value);
}
public static readonly DependencyProperty ValueProperty = DependencyProperty.Register(
nameof(Value), typeof(int), typeof(Badge), new PropertyMetadata(ValueBoxes.Int0Box, OnValueChanged));
private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var ctl = (Badge) d;
var v = (int) e.NewValue;
ctl.SetCurrentValue(TextProperty, v <= ctl.Maximum ? v.ToString() : $"{ctl.Maximum}+");
if (ctl.IsLoaded)
{
add => AddHandler(ValueChangedEvent, value);
remove => RemoveHandler(ValueChangedEvent, value);
}
public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
"Text", typeof(string), typeof(Badge), new PropertyMetadata("0"));
public string Text
{
get => (string) GetValue(TextProperty);
set => SetValue(TextProperty, value);
}
public static readonly DependencyProperty ValueProperty = DependencyProperty.Register(
"Value", typeof(int), typeof(Badge), new PropertyMetadata(ValueBoxes.Int0Box, OnValueChanged));
private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var ctl = (Badge) d;
var v = (int) e.NewValue;
ctl.SetCurrentValue(TextProperty, v <= ctl.Maximum ? v.ToString() : $"{ctl.Maximum}+");
if (ctl.IsInitialized)
ctl.RaiseEvent(new FunctionEventArgs<int>(ValueChangedEvent, ctl)
{
ctl.RaiseEvent(new FunctionEventArgs<int>(ValueChangedEvent, ctl)
{
Info = v
});
}
Info = v
});
}
public int Value
else
{
get => (int) GetValue(ValueProperty);
set => SetValue(ValueProperty, value);
ctl._originalValue = v;
}
}
public static readonly DependencyProperty StatusProperty = DependencyProperty.Register(
"Status", typeof(BadgeStatus), typeof(Badge), new PropertyMetadata(default(BadgeStatus)));
public int Value
{
get => (int) GetValue(ValueProperty);
set => SetValue(ValueProperty, value);
}
public BadgeStatus Status
public static readonly DependencyProperty StatusProperty = DependencyProperty.Register(
nameof(Status), typeof(BadgeStatus), typeof(Badge), new PropertyMetadata(default(BadgeStatus)));
public BadgeStatus Status
{
get => (BadgeStatus) GetValue(StatusProperty);
set => SetValue(StatusProperty, value);
}
public static readonly DependencyProperty MaximumProperty = DependencyProperty.Register(
nameof(Maximum), typeof(int), typeof(Badge), new PropertyMetadata(ValueBoxes.Int99Box, OnMaximumChanged));
private static void OnMaximumChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var ctl = (Badge) d;
var v = ctl.Value;
ctl.SetCurrentValue(TextProperty, v <= ctl.Maximum ? v.ToString() : $"{ctl.Maximum}+");
}
public int Maximum
{
get => (int) GetValue(MaximumProperty);
set => SetValue(MaximumProperty, value);
}
public static readonly DependencyProperty BadgeMarginProperty = DependencyProperty.Register(
nameof(BadgeMargin), typeof(Thickness), typeof(Badge), new PropertyMetadata(default(Thickness)));
public Thickness BadgeMargin
{
get => (Thickness) GetValue(BadgeMarginProperty);
set => SetValue(BadgeMarginProperty, value);
}
public static readonly DependencyProperty ShowBadgeProperty = DependencyProperty.Register(
nameof(ShowBadge), typeof(bool), typeof(Badge), new PropertyMetadata(ValueBoxes.TrueBox));
public bool ShowBadge
{
get => (bool) GetValue(ShowBadgeProperty);
set => SetValue(ShowBadgeProperty, ValueBoxes.BooleanBox(value));
}
protected override Geometry GetLayoutClip(Size layoutSlotSize) => null;
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
if (_originalValue != null)
{
get => (BadgeStatus) GetValue(StatusProperty);
set => SetValue(StatusProperty, value);
RaiseEvent(new FunctionEventArgs<int>(ValueChangedEvent, this)
{
Info = _originalValue.Value
});
_originalValue = null;
}
public static readonly DependencyProperty MaximumProperty = DependencyProperty.Register(
"Maximum", typeof(int), typeof(Badge), new PropertyMetadata(ValueBoxes.Int99Box, OnValueChanged));
public int Maximum
{
get => (int) GetValue(MaximumProperty);
set => SetValue(MaximumProperty, value);
}
public static readonly DependencyProperty BadgeMarginProperty = DependencyProperty.Register(
"BadgeMargin", typeof(Thickness), typeof(Badge), new PropertyMetadata(default(Thickness)));
public Thickness BadgeMargin
{
get => (Thickness) GetValue(BadgeMarginProperty);
set => SetValue(BadgeMarginProperty, value);
}
public static readonly DependencyProperty ShowBadgeProperty = DependencyProperty.Register(
"ShowBadge", typeof(bool), typeof(Badge), new PropertyMetadata(ValueBoxes.TrueBox));
public bool ShowBadge
{
get => (bool) GetValue(ShowBadgeProperty);
set => SetValue(ShowBadgeProperty, ValueBoxes.BooleanBox(value));
}
protected override Geometry GetLayoutClip(Size layoutSlotSize) => null;
}
}
@@ -7,110 +7,109 @@ using System.Windows.Data;
using System.Windows.Markup;
using System.Windows.Media.Effects;
namespace HandyControl.Controls
namespace HandyControl.Controls;
[DefaultProperty("Content")]
[ContentProperty("Content")]
public class BlendEffectBox : Control
{
[DefaultProperty("Content")]
[ContentProperty("Content")]
public class BlendEffectBox : Control
private readonly ContentPresenter _effectBottomPresenter;
private readonly ContentPresenter _effectTopPresenter;
private bool _isInternalAction;
public BlendEffectBox()
{
private readonly ContentPresenter _effectBottomPresenter;
_effectTopPresenter = new ContentPresenter();
ActualContent = _effectTopPresenter;
private readonly ContentPresenter _effectTopPresenter;
private bool _isInternalAction;
public BlendEffectBox()
_effectBottomPresenter = new ContentPresenter();
_effectBottomPresenter.SetBinding(ContentPresenter.ContentProperty, new Binding(ContentProperty.Name)
{
_effectTopPresenter = new ContentPresenter();
ActualContent = _effectTopPresenter;
Source = this
});
_effectBottomPresenter = new ContentPresenter();
_effectBottomPresenter.SetBinding(ContentPresenter.ContentProperty, new Binding(ContentProperty.Name)
{
Source = this
});
var effects = new ObservableCollection<Effect>();
effects.CollectionChanged += OnEffectsChanged;
Effects = effects;
}
var effects = new ObservableCollection<Effect>();
effects.CollectionChanged += OnEffectsChanged;
Effects = effects;
private void OnEffectsChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (Effects == null || Effects.Count == 0)
{
ClearEffect(_effectTopPresenter);
return;
}
private void OnEffectsChanged(object sender, NotifyCollectionChangedEventArgs e)
if (_isInternalAction) return;
_isInternalAction = true;
AddEffect(_effectTopPresenter, Effects.Count);
_isInternalAction = false;
}
private void ClearEffect(ContentPresenter presenter)
{
if (presenter == null) return;
if (ReferenceEquals(_effectBottomPresenter, presenter))
{
if (Effects == null || Effects.Count == 0)
_effectBottomPresenter.SetCurrentValue(EffectProperty, null);
return;
}
presenter.SetCurrentValue(EffectProperty, null);
ClearEffect(presenter.Content as ContentPresenter);
}
private void AddEffect(ContentPresenter presenter, int count)
{
var newCount = --count;
if (newCount >= 0)
{
presenter.Effect = Effects[newCount];
var nextCount = --count;
if (nextCount >= 1)
{
ClearEffect(_effectTopPresenter);
return;
var content = new ContentPresenter();
presenter.Content = content;
AddEffect(content, newCount);
}
if (_isInternalAction) return;
_isInternalAction = true;
AddEffect(_effectTopPresenter, Effects.Count);
_isInternalAction = false;
}
private void ClearEffect(ContentPresenter presenter)
{
if (presenter == null) return;
if (ReferenceEquals(_effectBottomPresenter, presenter))
else if (nextCount >= 0)
{
_effectBottomPresenter.SetCurrentValue(EffectProperty, null);
return;
_effectBottomPresenter.Effect = Effects[0];
presenter.Content = _effectBottomPresenter;
}
presenter.SetCurrentValue(EffectProperty, null);
ClearEffect(presenter.Content as ContentPresenter);
}
private void AddEffect(ContentPresenter presenter, int count)
{
var newCount = --count;
if (newCount >= 0)
else
{
presenter.Effect = Effects[newCount];
var nextCount = --count;
if (nextCount >= 1)
{
var content = new ContentPresenter();
presenter.Content = content;
AddEffect(content, newCount);
}
else if (nextCount >= 0)
{
_effectBottomPresenter.Effect = Effects[0];
presenter.Content = _effectBottomPresenter;
}
else
{
presenter.Content = _effectBottomPresenter;
}
presenter.Content = _effectBottomPresenter;
}
}
}
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
[Bindable(true)]
public Collection<Effect> Effects { get; }
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
[Bindable(true)]
public Collection<Effect> Effects { get; }
public static readonly DependencyProperty ContentProperty = DependencyProperty.Register(
"Content", typeof(FrameworkElement), typeof(BlendEffectBox), new PropertyMetadata(default(FrameworkElement)));
public static readonly DependencyProperty ContentProperty = DependencyProperty.Register(
nameof(Content), typeof(FrameworkElement), typeof(BlendEffectBox), new PropertyMetadata(default(FrameworkElement)));
public FrameworkElement Content
{
get => (FrameworkElement) GetValue(ContentProperty);
set => SetValue(ContentProperty, value);
}
public FrameworkElement Content
{
get => (FrameworkElement) GetValue(ContentProperty);
set => SetValue(ContentProperty, value);
}
internal static readonly DependencyProperty ActualContentProperty = DependencyProperty.Register(
"ActualContent", typeof(FrameworkElement), typeof(BlendEffectBox), new PropertyMetadata(default(FrameworkElement)));
internal static readonly DependencyProperty ActualContentProperty = DependencyProperty.Register(
nameof(ActualContent), typeof(FrameworkElement), typeof(BlendEffectBox), new PropertyMetadata(default(FrameworkElement)));
internal FrameworkElement ActualContent
{
get => (FrameworkElement) GetValue(ActualContentProperty);
set => SetValue(ActualContentProperty, value);
}
internal FrameworkElement ActualContent
{
get => (FrameworkElement) GetValue(ActualContentProperty);
set => SetValue(ActualContentProperty, value);
}
}
@@ -2,86 +2,85 @@
using System.Windows;
using System.Windows.Controls;
namespace HandyControl.Controls
namespace HandyControl.Controls;
public class Card : ContentControl
{
public class Card : ContentControl
public static readonly DependencyProperty HeaderProperty = DependencyProperty.Register(
nameof(Header), typeof(object), typeof(Card), new PropertyMetadata(default(object)));
public object Header
{
public static readonly DependencyProperty HeaderProperty = DependencyProperty.Register(
"Header", typeof(object), typeof(Card), new PropertyMetadata(default(object)));
get => GetValue(HeaderProperty);
set => SetValue(HeaderProperty, value);
}
public object Header
{
get => GetValue(HeaderProperty);
set => SetValue(HeaderProperty, value);
}
public static readonly DependencyProperty HeaderTemplateProperty = DependencyProperty.Register(
nameof(HeaderTemplate), typeof(DataTemplate), typeof(Card), new PropertyMetadata(default(DataTemplate)));
public static readonly DependencyProperty HeaderTemplateProperty = DependencyProperty.Register(
"HeaderTemplate", typeof(DataTemplate), typeof(Card), new PropertyMetadata(default(DataTemplate)));
[Bindable(true), Category("Content")]
public DataTemplate HeaderTemplate
{
get => (DataTemplate) GetValue(HeaderTemplateProperty);
set => SetValue(HeaderTemplateProperty, value);
}
[Bindable(true), Category("Content")]
public DataTemplate HeaderTemplate
{
get => (DataTemplate) GetValue(HeaderTemplateProperty);
set => SetValue(HeaderTemplateProperty, value);
}
public static readonly DependencyProperty HeaderTemplateSelectorProperty = DependencyProperty.Register(
nameof(HeaderTemplateSelector), typeof(DataTemplateSelector), typeof(Card), new PropertyMetadata(default(DataTemplateSelector)));
public static readonly DependencyProperty HeaderTemplateSelectorProperty = DependencyProperty.Register(
"HeaderTemplateSelector", typeof(DataTemplateSelector), typeof(Card), new PropertyMetadata(default(DataTemplateSelector)));
[Bindable(true), Category("Content")]
public DataTemplateSelector HeaderTemplateSelector
{
get => (DataTemplateSelector) GetValue(HeaderTemplateSelectorProperty);
set => SetValue(HeaderTemplateSelectorProperty, value);
}
[Bindable(true), Category("Content")]
public DataTemplateSelector HeaderTemplateSelector
{
get => (DataTemplateSelector) GetValue(HeaderTemplateSelectorProperty);
set => SetValue(HeaderTemplateSelectorProperty, value);
}
public static readonly DependencyProperty HeaderStringFormatProperty = DependencyProperty.Register(
nameof(HeaderStringFormat), typeof(string), typeof(Card), new PropertyMetadata(default(string)));
public static readonly DependencyProperty HeaderStringFormatProperty = DependencyProperty.Register(
"HeaderStringFormat", typeof(string), typeof(Card), new PropertyMetadata(default(string)));
[Bindable(true), Category("Content")]
public string HeaderStringFormat
{
get => (string) GetValue(HeaderStringFormatProperty);
set => SetValue(HeaderStringFormatProperty, value);
}
[Bindable(true), Category("Content")]
public string HeaderStringFormat
{
get => (string) GetValue(HeaderStringFormatProperty);
set => SetValue(HeaderStringFormatProperty, value);
}
public static readonly DependencyProperty FooterProperty = DependencyProperty.Register(
nameof(Footer), typeof(object), typeof(Card), new PropertyMetadata(default(object)));
public static readonly DependencyProperty FooterProperty = DependencyProperty.Register(
"Footer", typeof(object), typeof(Card), new PropertyMetadata(default(object)));
public object Footer
{
get => GetValue(FooterProperty);
set => SetValue(FooterProperty, value);
}
public object Footer
{
get => GetValue(FooterProperty);
set => SetValue(FooterProperty, value);
}
public static readonly DependencyProperty FooterTemplateProperty = DependencyProperty.Register(
nameof(FooterTemplate), typeof(DataTemplate), typeof(Card), new PropertyMetadata(default(DataTemplate)));
public static readonly DependencyProperty FooterTemplateProperty = DependencyProperty.Register(
"FooterTemplate", typeof(DataTemplate), typeof(Card), new PropertyMetadata(default(DataTemplate)));
[Bindable(true), Category("Content")]
public DataTemplate FooterTemplate
{
get => (DataTemplate) GetValue(FooterTemplateProperty);
set => SetValue(FooterTemplateProperty, value);
}
[Bindable(true), Category("Content")]
public DataTemplate FooterTemplate
{
get => (DataTemplate) GetValue(FooterTemplateProperty);
set => SetValue(FooterTemplateProperty, value);
}
public static readonly DependencyProperty FooterTemplateSelectorProperty = DependencyProperty.Register(
nameof(FooterTemplateSelector), typeof(DataTemplateSelector), typeof(Card), new PropertyMetadata(default(DataTemplateSelector)));
public static readonly DependencyProperty FooterTemplateSelectorProperty = DependencyProperty.Register(
"FooterTemplateSelector", typeof(DataTemplateSelector), typeof(Card), new PropertyMetadata(default(DataTemplateSelector)));
[Bindable(true), Category("Content")]
public DataTemplateSelector FooterTemplateSelector
{
get => (DataTemplateSelector) GetValue(FooterTemplateSelectorProperty);
set => SetValue(FooterTemplateSelectorProperty, value);
}
[Bindable(true), Category("Content")]
public DataTemplateSelector FooterTemplateSelector
{
get => (DataTemplateSelector) GetValue(FooterTemplateSelectorProperty);
set => SetValue(FooterTemplateSelectorProperty, value);
}
public static readonly DependencyProperty FooterStringFormatProperty = DependencyProperty.Register(
nameof(FooterStringFormat), typeof(string), typeof(Card), new PropertyMetadata(default(string)));
public static readonly DependencyProperty FooterStringFormatProperty = DependencyProperty.Register(
"FooterStringFormat", typeof(string), typeof(Card), new PropertyMetadata(default(string)));
[Bindable(true), Category("Content")]
public string FooterStringFormat
{
get => (string) GetValue(FooterStringFormatProperty);
set => SetValue(FooterStringFormatProperty, value);
}
[Bindable(true), Category("Content")]
public string FooterStringFormat
{
get => (string) GetValue(FooterStringFormatProperty);
set => SetValue(FooterStringFormatProperty, value);
}
}
@@ -2,45 +2,44 @@
using System.Windows;
using HandyControl.Data;
namespace HandyControl.Controls
namespace HandyControl.Controls;
public class ChatBubble : SelectableItem
{
public class ChatBubble : SelectableItem
public static readonly DependencyProperty RoleProperty = DependencyProperty.Register(
nameof(Role), typeof(ChatRoleType), typeof(ChatBubble), new PropertyMetadata(default(ChatRoleType)));
public ChatRoleType Role
{
public static readonly DependencyProperty RoleProperty = DependencyProperty.Register(
"Role", typeof(ChatRoleType), typeof(ChatBubble), new PropertyMetadata(default(ChatRoleType)));
get => (ChatRoleType) GetValue(RoleProperty);
set => SetValue(RoleProperty, value);
}
public ChatRoleType Role
{
get => (ChatRoleType) GetValue(RoleProperty);
set => SetValue(RoleProperty, value);
}
public static readonly DependencyProperty TypeProperty = DependencyProperty.Register(
nameof(Type), typeof(ChatMessageType), typeof(ChatBubble), new PropertyMetadata(default(ChatMessageType)));
public static readonly DependencyProperty TypeProperty = DependencyProperty.Register(
"Type", typeof(ChatMessageType), typeof(ChatBubble), new PropertyMetadata(default(ChatMessageType)));
public ChatMessageType Type
{
get => (ChatMessageType) GetValue(TypeProperty);
set => SetValue(TypeProperty, value);
}
public ChatMessageType Type
{
get => (ChatMessageType) GetValue(TypeProperty);
set => SetValue(TypeProperty, value);
}
public static readonly DependencyProperty IsReadProperty = DependencyProperty.Register(
nameof(IsRead), typeof(bool), typeof(ChatBubble), new PropertyMetadata(ValueBoxes.FalseBox));
public static readonly DependencyProperty IsReadProperty = DependencyProperty.Register(
"IsRead", typeof(bool), typeof(ChatBubble), new PropertyMetadata(ValueBoxes.FalseBox));
public bool IsRead
{
get => (bool) GetValue(IsReadProperty);
set => SetValue(IsReadProperty, ValueBoxes.BooleanBox(value));
}
public bool IsRead
{
get => (bool) GetValue(IsReadProperty);
set => SetValue(IsReadProperty, ValueBoxes.BooleanBox(value));
}
public Action<object> ReadAction { get; set; }
public Action<object> ReadAction { get; set; }
protected override void OnSelected(RoutedEventArgs e)
{
base.OnSelected(e);
protected override void OnSelected(RoutedEventArgs e)
{
base.OnSelected(e);
IsRead = true;
ReadAction?.Invoke(Content);
}
IsRead = true;
ReadAction?.Invoke(Content);
}
}
File diff suppressed because it is too large. Load diff
@@ -1,82 +1,83 @@
using System.Windows;
using System.Windows.Controls;
using System.Windows.Markup;
using System.Windows.Media;
using HandyControl.Data;
namespace HandyControl.Controls
namespace HandyControl.Controls;
[ContentProperty(nameof(Content))]
public class Divider : Control
{
public class Divider : Control
public static readonly DependencyProperty ContentProperty = DependencyProperty.Register(
nameof(Content), typeof(object), typeof(Divider), new PropertyMetadata(default(object)));
public object Content
{
public static readonly DependencyProperty ContentProperty = DependencyProperty.Register(
"Content", typeof(object), typeof(Divider), new PropertyMetadata(default(object)));
get => GetValue(ContentProperty);
set => SetValue(ContentProperty, value);
}
public object Content
{
get => GetValue(ContentProperty);
set => SetValue(ContentProperty, value);
}
public static readonly DependencyProperty OrientationProperty = DependencyProperty.Register(
nameof(Orientation), typeof(Orientation), typeof(Divider), new PropertyMetadata(default(Orientation)));
public static readonly DependencyProperty OrientationProperty = DependencyProperty.Register(
"Orientation", typeof(Orientation), typeof(Divider), new PropertyMetadata(default(Orientation)));
public Orientation Orientation
{
get => (Orientation) GetValue(OrientationProperty);
set => SetValue(OrientationProperty, value);
}
public Orientation Orientation
{
get => (Orientation) GetValue(OrientationProperty);
set => SetValue(OrientationProperty, value);
}
public static readonly DependencyProperty ContentTemplateProperty = DependencyProperty.Register(
nameof(ContentTemplate), typeof(DataTemplate), typeof(Divider), new PropertyMetadata(default(DataTemplate)));
public static readonly DependencyProperty ContentTemplateProperty = DependencyProperty.Register(
"ContentTemplate", typeof(DataTemplate), typeof(Divider), new PropertyMetadata(default(DataTemplate)));
public DataTemplate ContentTemplate
{
get => (DataTemplate) GetValue(ContentTemplateProperty);
set => SetValue(ContentTemplateProperty, value);
}
public DataTemplate ContentTemplate
{
get => (DataTemplate) GetValue(ContentTemplateProperty);
set => SetValue(ContentTemplateProperty, value);
}
public static readonly DependencyProperty ContentStringFormatProperty = DependencyProperty.Register(
nameof(ContentStringFormat), typeof(string), typeof(Divider), new PropertyMetadata(default(string)));
public static readonly DependencyProperty ContentStringFormatProperty = DependencyProperty.Register(
"ContentStringFormat", typeof(string), typeof(Divider), new PropertyMetadata(default(string)));
public string ContentStringFormat
{
get => (string) GetValue(ContentStringFormatProperty);
set => SetValue(ContentStringFormatProperty, value);
}
public string ContentStringFormat
{
get => (string) GetValue(ContentStringFormatProperty);
set => SetValue(ContentStringFormatProperty, value);
}
public static readonly DependencyProperty ContentTemplateSelectorProperty = DependencyProperty.Register(
nameof(ContentTemplateSelector), typeof(DataTemplateSelector), typeof(Divider), new PropertyMetadata(default(DataTemplateSelector)));
public static readonly DependencyProperty ContentTemplateSelectorProperty = DependencyProperty.Register(
"ContentTemplateSelector", typeof(DataTemplateSelector), typeof(Divider), new PropertyMetadata(default(DataTemplateSelector)));
public DataTemplateSelector ContentTemplateSelector
{
get => (DataTemplateSelector) GetValue(ContentTemplateSelectorProperty);
set => SetValue(ContentTemplateSelectorProperty, value);
}
public DataTemplateSelector ContentTemplateSelector
{
get => (DataTemplateSelector) GetValue(ContentTemplateSelectorProperty);
set => SetValue(ContentTemplateSelectorProperty, value);
}
public static readonly DependencyProperty LineStrokeProperty = DependencyProperty.Register(
nameof(LineStroke), typeof(Brush), typeof(Divider), new PropertyMetadata(default(Brush)));
public static readonly DependencyProperty LineStrokeProperty = DependencyProperty.Register(
"LineStroke", typeof(Brush), typeof(Divider), new PropertyMetadata(default(Brush)));
public Brush LineStroke
{
get => (Brush) GetValue(LineStrokeProperty);
set => SetValue(LineStrokeProperty, value);
}
public Brush LineStroke
{
get => (Brush) GetValue(LineStrokeProperty);
set => SetValue(LineStrokeProperty, value);
}
public static readonly DependencyProperty LineStrokeThicknessProperty = DependencyProperty.Register(
nameof(LineStrokeThickness), typeof(double), typeof(Divider), new PropertyMetadata(ValueBoxes.Double1Box));
public static readonly DependencyProperty LineStrokeThicknessProperty = DependencyProperty.Register(
"LineStrokeThickness", typeof(double), typeof(Divider), new PropertyMetadata(ValueBoxes.Double1Box));
public double LineStrokeThickness
{
get => (double) GetValue(LineStrokeThicknessProperty);
set => SetValue(LineStrokeThicknessProperty, value);
}
public double LineStrokeThickness
{
get => (double) GetValue(LineStrokeThicknessProperty);
set => SetValue(LineStrokeThicknessProperty, value);
}
public static readonly DependencyProperty LineStrokeDashArrayProperty = DependencyProperty.Register(
nameof(LineStrokeDashArray), typeof(DoubleCollection), typeof(Divider), new PropertyMetadata(new DoubleCollection()));
public static readonly DependencyProperty LineStrokeDashArrayProperty = DependencyProperty.Register(
"LineStrokeDashArray", typeof(DoubleCollection), typeof(Divider), new PropertyMetadata(new DoubleCollection()));
public DoubleCollection LineStrokeDashArray
{
get => (DoubleCollection) GetValue(LineStrokeDashArrayProperty);
set => SetValue(LineStrokeDashArrayProperty, value);
}
public DoubleCollection LineStrokeDashArray
{
get => (DoubleCollection) GetValue(LineStrokeDashArrayProperty);
set => SetValue(LineStrokeDashArrayProperty, value);
}
}
@@ -1,406 +0,0 @@
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Markup;
using System.Windows.Media;
using System.Windows.Media.Animation;
using HandyControl.Data;
using HandyControl.Interactivity;
using HandyControl.Tools;
namespace HandyControl.Controls
{
[ContentProperty("Content")]
public class Drawer : FrameworkElement
{
private Storyboard _storyboard;
private AdornerContainer _container;
private ContentControl _animationControl;
private TranslateTransform _translateTransform;
private double _animationLength;
private string _animationPropertyName;
private FrameworkElement _maskElement;
private AdornerLayer _layer;
private System.Windows.Window _window;
private UIElement _windowContentElement;
private Point _contentRenderTransformOrigin;
static Drawer()
{
DataContextProperty.OverrideMetadata(typeof(Drawer), new FrameworkPropertyMetadata(DataContextPropertyChanged));
}
public Drawer()
{
Loaded += Drawer_Loaded;
Unloaded += Drawer_Unloaded;
}
private void Drawer_Loaded(object sender, RoutedEventArgs e)
{
if (IsOpen)
{
OnIsOpenChanged(true);
}
}
private void Drawer_Unloaded(object sender, RoutedEventArgs e)
{
Loaded -= Drawer_Loaded;
if (_maskElement != null)
{
_maskElement.PreviewMouseLeftButtonDown -= MaskElement_PreviewMouseLeftButtonDown;
}
if (_storyboard != null)
{
_storyboard.Completed -= Storyboard_Completed;
}
}
private static void DataContextPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) =>
((Drawer) d).OnDataContextPropertyChanged(e);
private void OnDataContextPropertyChanged(DependencyPropertyChangedEventArgs e) => UpdateDataContext(_animationControl, e.OldValue, e.NewValue);
public static readonly RoutedEvent OpenedEvent =
EventManager.RegisterRoutedEvent("Opened", RoutingStrategy.Bubble,
typeof(RoutedEventHandler), typeof(Drawer));
public event RoutedEventHandler Opened
{
add => AddHandler(OpenedEvent, value);
remove => RemoveHandler(OpenedEvent, value);
}
public static readonly RoutedEvent ClosedEvent =
EventManager.RegisterRoutedEvent("Closed", RoutingStrategy.Bubble,
typeof(RoutedEventHandler), typeof(Drawer));
public event RoutedEventHandler Closed
{
add => AddHandler(ClosedEvent, value);
remove => RemoveHandler(ClosedEvent, value);
}
public static readonly DependencyProperty IsOpenProperty = DependencyProperty.Register(
"IsOpen", typeof(bool), typeof(Drawer), new FrameworkPropertyMetadata(ValueBoxes.FalseBox, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnIsOpenChanged));
private static void OnIsOpenChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var ctl = (Drawer) d;
ctl.OnIsOpenChanged((bool) e.NewValue);
}
public bool IsOpen
{
get => (bool) GetValue(IsOpenProperty);
set => SetValue(IsOpenProperty, ValueBoxes.BooleanBox(value));
}
public static readonly DependencyProperty MaskCanCloseProperty = DependencyProperty.Register(
"MaskCanClose", typeof(bool), typeof(Drawer), new PropertyMetadata(ValueBoxes.TrueBox));
public bool MaskCanClose
{
get => (bool) GetValue(MaskCanCloseProperty);
set => SetValue(MaskCanCloseProperty, ValueBoxes.BooleanBox(value));
}
public static readonly DependencyProperty ShowMaskProperty = DependencyProperty.Register(
"ShowMask", typeof(bool), typeof(Drawer), new PropertyMetadata(ValueBoxes.TrueBox));
public bool ShowMask
{
get => (bool) GetValue(ShowMaskProperty);
set => SetValue(ShowMaskProperty, ValueBoxes.BooleanBox(value));
}
public static readonly DependencyProperty DockProperty = DependencyProperty.Register(
"Dock", typeof(Dock), typeof(Drawer), new PropertyMetadata(default(Dock)));
public Dock Dock
{
get => (Dock) GetValue(DockProperty);
set => SetValue(DockProperty, value);
}
public static readonly DependencyProperty ShowModeProperty = DependencyProperty.Register(
"ShowMode", typeof(DrawerShowMode), typeof(Drawer), new PropertyMetadata(default(DrawerShowMode)));
public DrawerShowMode ShowMode
{
get => (DrawerShowMode) GetValue(ShowModeProperty);
set => SetValue(ShowModeProperty, value);
}
public static readonly DependencyProperty MaskBrushProperty = DependencyProperty.Register(
"MaskBrush", typeof(Brush), typeof(Drawer), new PropertyMetadata(default(Brush)));
public Brush MaskBrush
{
get => (Brush) GetValue(MaskBrushProperty);
set => SetValue(MaskBrushProperty, value);
}
public static readonly DependencyProperty ContentProperty = DependencyProperty.Register(
"Content", typeof(object), typeof(Drawer), new PropertyMetadata(default(object)));
public object Content
{
get => GetValue(ContentProperty);
set => SetValue(ContentProperty, value);
}
private void CreateContainer()
{
_storyboard = new Storyboard();
_storyboard.Completed += Storyboard_Completed;
_translateTransform = new TranslateTransform();
_animationControl = new ContentControl
{
Content = Content,
RenderTransform = _translateTransform,
DataContext = this
};
var panel = new SimplePanel
{
ClipToBounds = true
};
if (ShowMask)
{
_maskElement = new Border
{
Background = MaskBrush,
Opacity = 0
};
_maskElement.PreviewMouseLeftButtonDown += MaskElement_PreviewMouseLeftButtonDown;
panel.Children.Add(_maskElement);
}
_animationControl.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
var size = _animationControl.DesiredSize;
switch (Dock)
{
case Dock.Left:
_animationControl.HorizontalAlignment = HorizontalAlignment.Left;
_animationControl.VerticalAlignment = VerticalAlignment.Stretch;
_translateTransform.X = -size.Width;
_animationLength = -size.Width;
_animationPropertyName = "(UIElement.RenderTransform).(TranslateTransform.X)";
break;
case Dock.Top:
_animationControl.HorizontalAlignment = HorizontalAlignment.Stretch;
_animationControl.VerticalAlignment = VerticalAlignment.Top;
_translateTransform.Y = -size.Height;
_animationLength = -size.Height;
_animationPropertyName = "(UIElement.RenderTransform).(TranslateTransform.Y)";
break;
case Dock.Right:
_animationControl.HorizontalAlignment = HorizontalAlignment.Right;
_animationControl.VerticalAlignment = VerticalAlignment.Stretch;
_translateTransform.X = size.Width;
_animationLength = size.Width;
_animationPropertyName = "(UIElement.RenderTransform).(TranslateTransform.X)";
break;
case Dock.Bottom:
_animationControl.HorizontalAlignment = HorizontalAlignment.Stretch;
_animationControl.VerticalAlignment = VerticalAlignment.Bottom;
_translateTransform.Y = size.Height;
_animationLength = size.Height;
_animationPropertyName = "(UIElement.RenderTransform).(TranslateTransform.Y)";
break;
default:
throw new ArgumentOutOfRangeException();
}
_animationControl.DataContext = DataContext;
_animationControl.CommandBindings.Clear();
_animationControl.CommandBindings.Add(new CommandBinding(ControlCommands.Close, (s, e) => SetCurrentValue(IsOpenProperty, ValueBoxes.FalseBox)));
panel.Children.Add(_animationControl);
_container = new AdornerContainer(_layer)
{
Child = panel,
ClipToBounds = true
};
}
private void Storyboard_Completed(object sender, EventArgs e)
{
if (!IsOpen)
{
_windowContentElement.SetCurrentValue(RenderTransformOriginProperty, _contentRenderTransformOrigin);
_layer.Remove(_container);
RaiseEvent(new RoutedEventArgs(ClosedEvent, this));
}
else
{
RaiseEvent(new RoutedEventArgs(OpenedEvent, this));
}
}
private void OnIsOpenChanged(bool isOpen)
{
if (Content == null || DesignerHelper.IsInDesignMode) return;
_window = WindowHelper.GetActiveWindow();
if (_window == null) return;
_windowContentElement = _window.Content as UIElement;
if (_windowContentElement == null) return;
_contentRenderTransformOrigin = _windowContentElement.RenderTransformOrigin;
var decorator = VisualHelper.GetChild<AdornerDecorator>(_window);
_layer = decorator?.AdornerLayer;
if (_layer == null) return;
if (_container == null)
{
CreateContainer();
}
switch (ShowMode)
{
case DrawerShowMode.Push:
ShowByPush(isOpen);
break;
case DrawerShowMode.Press:
_windowContentElement.SetCurrentValue(RenderTransformOriginProperty, new Point(0.5, 0.5));
ShowByPress(isOpen);
break;
}
if (isOpen)
{
if (_maskElement != null)
{
var maskAnimation = AnimationHelper.CreateAnimation(1);
Storyboard.SetTarget(maskAnimation, _maskElement);
Storyboard.SetTargetProperty(maskAnimation, new PropertyPath(OpacityProperty.Name));
_storyboard.Children.Add(maskAnimation);
}
var drawerAnimation = AnimationHelper.CreateAnimation(0);
Storyboard.SetTarget(drawerAnimation, _animationControl);
Storyboard.SetTargetProperty(drawerAnimation, new PropertyPath(_animationPropertyName));
_storyboard.Children.Add(drawerAnimation);
_layer.Remove(_container);
_layer.Add(_container);
}
else
{
if (_maskElement != null)
{
var maskAnimation = AnimationHelper.CreateAnimation(0);
Storyboard.SetTarget(maskAnimation, _maskElement);
Storyboard.SetTargetProperty(maskAnimation, new PropertyPath(OpacityProperty.Name));
_storyboard.Children.Add(maskAnimation);
}
var drawerAnimation = AnimationHelper.CreateAnimation(_animationLength);
Storyboard.SetTarget(drawerAnimation, _animationControl);
Storyboard.SetTargetProperty(drawerAnimation, new PropertyPath(_animationPropertyName));
_storyboard.Children.Add(drawerAnimation);
}
_storyboard.Begin();
}
private void ShowByPush(bool isOpen)
{
string animationPropertyName;
switch (Dock)
{
case Dock.Left:
case Dock.Right:
animationPropertyName = "(UIElement.RenderTransform).(TranslateTransform.X)";
_windowContentElement.RenderTransform = new TranslateTransform
{
X = isOpen ? 0 : -_animationLength
};
break;
case Dock.Top:
case Dock.Bottom:
animationPropertyName = "(UIElement.RenderTransform).(TranslateTransform.Y)";
_windowContentElement.RenderTransform = new TranslateTransform
{
Y = isOpen ? 0 : -_animationLength
};
break;
default:
throw new ArgumentOutOfRangeException();
}
var animation = isOpen
? AnimationHelper.CreateAnimation(-_animationLength)
: AnimationHelper.CreateAnimation(0);
Storyboard.SetTarget(animation, _windowContentElement);
Storyboard.SetTargetProperty(animation, new PropertyPath(animationPropertyName));
_storyboard.Children.Add(animation);
}
private void ShowByPress(bool isOpen)
{
_windowContentElement.RenderTransform = isOpen
? new ScaleTransform()
: new ScaleTransform
{
ScaleX = 0.9,
ScaleY = 0.9
};
var animationX = isOpen
? AnimationHelper.CreateAnimation(.9)
: AnimationHelper.CreateAnimation(1);
Storyboard.SetTarget(animationX, _windowContentElement);
Storyboard.SetTargetProperty(animationX, new PropertyPath("(UIElement.RenderTransform).(ScaleTransform.ScaleX)"));
_storyboard.Children.Add(animationX);
var animationY = isOpen
? AnimationHelper.CreateAnimation(.9)
: AnimationHelper.CreateAnimation(1);
Storyboard.SetTarget(animationY, _windowContentElement);
Storyboard.SetTargetProperty(animationY, new PropertyPath("(UIElement.RenderTransform).(ScaleTransform.ScaleY)"));
_storyboard.Children.Add(animationY);
}
private void MaskElement_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (MaskCanClose)
{
SetCurrentValue(IsOpenProperty, ValueBoxes.FalseBox);
}
}
private void UpdateDataContext(FrameworkElement target, object oldValue, object newValue)
{
if (target == null || BindingOperations.GetBindingExpression(target, DataContextProperty) != null) return;
if (ReferenceEquals(this, target.DataContext) || Equals(oldValue, target.DataContext))
{
target.DataContext = newValue ?? this;
}
}
}
}
@@ -1,26 +1,35 @@
using System.Windows;
using System.Windows.Controls;
using HandyControl.Data;
namespace HandyControl.Controls
namespace HandyControl.Controls;
public class Empty : ContentControl
{
public class Empty : ContentControl
public static readonly DependencyProperty DescriptionProperty = DependencyProperty.Register(
nameof(Description), typeof(object), typeof(Empty), new PropertyMetadata(default(object)));
public object Description
{
public static readonly DependencyProperty DescriptionProperty = DependencyProperty.Register(
"Description", typeof(object), typeof(Empty), new PropertyMetadata(default(object)));
public object Description
{
get => GetValue(DescriptionProperty);
set => SetValue(DescriptionProperty, value);
}
public static readonly DependencyProperty LogoProperty = DependencyProperty.Register(
"Logo", typeof(object), typeof(Empty), new PropertyMetadata(default(object)));
public object Logo
{
get => GetValue(LogoProperty);
set => SetValue(LogoProperty, value);
}
get => GetValue(DescriptionProperty);
set => SetValue(DescriptionProperty, value);
}
public static readonly DependencyProperty LogoProperty = DependencyProperty.Register(
nameof(Logo), typeof(object), typeof(Empty), new PropertyMetadata(default(object)));
public object Logo
{
get => GetValue(LogoProperty);
set => SetValue(LogoProperty, value);
}
public static readonly DependencyProperty ShowEmptyProperty = DependencyProperty.RegisterAttached(
"ShowEmpty", typeof(bool), typeof(Empty), new FrameworkPropertyMetadata(ValueBoxes.FalseBox, FrameworkPropertyMetadataOptions.Inherits));
public static void SetShowEmpty(DependencyObject element, bool value)
=> element.SetValue(ShowEmptyProperty, ValueBoxes.BooleanBox(value));
public static bool GetShowEmpty(DependencyObject element)
=> (bool) element.GetValue(ShowEmptyProperty);
}
@@ -5,96 +5,95 @@ using HandyControl.Data;
using HandyControl.Tools;
using HandyControl.Tools.Extension;
namespace HandyControl.Controls
namespace HandyControl.Controls;
public class GotoTop : Button
{
public class GotoTop : Button
private Action _gotoTopAction;
private System.Windows.Controls.ScrollViewer _scrollViewer;
public static readonly DependencyProperty TargetProperty = DependencyProperty.Register(
nameof(Target), typeof(DependencyObject), typeof(GotoTop), new PropertyMetadata(default(DependencyObject)));
public DependencyObject Target
{
private Action _gotoTopAction;
get => (DependencyObject) GetValue(TargetProperty);
set => SetValue(TargetProperty, value);
}
private System.Windows.Controls.ScrollViewer _scrollViewer;
public GotoTop() => Loaded += (s, e) => CreateGotoAction(Target);
public static readonly DependencyProperty TargetProperty = DependencyProperty.Register(
"Target", typeof(DependencyObject), typeof(GotoTop), new PropertyMetadata(default(DependencyObject)));
public DependencyObject Target
public virtual void CreateGotoAction(DependencyObject obj)
{
if (_scrollViewer != null)
{
get => (DependencyObject) GetValue(TargetProperty);
set => SetValue(TargetProperty, value);
_scrollViewer.ScrollChanged -= ScrollViewer_ScrollChanged;
}
public GotoTop() => Loaded += (s, e) => CreateGotoAction(Target);
public virtual void CreateGotoAction(DependencyObject obj)
_scrollViewer = VisualHelper.GetChild<System.Windows.Controls.ScrollViewer>(obj);
if (_scrollViewer != null)
{
if (_scrollViewer != null)
_scrollViewer.ScrollChanged += ScrollViewer_ScrollChanged;
if (_scrollViewer is ScrollViewer scrollViewerHandy && Animated && scrollViewerHandy.IsInertiaEnabled)
{
_scrollViewer.ScrollChanged -= ScrollViewer_ScrollChanged;
_gotoTopAction = () => scrollViewerHandy.ScrollToTopInternal(AnimationTime);
}
_scrollViewer = VisualHelper.GetChild<System.Windows.Controls.ScrollViewer>(obj);
if (_scrollViewer != null)
else
{
_scrollViewer.ScrollChanged += ScrollViewer_ScrollChanged;
if (_scrollViewer is ScrollViewer scrollViewerHandy && Animated && scrollViewerHandy.IsInertiaEnabled)
{
_gotoTopAction = () => scrollViewerHandy.ScrollToTopInternal(AnimationTime);
}
else
{
_gotoTopAction = () => _scrollViewer.ScrollToTop();
}
_gotoTopAction = () => _scrollViewer.ScrollToTop();
}
}
}
private void ScrollViewer_ScrollChanged(object sender, ScrollChangedEventArgs e)
private void ScrollViewer_ScrollChanged(object sender, ScrollChangedEventArgs e)
{
if (AutoHiding)
{
if (AutoHiding)
{
this.Show(e.VerticalOffset >= HidingHeight);
}
this.Show(e.VerticalOffset >= HidingHeight);
}
}
public static readonly DependencyProperty AnimatedProperty = DependencyProperty.Register(
"Animated", typeof(bool), typeof(GotoTop), new PropertyMetadata(ValueBoxes.TrueBox));
public static readonly DependencyProperty AnimatedProperty = DependencyProperty.Register(
nameof(Animated), typeof(bool), typeof(GotoTop), new PropertyMetadata(ValueBoxes.TrueBox));
public bool Animated
{
get => (bool) GetValue(AnimatedProperty);
set => SetValue(AnimatedProperty, ValueBoxes.BooleanBox(value));
}
public bool Animated
{
get => (bool) GetValue(AnimatedProperty);
set => SetValue(AnimatedProperty, ValueBoxes.BooleanBox(value));
}
public static readonly DependencyProperty AnimationTimeProperty = DependencyProperty.Register(
"AnimationTime", typeof(double), typeof(GotoTop), new PropertyMetadata(ValueBoxes.Double200Box));
public static readonly DependencyProperty AnimationTimeProperty = DependencyProperty.Register(
nameof(AnimationTime), typeof(double), typeof(GotoTop), new PropertyMetadata(ValueBoxes.Double200Box));
public double AnimationTime
{
get => (double) GetValue(AnimationTimeProperty);
set => SetValue(AnimationTimeProperty, value);
}
public double AnimationTime
{
get => (double) GetValue(AnimationTimeProperty);
set => SetValue(AnimationTimeProperty, value);
}
public static readonly DependencyProperty HidingHeightProperty = DependencyProperty.Register(
"HidingHeight", typeof(double), typeof(GotoTop), new PropertyMetadata(ValueBoxes.Double0Box));
public static readonly DependencyProperty HidingHeightProperty = DependencyProperty.Register(
nameof(HidingHeight), typeof(double), typeof(GotoTop), new PropertyMetadata(ValueBoxes.Double0Box));
public double HidingHeight
{
get => (double) GetValue(HidingHeightProperty);
set => SetValue(HidingHeightProperty, value);
}
public double HidingHeight
{
get => (double) GetValue(HidingHeightProperty);
set => SetValue(HidingHeightProperty, value);
}
public static readonly DependencyProperty AutoHidingProperty = DependencyProperty.Register(
"AutoHiding", typeof(bool), typeof(GotoTop), new PropertyMetadata(ValueBoxes.TrueBox));
public static readonly DependencyProperty AutoHidingProperty = DependencyProperty.Register(
nameof(AutoHiding), typeof(bool), typeof(GotoTop), new PropertyMetadata(ValueBoxes.TrueBox));
public bool AutoHiding
{
get => (bool) GetValue(AutoHidingProperty);
set => SetValue(AutoHidingProperty, ValueBoxes.BooleanBox(value));
}
public bool AutoHiding
{
get => (bool) GetValue(AutoHidingProperty);
set => SetValue(AutoHidingProperty, ValueBoxes.BooleanBox(value));
}
protected override void OnClick()
{
base.OnClick();
protected override void OnClick()
{
base.OnClick();
_gotoTopAction?.Invoke();
}
_gotoTopAction?.Invoke();
}
}
@@ -1,57 +1,57 @@
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using HandyControl.Data;
using HandyControl.Tools;
namespace HandyControl.Controls
namespace HandyControl.Controls;
public class Gravatar : ContentControl
{
public class Gravatar : ContentControl
public static readonly DependencyProperty GeneratorProperty = DependencyProperty.Register(
nameof(Generator), typeof(IGravatarGenerator), typeof(Gravatar), new PropertyMetadata(new GithubGravatarGenerator()));
public IGravatarGenerator Generator
{
public static readonly DependencyProperty GeneratorProperty = DependencyProperty.Register(
"Generator", typeof(IGravatarGenerator), typeof(Gravatar), new PropertyMetadata(new GithubGravatarGenerator()));
get => (IGravatarGenerator) GetValue(GeneratorProperty);
set => SetValue(GeneratorProperty, value);
}
public IGravatarGenerator Generator
{
get => (IGravatarGenerator) GetValue(GeneratorProperty);
set => SetValue(GeneratorProperty, value);
}
public static readonly DependencyProperty IdProperty = DependencyProperty.Register(
nameof(Id), typeof(string), typeof(Gravatar), new PropertyMetadata(default(string), OnIdChanged));
public static readonly DependencyProperty IdProperty = DependencyProperty.Register(
"Id", typeof(string), typeof(Gravatar), new PropertyMetadata(default(string), OnIdChanged));
private static void OnIdChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var ctl = (Gravatar) d;
if (ctl.Source != null) return;
ctl.Content = ctl.Generator.GetGravatar((string) e.NewValue);
}
private static void OnIdChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var ctl = (Gravatar) d;
if (ctl.Source != null) return;
ctl.Content = ctl.Generator.GetGravatar((string) e.NewValue);
}
public string Id
{
get => (string) GetValue(IdProperty);
set => SetValue(IdProperty, value);
}
public string Id
{
get => (string) GetValue(IdProperty);
set => SetValue(IdProperty, value);
}
public static readonly DependencyProperty SourceProperty = DependencyProperty.Register(
nameof(Source), typeof(ImageSource), typeof(Gravatar), new PropertyMetadata(default(ImageSource), OnSourceChanged));
public static readonly DependencyProperty SourceProperty = DependencyProperty.Register(
"Source", typeof(ImageSource), typeof(Gravatar), new PropertyMetadata(default(ImageSource), OnSourceChanged));
private static void OnSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var ctl = (Gravatar) d;
var v = (ImageSource) e.NewValue;
private static void OnSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var ctl = (Gravatar) d;
var v = (ImageSource) e.NewValue;
ctl.Background = v != null
? new ImageBrush(v)
{
Stretch = Stretch.UniformToFill
}
: ResourceHelper.GetResourceInternal<Brush>(ResourceToken.SecondaryRegionBrush);
}
ctl.Background = v != null
? new ImageBrush(v)
{
Stretch = Stretch.UniformToFill
}
: ResourceHelper.GetResource<Brush>("SecondaryRegionBrush");
}
public ImageSource Source
{
get => (ImageSource) GetValue(SourceProperty);
set => SetValue(SourceProperty, value);
}
public ImageSource Source
{
get => (ImageSource) GetValue(SourceProperty);
set => SetValue(SourceProperty, value);
}
}
@@ -7,154 +7,153 @@ using HandyControl.Interactivity;
using HandyControl.Tools;
using HandyControl.Tools.Extension;
namespace HandyControl.Controls
namespace HandyControl.Controls;
[TemplatePart(Name = ElementVisualBrush, Type = typeof(VisualBrush))]
public class Magnifier : AdornerElement
{
[TemplatePart(Name = ElementVisualBrush, Type = typeof(VisualBrush))]
public class Magnifier : AdornerElement
private AdornerContainer _adornerContainer;
private Size _viewboxSize;
private const string ElementVisualBrush = "PART_VisualBrush";
private VisualBrush _visualBrush = new();
private readonly TranslateTransform _translateTransform;
public static readonly DependencyProperty HorizontalOffsetProperty = DependencyProperty.Register(
nameof(HorizontalOffset), typeof(double), typeof(Magnifier), new PropertyMetadata(ValueBoxes.Double0Box));
public double HorizontalOffset
{
private AdornerContainer _adornerContainer;
get => (double) GetValue(HorizontalOffsetProperty);
set => SetValue(HorizontalOffsetProperty, value);
}
private Size _viewboxSize;
public static readonly DependencyProperty VerticalOffsetProperty = DependencyProperty.Register(
nameof(VerticalOffset), typeof(double), typeof(Magnifier), new PropertyMetadata(ValueBoxes.Double0Box));
private const string ElementVisualBrush = "PART_VisualBrush";
public double VerticalOffset
{
get => (double) GetValue(VerticalOffsetProperty);
set => SetValue(VerticalOffsetProperty, value);
}
private VisualBrush _visualBrush = new VisualBrush();
public static Magnifier Default => new();
private readonly TranslateTransform _translateTransform;
public static readonly DependencyProperty ScaleProperty = DependencyProperty.Register(
nameof(Scale), typeof(double), typeof(Magnifier), new PropertyMetadata(5.0, OnScaleChanged), ValidateHelper.IsInRangeOfPosDouble);
public static readonly DependencyProperty HorizontalOffsetProperty = DependencyProperty.Register(
"HorizontalOffset", typeof(double), typeof(Magnifier), new PropertyMetadata(ValueBoxes.Double0Box));
private static void OnScaleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) => ((Magnifier) d).UpdateViewboxSize();
public double HorizontalOffset
public double Scale
{
get => (double) GetValue(ScaleProperty);
set => SetValue(ScaleProperty, value);
}
public Magnifier()
{
_translateTransform = new TranslateTransform();
RenderTransform = _translateTransform;
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
var visualBrush = GetTemplateChild(ElementVisualBrush) as VisualBrush ?? new VisualBrush();
visualBrush.Viewbox = _visualBrush.Viewbox;
_visualBrush = visualBrush;
}
protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
{
base.OnRenderSizeChanged(sizeInfo);
UpdateViewboxSize();
}
private void UpdateViewboxSize() => _viewboxSize = new Size(ActualWidth / Scale, ActualHeight / Scale);
protected sealed override void OnTargetChanged(FrameworkElement element, bool isNew)
{
base.OnTargetChanged(element, isNew);
if (element == null) return;
if (!isNew)
{
get => (double) GetValue(HorizontalOffsetProperty);
set => SetValue(HorizontalOffsetProperty, value);
element.MouseEnter -= Element_MouseEnter;
element.MouseLeave -= Element_MouseLeave;
element.MouseMove -= Element_MouseMove;
ElementTarget = null;
}
public static readonly DependencyProperty VerticalOffsetProperty = DependencyProperty.Register(
"VerticalOffset", typeof(double), typeof(Magnifier), new PropertyMetadata(ValueBoxes.Double0Box));
public double VerticalOffset
else
{
get => (double) GetValue(VerticalOffsetProperty);
set => SetValue(VerticalOffsetProperty, value);
}
public static Magnifier Default => new Magnifier();
public static readonly DependencyProperty ScaleProperty = DependencyProperty.Register(
"Scale", typeof(double), typeof(Magnifier), new PropertyMetadata(5.0, OnScaleChanged), ValidateHelper.IsInRangeOfPosDouble);
private static void OnScaleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) => ((Magnifier) d).UpdateViewboxSize();
public double Scale
{
get => (double) GetValue(ScaleProperty);
set => SetValue(ScaleProperty, value);
}
public Magnifier()
{
_translateTransform = new TranslateTransform();
RenderTransform = _translateTransform;
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
var visualBrush = GetTemplateChild(ElementVisualBrush) as VisualBrush ?? new VisualBrush();
visualBrush.Viewbox = _visualBrush.Viewbox;
_visualBrush = visualBrush;
}
protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
{
base.OnRenderSizeChanged(sizeInfo);
UpdateViewboxSize();
}
private void UpdateViewboxSize() => _viewboxSize = new Size(ActualWidth / Scale, ActualHeight / Scale);
protected sealed override void OnTargetChanged(FrameworkElement element, bool isNew)
{
base.OnTargetChanged(element, isNew);
if (element == null) return;
if (!isNew)
{
element.MouseEnter -= Element_MouseEnter;
element.MouseLeave -= Element_MouseLeave;
element.MouseMove -= Element_MouseMove;
ElementTarget = null;
}
else
{
element.MouseEnter += Element_MouseEnter;
element.MouseLeave += Element_MouseLeave;
element.MouseMove += Element_MouseMove;
ElementTarget = element;
}
}
protected override void Dispose() => HideAdornerElement();
private void UpdateLocation()
{
var targetPoint = Mouse.GetPosition(Target);
var subX = targetPoint.X - _visualBrush.Viewbox.Width / 2;
var subY = targetPoint.Y - _visualBrush.Viewbox.Height / 2;
var targetVector = VisualTreeHelper.GetOffset(Target);
_visualBrush.Viewbox = new Rect(new Point(subX + targetVector.X, subY + targetVector.Y), _viewboxSize);
var adornerPoint = Mouse.GetPosition(_adornerContainer);
_translateTransform.X = adornerPoint.X + HorizontalOffset;
_translateTransform.Y = adornerPoint.Y + VerticalOffset;
}
private void Element_MouseMove(object sender, MouseEventArgs e) => UpdateLocation();
private void Element_MouseLeave(object sender, MouseEventArgs e) => HideAdornerElement();
private void Element_MouseEnter(object sender, MouseEventArgs e) => ShowAdornerElement();
private void HideAdornerElement()
{
if (_adornerContainer == null) return;
var layer = AdornerLayer.GetAdornerLayer(Target);
if (layer != null)
{
layer.Remove(_adornerContainer);
}
else if (_adornerContainer != null && _adornerContainer.Parent is AdornerLayer parent)
{
parent.Remove(_adornerContainer);
}
if (_adornerContainer != null)
{
_adornerContainer.Child = null;
_adornerContainer = null;
}
}
private void ShowAdornerElement()
{
if (_adornerContainer == null)
{
var layer = AdornerLayer.GetAdornerLayer(Target);
if (layer == null) return;
_adornerContainer = new AdornerContainer(layer)
{
Child = this
};
layer.Add(_adornerContainer);
}
this.Show();
element.MouseEnter += Element_MouseEnter;
element.MouseLeave += Element_MouseLeave;
element.MouseMove += Element_MouseMove;
ElementTarget = element;
}
}
protected override void Dispose() => HideAdornerElement();
private void UpdateLocation()
{
var targetPoint = Mouse.GetPosition(Target);
var subX = targetPoint.X - _visualBrush.Viewbox.Width / 2;
var subY = targetPoint.Y - _visualBrush.Viewbox.Height / 2;
var targetVector = VisualTreeHelper.GetOffset(Target);
_visualBrush.Viewbox = new Rect(new Point(subX + targetVector.X, subY + targetVector.Y), _viewboxSize);
var adornerPoint = Mouse.GetPosition(_adornerContainer);
_translateTransform.X = adornerPoint.X + HorizontalOffset;
_translateTransform.Y = adornerPoint.Y + VerticalOffset;
}
private void Element_MouseMove(object sender, MouseEventArgs e) => UpdateLocation();
private void Element_MouseLeave(object sender, MouseEventArgs e) => HideAdornerElement();
private void Element_MouseEnter(object sender, MouseEventArgs e) => ShowAdornerElement();
private void HideAdornerElement()
{
if (_adornerContainer == null) return;
var layer = AdornerLayer.GetAdornerLayer(Target);
if (layer != null)
{
layer.Remove(_adornerContainer);
}
else if (_adornerContainer is { Parent: AdornerLayer parent })
{
parent.Remove(_adornerContainer);
}
if (_adornerContainer != null)
{
_adornerContainer.Child = null;
_adornerContainer = null;
}
}
private void ShowAdornerElement()
{
if (_adornerContainer == null)
{
var layer = AdornerLayer.GetAdornerLayer(Target);
if (layer == null) return;
_adornerContainer = new AdornerContainer(layer)
{
Child = this
};
layer.Add(_adornerContainer);
}
this.Show();
}
}
Loaded 100 of 1325 files, more files were not shown because too many files have changed in this diff. Show more