C#?wpf實(shí)現(xiàn)任意控件更多拖動(dòng)功能
前言
上一章我們已經(jīng)實(shí)現(xiàn)了任意控件統(tǒng)一的拖動(dòng)功能,以及能夠方便的給任意控件添加拖動(dòng)了。開發(fā)過程中發(fā)現(xiàn)還是有些功能可以繼續(xù)拓展的,比如cs代碼中移動(dòng)控件、響應(yīng)事件后觸發(fā)拖動(dòng)、限制拖動(dòng)范圍等功能。
一、添加的功能
在第五章基礎(chǔ)上添加了如下功能。
1、任意控件MoveTo
這個(gè)功能相對(duì)簡(jiǎn)單,對(duì)不同類型的容器進(jìn)行判斷區(qū)分不同的移動(dòng)邏輯即可。
代碼示例如下:
/// <summary> /// 任意控件移動(dòng)到指定坐標(biāo)點(diǎn) /// </summary> /// <param name="elememt">this</param> /// <param name="parentPoint">父容器的坐標(biāo)點(diǎn),之所以采樣容器的坐標(biāo)是因?yàn)?,采樣自身坐?biāo)控件位置改變后就會(huì)無效,采樣屏幕坐標(biāo)則需要自己換算dpi(PointToScreen不會(huì)做dpi換算)</param> public static void MoveTo(this FrameworkElement elememt, Point parentPoint) { var parent = VisualTreeHelper.GetParent(elememt); if (parent is Canvas) { //Canvas移動(dòng)邏輯 } else if (elememt is Window) { //Window移動(dòng)邏輯 } else { //Grid或Transform移動(dòng)邏輯,兩種都能適用任意控件 } }
在拓展一個(gè)獲取位置的方法,方便MoveTo使用
/// <summary> /// 獲取控件坐標(biāo),基于父控件。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功能,在鼠標(biāo)左鍵按下事件中調(diào)用此方法就能實(shí)現(xiàn)拖動(dòng)功能很方便。任意控件的DragMove也是可以實(shí)現(xiàn)的,我們需要使用第五章的DragMoveable對(duì)象結(jié)合手動(dòng)觸發(fā)事件來實(shí)現(xiàn)。
代碼示例如下:
/// <summary> /// 點(diǎn)擊拖動(dòng) /// 與Window的DragMove類似,必須鼠標(biāo)左鍵按下調(diào)用此方法。 /// await 可以等待拖動(dòng)結(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對(duì)象 //手動(dòng)觸發(fā)elememt的鼠標(biāo)左鍵按下事件 //拖動(dòng)完成后tcs.SetResult(); return tcs.Task; }
3、邊界限制
添加一個(gè)IsMoveInBounds附加屬性,表示拖動(dòng)范圍是否在父控件內(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)拖動(dòng),不會(huì)超出邊界 /// </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));
在第五章 附加屬性實(shí)現(xiàn)任意拖動(dòng)的拖動(dòng)邏輯中添加相應(yīng)的限制功能,比如Canvas的示例如下:
var p = _parent as Canvas; if (GetIsMoveInBounds(c)) //修正移動(dòng)范圍 { 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、窗口最大化拖動(dòng)還原
Windows系統(tǒng)的窗口最大化拖動(dòng)標(biāo)題時(shí)會(huì)自動(dòng)恢復(fù)為普通狀態(tài)的窗口,實(shí)現(xiàn)無邊框窗口后則失去了這個(gè)功能,需要自己實(shí)現(xiàn),而且恢復(fù)普通狀態(tài)的窗口的位置還有一定的邏輯。
代碼示例如下:
if (window.WindowState == WindowState.Maximized) //最大化時(shí)拖動(dòng)邏輯 { //恢復(fù)為普通窗口 window.WindowState = WindowState.Normal; double width = SystemParameters.PrimaryScreenWidth;//得到屏幕整體寬度 double height = SystemParameters.PrimaryScreenHeight;//得到屏幕整體高度 //根據(jù)鼠標(biāo)的位置調(diào)整窗口位置,基本邏輯是橫向?yàn)槭髽?biāo)為中點(diǎn),縱向?yàn)槭髽?biāo)頂部,超出屏幕范圍則修正到靠近的那一邊。 }
5、拖動(dòng)事件
提供3個(gè)拖動(dòng)事件,拖動(dòng)結(jié)束、拖動(dòng)變化、拖動(dòng)結(jié)束。
代碼示例如下:
/// <summary> /// 拖動(dòng)開始事件 /// </summary> public static readonly RoutedEvent DragMoveStartedEvent = EventManager.RegisterRoutedEvent("DragMoveStarted", RoutingStrategy.Direct, typeof(EventHandler<DragMoveStartedEventArgs>), typeof(Move)); /// <summary> /// 拖動(dòng)變化事件 /// </summary> public static readonly RoutedEvent DragMoveDeltaEvent = EventManager.RegisterRoutedEvent("DragMoveDelta", RoutingStrategy.Direct, typeof(EventHandler<DragMoveDeltaEventArgs>), typeof(Move)); /// <summary> /// 拖動(dòng)結(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中點(diǎn)擊按鈕右移10</Button> <StackPanel> <Button Width="180" Height="30" Click="Button_Click">StackPanel中點(diǎn)擊按鈕右移10</Button> </StackPanel> <Canvas> <Button Width="180" Height="30" Click="Button_Click">Canvas中點(diǎn)擊按鈕右移10</Button> </Canvas> </Grid> </Window>
因?yàn)槭峭卣狗椒ǎ垣@取到控件對(duì)象直接調(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); } } }
效果預(yù)覽
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中點(diǎn)擊拖動(dòng)</TextBlock> <StackPanel> <TextBlock Background="Aqua" TextAlignment="Center" Margin="0,80,0,0" Width="180" Height="30" MouseDown="TextBlock_MouseDown" >StackPanel中點(diǎn)擊拖動(dòng)</TextBlock> </StackPanel> <Canvas > <TextBlock Background="Aqua" TextAlignment="Center" Margin="0,80,0,0" Width="180" Height="30" MouseDown="TextBlock_MouseDown" >Canvas中點(diǎn)擊拖動(dòng)</TextBlock> </Canvas> </Grid> </Window>
此方法也是拓展方法,在鼠標(biāo)按下事件中任意控件都可以調(diào)用此方法,可以通過await等待拖動(dòng)完成。
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("拖動(dòng)完成"); } } }
效果預(yù)覽
3、邊界限制
通過附加屬性IsMoveInBounds設(shè)置是否限制邊界,默認(rèn)為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">拖動(dòng)限制邊界</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">拖動(dòng)不限制邊界</TextBlock> </Grid> </Window>
效果預(yù)覽
4、窗口最大化拖動(dòng)還原
內(nèi)部實(shí)現(xiàn)已支持最大化拖動(dòng)還原,只需要設(shè)置窗口可拖動(dòng)即可,使用場(chǎng)景是無邊框窗口自定義標(biāo)題欄。
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="標(biāo)題欄拖動(dòng)窗口"></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; } } }
效果預(yù)覽
5、拖動(dòng)事件
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中點(diǎn)擊拖動(dòng)</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("拖動(dòng)開始"); } private void window_DragMoveCompleted(object sender, DragMoveCompletedEventArgs e) { Console.WriteLine("拖動(dòng)完成"); } private void window_DragMoveDelta(object sender, DragMoveDeltaEventArgs e) { Console.WriteLine("橫向偏移:"+e.HorizontalOffset + "," +"縱向偏移:"+ e.VerticalOffset); } } }
效果預(yù)覽
總結(jié)
拓展更多的拖動(dòng)功能后使用變得更加方便了,靈活度也提高了。使用xmal或cs代碼都能實(shí)現(xiàn)拖動(dòng),實(shí)現(xiàn)自定義標(biāo)題欄也變得很簡(jiǎn)單,有了拖動(dòng)事件也可以做一些撤銷重做的功能??偟膩碚f,本文的拖動(dòng)功能一定程度可以作為通用的模塊在項(xiàng)目中使用了。
以上就是C# wpf實(shí)現(xiàn)任意控件更多拖動(dòng)功能的詳細(xì)內(nèi)容,更多關(guān)于C# wpf控件拖動(dòng)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
C#開發(fā)Winform實(shí)現(xiàn)學(xué)生管理系統(tǒng)
這篇文章介紹了C#開發(fā)Winform實(shí)現(xiàn)學(xué)生管理系統(tǒng)的項(xiàng)目案例,文中通過示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-05-05silverlight實(shí)現(xiàn)圖片局部放大效果的方法
這篇文章主要介紹了silverlight實(shí)現(xiàn)圖片局部放大效果的方法,結(jié)合實(shí)例形式分析了silverlight針對(duì)圖片屬性的相關(guān)操作技巧,需要的朋友可以參考下2017-03-03C#后臺(tái)調(diào)用前臺(tái)JS函數(shù)方法
今天小編就為大家分享一篇關(guān)于C#后臺(tái)調(diào)用前臺(tái)JS函數(shù)方法,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧2019-01-01C#使用ScrapySharp實(shí)現(xiàn)多線程下載操作
在現(xiàn)代互聯(lián)網(wǎng)應(yīng)用中,數(shù)據(jù)抓取是一個(gè)常見的需求,無論是為了數(shù)據(jù)分析、內(nèi)容聚合還是自動(dòng)化測(cè)試,ScrapySharp 是一個(gè)基于 .NET 的輕量級(jí)、高性能的網(wǎng)頁抓取庫,本文將探討如何在 C# 中使用 ScrapySharp 實(shí)現(xiàn)多線程下載策略,需要的朋友可以參考下2024-08-08