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(); }