using System.Runtime.InteropServices; using System.Windows.Automation.Peers; using System.Windows.Automation.Provider; using System.Windows.Input; using Melskin.Assists; namespace Melskin.Controls; /// /// MelWindow 类继承自 WPF 的 Window 类,用于创建具有特定样式和功能的窗口。 /// 该类通过设置默认样式资源引用来自定义窗口外观,并提供属性来控制窗口的行为和内容布局。 /// public class MelWindow : Window { private const string VbMinimizeButtonName = "minimizeButton"; private const string VbMaximizeRestoreButtonName = "maximizeRestoreButton"; /// /// 用于在XAML模板中标识关闭按钮的名称。 /// 此常量字符串值为 "closeButton",用于在窗口样式应用时通过GetTemplateChild方法查找对应的关闭按钮控件。 /// private const string VbCloseButtonName = "closeButton"; /// /// /// public MelWindow() { SetResourceReference(StyleProperty, typeof(MelWindow)); //设置默认的样式资源引用 } #region 自定义标题栏按钮行为 /// protected override void OnSourceInitialized(EventArgs e) { base.OnSourceInitialized(e); var helper = new WindowInteropHelper(this); var hwndSource = HwndSource.FromHwnd(helper.Handle); hwndSource?.AddHook(HwndSourceHook); } [DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool TrackMouseEvent(ref TRACKMOUSEEVENT lpEventTrack); /// /// 跟踪鼠标事件的结构体。 /// [StructLayout(LayoutKind.Sequential)] public struct TRACKMOUSEEVENT { /// /// 结构体大小 /// public int cbSize; /// /// 控制跟踪行为的标志 /// public uint dwFlags; /// /// 要为其跟踪鼠标事件的窗口句柄 /// public IntPtr hwndTrack; /// /// 可选的悬停时间(毫秒) /// public uint dwHoverTime; /// /// 初始化 TRACKMOUSEEVENT 结构体的新实例。 /// /// A combination of flags that specify the services requested. The value should correspond to the TME_* /// constants defined by the Windows API. /// 窗口句柄 public TRACKMOUSEEVENT(uint dwFlags, IntPtr hwndTrack) { this.cbSize = Marshal.SizeOf(typeof(TRACKMOUSEEVENT)); this.dwFlags = dwFlags; this.hwndTrack = hwndTrack; this.dwHoverTime = 0; } } private const int WM_NCHITTEST = 0x0084; private const int WM_NCMOUSEMOVE = 0x00A0; private const int WM_NCMOUSELEAVE = 0x02A2; private const int WM_NCLBUTTONDOWN = 0x00A1; private const int WM_NCLBUTTONUP = 0x00A2; private const int HTMAXBUTTON = 9; private const uint TME_LEAVE = 0x00000002; private const uint TME_NONCLIENT = 0x00000010; #endregion private bool isMouseOverMaximizeButton = false; private bool isMaximizeButtonPressed = false; private IntPtr HwndSourceHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) { switch (msg) { case WM_NCHITTEST: if (IsMouseOverMaximizeButton(lParam)) { handled = true; return new IntPtr(HTMAXBUTTON); } else if (isMouseOverMaximizeButton || isMaximizeButtonPressed) { // 如果鼠标移出按钮区域,重置状态 isMouseOverMaximizeButton = false; isMaximizeButtonPressed = false; UpdateMaximizeButtonVisualState(); } break; case WM_NCMOUSEMOVE: bool isCurrentlyOver = IsMouseOverMaximizeButton(lParam); if (isCurrentlyOver != isMouseOverMaximizeButton) { isMouseOverMaximizeButton = isCurrentlyOver; UpdateMaximizeButtonVisualState(); if (isMouseOverMaximizeButton) { var tme = new TRACKMOUSEEVENT(TME_LEAVE | TME_NONCLIENT, hwnd); TrackMouseEvent(ref tme); } } break; case WM_NCMOUSELEAVE: isMouseOverMaximizeButton = false; isMaximizeButtonPressed = false; UpdateMaximizeButtonVisualState(); break; case WM_NCLBUTTONDOWN: if (isMouseOverMaximizeButton) { isMaximizeButtonPressed = true; UpdateMaximizeButtonVisualState(); handled = true; } break; case WM_NCLBUTTONUP: if (isMaximizeButtonPressed) { isMaximizeButtonPressed = false; UpdateMaximizeButtonVisualState(); if (isMouseOverMaximizeButton) { maximizeRestoreButton?.RaiseEvent(new RoutedEventArgs(Button.ClickEvent)); } handled = true; } break; } return IntPtr.Zero; } private bool IsMouseOverMaximizeButton(IntPtr lParam) { var x = (short)(lParam.ToInt32() & 0xFFFF); var y = (short)(lParam.ToInt32() >> 16); var mousePos = new Point(x, y); var windowPos = this.PointFromScreen(mousePos); if (maximizeRestoreButton is Visual maximizeButtonVisual) { var bounds = VisualTreeHelper.GetDescendantBounds(maximizeButtonVisual); var buttonTransform = maximizeButtonVisual.TransformToAncestor(this); var buttonRect = buttonTransform.TransformBounds(bounds); return buttonRect.Contains(windowPos); } return false; } private void UpdateMaximizeButtonVisualState() { string stateName; if (isMaximizeButtonPressed && isMouseOverMaximizeButton) { stateName = "Pressed"; } else if (isMouseOverMaximizeButton) { stateName = "MouseOver"; } else { stateName = "Normal"; } //VisualStateManager.GoToState(maximizeRestoreButton, stateName, true); maximizeRestoreButton?.Background = stateName switch { "Pressed" => //VisualStateManager.GoToState(maximizeRestoreButton, "Pressed", true); new SolidColorBrush((Color)ColorConverter.ConvertFromString("#33000000")), "MouseOver" => //VisualStateManager.GoToState(maximizeRestoreButton, "MouseOver", true); new SolidColorBrush((Color)ColorConverter.ConvertFromString("#1A000000")), "Normal" => Brushes.Transparent, //VisualStateManager.GoToState(maximizeRestoreButton, "Normal", true); _ => maximizeRestoreButton?.Background }; } #region Window Control Methods private void TitleBar_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { if (e.ClickCount == 2) { ToggleWindowState(); } else { this.DragMove(); } } private void maximizeRestoreButton_Click(object sender, RoutedEventArgs e) { ToggleWindowState(); } private void ToggleWindowState() { this.WindowState = (this.WindowState == WindowState.Normal) ? WindowState.Maximized : WindowState.Normal; } private void CloseButton_Click(object sender, RoutedEventArgs e) { this.Close(); } #endregion static MelWindow() { DefaultStyleKeyProperty.OverrideMetadata(typeof(MelWindow), new FrameworkPropertyMetadata(typeof(MelWindow))); } /// /// 如果窗口不活动,请让窗口的内容淡出。 /// 默认值为真(已启用)。 /// public static readonly DependencyProperty FadeContentIfInactiveProperty = DependencyProperty.Register( nameof(FadeContentIfInactive), typeof(bool), typeof(MelWindow), new FrameworkPropertyMetadata(true)); /// /// 如果窗口不活动,请让窗口的内容淡出。 /// 默认值为真(已启用)。 /// public bool FadeContentIfInactive { get => (bool)GetValue(FadeContentIfInactiveProperty); set => SetValue(FadeContentIfInactiveProperty, value); } /// /// 标题栏左侧内容模板 /// public object TitleBarLeftContent { get => (object)GetValue(TitleBarLeftContentProperty); set => SetValue(TitleBarLeftContentProperty, value); } /// /// 用于定义窗口左侧内容区域的数据模板。 /// 此依赖属性允许开发者自定义左侧内容的布局和显示方式,通过设置不同的DataTemplate来改变该区域的内容呈现形式。 /// public static readonly DependencyProperty TitleBarLeftContentProperty = DependencyProperty.Register(nameof(TitleBarLeftContent), typeof(object), typeof(MelWindow)); /// /// 标题栏右侧内容模板 /// public object TitleBarRightContent { get => (object)GetValue(TitleBarRightContentProperty); set => SetValue(TitleBarRightContentProperty, value); } /// /// 用于定义窗口右侧内容区域的数据模板。 /// 此依赖属性允许开发者自定义右侧内容的布局和显示方式,通过设置不同的DataTemplate来改变该区域的内容呈现形式。 /// public static readonly DependencyProperty TitleBarRightContentProperty = DependencyProperty.Register(nameof(TitleBarRightContent), typeof(object), typeof(MelWindow)); private Button? minimizeButton; private Button? maximizeRestoreButton; private Button? closeButton; /// public override void OnApplyTemplate() { if (minimizeButton != null) { minimizeButton.Click -= MinimizeButtonClickHandler; } minimizeButton = GetTemplateChild(VbMinimizeButtonName) as Button; if (minimizeButton != null) { minimizeButton.Click += MinimizeButtonClickHandler; } if (maximizeRestoreButton != null) { maximizeRestoreButton.Click -= MaximizeRestoreButtonClickHandler; } maximizeRestoreButton = GetTemplateChild(VbMaximizeRestoreButtonName) as Button; if (maximizeRestoreButton != null) { maximizeRestoreButton.Click += MaximizeRestoreButtonClickHandler; } if (closeButton != null) { closeButton.Click -= CloseButtonClickHandler; } closeButton = GetTemplateChild(VbCloseButtonName) as Button; if (closeButton != null) { closeButton.Click += CloseButtonClickHandler; } base.OnApplyTemplate(); } private void CloseButtonClickHandler(object sender, RoutedEventArgs args) { Close(); } private void MaximizeRestoreButtonClickHandler(object sender, RoutedEventArgs args) { WindowState = (WindowState == WindowState.Normal) ? WindowState.Maximized : WindowState.Normal; } private void MinimizeButtonClickHandler(object sender, RoutedEventArgs args) { WindowState = WindowState.Minimized; } }