mirror of
https://github.com/2dust/v2rayN.git
synced 2026-11-01 18:15:00 +03:00
Add ProDataGrid (#10347)
* Bump Avalonia.Desktop from 12.1.2 to 12.1.4 --- updated-dependencies: - dependency-name: Avalonia.Desktop dependency-version: 12.1.4 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> * Remove Avalonia.Controls.DataGrid * Add ProDataGrid Feature --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: 2dust <31833384+2dust@users.noreply.github.com>
This commit is contained in:
@@ -6,8 +6,7 @@
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageVersion Include="Avalonia.AvaloniaEdit" Version="12.0.0" />
|
||||
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="12.1.2" />
|
||||
<PackageVersion Include="Avalonia.Desktop" Version="12.1.2" />
|
||||
<PackageVersion Include="Avalonia.Desktop" Version="12.1.4" />
|
||||
<PackageVersion Include="AvaloniaUI.DiagnosticsSupport" Version="2.2.3" />
|
||||
<PackageVersion Include="DialogHost.Avalonia" Version="0.12.3" />
|
||||
<PackageVersion Include="IPNetwork2" Version="4.3.0" />
|
||||
@@ -32,4 +31,4 @@
|
||||
<PackageVersion Include="ZXing.Net.Bindings.SkiaSharp" Version="0.16.22" />
|
||||
<PackageVersion Include="SkiaSharp.NativeAssets.Linux" Version="3.119.4" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@@ -540,6 +540,67 @@ public static class ConfigHandler
|
||||
return await Task.FromResult(0);
|
||||
}
|
||||
|
||||
public static Task<int> MoveServersTo(
|
||||
Config config,
|
||||
IReadOnlyList<string> orderedProfileIds,
|
||||
IReadOnlyList<int> sourceIndices,
|
||||
int insertIndex)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(orderedProfileIds);
|
||||
ArgumentNullException.ThrowIfNull(sourceIndices);
|
||||
|
||||
if (orderedProfileIds.Count == 0 ||
|
||||
sourceIndices.Count == 0 ||
|
||||
insertIndex < 0 ||
|
||||
insertIndex > orderedProfileIds.Count)
|
||||
{
|
||||
return Task.FromResult(-1);
|
||||
}
|
||||
|
||||
int[] movingIndices = sourceIndices
|
||||
.Distinct()
|
||||
.OrderBy(index => index)
|
||||
.ToArray();
|
||||
|
||||
if (movingIndices.Length != sourceIndices.Count ||
|
||||
movingIndices.Any(index =>
|
||||
index < 0 || index >= orderedProfileIds.Count))
|
||||
{
|
||||
return Task.FromResult(-1);
|
||||
}
|
||||
|
||||
var movingIndexSet = movingIndices.ToHashSet();
|
||||
|
||||
var movingIds = movingIndices
|
||||
.Select(index => orderedProfileIds[index])
|
||||
.ToList();
|
||||
|
||||
var remainingIds = orderedProfileIds
|
||||
.Where((_, index) => !movingIndexSet.Contains(index))
|
||||
.ToList();
|
||||
|
||||
if (insertIndex > remainingIds.Count)
|
||||
{
|
||||
return Task.FromResult(-1);
|
||||
}
|
||||
|
||||
remainingIds.InsertRange(insertIndex, movingIds);
|
||||
|
||||
if (remainingIds.Count != orderedProfileIds.Count)
|
||||
{
|
||||
return Task.FromResult(-1);
|
||||
}
|
||||
|
||||
for (var index = 0; index < remainingIds.Count; index++)
|
||||
{
|
||||
ProfileExManager.Instance.SetSort(
|
||||
remainingIds[index],
|
||||
(index + 1) * 10);
|
||||
}
|
||||
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add a custom server configuration from a file
|
||||
/// Copies the configuration file to the app's config directory
|
||||
|
||||
Generated
+9
@@ -978,6 +978,15 @@ namespace ServiceLib.Resx {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Columns 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string menuColumns {
|
||||
get {
|
||||
return ResourceManager.GetString("menuColumns", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Close Connection 的本地化字符串。
|
||||
/// </summary>
|
||||
|
||||
@@ -1905,4 +1905,7 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
|
||||
<data name="menuAddMasqueServer" xml:space="preserve">
|
||||
<value>Add [MASQUE]</value>
|
||||
</data>
|
||||
<data name="menuColumns" xml:space="preserve">
|
||||
<value>Columns</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -1897,4 +1897,7 @@
|
||||
<data name="menuAddMasqueServer" xml:space="preserve">
|
||||
<value>添加 [MASQUE]</value>
|
||||
</data>
|
||||
<data name="menuColumns" xml:space="preserve">
|
||||
<value>列</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -702,6 +702,33 @@ public partial class ProfilesViewModel : MyReactiveObject
|
||||
}
|
||||
}
|
||||
|
||||
public async Task MoveServersTo(
|
||||
IReadOnlyList<int> sourceIndices,
|
||||
int insertIndex)
|
||||
{
|
||||
if (sourceIndices.Count == 0 ||
|
||||
insertIndex < 0 ||
|
||||
insertIndex > ProfileItems.Count)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var orderedProfileIds = ProfileItems
|
||||
.Select(item => item.IndexId)
|
||||
.ToList();
|
||||
|
||||
var result = await ConfigHandler.MoveServersTo(
|
||||
_config,
|
||||
orderedProfileIds,
|
||||
sourceIndices,
|
||||
insertIndex);
|
||||
|
||||
if (result == 0)
|
||||
{
|
||||
await RefreshServers();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task ServerSpeedtest(ESpeedActionType actionType)
|
||||
{
|
||||
List<ProfileItem>? lstSelected;
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
xmlns:dialogHost="clr-namespace:DialogHostAvalonia;assembly=DialogHost.Avalonia"
|
||||
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
|
||||
xmlns:semi="https://irihi.tech/semi"
|
||||
xmlns:service="clr-namespace:ServiceLib;assembly=ServiceLib"
|
||||
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
|
||||
Name="v2rayN"
|
||||
x:DataType="vms:StatusBarViewModel"
|
||||
@@ -37,7 +38,7 @@
|
||||
<TrayIcon
|
||||
Command="{Binding NotifyLeftClickCmd}"
|
||||
Icon="/Assets/NotifyIcon1.ico"
|
||||
ToolTipText="{Binding RunningServerToolTipText}">
|
||||
ToolTipText="{Binding RunningServerToolTipText, FallbackValue={x:Static service:Global.AppName}}">
|
||||
<TrayIcon.Menu>
|
||||
<NativeMenu>
|
||||
<NativeMenuItem
|
||||
|
||||
@@ -148,11 +148,13 @@
|
||||
CanUserReorderColumns="False"
|
||||
CanUserResizeColumns="True"
|
||||
CanUserSortColumns="False"
|
||||
FrozenColumnCount="2"
|
||||
GridLinesVisibility="All"
|
||||
HeadersVisibility="Column"
|
||||
HeadersVisibility="All"
|
||||
IsReadOnly="True"
|
||||
ItemsSource="{Binding ChildItemsObs}"
|
||||
SelectionMode="Extended">
|
||||
SelectionMode="Extended"
|
||||
ShowRowNumbers="True">
|
||||
<DataGrid.ContextMenu>
|
||||
<ContextMenu>
|
||||
<MenuItem x:Name="menuAddChildServer" Header="{x:Static resx:ResUI.menuAddChildServer}" />
|
||||
@@ -220,11 +222,13 @@
|
||||
CanUserReorderColumns="False"
|
||||
CanUserResizeColumns="True"
|
||||
CanUserSortColumns="False"
|
||||
FrozenColumnCount="2"
|
||||
GridLinesVisibility="All"
|
||||
HeadersVisibility="Column"
|
||||
HeadersVisibility="All"
|
||||
IsReadOnly="True"
|
||||
ItemsSource="{Binding AllProfilePreviewItemsObs}"
|
||||
SelectionMode="Extended">
|
||||
SelectionMode="Extended"
|
||||
ShowRowNumbers="True">
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn
|
||||
Width="150"
|
||||
|
||||
@@ -55,7 +55,6 @@ public partial class AddGroupServerWindow : WindowBase<AddGroupServerViewModel>
|
||||
|
||||
// Keyboard shortcuts when focus is within grid
|
||||
AddHandler(KeyDownEvent, AddGroupServerWindow_KeyDown, RoutingStrategies.Tunnel);
|
||||
lstChild.LoadingRow += LstChild_LoadingRow;
|
||||
}
|
||||
|
||||
private void InitializeData(ProfileItem profileItem)
|
||||
@@ -77,11 +76,6 @@ public partial class AddGroupServerWindow : WindowBase<AddGroupServerViewModel>
|
||||
}
|
||||
}
|
||||
|
||||
private void LstChild_LoadingRow(object? sender, DataGridRowEventArgs e)
|
||||
{
|
||||
e.Row.Header = $" {e.Row.Index + 1}";
|
||||
}
|
||||
|
||||
private void Window_Loaded(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
txtRemarks.Focus();
|
||||
|
||||
@@ -71,8 +71,8 @@
|
||||
Width="200"
|
||||
Margin="{StaticResource MarginLr4}"
|
||||
VerticalContentAlignment="Center"
|
||||
Text="{Binding ServerFilter, Mode=TwoWay}"
|
||||
PlaceholderText="{x:Static resx:ResUI.MsgServerTitle}"
|
||||
Text="{Binding ServerFilter, Mode=TwoWay}"
|
||||
ToolTip.Tip="{x:Static resx:ResUI.MsgServerTitle}" />
|
||||
</WrapPanel>
|
||||
|
||||
@@ -83,11 +83,13 @@
|
||||
BorderThickness="1"
|
||||
CanUserReorderColumns="True"
|
||||
CanUserResizeColumns="True"
|
||||
FrozenColumnCount="2"
|
||||
GridLinesVisibility="All"
|
||||
HeadersVisibility="All"
|
||||
IsReadOnly="True"
|
||||
ItemsSource="{Binding ProfileItems}"
|
||||
SelectionMode="Single">
|
||||
SelectionMode="Single"
|
||||
ShowRowNumbers="True">
|
||||
<DataGrid.KeyBindings>
|
||||
<KeyBinding Command="{Binding SelectFinish}" Gesture="Enter" />
|
||||
</DataGrid.KeyBindings>
|
||||
|
||||
@@ -14,7 +14,6 @@ public partial class ProfilesSelectWindow : WindowBase<ProfilesSelectViewModel>
|
||||
txtServerFilter.KeyDown += TxtServerFilter_KeyDown;
|
||||
lstProfiles.KeyDown += LstProfiles_KeyDown;
|
||||
lstProfiles.SelectionChanged += LstProfiles_SelectionChanged;
|
||||
lstProfiles.LoadingRow += LstProfiles_LoadingRow;
|
||||
lstProfiles.Sorting += LstProfiles_Sorting;
|
||||
lstProfiles.DoubleTapped += LstProfiles_DoubleTapped;
|
||||
|
||||
@@ -61,11 +60,6 @@ public partial class ProfilesSelectWindow : WindowBase<ProfilesSelectViewModel>
|
||||
}
|
||||
}
|
||||
|
||||
private void LstProfiles_LoadingRow(object? sender, DataGridRowEventArgs e)
|
||||
{
|
||||
e.Row.Header = $" {e.Row.Index + 1}";
|
||||
}
|
||||
|
||||
private void LstProfiles_DoubleTapped(object? sender, TappedEventArgs e)
|
||||
{
|
||||
// Ignore double-taps on the column header area
|
||||
|
||||
@@ -16,6 +16,48 @@
|
||||
mc:Ignorable="d">
|
||||
<UserControl.Resources>
|
||||
<conv:DelayColorConverter x:Key="DelayColorConverter" />
|
||||
|
||||
<ControlTheme x:Key="{x:Type DataGridColumnChooser}" TargetType="DataGridColumnChooser">
|
||||
<Setter Property="ItemTemplate">
|
||||
<Setter.Value>
|
||||
<DataTemplate x:DataType="DataGridColumnChooserItem">
|
||||
<CheckBox
|
||||
Margin="0,2"
|
||||
Content="{Binding Header}"
|
||||
IsChecked="{Binding IsVisible, Mode=TwoWay}"
|
||||
IsEnabled="{Binding CanUserHide}" />
|
||||
</DataTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Setter Property="ItemsPanel">
|
||||
<Setter.Value>
|
||||
<ItemsPanelTemplate>
|
||||
<StackPanel Spacing="4" />
|
||||
</ItemsPanelTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate>
|
||||
<Button Classes="IconButton Success" ToolTip.Tip="{TemplateBinding ToolTip.Tip}">
|
||||
<Button.Flyout>
|
||||
<Flyout Placement="BottomEdgeAlignedLeft">
|
||||
<Border
|
||||
MaxWidth="260"
|
||||
MaxHeight="360"
|
||||
Padding="8">
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
||||
<ItemsPresenter />
|
||||
</ScrollViewer>
|
||||
</Border>
|
||||
</Flyout>
|
||||
</Button.Flyout>
|
||||
<ContentPresenter Content="{TemplateBinding Header}" ContentTemplate="{TemplateBinding HeaderTemplate}" />
|
||||
</Button>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</ControlTheme>
|
||||
</UserControl.Resources>
|
||||
<Grid>
|
||||
<DockPanel>
|
||||
@@ -79,6 +121,15 @@
|
||||
</Button.Content>
|
||||
</Button>
|
||||
|
||||
<DataGridColumnChooser
|
||||
Margin="{StaticResource MarginLr4}"
|
||||
DataGrid="{Binding ElementName=lstProfiles}"
|
||||
ToolTip.Tip="{x:Static resx:ResUI.menuColumns}">
|
||||
<DataGridColumnChooser.Header>
|
||||
<PathIcon Data="{StaticResource SemiIconSetting}" />
|
||||
</DataGridColumnChooser.Header>
|
||||
</DataGridColumnChooser>
|
||||
|
||||
<Button
|
||||
x:Name="btnFastRealPing"
|
||||
Margin="{StaticResource MarginLr4}"
|
||||
@@ -97,20 +148,27 @@
|
||||
<PathIcon Data="{StaticResource building_ping}" />
|
||||
</Button.Content>
|
||||
</Button>
|
||||
|
||||
</StackPanel>
|
||||
|
||||
</WrapPanel>
|
||||
|
||||
<DataGrid
|
||||
x:Name="lstProfiles"
|
||||
AutoGenerateColumns="False"
|
||||
BorderThickness="1"
|
||||
CanUserAddRows="False"
|
||||
CanUserDeleteRows="False"
|
||||
CanUserHideColumns="True"
|
||||
CanUserReorderColumns="True"
|
||||
CanUserResizeColumns="True"
|
||||
Classes.InsetContent="True"
|
||||
FrozenColumnCount="2"
|
||||
GridLinesVisibility="All"
|
||||
HeadersVisibility="All"
|
||||
IsReadOnly="True"
|
||||
ItemsSource="{Binding ProfileItems}">
|
||||
IsReadOnly="False"
|
||||
ItemsSource="{Binding ProfileItems}"
|
||||
ShowRowNumbers="True">
|
||||
<DataGrid.KeyBindings>
|
||||
<KeyBinding Command="{Binding Export2ShareUrlCmd}" Gesture="Ctrl+C" />
|
||||
<KeyBinding Command="{Binding SetDefaultServerCmd}" Gesture="Enter" />
|
||||
@@ -212,12 +270,13 @@
|
||||
Width="80"
|
||||
Binding="{Binding ConfigType}"
|
||||
Header="{x:Static resx:ResUI.LvServiceType}"
|
||||
IsReadOnly="True"
|
||||
Tag="ConfigType" />
|
||||
|
||||
<DataGridTemplateColumn SortMemberPath="Remarks" Tag="Remarks">
|
||||
<DataGridTemplateColumn.Header>
|
||||
<TextBlock Text="{x:Static resx:ResUI.LvRemarks}" />
|
||||
</DataGridTemplateColumn.Header>
|
||||
<DataGridTemplateColumn
|
||||
Header="{x:Static resx:ResUI.LvRemarks}"
|
||||
SortMemberPath="Remarks"
|
||||
Tag="Remarks">
|
||||
<DataGridTemplateColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<StackPanel Margin="{StaticResource MarginLr8}" Orientation="Horizontal">
|
||||
@@ -240,31 +299,36 @@
|
||||
Width="120"
|
||||
Binding="{Binding Address}"
|
||||
Header="{x:Static resx:ResUI.LvAddress}"
|
||||
IsReadOnly="True"
|
||||
Tag="Address" />
|
||||
<DataGridTextColumn
|
||||
Width="60"
|
||||
Binding="{Binding Port}"
|
||||
Header="{x:Static resx:ResUI.LvPort}"
|
||||
IsReadOnly="True"
|
||||
Tag="Port" />
|
||||
<DataGridTextColumn
|
||||
Width="100"
|
||||
Binding="{Binding Network}"
|
||||
Header="{x:Static resx:ResUI.LvTransportProtocol}"
|
||||
IsReadOnly="True"
|
||||
Tag="Network" />
|
||||
<DataGridTextColumn
|
||||
Width="100"
|
||||
Binding="{Binding StreamSecurity}"
|
||||
Header="{x:Static resx:ResUI.LvTLS}"
|
||||
IsReadOnly="True"
|
||||
Tag="StreamSecurity" />
|
||||
<DataGridTextColumn
|
||||
Width="100"
|
||||
Binding="{Binding SubRemarks}"
|
||||
Header="{x:Static resx:ResUI.LvSubscription}"
|
||||
IsReadOnly="True"
|
||||
Tag="SubRemarks" />
|
||||
<DataGridTemplateColumn SortMemberPath="Delay" Tag="DelayVal">
|
||||
<DataGridTemplateColumn.Header>
|
||||
<TextBlock Text="{x:Static resx:ResUI.LvTestDelay}" />
|
||||
</DataGridTemplateColumn.Header>
|
||||
<DataGridTemplateColumn
|
||||
Header="{x:Static resx:ResUI.LvTestDelay}"
|
||||
SortMemberPath="Delay"
|
||||
Tag="DelayVal">
|
||||
<DataGridTemplateColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock
|
||||
@@ -280,12 +344,13 @@
|
||||
Width="100"
|
||||
Binding="{Binding SpeedVal}"
|
||||
Header="{x:Static resx:ResUI.LvTestSpeed}"
|
||||
IsReadOnly="True"
|
||||
Tag="SpeedVal" />
|
||||
|
||||
<DataGridTemplateColumn SortMemberPath="IpInfo" Tag="IpInfo">
|
||||
<DataGridTemplateColumn.Header>
|
||||
<TextBlock Text="{x:Static resx:ResUI.LvTestIpInfo}" />
|
||||
</DataGridTemplateColumn.Header>
|
||||
<DataGridTemplateColumn
|
||||
Header="{x:Static resx:ResUI.LvTestIpInfo}"
|
||||
SortMemberPath="IpInfo"
|
||||
Tag="IpInfo">
|
||||
<DataGridTemplateColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<SelectableTextBlock
|
||||
@@ -300,21 +365,25 @@
|
||||
Width="100"
|
||||
Binding="{Binding TodayUp}"
|
||||
Header="{x:Static resx:ResUI.LvTodayUploadDataAmount}"
|
||||
IsReadOnly="True"
|
||||
Tag="TodayUp" />
|
||||
<DataGridTextColumn
|
||||
Width="100"
|
||||
Binding="{Binding TodayDown}"
|
||||
Header="{x:Static resx:ResUI.LvTodayDownloadDataAmount}"
|
||||
IsReadOnly="True"
|
||||
Tag="TodayDown" />
|
||||
<DataGridTextColumn
|
||||
Width="100"
|
||||
Binding="{Binding TotalUp}"
|
||||
Header="{x:Static resx:ResUI.LvTotalUploadDataAmount}"
|
||||
IsReadOnly="True"
|
||||
Tag="TotalUp" />
|
||||
<DataGridTextColumn
|
||||
Width="100"
|
||||
Binding="{Binding TotalDown}"
|
||||
Header="{x:Static resx:ResUI.LvTotalDownloadDataAmount}"
|
||||
IsReadOnly="True"
|
||||
Tag="TotalDown" />
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Avalonia.VisualTree;
|
||||
using Avalonia.Controls.DataGridDragDrop;
|
||||
using DialogHostAvalonia;
|
||||
using v2rayN.Desktop.Common;
|
||||
|
||||
@@ -8,6 +8,7 @@ public partial class ProfilesView : ReactiveUserControl<ProfilesViewModel>
|
||||
{
|
||||
private static Config _config;
|
||||
private static readonly string _tag = "ProfilesView";
|
||||
private bool _isDndRegistered = false;
|
||||
|
||||
public ProfilesView()
|
||||
{
|
||||
@@ -21,18 +22,17 @@ public partial class ProfilesView : ReactiveUserControl<ProfilesViewModel>
|
||||
lstProfiles.KeyDown += LstProfiles_KeyDown;
|
||||
lstProfiles.SelectionChanged += lstProfiles_SelectionChanged;
|
||||
lstProfiles.DoubleTapped += LstProfiles_DoubleTapped;
|
||||
lstProfiles.LoadingRow += LstProfiles_LoadingRow;
|
||||
lstProfiles.Sorting += LstProfiles_Sorting;
|
||||
if (_config.UiItem.EnableDragDropSort)
|
||||
{
|
||||
lstProfiles.SetValue(DragDrop.AllowDropProperty, true);
|
||||
//if (_config.UiItem.EnableDragDropSort)
|
||||
//{
|
||||
// lstProfiles.SetValue(DragDrop.AllowDropProperty, true);
|
||||
|
||||
lstProfiles.AddHandler(PointerPressedEvent, LstProfiles_PointerPressed, RoutingStrategies.Bubble, true);
|
||||
lstProfiles.AddHandler(PointerMovedEvent, LstProfiles_PointerMoved, RoutingStrategies.Bubble, true);
|
||||
lstProfiles.AddHandler(PointerReleasedEvent, LstProfiles_PointerReleased, RoutingStrategies.Bubble, true);
|
||||
lstProfiles.AddHandler(DragDrop.DragOverEvent, LstProfiles_DragOver, RoutingStrategies.Bubble);
|
||||
lstProfiles.AddHandler(DragDrop.DropEvent, LstProfiles_Drop, RoutingStrategies.Bubble);
|
||||
}
|
||||
// lstProfiles.AddHandler(PointerPressedEvent, LstProfiles_PointerPressed, RoutingStrategies.Bubble, true);
|
||||
// lstProfiles.AddHandler(PointerMovedEvent, LstProfiles_PointerMoved, RoutingStrategies.Bubble, true);
|
||||
// lstProfiles.AddHandler(PointerReleasedEvent, LstProfiles_PointerReleased, RoutingStrategies.Bubble, true);
|
||||
// lstProfiles.AddHandler(DragDrop.DragOverEvent, LstProfiles_DragOver, RoutingStrategies.Bubble);
|
||||
// lstProfiles.AddHandler(DragDrop.DropEvent, LstProfiles_Drop, RoutingStrategies.Bubble);
|
||||
//}
|
||||
|
||||
this.WhenActivated(disposables =>
|
||||
{
|
||||
@@ -84,6 +84,22 @@ public partial class ProfilesView : ReactiveUserControl<ProfilesViewModel>
|
||||
this.BindCommand(ViewModel, vm => vm.Export2ShareUrlBase64Cmd, v => v.menuExport2ShareUrlBase64).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.Export2InnerUriCmd, v => v.menuExport2InnerUri).DisposeWith(disposables);
|
||||
|
||||
if (_config.UiItem.EnableDragDropSort && !_isDndRegistered)
|
||||
{
|
||||
lstProfiles.CanUserReorderRows = true;
|
||||
lstProfiles.RowDragHandle = DataGridRowDragHandle.RowHeaderAndRow;
|
||||
lstProfiles.RowDragHandleVisible = true;
|
||||
var rowDragDropOptions = new DataGridRowDragDropOptions()
|
||||
{
|
||||
AllowedEffects = DragDropEffects.Move,
|
||||
DragSelectedRows = true,
|
||||
SuppressSelectionDragFromDragHandle = true,
|
||||
};
|
||||
lstProfiles.RowDragDropOptions = rowDragDropOptions;
|
||||
lstProfiles.RowDropHandler = new ProfileRowDropHandler(ViewModel);
|
||||
_isDndRegistered = true;
|
||||
}
|
||||
|
||||
ViewModel.ShowYesNoInteraction.RegisterHandler(async interaction =>
|
||||
{
|
||||
var message = interaction.Input;
|
||||
@@ -216,11 +232,6 @@ public partial class ProfilesView : ReactiveUserControl<ProfilesViewModel>
|
||||
}
|
||||
}
|
||||
|
||||
private void LstProfiles_LoadingRow(object? sender, DataGridRowEventArgs e)
|
||||
{
|
||||
e.Row.Header = $" {e.Row.Index + 1}";
|
||||
}
|
||||
|
||||
//private void LstProfiles_ColumnHeader_Click(object? sender, RoutedEventArgs e)
|
||||
//{
|
||||
// var colHeader = sender as DataGridColumnHeader;
|
||||
@@ -435,140 +446,174 @@ public partial class ProfilesView : ReactiveUserControl<ProfilesViewModel>
|
||||
|
||||
#endregion UI
|
||||
|
||||
#region Drag and Drop
|
||||
//#region Drag and Drop
|
||||
|
||||
private static readonly DataFormat<ProfileItemModel> LstProfilesRowFormat =
|
||||
DataFormat.CreateInProcessFormat<ProfileItemModel>("LstProfilesRow");
|
||||
//private static readonly DataFormat<ProfileItemModel> LstProfilesRowFormat =
|
||||
// DataFormat.CreateInProcessFormat<ProfileItemModel>("LstProfilesRow");
|
||||
|
||||
private (Point, PointerPressedEventArgs)? _dragStartPoint;
|
||||
//private (Point, PointerPressedEventArgs)? _dragStartPoint;
|
||||
|
||||
private void LstProfiles_PointerPressed(object? sender, PointerPressedEventArgs e)
|
||||
//private void LstProfiles_PointerPressed(object? sender, PointerPressedEventArgs e)
|
||||
//{
|
||||
// var properties = e.GetCurrentPoint(this).Properties;
|
||||
// if (properties.IsLeftButtonPressed)
|
||||
// {
|
||||
// _dragStartPoint = (e.GetPosition(this), e);
|
||||
// }
|
||||
//}
|
||||
|
||||
//private async void LstProfiles_PointerMoved(object? sender, PointerEventArgs e)
|
||||
//{
|
||||
// try
|
||||
// {
|
||||
// if (_dragStartPoint == null)
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
|
||||
// var properties = e.GetCurrentPoint(this).Properties;
|
||||
// if (!properties.IsLeftButtonPressed)
|
||||
// {
|
||||
// _dragStartPoint = null;
|
||||
// return;
|
||||
// }
|
||||
|
||||
// var currentPoint = e.GetPosition(this);
|
||||
// var startPoint = _dragStartPoint.Value.Item1;
|
||||
// var delta = startPoint - currentPoint;
|
||||
|
||||
// var threshold = new Vector(4, 4);
|
||||
|
||||
// if (!(Math.Abs(delta.X) >= threshold.X) && !(Math.Abs(delta.Y) >= threshold.Y))
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
// var dragStartEventArgs = _dragStartPoint.Value.Item2;
|
||||
// _dragStartPoint = null;
|
||||
|
||||
// if (e.Source is not Visual visualSource)
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
// var row = visualSource.FindAncestorOfType<DataGridRow>(true);
|
||||
// if (row?.DataContext == null)
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
|
||||
// e.Handled = true;
|
||||
|
||||
// var dragData = new DataTransfer();
|
||||
// var item = DataTransferItem.Create(LstProfilesRowFormat, row.DataContext as ProfileItemModel);
|
||||
|
||||
// dragData.Add(item);
|
||||
|
||||
// await DragDrop.DoDragDropAsync(dragStartEventArgs, dragData, DragDropEffects.Move);
|
||||
// }
|
||||
// catch
|
||||
// {
|
||||
// // Ignore
|
||||
// }
|
||||
//}
|
||||
|
||||
//private void LstProfiles_PointerReleased(object? sender, PointerReleasedEventArgs e)
|
||||
//{
|
||||
// _dragStartPoint = null;
|
||||
//}
|
||||
|
||||
//private void LstProfiles_DragOver(object? sender, DragEventArgs e)
|
||||
//{
|
||||
// if (!e.DataTransfer.Contains(LstProfilesRowFormat))
|
||||
// {
|
||||
// e.DragEffects = DragDropEffects.None;
|
||||
// return;
|
||||
// }
|
||||
// e.DragEffects = DragDropEffects.Move;
|
||||
//}
|
||||
|
||||
//private void LstProfiles_Drop(object? sender, DragEventArgs e)
|
||||
//{
|
||||
// if (!e.DataTransfer.Contains(LstProfilesRowFormat))
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
// ProfileItemModel? sourceItem = null;
|
||||
// foreach (var item in e.DataTransfer.Items)
|
||||
// {
|
||||
// if (!item.Formats.Contains(LstProfilesRowFormat))
|
||||
// {
|
||||
// continue;
|
||||
// }
|
||||
// if (item.TryGetRaw(LstProfilesRowFormat) is not ProfileItemModel model)
|
||||
// {
|
||||
// continue;
|
||||
// }
|
||||
// sourceItem = model;
|
||||
// break;
|
||||
// }
|
||||
// if (sourceItem == null)
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
// if (e.Source is not Visual visualTarget)
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
|
||||
// var targetRow = visualTarget.FindAncestorOfType<DataGridRow>(true);
|
||||
// if (targetRow is not { DataContext: ProfileItemModel targetItem })
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
// if (ReferenceEquals(sourceItem, targetItem))
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
// if (lstProfiles.ItemsSource is not IList<ProfileItemModel> items)
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
// var oldIndex = items.IndexOf(sourceItem);
|
||||
// var newIndex = items.IndexOf(targetItem);
|
||||
// if (oldIndex >= 0 && newIndex >= 0)
|
||||
// {
|
||||
// ViewModel?.MoveServerTo(oldIndex, targetItem);
|
||||
// }
|
||||
//}
|
||||
|
||||
//#endregion Drag and Drop
|
||||
|
||||
private sealed class ProfileRowDropHandler(ProfilesViewModel viewModel) : IDataGridRowDropHandler
|
||||
{
|
||||
var properties = e.GetCurrentPoint(this).Properties;
|
||||
if (properties.IsLeftButtonPressed)
|
||||
private readonly DataGridRowReorderHandler _reorderHandler = new();
|
||||
|
||||
public bool Validate(DataGridRowDropEventArgs args)
|
||||
{
|
||||
_dragStartPoint = (e.GetPosition(this), e);
|
||||
ArgumentNullException.ThrowIfNull(args);
|
||||
return _reorderHandler.Validate(args);
|
||||
}
|
||||
|
||||
public bool Execute(DataGridRowDropEventArgs args)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(args);
|
||||
|
||||
if (!_reorderHandler.Execute(args))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (viewModel?.ProfileItems != null)
|
||||
{
|
||||
for (var index = 0; index < viewModel.ProfileItems.Count; index++)
|
||||
{
|
||||
var item = viewModel.ProfileItems[index];
|
||||
var sort = (index + 1) * 10;
|
||||
item.Sort = sort;
|
||||
ProfileExManager.Instance.SetSort(item.IndexId, sort);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private async void LstProfiles_PointerMoved(object? sender, PointerEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_dragStartPoint == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var properties = e.GetCurrentPoint(this).Properties;
|
||||
if (!properties.IsLeftButtonPressed)
|
||||
{
|
||||
_dragStartPoint = null;
|
||||
return;
|
||||
}
|
||||
|
||||
var currentPoint = e.GetPosition(this);
|
||||
var startPoint = _dragStartPoint.Value.Item1;
|
||||
var delta = startPoint - currentPoint;
|
||||
|
||||
var threshold = new Vector(4, 4);
|
||||
|
||||
if (!(Math.Abs(delta.X) >= threshold.X) && !(Math.Abs(delta.Y) >= threshold.Y))
|
||||
{
|
||||
return;
|
||||
}
|
||||
var dragStartEventArgs = _dragStartPoint.Value.Item2;
|
||||
_dragStartPoint = null;
|
||||
|
||||
if (e.Source is not Visual visualSource)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var row = visualSource.FindAncestorOfType<DataGridRow>(true);
|
||||
if (row?.DataContext == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
e.Handled = true;
|
||||
|
||||
var dragData = new DataTransfer();
|
||||
var item = DataTransferItem.Create(LstProfilesRowFormat, row.DataContext as ProfileItemModel);
|
||||
|
||||
dragData.Add(item);
|
||||
|
||||
await DragDrop.DoDragDropAsync(dragStartEventArgs, dragData, DragDropEffects.Move);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
|
||||
private void LstProfiles_PointerReleased(object? sender, PointerReleasedEventArgs e)
|
||||
{
|
||||
_dragStartPoint = null;
|
||||
}
|
||||
|
||||
private void LstProfiles_DragOver(object? sender, DragEventArgs e)
|
||||
{
|
||||
if (!e.DataTransfer.Contains(LstProfilesRowFormat))
|
||||
{
|
||||
e.DragEffects = DragDropEffects.None;
|
||||
return;
|
||||
}
|
||||
e.DragEffects = DragDropEffects.Move;
|
||||
}
|
||||
|
||||
private void LstProfiles_Drop(object? sender, DragEventArgs e)
|
||||
{
|
||||
if (!e.DataTransfer.Contains(LstProfilesRowFormat))
|
||||
{
|
||||
return;
|
||||
}
|
||||
ProfileItemModel? sourceItem = null;
|
||||
foreach (var item in e.DataTransfer.Items)
|
||||
{
|
||||
if (!item.Formats.Contains(LstProfilesRowFormat))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (item.TryGetRaw(LstProfilesRowFormat) is not ProfileItemModel model)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
sourceItem = model;
|
||||
break;
|
||||
}
|
||||
if (sourceItem == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (e.Source is not Visual visualTarget)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var targetRow = visualTarget.FindAncestorOfType<DataGridRow>(true);
|
||||
if (targetRow is not { DataContext: ProfileItemModel targetItem })
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (ReferenceEquals(sourceItem, targetItem))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (lstProfiles.ItemsSource is not IList<ProfileItemModel> items)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var oldIndex = items.IndexOf(sourceItem);
|
||||
var newIndex = items.IndexOf(targetItem);
|
||||
if (oldIndex >= 0 && newIndex >= 0)
|
||||
{
|
||||
ViewModel?.MoveServerTo(oldIndex, targetItem);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Drag and Drop
|
||||
}
|
||||
|
||||
@@ -16,12 +16,10 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Avalonia.AvaloniaEdit" />
|
||||
<PackageReference Include="Avalonia.Controls.DataGrid">
|
||||
<TreatAsUsed>true</TreatAsUsed>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Avalonia.Desktop" />
|
||||
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="AvaloniaUI.DiagnosticsSupport" />
|
||||
<PackageReference Include="DialogHost.Avalonia" />
|
||||
<PackageReference Include="ProDataGrid" />
|
||||
<PackageReference Include="ReactiveUI.Avalonia" />
|
||||
<PackageReference Include="Semi.Avalonia" />
|
||||
<PackageReference Include="Semi.Avalonia.AvaloniaEdit" />
|
||||
|
||||
Reference in New Issue
Block a user