Avalonia封裝實(shí)現(xiàn)指定組件允許拖動(dòng)的工具類
創(chuàng)建Avalonia的MVVM項(xiàng)目,命名DragDemo ,然后將項(xiàng)目的Nuget包更新到預(yù)覽版
<ItemGroup>
<PackageReference Include="Avalonia" Version="11.0.0-preview5" />
<PackageReference Include="Avalonia.Desktop" Version="11.0.0-preview5" />
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="11.0.0-preview5" />
<PackageReference Include="Avalonia.ReactiveUI" Version="11.0.0-preview5" />
<PackageReference Include="XamlNameReferenceGenerator" Version="1.5.1" />
</ItemGroup>
更新完成以后ViewLocator和App.axaml會(huì)報(bào)錯(cuò),
修改ViewLocator.cs為下面的代碼
using System;
using Avalonia.Controls;
using Avalonia.Controls.Templates;
using DragDemo.ViewModels;
namespace DragDemo;
public class ViewLocator : IDataTemplate
{
/// <summary>
/// 將IControl修改成Control
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
public Control Build(object data)
{
var name = data.GetType().FullName!.Replace("ViewModel", "View");
var type = Type.GetType(name);
if (type != null)
{
return (Control)Activator.CreateInstance(type)!;
}
return new TextBlock { Text = "Not Found: " + name };
}
public bool Match(object data)
{
return data is ViewModelBase;
}
}
添加Avalonia.Themes.Fluent,因?yàn)轭A(yù)覽版本的包已經(jīng)獨(dú)立需要單獨(dú)安裝
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.0.0-preview5" />
打開App.axaml文件,修改為以下代碼
<Application xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:DragDemo"
RequestedThemeVariant="Light"
x:Class="DragDemo.App">
<Application.DataTemplates>
<local:ViewLocator/>
</Application.DataTemplates>
<Application.Styles>
<FluentTheme DensityStyle="Compact"/>
</Application.Styles>
</Application>
打開Views/MainWindow.axaml
在頭部添加以下代碼,讓窗口無邊框,設(shè)置指定窗口Height="38" Width="471",參數(shù)讓其不要占用整個(gè)屏幕,
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="using:DragDemo.ViewModels"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="DragDemo.Views.MainWindow"
Icon="/Assets/avalonia-logo.ico"
ExtendClientAreaToDecorationsHint="True"
ExtendClientAreaChromeHints="NoChrome"
ExtendClientAreaTitleBarHeightHint="-1"
MaxHeight="38" MaxWidth="471"
Title="DragDemo">
<Window.Styles>
<Style Selector="Window">
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Padding" Value="0"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderBrush" Value="Transparent"/>
</Style>
</Window.Styles>
<Design.DataContext>
<vm:MainWindowViewModel/>
</Design.DataContext>
<StackPanel>
<StackPanel Opacity="0.1" Height="38" Width="471">
</StackPanel>
<Border Name="Border" Width="471" CornerRadius="10" Opacity="1" Background="#FFFFFF">
<Button>按鈕</Button>
</Border>
</StackPanel>
</Window>
以下代碼在上面窗口用于設(shè)置窗口無邊框
<Window.Styles>
<Style Selector="Window">
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Padding" Value="0"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderBrush" Value="Transparent"/>
</Style>
</Window.Styles>
然后打開/Views/MainWindow.axaml.cs文件,將邊框設(shè)置成無邊框,并且設(shè)置窗體透明為WindowTransparencyLevel.Transparent
using Avalonia;
using Avalonia.Controls;
namespace DragDemo.Views;
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.TransparencyLevelHint = WindowTransparencyLevel.Transparent;
ExtendClientAreaToDecorationsHint = true;
WindowState = WindowState.Maximized;
}
}
效果圖如下,因?yàn)橄拗屏舜绑w最大大小,并且在按鈕上面添加了透明區(qū)塊,這樣看起來就像是懸浮了
然后我們開始寫指定組件拖動(dòng)工具類,創(chuàng)建DragControlHelper.cs 以下就是封裝的工具類 定義了一個(gè)ConcurrentDictionary靜態(tài)參數(shù),指定組件為Key ,Value為DragModule ,DragModule模型中定義了拖動(dòng)的邏輯在調(diào)用StartDrag的時(shí)候傳遞需要拖動(dòng)的組件,他會(huì)創(chuàng)建一個(gè)DragModule對(duì)象,創(chuàng)建的時(shí)候會(huì)創(chuàng)建定時(shí)器,當(dāng)鼠標(biāo)被按下時(shí)啟動(dòng)定時(shí)器,當(dāng)鼠標(biāo)被釋放時(shí)定時(shí)器被停止,定時(shí)器用于平滑更新窗體移動(dòng),如果直接移動(dòng)窗體會(huì)抖動(dòng)。
using System;
using System.Collections.Concurrent;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Threading;
using Avalonia.VisualTree;
namespace DragDemo;
public class DragControlHelper
{
private static ConcurrentDictionary<Control, DragModule> _dragModules = new();
public static void StartDrag(Control userControl)
{
_dragModules.TryAdd(userControl, new DragModule(userControl));
}
public static void StopDrag(Control userControl)
{
if (_dragModules.TryRemove(userControl, out var dragModule))
{
dragModule.Dispose();
}
}
}
class DragModule : IDisposable
{
/// <summary>
/// 記錄上一次鼠標(biāo)位置
/// </summary>
private Point? lastMousePosition;
/// <summary>
/// 用于平滑更新坐標(biāo)的計(jì)時(shí)器
/// </summary>
private DispatcherTimer _timer;
/// <summary>
/// 標(biāo)記是否先啟動(dòng)了拖動(dòng)
/// </summary>
private bool isDragging = false;
/// <summary>
/// 需要更新的坐標(biāo)點(diǎn)
/// </summary>
private PixelPoint? _targetPosition;
public Control UserControl { get; set; }
public DragModule(Control userControl)
{
UserControl = userControl;
// 添加當(dāng)前控件的事件監(jiān)聽
UserControl.PointerPressed += OnPointerPressed;
UserControl.PointerMoved += OnPointerMoved;
UserControl.PointerReleased += OnPointerReleased;
// 初始化計(jì)時(shí)器
_timer = new DispatcherTimer
{
Interval = TimeSpan.FromMilliseconds(10)
};
_timer.Tick += OnTimerTick;
}
/// <summary>
/// 計(jì)時(shí)器事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void OnTimerTick(object sender, EventArgs e)
{
var window = UserControl.FindAncestorOfType<Window>();
if (window != null && window.Position != _targetPosition)
{
// 更新坐標(biāo)
window.Position = (PixelPoint)_targetPosition;
}
}
private void OnPointerPressed(object sender, PointerPressedEventArgs e)
{
if (!e.GetCurrentPoint(UserControl).Properties.IsLeftButtonPressed) return;
// 啟動(dòng)拖動(dòng)
isDragging = true;
// 記錄當(dāng)前坐標(biāo)
lastMousePosition = e.GetPosition(UserControl);
e.Handled = true;
// 啟動(dòng)計(jì)時(shí)器
_timer.Start();
}
private void OnPointerReleased(object sender, PointerReleasedEventArgs e)
{
if (!isDragging) return;
// 停止拖動(dòng)
isDragging = false;
e.Handled = true;
// 停止計(jì)時(shí)器
_timer.Stop();
}
private void OnPointerMoved(object sender, PointerEventArgs e)
{
if (!e.GetCurrentPoint(UserControl).Properties.IsLeftButtonPressed) return;
// 如果沒有啟動(dòng)拖動(dòng),則不執(zhí)行
if (!isDragging) return;
var currentMousePosition = e.GetPosition(UserControl);
var offset =currentMousePosition - lastMousePosition.Value;
var window = UserControl.FindAncestorOfType<Window>();
if (window != null)
{
// 記錄當(dāng)前坐標(biāo)
_targetPosition = new PixelPoint(window.Position.X + (int)offset.X,
window.Position.Y + (int)offset.Y);
}
}
public void Dispose()
{
_timer.Stop();
_targetPosition = null;
lastMousePosition = null;
}
}
打開MainWindow.axaml.cs,修改成以下代碼 ,在渲染成功以后拿到Border(需要移動(dòng)的組件),添加到DragControlHelper.StartDrag(border);中,然后再OnUnloaded的時(shí)候?qū)?code>Border再卸載掉
using Avalonia;
using Avalonia.Controls;
using Avalonia.Media;
using Avalonia.Threading;
namespace DragDemo.Views;
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.TransparencyLevelHint = WindowTransparencyLevel.Transparent;
ExtendClientAreaToDecorationsHint = true;
WindowState = WindowState.Maximized;
}
public override void Render(DrawingContext context)
{
base.Render(context);
Dispatcher.UIThread.Post(() =>
{
var border = this.Find<Border>("Border");
DragControlHelper.StartDrag(border);
});
}
protected override void OnUnloaded()
{
var border = this.Find<Border>("Border");
DragControlHelper.StopDrag(border);
base.OnUnloaded();
}
}
效果展示:

到此這篇關(guān)于Avalonia封裝實(shí)現(xiàn)指定組件允許拖動(dòng)的工具類的文章就介紹到這了,更多相關(guān)Avalonia拖動(dòng)指定組件內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
關(guān)于Unity中RectTransform與transform的區(qū)別
這篇文章主要介紹了Unity中RectTransform與transform的區(qū)別,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-01-01
C#使用Microsoft消息隊(duì)列(MSMQ)的示例詳解
Microsoft?Message?Queuing?(MSMQ)?是在多個(gè)不同的應(yīng)用之間實(shí)現(xiàn)相互通信的一種異步傳輸模式,本文主要介紹了C#如何使用Microsoft消息隊(duì)列,需要的可以了解下2024-01-01
js驗(yàn)證電話號(hào)碼手機(jī)號(hào)碼的正則表達(dá)式
本篇文章主要是對(duì)js驗(yàn)證電話號(hào)碼手機(jī)號(hào)碼的正則表達(dá)式進(jìn)行了介紹。需要的朋友可以過來參考下,希望對(duì)大家有所幫助2014-01-01
C#簡(jiǎn)易圖片格式轉(zhuǎn)換器實(shí)現(xiàn)方法
這篇文章主要介紹了C#簡(jiǎn)易圖片格式轉(zhuǎn)換器實(shí)現(xiàn)方法,涉及C#基于WinForm操作圖片的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-11-11

