C#?wpf實現(xiàn)任意控件更多拖動功能
前言
上一章我們已經(jīng)實現(xiàn)了任意控件統(tǒng)一的拖動功能,以及能夠方便的給任意控件添加拖動了。開發(fā)過程中發(fā)現(xiàn)還是有些功能可以繼續(xù)拓展的,比如cs代碼中移動控件、響應事件后觸發(fā)拖動、限制拖動范圍等功能。
一、添加的功能
在第五章基礎(chǔ)上添加了如下功能。
1、任意控件MoveTo
這個功能相對簡單,對不同類型的容器進行判斷區(qū)分不同的移動邏輯即可。
代碼示例如下:
/// <summary>
/// 任意控件移動到指定坐標點
/// </summary>
/// <param name="elememt">this</param>
/// <param name="parentPoint">父容器的坐標點,之所以采樣容器的坐標是因為,采樣自身坐標控件位置改變后就會無效,采樣屏幕坐標則需要自己換算dpi(PointToScreen不會做dpi換算)</param>
public static void MoveTo(this FrameworkElement elememt, Point parentPoint)
{
var parent = VisualTreeHelper.GetParent(elememt);
if (parent is Canvas)
{
//Canvas移動邏輯
}
else if (elememt is Window)
{
//Window移動邏輯
}
else
{
//Grid或Transform移動邏輯,兩種都能適用任意控件
}
}
在拓展一個獲取位置的方法,方便MoveTo使用
/// <summary>
/// 獲取控件坐標,基于父控件。Window則是桌面位置。
/// </summary>
/// <param name="elememt"></param>
public static Point GetPosition(this FrameworkElement elememt)
{
var parent = VisualTreeHelper.GetParent(elememt);
if (elememt is Window)
{
var window = elememt as Window;
return new Point(window!.Left, window.Top);
}
return elememt.TranslatePoint(new Point(0, 0), parent as UIElement);
}
2、任意控件DragMove
我們知道wpf的Window有DragMove功能,在鼠標左鍵按下事件中調(diào)用此方法就能實現(xiàn)拖動功能很方便。任意控件的DragMove也是可以實現(xiàn)的,我們需要使用第五章的DragMoveable對象結(jié)合手動觸發(fā)事件來實現(xiàn)。
代碼示例如下:
/// <summary>
/// 點擊拖動
/// 與Window的DragMove類似,必須鼠標左鍵按下調(diào)用此方法。
/// await 可以等待拖動結(jié)束
/// </summary>
/// <param name="elememt">this</param>
/// <returns></returns>
/// <exception cref="InvalidOperationException"></exception>
public static Task DragMove(this FrameworkElement elememt)
{
if (Mouse.LeftButton != MouseButtonState.Pressed)
{
throw new InvalidOperationException("Left button down to call this method");
}
var tcs = new TaskCompletionSource();
//初始化DragMoveable對象
//手動觸發(fā)elememt的鼠標左鍵按下事件
//拖動完成后tcs.SetResult();
return tcs.Task;
}
3、邊界限制
添加一個IsMoveInBounds附加屬性,表示拖動范圍是否在父控件內(nèi)。
代碼示例如下:
public static bool GetIsMoveInBounds(DependencyObject obj)
{
return (bool)obj.GetValue(IsMoveInBoundsProperty);
}
public static void SetIsMoveInBounds(DependencyObject obj, bool value)
{
obj.SetValue(IsMoveInBoundsProperty, value);
}
/// <summary>
/// 是否在父容器區(qū)域內(nèi)拖動,不會超出邊界
/// </summary>
// Using a DependencyProperty as the backing store for IsMoveInBounds. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IsMoveInBoundsProperty =
DependencyProperty.RegisterAttached("IsMoveInBounds", typeof(bool), typeof(Move), new PropertyMetadata(true));
在第五章 附加屬性實現(xiàn)任意拖動的拖動邏輯中添加相應的限制功能,比如Canvas的示例如下:
var p = _parent as Canvas;
if (GetIsMoveInBounds(c))
//修正移動范圍
{
if (left < 0) left = 0;
if (top < 0) top = 0;
if (left + c.ActualWidth > p.ActualWidth) left = p.ActualWidth - c.ActualWidth;
if (top + c.ActualHeight > p.ActualHeight) top = p.ActualHeight - c.ActualHeight;
}
4、窗口最大化拖動還原
Windows系統(tǒng)的窗口最大化拖動標題時會自動恢復為普通狀態(tài)的窗口,實現(xiàn)無邊框窗口后則失去了這個功能,需要自己實現(xiàn),而且恢復普通狀態(tài)的窗口的位置還有一定的邏輯。
代碼示例如下:
if (window.WindowState == WindowState.Maximized)
//最大化時拖動邏輯
{
//恢復為普通窗口
window.WindowState = WindowState.Normal;
double width = SystemParameters.PrimaryScreenWidth;//得到屏幕整體寬度
double height = SystemParameters.PrimaryScreenHeight;//得到屏幕整體高度
//根據(jù)鼠標的位置調(diào)整窗口位置,基本邏輯是橫向為鼠標為中點,縱向為鼠標頂部,超出屏幕范圍則修正到靠近的那一邊。
}
5、拖動事件
提供3個拖動事件,拖動結(jié)束、拖動變化、拖動結(jié)束。
代碼示例如下:
/// <summary>
/// 拖動開始事件
/// </summary>
public static readonly RoutedEvent DragMoveStartedEvent = EventManager.RegisterRoutedEvent("DragMoveStarted", RoutingStrategy.Direct, typeof(EventHandler<DragMoveStartedEventArgs>), typeof(Move));
/// <summary>
/// 拖動變化事件
/// </summary>
public static readonly RoutedEvent DragMoveDeltaEvent = EventManager.RegisterRoutedEvent("DragMoveDelta", RoutingStrategy.Direct, typeof(EventHandler<DragMoveDeltaEventArgs>), typeof(Move));
/// <summary>
/// 拖動結(jié)束事件
/// </summary>
public static readonly RoutedEvent DragMoveCompletedEvent = EventManager.RegisterRoutedEvent("DragMoveCompleted", RoutingStrategy.Direct, typeof(EventHandler<DragMoveCompletedEventArgs>), typeof(Move));
二、使用示例
由于本章是第五章的拓展,基本功能可以參考第五章。
1、MoveTo
xaml
<Window x:Class="WpfMove.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:WpfMove"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800"
>
<Grid>
<Button Width="180" Height="30" Click="Button_Click">Gird中點擊按鈕右移10</Button>
<StackPanel>
<Button Width="180" Height="30" Click="Button_Click">StackPanel中點擊按鈕右移10</Button>
</StackPanel>
<Canvas>
<Button Width="180" Height="30" Click="Button_Click">Canvas中點擊按鈕右移10</Button>
</Canvas>
</Grid>
</Window>
因為是拓展方法,所以獲取到控件對象直接調(diào)用moveTo即可。
cs
using AC;
using System.Windows;
using System.Windows.Media;
namespace WpfMove
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
var fe = sender as FrameworkElement;
//獲取控件的位置
var p = fe.GetPosition();
//右偏移10
p.Offset(10, 0);
fe.MoveTo(p);
}
}
}
效果預覽

