mirror of https://github.com/2dust/v2rayN
Fix
parent
e6c57fcac0
commit
f11b46ed37
|
@ -277,6 +277,33 @@ public class ProfilesSelectViewModel : MyReactiveObject
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<List<ProfileItem>?> GetProfileItems()
|
||||||
|
{
|
||||||
|
if (SelectedProfiles == null || SelectedProfiles.Count == 0)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
var lst = new List<ProfileItem>();
|
||||||
|
foreach (var sp in SelectedProfiles)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(sp?.IndexId))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
var item = await AppManager.Instance.GetProfileItem(sp.IndexId);
|
||||||
|
if (item != null)
|
||||||
|
{
|
||||||
|
lst.Add(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (lst.Count == 0)
|
||||||
|
{
|
||||||
|
NoticeManager.Instance.Enqueue(ResUI.PleaseSelectServer);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return lst;
|
||||||
|
}
|
||||||
|
|
||||||
public void SortServer(string colName)
|
public void SortServer(string colName)
|
||||||
{
|
{
|
||||||
if (colName.IsNullOrEmpty())
|
if (colName.IsNullOrEmpty())
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
Width="800"
|
Width="800"
|
||||||
Height="450"
|
Height="450"
|
||||||
x:DataType="vms:ProfilesSelectViewModel"
|
x:DataType="vms:ProfilesSelectViewModel"
|
||||||
|
WindowStartupLocation="CenterScreen"
|
||||||
mc:Ignorable="d">
|
mc:Ignorable="d">
|
||||||
|
|
||||||
<Window.Resources>
|
<Window.Resources>
|
||||||
|
@ -27,6 +28,7 @@
|
||||||
<Button
|
<Button
|
||||||
x:Name="btnSave"
|
x:Name="btnSave"
|
||||||
Width="100"
|
Width="100"
|
||||||
|
Click="BtnSave_Click"
|
||||||
Content="{x:Static resx:ResUI.TbConfirm}" />
|
Content="{x:Static resx:ResUI.TbConfirm}" />
|
||||||
<Button
|
<Button
|
||||||
x:Name="btnCancel"
|
x:Name="btnCancel"
|
||||||
|
@ -83,6 +85,9 @@
|
||||||
IsReadOnly="True"
|
IsReadOnly="True"
|
||||||
ItemsSource="{Binding ProfileItems}"
|
ItemsSource="{Binding ProfileItems}"
|
||||||
SelectionMode="Single">
|
SelectionMode="Single">
|
||||||
|
<DataGrid.KeyBindings>
|
||||||
|
<KeyBinding Command="{Binding SelectFinish}" Gesture="Enter" />
|
||||||
|
</DataGrid.KeyBindings>
|
||||||
<DataGrid.Columns>
|
<DataGrid.Columns>
|
||||||
<DataGridTextColumn
|
<DataGridTextColumn
|
||||||
Width="80"
|
Width="80"
|
||||||
|
@ -90,7 +95,7 @@
|
||||||
Header="{x:Static resx:ResUI.LvServiceType}"
|
Header="{x:Static resx:ResUI.LvServiceType}"
|
||||||
Tag="ConfigType" />
|
Tag="ConfigType" />
|
||||||
|
|
||||||
<DataGridTemplateColumn Tag="Remarks">
|
<DataGridTemplateColumn Tag="Remarks" SortMemberPath="Remarks">
|
||||||
<DataGridTemplateColumn.Header>
|
<DataGridTemplateColumn.Header>
|
||||||
<TextBlock Text="{x:Static resx:ResUI.LvRemarks}" />
|
<TextBlock Text="{x:Static resx:ResUI.LvRemarks}" />
|
||||||
</DataGridTemplateColumn.Header>
|
</DataGridTemplateColumn.Header>
|
||||||
|
|
|
@ -3,9 +3,11 @@ using System.Reactive.Disposables;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Controls.Primitives;
|
||||||
using Avalonia.Input;
|
using Avalonia.Input;
|
||||||
using Avalonia.Interactivity;
|
using Avalonia.Interactivity;
|
||||||
using Avalonia.Markup.Xaml;
|
using Avalonia.Markup.Xaml;
|
||||||
|
using Avalonia.VisualTree;
|
||||||
using Avalonia.ReactiveUI;
|
using Avalonia.ReactiveUI;
|
||||||
using ReactiveUI;
|
using ReactiveUI;
|
||||||
using ServiceLib.Manager;
|
using ServiceLib.Manager;
|
||||||
|
@ -17,7 +19,9 @@ public partial class ProfilesSelectWindow : ReactiveWindow<ProfilesSelectViewMod
|
||||||
{
|
{
|
||||||
private static Config _config;
|
private static Config _config;
|
||||||
|
|
||||||
public Task<ProfileItem?> ProfileItem => GetFirstProfileItemAsync();
|
public Task<ProfileItem?> ProfileItem => GetProfileItem();
|
||||||
|
public Task<List<ProfileItem>?> ProfileItems => GetProfileItems();
|
||||||
|
private bool _allowMultiSelect = false;
|
||||||
|
|
||||||
public ProfilesSelectWindow()
|
public ProfilesSelectWindow()
|
||||||
{
|
{
|
||||||
|
@ -45,12 +49,12 @@ public partial class ProfilesSelectWindow : ReactiveWindow<ProfilesSelectViewMod
|
||||||
this.Bind(ViewModel, vm => vm.ServerFilter, v => v.txtServerFilter.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.ServerFilter, v => v.txtServerFilter.Text).DisposeWith(disposables);
|
||||||
});
|
});
|
||||||
|
|
||||||
btnSave.Click += (s, e) => ViewModel?.SelectFinish();
|
|
||||||
btnCancel.Click += (s, e) => Close(false);
|
btnCancel.Click += (s, e) => Close(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AllowMultiSelect(bool allow)
|
public void AllowMultiSelect(bool allow)
|
||||||
{
|
{
|
||||||
|
_allowMultiSelect = allow;
|
||||||
if (allow)
|
if (allow)
|
||||||
{
|
{
|
||||||
lstProfiles.SelectionMode = DataGridSelectionMode.Extended;
|
lstProfiles.SelectionMode = DataGridSelectionMode.Extended;
|
||||||
|
@ -94,17 +98,32 @@ public partial class ProfilesSelectWindow : ReactiveWindow<ProfilesSelectViewMod
|
||||||
|
|
||||||
private void LstProfiles_DoubleTapped(object? sender, TappedEventArgs e)
|
private void LstProfiles_DoubleTapped(object? sender, TappedEventArgs e)
|
||||||
{
|
{
|
||||||
ViewModel?.SelectFinish();
|
// 忽略表头区域的双击
|
||||||
|
if (e.Source is Control src)
|
||||||
|
{
|
||||||
|
if (src.FindAncestorOfType<DataGridColumnHeader>() != null)
|
||||||
|
{
|
||||||
|
e.Handled = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 仅当在数据行或其子元素上双击时才触发选择
|
||||||
|
if (src.FindAncestorOfType<DataGridRow>() != null)
|
||||||
|
{
|
||||||
|
ViewModel?.SelectFinish();
|
||||||
|
e.Handled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void LstProfiles_Sorting(object? sender, DataGridColumnEventArgs e)
|
private void LstProfiles_Sorting(object? sender, DataGridColumnEventArgs e)
|
||||||
{
|
{
|
||||||
|
// 自定义排序,防止默认行为导致误触发
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
if (ViewModel != null && e.Column?.Tag?.ToString() != null)
|
if (ViewModel != null && e.Column?.Tag?.ToString() != null)
|
||||||
{
|
{
|
||||||
await ViewModel.SortServer(e.Column.Tag.ToString());
|
ViewModel.SortServer(e.Column.Tag.ToString());
|
||||||
}
|
}
|
||||||
e.Handled = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void LstProfiles_KeyDown(object? sender, KeyEventArgs e)
|
private void LstProfiles_KeyDown(object? sender, KeyEventArgs e)
|
||||||
|
@ -113,7 +132,11 @@ public partial class ProfilesSelectWindow : ReactiveWindow<ProfilesSelectViewMod
|
||||||
{
|
{
|
||||||
if (e.Key == Key.A)
|
if (e.Key == Key.A)
|
||||||
{
|
{
|
||||||
lstProfiles.SelectAll();
|
if (_allowMultiSelect)
|
||||||
|
{
|
||||||
|
lstProfiles.SelectAll();
|
||||||
|
}
|
||||||
|
e.Handled = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -121,6 +144,7 @@ public partial class ProfilesSelectWindow : ReactiveWindow<ProfilesSelectViewMod
|
||||||
if (e.Key is Key.Enter or Key.Return)
|
if (e.Key is Key.Enter or Key.Return)
|
||||||
{
|
{
|
||||||
ViewModel?.SelectFinish();
|
ViewModel?.SelectFinish();
|
||||||
|
e.Handled = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -152,9 +176,21 @@ public partial class ProfilesSelectWindow : ReactiveWindow<ProfilesSelectViewMod
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<ProfileItem?> GetFirstProfileItemAsync()
|
public async Task<ProfileItem?> GetProfileItem()
|
||||||
{
|
{
|
||||||
var item = await ViewModel?.GetProfileItem();
|
var item = await ViewModel?.GetProfileItem();
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<List<ProfileItem>?> GetProfileItems()
|
||||||
|
{
|
||||||
|
var item = await ViewModel?.GetProfileItems();
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void BtnSave_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
// Trigger selection finalize when Confirm is clicked
|
||||||
|
ViewModel?.SelectFinish();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
Height="450"
|
Height="450"
|
||||||
x:TypeArguments="vms:ProfilesSelectViewModel"
|
x:TypeArguments="vms:ProfilesSelectViewModel"
|
||||||
Style="{StaticResource WindowGlobal}"
|
Style="{StaticResource WindowGlobal}"
|
||||||
|
WindowStartupLocation="CenterScreen"
|
||||||
mc:Ignorable="d">
|
mc:Ignorable="d">
|
||||||
|
|
||||||
<Window.Resources>
|
<Window.Resources>
|
||||||
|
@ -33,6 +34,7 @@
|
||||||
<Button
|
<Button
|
||||||
x:Name="btnSave"
|
x:Name="btnSave"
|
||||||
Width="100"
|
Width="100"
|
||||||
|
Click="BtnSave_Click"
|
||||||
Content="{x:Static resx:ResUI.TbConfirm}"
|
Content="{x:Static resx:ResUI.TbConfirm}"
|
||||||
IsDefault="True"
|
IsDefault="True"
|
||||||
Style="{StaticResource DefButton}" />
|
Style="{StaticResource DefButton}" />
|
||||||
|
|
|
@ -15,7 +15,9 @@ public partial class ProfilesSelectWindow
|
||||||
{
|
{
|
||||||
private static Config _config;
|
private static Config _config;
|
||||||
|
|
||||||
public Task<ProfileItem?> ProfileItem => GetFirstProfileItemAsync();
|
public Task<ProfileItem?> ProfileItem => GetProfileItem();
|
||||||
|
public Task<List<ProfileItem>?> ProfileItems => GetProfileItems();
|
||||||
|
private bool _allowMultiSelect = false;
|
||||||
|
|
||||||
public ProfilesSelectWindow()
|
public ProfilesSelectWindow()
|
||||||
{
|
{
|
||||||
|
@ -46,6 +48,7 @@ public partial class ProfilesSelectWindow
|
||||||
|
|
||||||
public void AllowMultiSelect(bool allow)
|
public void AllowMultiSelect(bool allow)
|
||||||
{
|
{
|
||||||
|
_allowMultiSelect = allow;
|
||||||
if (allow)
|
if (allow)
|
||||||
{
|
{
|
||||||
lstProfiles.SelectionMode = DataGridSelectionMode.Extended;
|
lstProfiles.SelectionMode = DataGridSelectionMode.Extended;
|
||||||
|
@ -108,6 +111,10 @@ public partial class ProfilesSelectWindow
|
||||||
|
|
||||||
private void menuSelectAll_Click(object sender, RoutedEventArgs e)
|
private void menuSelectAll_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
|
if (!_allowMultiSelect)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
lstProfiles.SelectAll();
|
lstProfiles.SelectAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,6 +126,7 @@ public partial class ProfilesSelectWindow
|
||||||
{
|
{
|
||||||
case Key.A:
|
case Key.A:
|
||||||
menuSelectAll_Click(null, null);
|
menuSelectAll_Click(null, null);
|
||||||
|
e.Handled = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -127,6 +135,7 @@ public partial class ProfilesSelectWindow
|
||||||
if (e.Key is Key.Enter or Key.Return)
|
if (e.Key is Key.Enter or Key.Return)
|
||||||
{
|
{
|
||||||
ViewModel?.SelectFinish();
|
ViewModel?.SelectFinish();
|
||||||
|
e.Handled = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -156,13 +165,26 @@ public partial class ProfilesSelectWindow
|
||||||
if (e.Key is Key.Enter or Key.Return)
|
if (e.Key is Key.Enter or Key.Return)
|
||||||
{
|
{
|
||||||
ViewModel?.RefreshServers();
|
ViewModel?.RefreshServers();
|
||||||
|
e.Handled = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<ProfileItem?> GetFirstProfileItemAsync()
|
public async Task<ProfileItem?> GetProfileItem()
|
||||||
{
|
{
|
||||||
var item = await ViewModel?.GetProfileItem();
|
var item = await ViewModel?.GetProfileItem();
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<List<ProfileItem>?> GetProfileItems()
|
||||||
|
{
|
||||||
|
var item = await ViewModel?.GetProfileItems();
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void BtnSave_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
// Trigger selection finalize when Confirm is clicked
|
||||||
|
ViewModel?.SelectFinish();
|
||||||
|
}
|
||||||
#endregion Event
|
#endregion Event
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue