mirror of
https://github.com/LorisYounger/VPet.git
synced 2024-08-30 18:42:36 +00:00
271 lines
8.9 KiB
C#
271 lines
8.9 KiB
C#
using HKW.WPF.Extensions;
|
|
using System.Windows;
|
|
using System.Windows.Input;
|
|
|
|
namespace HKW.WPF.Helpers;
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
public static class ElementHelper
|
|
{
|
|
#region IsEnabled
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
/// <param name="element"></param>
|
|
/// <returns></returns>
|
|
public static bool GetIsEnabled(FrameworkElement element)
|
|
{
|
|
return (bool)element.GetValue(IsEnabledProperty);
|
|
}
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
public static void SetIsEnabled(FrameworkElement element, bool value)
|
|
{
|
|
element.SetValue(IsEnabledProperty, value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 在按下指定按键时清除选中状态
|
|
/// </summary>
|
|
public static readonly DependencyProperty IsEnabledProperty =
|
|
DependencyProperty.RegisterAttached(
|
|
"IsEnabled",
|
|
typeof(bool),
|
|
typeof(ElementHelper),
|
|
new FrameworkPropertyMetadata(default(bool), IsEnabledPropertyChangedCallback)
|
|
);
|
|
|
|
private static void IsEnabledPropertyChangedCallback(
|
|
DependencyObject obj,
|
|
DependencyPropertyChangedEventArgs e
|
|
)
|
|
{
|
|
if (obj is not FrameworkElement element)
|
|
return;
|
|
element.IsEnabled = GetIsEnabled(element);
|
|
}
|
|
#endregion
|
|
|
|
#region ClearFocusOnKeyDown
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
/// <param name="element"></param>
|
|
/// <returns></returns>
|
|
public static string GetClearFocusOnKeyDown(FrameworkElement element)
|
|
{
|
|
return (string)element.GetValue(ClearFocusOnKeyDownProperty);
|
|
}
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
/// <exception cref="Exception">禁止使用此方法</exception>
|
|
public static void SetClearFocusOnKeyDown(FrameworkElement element, string value)
|
|
{
|
|
element.SetValue(ClearFocusOnKeyDownProperty, value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 在按下指定按键时清除选中状态
|
|
/// </summary>
|
|
public static readonly DependencyProperty ClearFocusOnKeyDownProperty =
|
|
DependencyProperty.RegisterAttached(
|
|
"ClearFocusOnKeyDown",
|
|
typeof(string),
|
|
typeof(ElementHelper),
|
|
new FrameworkPropertyMetadata(default(string), ClearFocusOnKeyDownPropertyChanged)
|
|
);
|
|
|
|
private static void ClearFocusOnKeyDownPropertyChanged(
|
|
DependencyObject obj,
|
|
DependencyPropertyChangedEventArgs e
|
|
)
|
|
{
|
|
if (obj is not FrameworkElement element)
|
|
return;
|
|
var keyName = GetClearFocusOnKeyDown(element);
|
|
if (Enum.TryParse<Key>(keyName, false, out _) is false)
|
|
throw new Exception($"Unknown key {keyName}");
|
|
element.KeyDown -= Element_KeyDown;
|
|
element.KeyDown += Element_KeyDown;
|
|
|
|
static void Element_KeyDown(object? sender, KeyEventArgs e)
|
|
{
|
|
if (sender is not FrameworkElement element)
|
|
return;
|
|
var key = (Key)Enum.Parse(typeof(Key), GetClearFocusOnKeyDown(element));
|
|
if (e.Key == key)
|
|
{
|
|
// 清除控件焦点
|
|
element.ClearFocus();
|
|
// 清除键盘焦点
|
|
Keyboard.ClearFocus();
|
|
}
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region UniformMinWidthGroup
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
/// <param name="element"></param>
|
|
/// <returns></returns>
|
|
public static string GetUniformMinWidthGroup(FrameworkElement element)
|
|
{
|
|
return (string)element.GetValue(UniformWidthGroupProperty);
|
|
}
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
public static void SetUniformMinWidthGroup(FrameworkElement element, string value)
|
|
{
|
|
element.SetValue(UniformWidthGroupProperty, value);
|
|
}
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
public static readonly DependencyProperty UniformWidthGroupProperty =
|
|
DependencyProperty.RegisterAttached(
|
|
"UniformMinWidthGroup",
|
|
typeof(string),
|
|
typeof(ElementHelper),
|
|
new FrameworkPropertyMetadata(null, UniformMinWidthGroupPropertyChanged)
|
|
);
|
|
|
|
/// <summary>
|
|
/// (TopParent ,(GroupName, UniformMinWidthGroupInfo))
|
|
/// </summary>
|
|
private readonly static Dictionary<
|
|
FrameworkElement,
|
|
Dictionary<string, UniformMinWidthGroupInfo>
|
|
> _uniformMinWidthGroups = new();
|
|
|
|
private static void UniformMinWidthGroupPropertyChanged(
|
|
DependencyObject obj,
|
|
DependencyPropertyChangedEventArgs e
|
|
)
|
|
{
|
|
if (obj is not FrameworkElement element)
|
|
return;
|
|
var groupName = GetUniformMinWidthGroup(element);
|
|
var topParent = element.FindTopParentOnVisualTree();
|
|
// 在设计器中会无法获取顶级元素, 会提示错误, 忽略即可
|
|
if (topParent is null)
|
|
return;
|
|
if (_uniformMinWidthGroups.TryGetValue(topParent, out var groups) is false)
|
|
{
|
|
topParent.Dispatcher.ShutdownStarted += Dispatcher_ShutdownStarted;
|
|
groups = _uniformMinWidthGroups[topParent] = new();
|
|
}
|
|
if (groups.TryGetValue(groupName, out var group) is false)
|
|
group = groups[groupName] = new();
|
|
group.Elements.Add(element);
|
|
|
|
element.SizeChanged -= Element_SizeChanged;
|
|
element.SizeChanged += Element_SizeChanged;
|
|
|
|
#region TopParent
|
|
|
|
static void Dispatcher_ShutdownStarted(object? sender, EventArgs e)
|
|
{
|
|
if (sender is not FrameworkElement element)
|
|
return;
|
|
_uniformMinWidthGroups.Remove(element);
|
|
}
|
|
#endregion
|
|
|
|
#region Element
|
|
static void Element_SizeChanged(object? sender, SizeChangedEventArgs e)
|
|
{
|
|
if (sender is not FrameworkElement element)
|
|
return;
|
|
var groupName = GetUniformMinWidthGroup(element);
|
|
var topParent = element.FindTopParentOnVisualTree();
|
|
var groups = _uniformMinWidthGroups[topParent];
|
|
var group = groups[groupName];
|
|
var maxWidthElement = group.Elements.MaxBy(i => i.ActualWidth);
|
|
if (maxWidthElement is null)
|
|
return;
|
|
|
|
if (maxWidthElement.ActualWidth == element.ActualWidth)
|
|
maxWidthElement = element;
|
|
if (maxWidthElement.ActualWidth > group.MaxWidth)
|
|
{
|
|
// 如果当前控件最大宽度的超过历史最大宽度, 表明非最大宽度列表中的控件超过了历史最大宽度
|
|
foreach (var item in group.Elements)
|
|
item.MinWidth = maxWidthElement.ActualWidth;
|
|
// 将当前控件最小宽度设为0
|
|
maxWidthElement.MinWidth = 0;
|
|
group.MaxWidthElements.Clear();
|
|
// 设为最大宽度的唯一控件
|
|
group.MaxWidthElements.Add(maxWidthElement);
|
|
group.MaxWidth = maxWidthElement.ActualWidth;
|
|
}
|
|
else if (group.MaxWidthElements.Count == 1)
|
|
{
|
|
maxWidthElement = group.MaxWidthElements.First();
|
|
// 当最大宽度控件只有一个时, 并且当前控件宽度小于历史最大宽度时, 表明需要降低全体宽度
|
|
if (group.MaxWidth > maxWidthElement.ActualWidth)
|
|
{
|
|
// 最小宽度设为0以自适应宽度
|
|
foreach (var item in group.Elements)
|
|
item.MinWidth = 0;
|
|
// 清空最大宽度列表, 让其刷新
|
|
group.MaxWidthElements.Clear();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// 将 MaxWidth 设置为 double.MaxValue 时, 可以让首次加载时进入此处
|
|
foreach (var item in group.Elements)
|
|
{
|
|
// 当控件最小宽度为0(表示其为主导宽度的控件), 并且其宽度等于最大宽度, 加入最大宽度列表
|
|
if (item.MinWidth == 0 && item.ActualWidth == maxWidthElement.ActualWidth)
|
|
{
|
|
group.MaxWidthElements.Add(item);
|
|
}
|
|
else
|
|
{
|
|
// 如果不是, 则从最大宽度列表删除, 并设置最小宽度为当前最大宽度
|
|
group.MaxWidthElements.Remove(item);
|
|
item.MinWidth = maxWidthElement.ActualWidth;
|
|
}
|
|
}
|
|
group.MaxWidth = maxWidthElement.ActualWidth;
|
|
}
|
|
}
|
|
#endregion
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
|
|
/// <summary>
|
|
/// 统一最小宽度分组信息
|
|
/// </summary>
|
|
public class UniformMinWidthGroupInfo
|
|
{
|
|
/// <summary>
|
|
/// 最后一个最大宽度
|
|
/// </summary>
|
|
public double MaxWidth { get; set; } = double.MaxValue;
|
|
|
|
/// <summary>
|
|
/// 所有控件
|
|
/// </summary>
|
|
public HashSet<FrameworkElement> Elements { get; } = new();
|
|
|
|
/// <summary>
|
|
/// 最大宽度的控件
|
|
/// </summary>
|
|
public HashSet<FrameworkElement> MaxWidthElements { get; } = new();
|
|
}
|