2、DragMove
xaml
<Window x:Class="WpfMove.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:WpfMove"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800"
>
<Grid>
<TextBlock Background="Aqua" TextAlignment="Center" Margin="0,80,0,0" Width="180" Height="30" MouseDown="TextBlock_MouseDown" >Gird中點擊拖動</TextBlock>
<StackPanel>
<TextBlock Background="Aqua" TextAlignment="Center" Margin="0,80,0,0" Width="180" Height="30" MouseDown="TextBlock_MouseDown" >StackPanel中點擊拖動</TextBlock>
</StackPanel>
<Canvas >
<TextBlock Background="Aqua" TextAlignment="Center" Margin="0,80,0,0" Width="180" Height="30" MouseDown="TextBlock_MouseDown" >Canvas中點擊拖動</TextBlock>
</Canvas>
</Grid>
</Window>
此方法也是拓展方法,在鼠標按下事件中任意控件都可以調(diào)用此方法,可以通過await等待拖動完成。
cs
using AC;
using System;
using System.Windows;
namespace WpfMove
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private async void TextBlock_MouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
var fe = sender as FrameworkElement;
await fe.DragMove();
Console.WriteLine("拖動完成");
}
}
}
效果預覽

3、邊界限制
通過附加屬性IsMoveInBounds設置是否限制邊界,默認為false。
xaml
<Window x:Class="WpfMove.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:WpfMove"
xmlns:ac="clr-namespace:AC"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800"
>
<Grid>
<TextBlock HorizontalAlignment="Left" Background="Aqua" TextAlignment="Center" Margin="0,80,0,0" Width="180" Height="60" ac:Move.IsDragMoveable="True" ac:Move.IsMoveInBounds="True">拖動限制邊界</TextBlock>
<TextBlock HorizontalAlignment="Right" Background="Aqua" TextAlignment="Center" Margin="0,80,0,0" Width="180" Height="60" ac:Move.IsDragMoveable="True" ac:Move.IsMoveInBounds="False">拖動不限制邊界</TextBlock>
</Grid>
</Window>
效果預覽

