第一章 Grid内控件拖动
第二章 Canvas内控件拖动
第三章 任意控件拖动
第四章 窗口拖动(本章)
第五章 附加属性实现任意拖动
点击窗口任意区域可移动功能,在一些业务场景中会使用到,比如工具条或者球形状的窗口等。在wpf要实现此功能简单形式还是比较容易的,但是有一些细节需要专门处理,比如与按钮的点击事件冲突问题,解决事件冲突问题后拖动的灵敏度,可复用性等。
基础的拖动功能直接使用Window类的DragMove即可实现:
在Window的PreviewMouseLeftButtonDown中调用DragMove即可。
示例如下:
<Window PreviewMouseLeftButtonDown="Window_PreviewMouseLeftButtonDown" />
void Window_PreviewMouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
DragMove();
}
注:本文实现的是窗口任意区域拖动(比如按钮占满整个窗口),不适用于一般窗口拖动,一般窗口拖动请使用MouseLeftButtonDown事件
根据上述方法实现窗口拖动后发现出现了事件冲突,即窗口内的控件无法相应鼠标点击事件了。因为DragMove的内部实现使用了SC_MOVE,使标题栏(win32窗口)捕获鼠标,原本窗口失去鼠标捕获。窗口内的控件无法响应鼠标消息,因此上述简单拖动在有控件的窗口中是不可行的(局部拖动是可行的,但本文讲的是任意区域拖动)。
解决方法是不在鼠标按下事件中触发拖动,而是在鼠标移动后触发拖动操作。具体步骤如下:
1、注册3个事件如下:
<Window PreviewMouseLeftButtonDown="Window_PreviewMouseLeftButtonDown"
PreviewMouseMove="Window_PreviewMouseMove"
PreviewMouseLeftButtonUp="Window_PreviewMouseLeftButtonUp"/>
2、定义2个变量记录信息。
Point _pressedPosition ;
bool _isDragMoved = false;
3.记录鼠标按下位置
void Window_PreviewMouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
_pressedPosition = e.GetPosition(this);
}
4.鼠标移动触发拖动
void Window_PreviewMouseMove(object sender, System.Windows.Input.MouseEventArgs e)
{
if (Mouse.LeftButton==MouseButtonState.Pressed && _pressedPosition != e.GetPosition(this))
{
_isDragMoved = true;
DragMove();
}
}
5.鼠标弹起屏蔽消息
void Window_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if (_isDragMoved)
{
_isDragMoved = false;
e.Handled = true;
}
}
由于评论区反馈上述方法存在bug,但是无奈笔者始终没有重现,怀疑是使用方法不正确,或者对本博文讲述功能理解上的差异导致的,在此根据上述方法原封不动的编写如下使用示例,以说明使用场景以及使用方法。
<Window x:Class="WpfApp3.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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp3"
mc:Ignorable="d"
Background="Transparent"
AllowsTransparency="True"
WindowStyle="None"
Title="MainWindow" Height="60" Width="410"
PreviewMouseLeftButtonDown="Window_PreviewMouseLeftButtonDown"
PreviewMouseMove="Window_PreviewMouseMove"
PreviewMouseLeftButtonUp="Window_PreviewMouseLeftButtonUp">
<Border Background="White" CornerRadius="25" Margin="5">
<Border.Effect>
<DropShadowEffect ShadowDepth="0" BlurRadius="5" Opacity="0.8" Color="#AAAAAA"/>
Border.Effect>
<StackPanel Margin="20,0,0,0" Orientation="Horizontal">
<RadioButton Content="画笔" Width="50" Height="50" Cursor="Hand">
<RadioButton.Template>
<ControlTemplate TargetType="RadioButton" >
<Border Background="Transparent">
<TextBlock x:Name="txt" Background="Transparent" TextAlignment="Center" HorizontalAlignment="Center" VerticalAlignment="Center" Foreground="#666666" FontSize="18"
Text="{TemplateBinding Content}">TextBlock>
Border>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="true">
<Setter TargetName="txt" Property="Foreground" Value="red">Setter>
Trigger>
ControlTemplate.Triggers>
ControlTemplate>
RadioButton.Template>
RadioButton>
<RadioButton Content="矩形" Width="50" Height="50" Cursor="Hand">
<RadioButton.Template>
<ControlTemplate TargetType="RadioButton" >
<Border Background="Transparent">
<TextBlock x:Name="txt" Background="Transparent" TextAlignment="Center" HorizontalAlignment="Center" VerticalAlignment="Center" Foreground="#666666" FontSize="18"
Text="{TemplateBinding Content}">TextBlock>
Border>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="true">
<Setter TargetName="txt" Property="Foreground" Value="red">Setter>
Trigger>
ControlTemplate.Triggers>
ControlTemplate>
RadioButton.Template>
RadioButton>
<RadioButton Content="文本" Width="50" Height="50" Cursor="Hand">
<RadioButton.Template>
<ControlTemplate TargetType="RadioButton" >
<Border Background="Transparent">
<TextBlock x:Name="txt" Background="Transparent" TextAlignment="Center" HorizontalAlignment="Center" VerticalAlignment="Center" Foreground="#666666" FontSize="18"
Text="{TemplateBinding Content}">TextBlock>
Border>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="true">
<Setter TargetName="txt" Property="Foreground" Value="red">Setter>
Trigger>
ControlTemplate.Triggers>
ControlTemplate>
RadioButton.Template>
RadioButton>
<RadioButton Content="箭头" Width="50" Height="50" Cursor="Hand">
<RadioButton.Template>
<ControlTemplate TargetType="RadioButton" >
<Border Background="Transparent">
<TextBlock x:Name="txt" Background="Transparent" TextAlignment="Center" HorizontalAlignment="Center" VerticalAlignment="Center" Foreground="#666666" FontSize="18"
Text="{TemplateBinding Content}">TextBlock>
Border>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="true">
<Setter TargetName="txt" Property="Foreground" Value="red">Setter>
Trigger>
ControlTemplate.Triggers>
ControlTemplate>
RadioButton.Template>
RadioButton>
<RadioButton Content="图片" Width="50" Height="50" Cursor="Hand">
<RadioButton.Template>
<ControlTemplate TargetType="RadioButton" >
<Border Background="Transparent">
<TextBlock x:Name="txt" Background="Transparent" TextAlignment="Center" HorizontalAlignment="Center" VerticalAlignment="Center" Foreground="#666666" FontSize="18"
Text="{TemplateBinding Content}">TextBlock>
Border>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="true">
<Setter TargetName="txt" Property="Foreground" Value="red">Setter>
Trigger>
ControlTemplate.Triggers>
ControlTemplate>
RadioButton.Template>
RadioButton>
<Border Margin="10,0,0,0" BorderThickness="1,0,0,0" BorderBrush="#999999">Border>
<Button Name="btn_upload" Margin="0,0,0,0" Content="上传" Width="50" Height="50" Cursor="Hand" Click="btn_upload_Click">
<Button.Template>
<ControlTemplate TargetType="Button" >
<Border Background="Transparent">
<TextBlock Background="Transparent" TextAlignment="Center" HorizontalAlignment="Center" VerticalAlignment="Center" Foreground="#666666" FontSize="18"
Text="{TemplateBinding Content}">TextBlock>
Border>
ControlTemplate>
Button.Template>
Button>
<Button Name="btn_close" Margin="0,0,0,0" Content="关闭" Width="50" Height="50" Cursor="Hand" Click="btn_close_Click">
<Button.Template>
<ControlTemplate TargetType="Button" >
<Border Background="Transparent">
<TextBlock Background="Transparent" TextAlignment="Center" HorizontalAlignment="Center" VerticalAlignment="Center" Foreground="#666666" FontSize="18"
Text="{TemplateBinding Content}">TextBlock>
Border>
ControlTemplate>
Button.Template>
Button>
StackPanel>
Border>
Window>
using System.Windows;
using System.Windows.Input;
namespace WpfApp3
{
///
/// MainWindow.xaml 的交互逻辑
///
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
Point _pressedPosition;
bool _isDragMoved = false;
void Window_PreviewMouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
_pressedPosition = e.GetPosition(this);
}
void Window_PreviewMouseMove(object sender, System.Windows.Input.MouseEventArgs e)
{
if (Mouse.LeftButton == MouseButtonState.Pressed && _pressedPosition != e.GetPosition(this))
{
_isDragMoved = true;
DragMove();
}
}
void Window_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if (_isDragMoved)
{
_isDragMoved = false;
e.Handled = true;
}
}
private void btn_upload_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show("上传成功");
}
private void btn_close_Click(object sender, RoutedEventArgs e)
{
Close();
}
}
}
本文介绍了在窗口任意区域点击拖动的方法。简单的拖动是不可行的,往深一步探究,发现了巧妙的解决方法:在鼠标移动时触发拖动。但这也不是一蹴而就的,这是笔者曾经做项目经过了一定的探究最终得以实现,而本文呈现的是优化后的精简版本,易于实现也很好理解,即是所谓的大道至简。