许多应用窗口除了最大化、最小化、关闭按钮外还包含固定置顶按钮,例如微信、一些播放窗口。
这里尝试用WPF来实现这一功能。
目录
实现步骤:
1. 窗体增加一个固定按钮
设置窗体Style为None,便于重写窗口按钮
增加最大化最小化关闭按钮并实现
除了最大化最小化等按钮,新增ToggleButon表示是否固定
private void CloseButton_Click(object sender, RoutedEventArgs e)
{
this.Close();
}
private void MaximizeButton_Click(object sender, RoutedEventArgs e)
{
if (this.WindowState == WindowState.Maximized)
this.WindowState = WindowState.Normal;
else
this.WindowState = WindowState.Maximized;
}
private void MinimizeButton_Click(object sender, RoutedEventArgs e)
{
this.WindowState = WindowState.Minimized;
}
2. 实现窗体置顶
要实现固定置顶,需要在窗体失去焦点时,仍保持置顶。
所以需要监控窗体失去焦点的事件,让窗体保持TopMost = true;
this.LostKeyboardFocus += Window_PreviewLostKeyboardFocus;
private void Window_PreviewLostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
if (this.TopMostButton.IsChecked == false) return;
this.Topmost = true;
}
3. 由于设置了WindowsStyle = None,窗口得拖拽和窗口得放大放小不支持 ——错误内容,请查看改动标注
文章内容错误改动:这里是因为我设置了AllowsTransparency=true,导致窗口非工作区不起效果了。所以才需要重写Thumb。其实完全可以可以将其设置为False。因为AllowsTransparency属性是为了绘制非矩形窗口提供的。一般情况下不需要使用。对于设置了WindowStyle=None,仍然会存在一定边框的问题,需要通过WindowChromelai来设置非工作区样式。可以参考链接: WPF Window始末|快速了解、自定义Window控件
所以这里实现了对这两个功能得重写。
-
窗口拖拽位置实现
只需要在窗口的根控件上绑定MouseLeftButtonDown,并调用this.DragMove();
/// <summary>
/// 窗口移动
/// </summary>
private void RooGrid_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
this.DragMove();
}
- 鼠标左键时,支持拖拽调整窗口大小
窗体上下左右,右上、右下、左上、左下、四个方向增加Thumb实现窗口的大小调整
<!-- 添加 Thumb 控件以实现窗口调整大小 -->
<Thumb
Name="ResizeThumbLeft"
Grid.RowSpan="2"
Width="5"
HorizontalAlignment="Left"
VerticalAlignment="Stretch"
Cursor="SizeWE"
DragDelta="ResizeThumb_DragDelta" />
<Thumb
Name="ResizeThumbTop"
Grid.RowSpan="2"
Height="5"
HorizontalAlignment="Stretch"
VerticalAlignment="Top"
Cursor="SizeNS"
DragDelta="ResizeThumb_DragDelta" />
<Thumb
Name="ResizeThumbTopLeft"
Grid.RowSpan="2"
Width="10"
Height="10"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Cursor="SizeNWSE"
DragDelta="ResizeThumb_DragDelta" />
<Thumb
Name="ResizeThumbTopRight"
Grid.RowSpan="2"
Width="10"
Height="10"
HorizontalAlignment="Right"
VerticalAlignment="Top"
Cursor="SizeNESW"
DragDelta="ResizeThumb_DragDelta" />
<Thumb
Name="ResizeThumbRight"
Grid.RowSpan="2"
Width="5"
HorizontalAlignment="Right"
VerticalAlignment="Stretch"
Cursor="SizeWE"
DragDelta="ResizeThumb_DragDelta" />
<Thumb
Name="ResizeThumbBottom"
Grid.RowSpan="2"
Height="5"
HorizontalAlignment="Stretch"
VerticalAlignment="Bottom"
Cursor="SizeNS"
DragDelta="ResizeThumb_DragDelta" />
<Thumb
Name="ResizeThumbBottomRight"
Grid.RowSpan="2"
Width="10"
Height="10"
HorizontalAlignment="Right"
VerticalAlignment="Bottom"
Cursor="SizeNWSE"
DragDelta="ResizeThumb_DragDelta" />
ResizeThumb_DragDelta具体的实现
private void ResizeThumb_DragDelta(object sender, DragDeltaEventArgs e)
{
var thumb = sender as Thumb;
var window = Window.GetWindow(thumb);
if (window != null)
{
double minWidth = 200;
double minHeight = 150;
if (thumb.Name == "ResizeThumbRight" || thumb.Name == "ResizeThumbBottomRight")
{
window.Width = Math.Max(window.Width + e.HorizontalChange, minWidth);
}
if (thumb.Name == "ResizeThumbBottom" || thumb.Name == "ResizeThumbBottomRight")
{
window.Height = Math.Max(window.Height + e.VerticalChange, minHeight);
}
if (thumb.Name == "ResizeThumbLeft" || thumb.Name == "ResizeThumbTopLeft")
{
window.Width = Math.Max(window.Width - e.HorizontalChange, minWidth);
window.Left += e.HorizontalChange;
}
if (thumb.Name == "ResizeThumbTop" || thumb.Name == "ResizeThumbTopLeft")
{
window.Height = Math.Max(window.Height - e.VerticalChange, minHeight);
window.Top += e.VerticalChange;
}
}
}
效果
完整代码
<Window
x:Class="WPFTopMost.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WPFTopMost"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="MainWindow"
Width="800"
Height="450"
AllowsTransparency="True"
WindowStartupLocation="CenterOwner"
WindowStyle="None"
mc:Ignorable="d">
<Window.Resources>
<Style TargetType="{x:Type Thumb}">
<Setter Property="Stylus.IsPressAndHoldEnabled" Value="false" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="Foreground" Value="Transparent" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Thumb}">
<Border
Background="{TemplateBinding Background}"
BorderBrush="Transparent"
BorderThickness="0">
<Grid>
<Border
Background="Transparent"
BorderBrush="Transparent"
BorderThickness="0" />
<Border
Margin="1"
Background="Transparent"
BorderBrush="Transparent"
BorderThickness="0" />
<Border
Margin="2"
Background="{TemplateBinding Background}" />
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid
Name="RooGrid"
MouseLeftButtonDown="RooGrid_MouseLeftButtonDown">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Border
Grid.Row="0"
Height="30"
Background="Transparent">
<DockPanel LastChildFill="False">
<ToggleButton
x:Name="TopMostButton"
Width="75"
Click="TopMostButton_Click"
Content="📌"
DockPanel.Dock="Right" />
<Button
Width="30"
Click="CloseButton_Click"
Content="X"
DockPanel.Dock="Right" />
<Button
Width="30"
Click="MaximizeButton_Click"
Content="□"
DockPanel.Dock="Right" />
<Button
Width="30"
Click="MinimizeButton_Click"
Content="—"
DockPanel.Dock="Right" />
</DockPanel>
</Border>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="9*" />
<ColumnDefinition Width="151*" />
</Grid.ColumnDefinitions>
<!-- 其他内容 -->
</Grid>
<!-- 添加 Thumb 控件以实现窗口调整大小 -->
<Thumb
Name="ResizeThumbLeft"
Grid.RowSpan="2"
Width="5"
HorizontalAlignment="Left"
VerticalAlignment="Stretch"
Cursor="SizeWE"
DragDelta="ResizeThumb_DragDelta" />
<Thumb
Name="ResizeThumbTop"
Grid.RowSpan="2"
Height="5"
HorizontalAlignment="Stretch"
VerticalAlignment="Top"
Cursor="SizeNS"
DragDelta="ResizeThumb_DragDelta" />
<Thumb
Name="ResizeThumbTopLeft"
Grid.RowSpan="2"
Width="10"
Height="10"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Cursor="SizeNWSE"
DragDelta="ResizeThumb_DragDelta" />
<Thumb
Name="ResizeThumbTopRight"
Grid.RowSpan="2"
Width="10"
Height="10"
HorizontalAlignment="Right"
VerticalAlignment="Top"
Cursor="SizeNESW"
DragDelta="ResizeThumb_DragDelta" />
<Thumb
Name="ResizeThumbRight"
Grid.RowSpan="2"
Width="5"
HorizontalAlignment="Right"
VerticalAlignment="Stretch"
Cursor="SizeWE"
DragDelta="ResizeThumb_DragDelta" />
<Thumb
Name="ResizeThumbBottom"
Grid.RowSpan="2"
Height="5"
HorizontalAlignment="Stretch"
VerticalAlignment="Bottom"
Cursor="SizeNS"
DragDelta="ResizeThumb_DragDelta" />
<Thumb
Name="ResizeThumbBottomRight"
Grid.RowSpan="2"
Width="10"
Height="10"
HorizontalAlignment="Right"
VerticalAlignment="Bottom"
Cursor="SizeNWSE"
DragDelta="ResizeThumb_DragDelta" />
</Grid>
</Window>
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace WPFTopMost
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.LostKeyboardFocus += Window_PreviewLostKeyboardFocus;
this.MaxHeight = SystemParameters.PrimaryScreenHeight;//防止最大化时系统任务栏被遮盖
}
private void CloseButton_Click(object sender, RoutedEventArgs e)
{
this.Close();
}
private void MaximizeButton_Click(object sender, RoutedEventArgs e)
{
if (this.WindowState == WindowState.Maximized)
this.WindowState = WindowState.Normal;
else
this.WindowState = WindowState.Maximized;
}
private void MinimizeButton_Click(object sender, RoutedEventArgs e)
{
this.WindowState = WindowState.Minimized;
}
private void TopMostButton_Click(object sender, RoutedEventArgs e)
{
this.Topmost = (bool)this.TopMostButton.IsChecked;
}
private void Window_PreviewLostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
if (this.TopMostButton.IsChecked == false) return;
this.Topmost = true;
}
/// <summary>
/// 窗口移动
/// </summary>
private void RooGrid_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
this.DragMove();
}
private void ResizeThumb_DragDelta(object sender, DragDeltaEventArgs e)
{
var thumb = sender as Thumb;
var window = Window.GetWindow(thumb);
if (window != null)
{
double minWidth = 200;
double minHeight = 150;
if (thumb.Name == "ResizeThumbRight" || thumb.Name == "ResizeThumbBottomRight")
{
window.Width = Math.Max(window.Width + e.HorizontalChange, minWidth);
}
if (thumb.Name == "ResizeThumbBottom" || thumb.Name == "ResizeThumbBottomRight")
{
window.Height = Math.Max(window.Height + e.VerticalChange, minHeight);
}
if (thumb.Name == "ResizeThumbLeft" || thumb.Name == "ResizeThumbTopLeft")
{
window.Width = Math.Max(window.Width - e.HorizontalChange, minWidth);
window.Left += e.HorizontalChange;
}
if (thumb.Name == "ResizeThumbTop" || thumb.Name == "ResizeThumbTopLeft")
{
window.Height = Math.Max(window.Height - e.VerticalChange, minHeight);
window.Top += e.VerticalChange;
}
}
}
}
}
Demo地址
NitasDemo/04WPFTopMost at main · Nita121388/NitasDemo (github.com)