4、窗口最大化拖動還原
內(nèi)部實現(xiàn)已支持最大化拖動還原,只需要設置窗口可拖動即可,使用場景是無邊框窗口自定義標題欄。
xaml
<Window x:Class="WpfMove.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:WpfMove"
xmlns:ac="clr-namespace:AC"
mc:Ignorable="d"
WindowStyle="None"
ResizeMode="NoResize"
WindowState="Normal"
Title="MainWindow" Height="450" Width="800"
x:Name="window"
>
<Grid>
<Border VerticalAlignment="Top" Height="40" Background="#333333" ac:Move.DragMoveTarget="{Binding ElementName= window}" MouseLeftButtonDown="Border_MouseLeftButtonDown">
<TextBlock Margin="0,0,10,0" VerticalAlignment="Center" HorizontalAlignment="Right" Foreground="White" Text="標題欄拖動窗口"></TextBlock>
</Border>
</Grid>
</Window>
cs
using System.Windows;
namespace WpfMove
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Border_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
if (e.ClickCount == 2) WindowState = WindowState != WindowState.Maximized ? WindowState = WindowState.Maximized : WindowState = WindowState.Normal;
}
}
}
效果預覽

5、拖動事件
xaml
<Window x:Class="WpfMove.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:WpfMove"
xmlns:ac="clr-namespace:AC"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800"
>
<StackPanel>
<TextBlock Background="Aqua"
TextAlignment="Center"
Margin="0,80,0,0" Width="180" Height="30"
ac:Move.IsDragMoveable="True"
ac:Move.DragMoveStarted="window_DragMoveStarted"
ac:Move.DragMoveCompleted="window_DragMoveCompleted"
ac:Move.DragMoveDelta="window_DragMoveDelta">StackPanel中點擊拖動</TextBlock>
</StackPanel>
</Window>
cs
using AC;
using System;
using System.Windows;
namespace WpfMove
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void window_DragMoveStarted(object sender, DragMoveStartedEventArgs e)
{
Console.WriteLine("拖動開始");
}
private void window_DragMoveCompleted(object sender, DragMoveCompletedEventArgs e)
{
Console.WriteLine("拖動完成");
}
private void window_DragMoveDelta(object sender, DragMoveDeltaEventArgs e)
{
Console.WriteLine("橫向偏移:"+e.HorizontalOffset + "," +"縱向偏移:"+ e.VerticalOffset);
}
}
}效果預覽

總結(jié)
拓展更多的拖動功能后使用變得更加方便了,靈活度也提高了。使用xmal或cs代碼都能實現(xiàn)拖動,實現(xiàn)自定義標題欄也變得很簡單,有了拖動事件也可以做一些撤銷重做的功能??偟膩碚f,本文的拖動功能一定程度可以作為通用的模塊在項目中使用了。
以上就是C# wpf實現(xiàn)任意控件更多拖動功能的詳細內(nèi)容,更多關(guān)于C# wpf控件拖動的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Windows系統(tǒng)中使用C#讀取文本文件內(nèi)容的小示例
這篇文章主要介紹了Windows系統(tǒng)中使用C#讀取文本文件內(nèi)容的小示例,包括一次一行地讀取文本文件的方法,需要的朋友可以參考下2016-02-02
c#之用戶定義的數(shù)據(jù)類型轉(zhuǎn)換介紹
c#允許定義自己的數(shù)據(jù)類型,這意味著需要某些工具支持在自己的數(shù)據(jù)類型間進行數(shù)據(jù)轉(zhuǎn)換。方法是把數(shù)據(jù)類型轉(zhuǎn)換定義為相關(guān)類的一個成員運算符,數(shù)據(jù)類型轉(zhuǎn)換必須聲明是隱式或者顯式,以說明怎么使用它2014-01-01
C# menuStrip控件實現(xiàn)鼠標滑過自動彈出功能
MenuStrip 控件是 Visual Studio 和 .NET Framework 中的功能。使用該控件,可以輕松創(chuàng)建 Microsoft Office 中那樣的菜單。本文給大家分享menuStrip鼠標滑過自動彈出效果2021-07-07
C#?WinForm實現(xiàn)檢查目標IP端口是否可連接
這篇文章主要為大家詳細介紹了如何基于C#?WinForm實現(xiàn)檢查目標IP端口是否可連接的小工具,感興趣的小伙伴可以跟隨小編一起學習一下2025-01-01
C#使用CefSharp和網(wǎng)頁進行自動化交互的示例代碼
CefSharp 是一個用 C# 編寫的開源庫,它封裝了 Google Chrome 瀏覽器的 Chromium 內(nèi)核,CefSharp 允許開發(fā)者在其應用程序中嵌入瀏覽器功能,從而能夠展示網(wǎng)頁內(nèi)容、執(zhí)行JavaScript代碼,本文給大家介紹了C#使用CefSharp和網(wǎng)頁進行自動化交互,需要的朋友可以參考下2024-07-07

