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