【转】【WPF】WPF无边框窗体
窗体部分代码:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Interop; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; namespace NoneWindowWPF { /// <summary> /// MainWindow.xaml 的交互逻辑 /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } private void Window_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { if (e.OriginalSource is Grid || e.OriginalSource is Border || e.OriginalSource is Window) { WindowInteropHelper wih = new WindowInteropHelper(this); WindowBehaviorHelper.SendMessage(wih.Handle, 0x00A1, (IntPtr)WindowBehaviorHelper.HitTest.HTCAPTION, IntPtr.Zero); return; } } } }
消息处理代码:
using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Interop; namespace NoneWindowWPF { public class WindowBehaviorHelper { private const int WM_NCHITTEST = 0x0084; //测试消息 private const int WM_GETMINMAXINFO = 0x0024;//大小变化 private Window WindowTarget; //目标窗口 private int WidthCorner = 4; //拐角宽度 private int ThicknessTransparentBorder = 5; //透明宽度 private int ThicknessBorder = 4; //边框宽度 private Point PointMouse = new Point(); //鼠标坐标 public enum HitTest : int //测试句柄 { #region 测试句柄 HTERROR = -2, HTTRANSPARENT = -1, HTNOWHERE = 0, HTCLIENT = 1, HTCAPTION = 2, HTSYSMENU = 3, HTGROWBOX = 4, HTSIZE = HTGROWBOX, HTMENU = 5, HTHSCROLL = 6, HTVSCROLL = 7, HTMINBUTTON = 8, HTMAXBUTTON = 9, HTLEFT = 10, HTRIGHT = 11, HTTOP = 12, HTTOPLEFT = 13, HTTOPRIGHT = 14, HTBOTTOM = 15, HTBOTTOMLEFT = 16, HTBOTTOMRIGHT = 17, HTBORDER = 18, HTREDUCE = HTMINBUTTON, HTZOOM = HTMAXBUTTON, HTSIZEFIRST = HTLEFT, HTSIZELAST = HTBOTTOMRIGHT, HTOBJECT = 19, HTCLOSE = 20, HTHELP = 21 #endregion } //构造函数 public WindowBehaviorHelper(Window window) { this.WindowTarget = window; } public IntPtr handle; //修复行为 public void RepairBehavior() { if (WindowTarget == null) return; this.WindowTarget.SourceInitialized += delegate { handle = (new WindowInteropHelper(WindowTarget)).Handle; HwndSource hwndSource = HwndSource.FromHwnd(handle); if (hwndSource != null) { hwndSource.AddHook(WindowProc); } }; } //消息循环 private IntPtr WindowProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) { switch (msg) { case WM_NCHITTEST: if (WindowTarget.WindowState != WindowState.Normal) { break; } this.PointMouse.X = (lParam.ToInt32() & 0xFFFF); this.PointMouse.Y = (lParam.ToInt32() >> 16); //窗口左上角 if (this.PointMouse.X > this.WindowTarget.Left + this.ThicknessTransparentBorder && this.PointMouse.X <= this.WindowTarget.Left + this.ThicknessTransparentBorder + this.WidthCorner && this.PointMouse.Y > this.WindowTarget.Top + this.ThicknessTransparentBorder && this.PointMouse.Y <= this.WindowTarget.Top + this.ThicknessTransparentBorder + this.WidthCorner) { handled = true; return new IntPtr((int)HitTest.HTTOPLEFT); } //窗口左下角 else if (this.PointMouse.X > this.WindowTarget.Left + this.ThicknessTransparentBorder && this.PointMouse.X <= this.WindowTarget.Left + this.ThicknessTransparentBorder + this.WidthCorner && this.PointMouse.Y < this.WindowTarget.Top + this.WindowTarget.ActualHeight - this.ThicknessTransparentBorder && this.PointMouse.Y >= this.WindowTarget.Top + this.WindowTarget.ActualHeight - this.ThicknessTransparentBorder - this.WidthCorner) { handled = true; return new IntPtr((int)HitTest.HTBOTTOMLEFT); } //窗口右上角 else if (this.PointMouse.X < this.WindowTarget.Left + this.WindowTarget.ActualWidth - this.ThicknessTransparentBorder && this.PointMouse.X >= this.WindowTarget.Left + this.WindowTarget.ActualWidth - this.ThicknessTransparentBorder - this.WidthCorner && this.PointMouse.Y > this.WindowTarget.Top + this.ThicknessTransparentBorder && this.PointMouse.Y <= this.WindowTarget.Top + this.ThicknessTransparentBorder + this.WidthCorner) { handled = true; return new IntPtr((int)HitTest.HTTOPRIGHT); } //窗口右下角 else if (this.PointMouse.X < this.WindowTarget.Left + this.WindowTarget.ActualWidth - this.ThicknessTransparentBorder && this.PointMouse.X >= this.WindowTarget.Left + this.WindowTarget.ActualWidth - this.ThicknessTransparentBorder - this.WidthCorner && this.PointMouse.Y < this.WindowTarget.Top + this.WindowTarget.ActualHeight - this.ThicknessTransparentBorder && this.PointMouse.Y >= this.WindowTarget.Top + this.WindowTarget.ActualHeight - this.ThicknessTransparentBorder - this.WidthCorner) { handled = true; return new IntPtr((int)HitTest.HTBOTTOMRIGHT); } //窗口左侧 else if (this.PointMouse.X > this.WindowTarget.Left + this.ThicknessTransparentBorder && this.PointMouse.X <= this.WindowTarget.Left + this.ThicknessTransparentBorder + this.ThicknessBorder && this.PointMouse.Y > this.WindowTarget.Top + this.ThicknessTransparentBorder && this.PointMouse.Y < this.WindowTarget.Top + this.WindowTarget.ActualHeight - this.ThicknessTransparentBorder) { handled = true; return new IntPtr((int)HitTest.HTLEFT); } //窗口右侧 else if (this.PointMouse.X < this.WindowTarget.Left + this.WindowTarget.ActualWidth - this.ThicknessTransparentBorder && this.PointMouse.X >= this.WindowTarget.Left + this.WindowTarget.ActualWidth - this.ThicknessTransparentBorder - this.ThicknessBorder && this.PointMouse.Y > this.WindowTarget.Top + this.ThicknessTransparentBorder && this.PointMouse.Y < this.WindowTarget.Top + this.WindowTarget.ActualHeight - this.ThicknessTransparentBorder) { handled = true; return new IntPtr((int)HitTest.HTRIGHT); } //窗口上方 else if (this.PointMouse.X > this.WindowTarget.Left + this.ThicknessTransparentBorder && this.PointMouse.X < this.WindowTarget.Left + this.WindowTarget.ActualWidth - this.ThicknessTransparentBorder && this.PointMouse.Y > this.WindowTarget.Top + this.ThicknessTransparentBorder && this.PointMouse.Y <= this.WindowTarget.Top + this.ThicknessTransparentBorder + this.ThicknessBorder) { handled = true; return new IntPtr((int)HitTest.HTTOP); } //窗口下方 else if (this.PointMouse.X > this.WindowTarget.Left + this.ThicknessTransparentBorder && this.PointMouse.X < this.WindowTarget.Left + this.WindowTarget.ActualWidth - this.ThicknessTransparentBorder && this.PointMouse.Y < this.WindowTarget.Top + this.WindowTarget.ActualHeight - this.ThicknessTransparentBorder && this.PointMouse.Y >= this.WindowTarget.Top + this.WindowTarget.ActualHeight - this.ThicknessTransparentBorder - this.ThicknessBorder) { handled = true; return new IntPtr((int)HitTest.HTBOTTOM); } //其他消息 else { SendMessage(handle, (int)0x00A1, (IntPtr)HitTest.HTCAPTION, IntPtr.Zero); break; } case WM_GETMINMAXINFO: WmGetMinMaxInfo(hwnd, lParam); handled = true; break; default: break; } return IntPtr.Zero; } //更改最小化最大化时窗口位置大小 private void WmGetMinMaxInfo(IntPtr hwnd, IntPtr lParam) { MINMAXINFO mmi = (MINMAXINFO)Marshal.PtrToStructure(lParam, typeof(MINMAXINFO)); int MONITOR_DEFAULTTONEAREST = 0x00000002; IntPtr monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST); if (monitor != IntPtr.Zero) { MONITORINFO monitorInfo = new MONITORINFO(); GetMonitorInfo(monitor, monitorInfo); RECT rcWorkArea = monitorInfo.rcWork; RECT rcMonitorArea = monitorInfo.rcMonitor; mmi.ptMaxPosition.x = Math.Abs(rcWorkArea.left - rcMonitorArea.left) - 3; mmi.ptMaxPosition.y = Math.Abs(rcWorkArea.top - rcMonitorArea.top) - 3; mmi.ptMaxSize.x = Math.Abs(rcWorkArea.right - rcWorkArea.left) + 6; mmi.ptMaxSize.y = Math.Abs(rcWorkArea.bottom - rcWorkArea.top) + 6; mmi.ptMinTrackSize.x = (int)this.WindowTarget.MinWidth; mmi.ptMinTrackSize.y = (int)this.WindowTarget.MinHeight; } Marshal.StructureToPtr(mmi, lParam, true); } [DllImport("user32")] internal static extern bool GetMonitorInfo(IntPtr hMonitor, MONITORINFO lpmi); [DllImport("User32")] internal static extern IntPtr MonitorFromWindow(IntPtr handle, int flags); [DllImport("user32.dll")] public static extern int SendMessage(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam); #region Nested type: MINMAXINFO [StructLayout(LayoutKind.Sequential)] internal struct MINMAXINFO { public POINT ptReserved; public POINT ptMaxSize; public POINT ptMaxPosition; public POINT ptMinTrackSize; public POINT ptMaxTrackSize; } #endregion #region Nested type: MONITORINFO [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] internal class MONITORINFO { public int cbSize = Marshal.SizeOf(typeof(MONITORINFO)); public RECT rcMonitor; public RECT rcWork; public int dwFlags; } #endregion #region Nested type: POINT [StructLayout(LayoutKind.Sequential)] internal struct POINT { public int x; public int y; public POINT(int x, int y) { this.x = x; this.y = y; } } #endregion #region Nested type: RECT [StructLayout(LayoutKind.Sequential, Pack = 0)] internal struct RECT { public int left; public int top; public int right; public int bottom; public static readonly RECT Empty; public int Width { get { return Math.Abs(right - left); } } public int Height { get { return bottom - top; } } public RECT(int left, int top, int right, int bottom) { this.left = left; this.top = top; this.right = right; this.bottom = bottom; } public RECT(RECT rcSrc) { left = rcSrc.left; top = rcSrc.top; right = rcSrc.right; bottom = rcSrc.bottom; } public bool IsEmpty { get { return left >= right || top >= bottom; } } public override string ToString() { if (this == Empty) { return "RECT {Empty}"; } return "RECT { left : " + left + " / top : " + top + " / right : " + right + " / bottom : " + bottom + " }"; } public override bool Equals(object obj) { if (!(obj is Rect)) { return false; } return (this == (RECT)obj); } public override int GetHashCode() { return left.GetHashCode() + top.GetHashCode() + right.GetHashCode() + bottom.GetHashCode(); } public static bool operator ==(RECT rect1, RECT rect2) { return (rect1.left == rect2.left && rect1.top == rect2.top && rect1.right == rect2.right && rect1.bottom == rect2.bottom); } public static bool operator !=(RECT rect1, RECT rect2) { return !(rect1 == rect2); } } #endregion } }
参考原文:https://blog.csdn.net/weixin_30439067/article/details/96898188
新增无边框窗体实例:2022/08/01
BaseWindow.cs
public class BaseWindow : Window { public static readonly DependencyProperty WindowBorderThicknessProperty; public static readonly DependencyProperty WindowBorderBrushProperty; static BaseWindow() { WindowBorderThicknessProperty = DependencyProperty.Register("WindowBorderThickness", typeof(Thickness), typeof(BaseWindow), new PropertyMetadata(new Thickness(1))); WindowBorderBrushProperty = DependencyProperty.Register("WindowBorderBrush", typeof(Brush), typeof(BaseWindow), new PropertyMetadata(new SolidColorBrush(Color.FromRgb(213, 213, 213)))); } public BaseWindow() { Initialize(); } protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e) { if (e.GetPosition(this).Y < _titleHeight) { WindowInteropHelper wih = new WindowInteropHelper(this); WindowBehaviorHelper.SendMessage(wih.Handle, 0x00A1, (IntPtr)WindowBehaviorHelper.HitTest.HTCAPTION, IntPtr.Zero); return; } base.OnMouseLeftButtonDown(e); } public Thickness WindowBorderThickness { get { return (Thickness)this.GetValue(WindowBorderThicknessProperty); } set { this.SetValue(WindowBorderThicknessProperty, value); } } public Brush WindowBorderBrush { get { return (Brush)this.GetValue(WindowBorderBrushProperty); } set { this.SetValue(WindowBorderBrushProperty, value); } } /// <summary> /// 初始化 /// </summary> private void Initialize() { UpdateResource("/WPFDLL;Component/BaseWindowStyle.xaml"); this.Style = (Style)this.FindResource("BaseWindowStyle"); ControlTemplate baseWindowTemplate = (ControlTemplate)this.FindResource("BaseWindowControlTemplate"); this.ApplyTemplate(); Button minBtn = (Button)baseWindowTemplate.FindName("btnMin", this); minBtn.Click += delegate { this.WindowState = WindowState.Minimized; }; Button maxBtn = (Button)baseWindowTemplate.FindName("btnMax", this); maxBtn.Click += delegate { this.WindowState = this.WindowState == WindowState.Normal ? WindowState.Maximized : WindowState.Normal; }; Button closeBtn = (Button)baseWindowTemplate.FindName("btnClose", this); closeBtn.Click += delegate { this.Close(); }; WindowBehaviorHelper windowBehaviorHelper = new WindowBehaviorHelper(this); windowBehaviorHelper.RepairBehavior(); } /// <summary> /// 添加资源到程序环境中 /// /Library.Name;component/Themes/{0}Theme.xaml /// </summary> /// <param name="path">资源路径</param> private void UpdateResource(string path) { var uri = new Uri(path, UriKind.Relative); ResourceDictionary resources = (ResourceDictionary)Application.LoadComponent(uri); this.Resources.MergedDictionaries.Add(resources); } private double _titleHeight = 30; }
BaseWindowStyle.xaml
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:WPFDLL"> <ControlTemplate x:Key="BaseWindowControlTemplate" TargetType="{x:Type local:BaseWindow}"> <Border BorderThickness="{Binding WindowBorderThickness,RelativeSource={RelativeSource Mode=TemplatedParent}}" BorderBrush="{Binding WindowBorderBrush,RelativeSource={RelativeSource Mode=TemplatedParent}}"> <DockPanel x:Name="dockpanel" LastChildFill="True"> <!--外边框--> <Border x:Name="borderTitle" Width="Auto" DockPanel.Dock="Top" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" CornerRadius="0"> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="auto"></ColumnDefinition> <ColumnDefinition></ColumnDefinition> </Grid.ColumnDefinitions> <TextBlock Text="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},Path=Title}" HorizontalAlignment="Left" VerticalAlignment="Center" Grid.Column="0" Margin="8 0 0 0" FontFamily="{TemplateBinding FontFamily}" FontSize="{TemplateBinding FontSize}" FontWeight="{TemplateBinding FontWeight}" FontStyle="{TemplateBinding FontStyle}" Foreground="{TemplateBinding Foreground}"></TextBlock> <StackPanel Grid.Column="1" HorizontalAlignment="Right" Orientation="Horizontal" Height="30"> <!--最小化按钮--> <Button Content="Min" x:Name="btnMin"> <Button.Style> <Style TargetType="Button"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="Button"> <Border x:Name="bdMin" Background="#01FFFFFF" Width="30" ToolTip="最小化"> <Path Stroke="#3B3B3B" StrokeThickness="2" Data="M 10,15 L 20,15" /> </Border> <ControlTemplate.Triggers> <Trigger Property="IsMouseOver" Value="True"> <Setter TargetName="bdMin" Property="Background" Value="#30FFFFFF"></Setter> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style> </Button.Style> </Button> <!--最大化按钮--> <Button Content="Max" x:Name="btnMax" > <Button.Style> <Style TargetType="Button"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="Button"> <Border x:Name="bdMax" Background="#01FFFFFF" Width="30" ToolTip="最大化"> <Path x:Name="pathMax" Stroke="#3B3B3B" StrokeThickness="2" Data="M 10,10 L 20,10 L 20,20 L 10,20 Z" /> </Border> <ControlTemplate.Triggers> <Trigger Property="IsMouseOver" Value="True"> <Setter TargetName="bdMax" Property="Background" Value="#30FFFFFF"></Setter> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style> </Button.Style> </Button> <!--关闭按钮--> <Button Content="Close" x:Name="btnClose"> <Button.Style> <Style TargetType="Button"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="Button"> <Border x:Name="bdclose" Background="#01FFFFFF" Width="30" ToolTip="关闭"> <Path x:Name="txtclose" Stroke="#3B3B3B" StrokeThickness="2" Data="M 10,10 L 20,20 M20,10 L10,20" /> </Border> <ControlTemplate.Triggers> <Trigger Property="IsMouseOver" Value="True"> <Setter TargetName="bdclose" Property="Background" Value="Red"></Setter> <Setter TargetName="txtclose" Property="Stroke" Value="White"></Setter> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style> </Button.Style> </Button> </StackPanel> </Grid> </Border> <Border Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Width="Auto" Height="Auto" DockPanel.Dock="Top" CornerRadius="0"> <AdornerDecorator> <ContentPresenter /> </AdornerDecorator> </Border> </DockPanel> </Border> <ControlTemplate.Triggers> <DataTrigger Binding="{Binding RelativeSource={RelativeSource self},Path= WindowState}" Value="2"> <Setter TargetName="btnMax" Property="Template"> <Setter.Value> <ControlTemplate TargetType="Button"> <Border x:Name="bdMax" Background="#01FFFFFF" Width="30" ToolTip="还原"> <Path x:Name="pathMax" Stroke="#3B3B3B" StrokeThickness="2" Data="M 10,12 L 18,12 L 18,20 L 10,20 L10,12 M13,11 L13,9 L21,9 L21,18 L19,18" /> </Border> <ControlTemplate.Triggers> <Trigger Property="IsMouseOver" Value="True"> <Setter TargetName="bdMax" Property="Background" Value="#30FFFFFF"></Setter> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </DataTrigger> <DataTrigger Binding="{Binding RelativeSource={RelativeSource self},Path= ResizeMode}" Value="NoResize"> <Setter TargetName="btnMax" Property="Visibility" Value="Collapsed"></Setter> <Setter TargetName="btnMin" Property="Visibility" Value="Collapsed"></Setter> </DataTrigger> <DataTrigger Binding="{Binding RelativeSource={RelativeSource self},Path= ResizeMode}" Value="CanMinimize"> <Setter TargetName="btnMax" Property="Visibility" Value="Collapsed"></Setter> </DataTrigger> </ControlTemplate.Triggers> </ControlTemplate> <Style x:Key="BaseWindowStyle" TargetType="{x:Type local:BaseWindow}"> <Setter Property="Template" Value="{StaticResource BaseWindowControlTemplate}"/> <Setter Property="AllowsTransparency" Value="True" /> <Setter Property="WindowStyle" Value="None" /> <Setter Property="Background" Value="#FF7097D0" /> <Setter Property="BorderBrush" Value="#FF7097D0" /> <Setter Property="BorderThickness" Value="0" /> </Style> </ResourceDictionary>