Files
SZMCToolkit.RevitAddins/WpfAppTest/ElementHelper.cs
2026-02-23 10:28:26 +08:00

237 lines
10 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using System.ComponentModel;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shell;
namespace ModernWpf.Controls
{
// ==========================================
// 1. 附加属性帮助类 (新增 Prefix)
// ==========================================
public class ElementHelper : DependencyObject
{
// ... 原有的属性保持不变 (Watermark, Icon, CornerRadius, Header) ...
public static readonly DependencyProperty WatermarkProperty = DependencyProperty.RegisterAttached("Watermark", typeof(string), typeof(ElementHelper), new PropertyMetadata(""));
public static void SetWatermark(DependencyObject element, string value) => element.SetValue(WatermarkProperty, value);
public static string GetWatermark(DependencyObject element) => (string)element.GetValue(WatermarkProperty);
public static readonly DependencyProperty IconProperty = DependencyProperty.RegisterAttached("Icon", typeof(Geometry), typeof(ElementHelper), new PropertyMetadata(null));
public static void SetIcon(DependencyObject element, Geometry value) => element.SetValue(IconProperty, value);
public static Geometry GetIcon(DependencyObject element) => (Geometry)element.GetValue(IconProperty);
public static readonly DependencyProperty CornerRadiusProperty = DependencyProperty.RegisterAttached("CornerRadius", typeof(CornerRadius), typeof(ElementHelper), new PropertyMetadata(new CornerRadius(4)));
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 HeaderProperty = DependencyProperty.RegisterAttached("Header", typeof(string), typeof(ElementHelper), new PropertyMetadata(""));
public static void SetHeader(DependencyObject element, string value) => element.SetValue(HeaderProperty, value);
public static string GetHeader(DependencyObject element) => (string)element.GetValue(HeaderProperty);
// === 新增 Prefix 属性 (支持文字或UI元素) ===
public static readonly DependencyProperty PrefixProperty = DependencyProperty.RegisterAttached("Prefix", typeof(object), typeof(ElementHelper), new PropertyMetadata(null));
public static void SetPrefix(DependencyObject element, object value) => element.SetValue(PrefixProperty, value);
public static object GetPrefix(DependencyObject element) => element.GetValue(PrefixProperty);
}
// ==========================================
// 2. ModernWindow (保持不变)
// ==========================================
public class ModernWindow : Window
{
static ModernWindow()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(ModernWindow), new FrameworkPropertyMetadata(typeof(ModernWindow)));
}
public ModernWindow()
{
var chrome = new WindowChrome
{
CaptionHeight = 32,
ResizeBorderThickness = new Thickness(4),
// 让系统边框可见,从而触发 Win11 原生圆角和阴影
GlassFrameThickness = new Thickness(1),
UseAeroCaptionButtons = false
};
WindowChrome.SetWindowChrome(this, chrome);
CommandBindings.Add(new CommandBinding(SystemCommands.CloseWindowCommand, (s, e) => SystemCommands.CloseWindow(this)));
CommandBindings.Add(new CommandBinding(SystemCommands.MaximizeWindowCommand, (s, e) => SystemCommands.MaximizeWindow(this)));
CommandBindings.Add(new CommandBinding(SystemCommands.MinimizeWindowCommand, (s, e) => SystemCommands.MinimizeWindow(this)));
CommandBindings.Add(new CommandBinding(SystemCommands.RestoreWindowCommand, (s, e) => SystemCommands.RestoreWindow(this)));
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
if (GetTemplateChild("PART_TitleBar") is Border titleBar)
{
titleBar.MouseLeftButtonDown += (s, e) =>
{
if (e.ClickCount == 2)
{
if (ResizeMode != ResizeMode.NoResize)
WindowState = WindowState == WindowState.Maximized ? WindowState.Normal : WindowState.Maximized;
}
else
{
DragMove();
}
};
}
}
}
public class SearchableComboBox : ComboBox
{
private TextBox _editableTextBox;
private bool _isInternalOperation;
static SearchableComboBox()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(SearchableComboBox), new FrameworkPropertyMetadata(typeof(SearchableComboBox)));
}
public SearchableComboBox()
{
IsEditable = true;
IsTextSearchEnabled = false;
StaysOpenOnEdit = true;
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
_editableTextBox = GetTemplateChild("PART_EditableTextBox") as TextBox;
if (_editableTextBox != null)
{
_editableTextBox.TextChanged += OnEditableTextBoxTextChanged;
// 点击时全选
_editableTextBox.GotFocus += (s, e) => _editableTextBox.SelectAll();
}
}
protected override void OnSelectionChanged(SelectionChangedEventArgs e)
{
if (_isInternalOperation) return;
_isInternalOperation = true;
base.OnSelectionChanged(e);
// 重点 1选中项改变后必须彻底清空过滤器否则下次打开下拉框只剩下一项
this.Items.Filter = null;
_isInternalOperation = false;
}
private void OnEditableTextBoxTextChanged(object sender, TextChangedEventArgs e)
{
if (_isInternalOperation) return;
string searchText = _editableTextBox.Text;
string selectedText = GetItemDisplayText(SelectedItem);
// 重点 2断开锁定逻辑
// 如果文本框的内容和当前选中项的文本不一致,说明用户正在打字替换内容
if (SelectedItem != null && searchText != selectedText)
{
_isInternalOperation = true;
SelectedItem = null; // 必须将 SelectedItem 设为 null否则 WPF 会强行还原文本
_isInternalOperation = false;
}
// 重点 3执行过滤
this.Items.Filter = item =>
{
if (string.IsNullOrEmpty(searchText)) return true;
string itemText = GetItemDisplayText(item);
// 只要包含关键字就显示(忽略大小写)
return itemText.IndexOf(searchText, StringComparison.OrdinalIgnoreCase) >= 0;
};
// 自动打开下拉框
if (!IsDropDownOpen && _editableTextBox.IsFocused && !string.IsNullOrEmpty(searchText))
{
IsDropDownOpen = true;
}
}
private string GetItemDisplayText(object item)
{
if (item == null) return string.Empty;
if (item is ComboBoxItem cbi) return cbi.Content?.ToString() ?? string.Empty;
// 兼容 DisplayMemberPath 或 TextSearch.TextPath
string path = DisplayMemberPath;
if (string.IsNullOrEmpty(path)) path = TextSearch.GetTextPath(this);
if (!string.IsNullOrEmpty(path))
{
var prop = item.GetType().GetProperty(path);
if (prop != null) return prop.GetValue(item)?.ToString() ?? string.Empty;
}
return item.ToString();
}
}
public class ModernPasswordBox : Control
{
static ModernPasswordBox()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(ModernPasswordBox), new FrameworkPropertyMetadata(typeof(ModernPasswordBox)));
}
public string Password
{
get => (string)GetValue(PasswordProperty);
set => SetValue(PasswordProperty, value);
}
public static readonly DependencyProperty PasswordProperty = DependencyProperty.Register("Password", typeof(string), typeof(ModernPasswordBox), new PropertyMetadata(string.Empty));
// 辅助属性:标记是否为空,用于 XAML 控制水印显隐
public bool IsEmpty
{
get => (bool)GetValue(IsEmptyProperty);
private set => SetValue(IsEmptyProperty, value);
}
public static readonly DependencyProperty IsEmptyProperty = DependencyProperty.Register("IsEmpty", typeof(bool), typeof(ModernPasswordBox), new PropertyMetadata(true));
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
var pb = GetTemplateChild("PART_PasswordBox") as PasswordBox;
var tb = GetTemplateChild("PART_TextBox") as TextBox;
var btn = GetTemplateChild("PART_ToggleBtn") as ToggleButton;
if (pb != null && tb != null && btn != null)
{
pb.PasswordChanged += (s, e) =>
{
if (pb.Visibility == Visibility.Visible)
{
Password = pb.Password;
IsEmpty = string.IsNullOrEmpty(pb.Password);
}
};
tb.TextChanged += (s, e) =>
{
if (tb.Visibility == Visibility.Visible)
{
Password = tb.Text;
IsEmpty = string.IsNullOrEmpty(tb.Text);
}
};
btn.Checked += (s, e) => { tb.Text = pb.Password; tb.Visibility = Visibility.Visible; pb.Visibility = Visibility.Collapsed; };
btn.Unchecked += (s, e) => { pb.Password = tb.Text; pb.Visibility = Visibility.Visible; tb.Visibility = Visibility.Collapsed; };
}
}
}
}