Persist DataGrid column layout for ClashConnectionsView

https://github.com/2dust/v2rayN/issues/8893
This commit is contained in:
2dust 2026-03-08 17:57:09 +08:00
parent 66e1aeae1f
commit 679bd8afcc
5 changed files with 136 additions and 33 deletions

View file

@ -154,6 +154,7 @@ public static class ConfigHandler
DownMbps = 100 DownMbps = 100
}; };
config.ClashUIItem ??= new(); config.ClashUIItem ??= new();
config.ClashUIItem.ConnectionsColumnItem ??= new();
config.SystemProxyItem ??= new(); config.SystemProxyItem ??= new();
config.WebDavItem ??= new(); config.WebDavItem ??= new();
config.CheckUpdateItem ??= new(); config.CheckUpdateItem ??= new();

View file

@ -208,6 +208,7 @@ public class ClashUIItem
public int ProxiesAutoDelayTestInterval { get; set; } = 10; public int ProxiesAutoDelayTestInterval { get; set; } = 10;
public bool ConnectionsAutoRefresh { get; set; } public bool ConnectionsAutoRefresh { get; set; }
public int ConnectionsRefreshInterval { get; set; } = 2; public int ConnectionsRefreshInterval { get; set; } = 2;
public List<ColumnItem> ConnectionsColumnItem { get; set; }
} }
[Serializable] [Serializable]

View file

@ -2,6 +2,7 @@
x:Class="v2rayN.Views.ClashConnectionsView" x:Class="v2rayN.Views.ClashConnectionsView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:base="clr-namespace:v2rayN.Base"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes" xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
@ -78,25 +79,30 @@
</ContextMenu> </ContextMenu>
</DataGrid.ContextMenu> </DataGrid.ContextMenu>
<DataGrid.Columns> <DataGrid.Columns>
<DataGridTextColumn <base:MyDGTextColumn
Width="300" Width="300"
Binding="{Binding Host}" Binding="{Binding Host}"
ExName="Host"
Header="{x:Static resx:ResUI.TbSortingHost}" /> Header="{x:Static resx:ResUI.TbSortingHost}" />
<DataGridTextColumn <base:MyDGTextColumn
Width="500" Width="500"
Binding="{Binding Chain}" Binding="{Binding Chain}"
ExName="Chain"
Header="{x:Static resx:ResUI.TbSortingChain}" /> Header="{x:Static resx:ResUI.TbSortingChain}" />
<DataGridTextColumn <base:MyDGTextColumn
Width="80" Width="80"
Binding="{Binding Network}" Binding="{Binding Network}"
ExName="Network"
Header="{x:Static resx:ResUI.TbSortingNetwork}" /> Header="{x:Static resx:ResUI.TbSortingNetwork}" />
<DataGridTextColumn <base:MyDGTextColumn
Width="160" Width="160"
Binding="{Binding Type}" Binding="{Binding Type}"
ExName="Type"
Header="{x:Static resx:ResUI.TbSortingType}" /> Header="{x:Static resx:ResUI.TbSortingType}" />
<DataGridTextColumn <base:MyDGTextColumn
Width="100" Width="100"
Binding="{Binding Elapsed}" Binding="{Binding Elapsed}"
ExName="Elapsed"
Header="{x:Static resx:ResUI.TbSortingTime}" /> Header="{x:Static resx:ResUI.TbSortingTime}" />
</DataGrid.Columns> </DataGrid.Columns>
</DataGrid> </DataGrid>

View file

@ -1,4 +1,5 @@
using System.Windows.Controls; using System.Windows.Controls;
using v2rayN.Base;
namespace v2rayN.Views; namespace v2rayN.Views;
@ -7,9 +8,14 @@ namespace v2rayN.Views;
/// </summary> /// </summary>
public partial class ClashConnectionsView public partial class ClashConnectionsView
{ {
private static Config _config;
private static readonly string _tag = "ClashConnectionsView";
public ClashConnectionsView() public ClashConnectionsView()
{ {
InitializeComponent(); InitializeComponent();
_config = AppManager.Instance.Config;
ViewModel = new ClashConnectionsViewModel(UpdateViewHandler); ViewModel = new ClashConnectionsViewModel(UpdateViewHandler);
btnAutofitColumnWidth.Click += BtnAutofitColumnWidth_Click; btnAutofitColumnWidth.Click += BtnAutofitColumnWidth_Click;
@ -24,7 +30,15 @@ public partial class ClashConnectionsView
this.Bind(ViewModel, vm => vm.HostFilter, v => v.txtHostFilter.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.HostFilter, v => v.txtHostFilter.Text).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.ConnectionCloseAllCmd, v => v.btnConnectionCloseAll).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.ConnectionCloseAllCmd, v => v.btnConnectionCloseAll).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.AutoRefresh, v => v.togAutoRefresh.IsChecked).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.AutoRefresh, v => v.togAutoRefresh.IsChecked).DisposeWith(disposables);
AppEvents.AppExitRequested
.AsObservable()
.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(_ => StorageUI())
.DisposeWith(disposables);
}); });
RestoreUI();
} }
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj) private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
@ -48,7 +62,7 @@ public partial class ClashConnectionsView
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog("ClashConnectionsView", ex); Logging.SaveLog(_tag, ex);
} }
} }
@ -56,4 +70,71 @@ public partial class ClashConnectionsView
{ {
ViewModel?.ClashConnectionClose(false); ViewModel?.ClashConnectionClose(false);
} }
#region UI
private void RestoreUI()
{
try
{
var lvColumnItem = _config.ClashUIItem?.ConnectionsColumnItem?.OrderBy(t => t.Index).ToList();
if (lvColumnItem == null)
{
return;
}
var displayIndex = 0;
foreach (var item in lvColumnItem)
{
foreach (var col in lstConnections.Columns.Cast<MyDGTextColumn>())
{
if (col.ExName == item.Name)
{
if (item.Width > 0)
{
col.Width = item.Width;
}
col.DisplayIndex = displayIndex++;
break;
}
}
}
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
}
}
private void StorageUI()
{
try
{
List<ColumnItem> lvColumnItem = new();
foreach (var col in lstConnections.Columns.Cast<MyDGTextColumn>())
{
var name = col.ExName;
if (string.IsNullOrWhiteSpace(name))
{
continue;
}
lvColumnItem.Add(new()
{
Name = name,
Width = (int)col.ActualWidth,
Index = col.DisplayIndex
});
}
_config.ClashUIItem.ConnectionsColumnItem = lvColumnItem;
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
}
}
#endregion UI
} }

View file

@ -10,6 +10,7 @@ namespace v2rayN.Views;
public partial class ProfilesView public partial class ProfilesView
{ {
private static Config _config; private static Config _config;
private static readonly string _tag = "ProfilesView";
public ProfilesView() public ProfilesView()
{ {
@ -339,7 +340,7 @@ public partial class ProfilesView
} }
catch (Exception ex) catch (Exception ex)
{ {
Logging.SaveLog("ProfilesView", ex); Logging.SaveLog(_tag, ex);
} }
} }
@ -357,46 +358,59 @@ public partial class ProfilesView
private void RestoreUI() private void RestoreUI()
{ {
var lvColumnItem = _config.UiItem.MainColumnItem.OrderBy(t => t.Index).ToList(); try
var displayIndex = 0;
foreach (var item in lvColumnItem)
{ {
foreach (MyDGTextColumn item2 in lstProfiles.Columns) var lvColumnItem = _config.UiItem.MainColumnItem.OrderBy(t => t.Index).ToList();
var displayIndex = 0;
foreach (var item in lvColumnItem)
{ {
if (item2.ExName == item.Name) foreach (var item2 in lstProfiles.Columns.Cast<MyDGTextColumn>())
{ {
if (item.Width < 0) if (item2.ExName == item.Name)
{ {
item2.Visibility = Visibility.Hidden; if (item.Width < 0)
} {
else item2.Visibility = Visibility.Hidden;
{ }
item2.Width = item.Width; else
item2.DisplayIndex = displayIndex++; {
} item2.Width = item.Width;
if (item.Name.ToLower().StartsWith("to")) item2.DisplayIndex = displayIndex++;
{ }
item2.Visibility = _config.GuiItem.EnableStatistics ? Visibility.Visible : Visibility.Hidden; if (item.Name.ToLower().StartsWith("to"))
{
item2.Visibility = _config.GuiItem.EnableStatistics ? Visibility.Visible : Visibility.Hidden;
}
} }
} }
} }
} }
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
}
} }
private void StorageUI() private void StorageUI()
{ {
List<ColumnItem> lvColumnItem = new(); try
foreach (var t in lstProfiles.Columns)
{ {
var item2 = (MyDGTextColumn)t; List<ColumnItem> lvColumnItem = new();
lvColumnItem.Add(new() foreach (var item2 in lstProfiles.Columns.Cast<MyDGTextColumn>())
{ {
Name = item2.ExName, lvColumnItem.Add(new()
Width = (int)(item2.Visibility == Visibility.Visible ? item2.ActualWidth : -1), {
Index = item2.DisplayIndex Name = item2.ExName,
}); Width = (int)(item2.Visibility == Visibility.Visible ? item2.ActualWidth : -1),
Index = item2.DisplayIndex
});
}
_config.UiItem.MainColumnItem = lvColumnItem;
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
} }
_config.UiItem.MainColumnItem = lvColumnItem;
} }
#endregion UI #endregion UI
@ -405,7 +419,7 @@ public partial class ProfilesView
private Point startPoint = new(); private Point startPoint = new();
private int startIndex = -1; private int startIndex = -1;
private string formatData = "ProfileItemModel"; private readonly string formatData = "ProfileItemModel";
/// <summary> /// <summary>
/// Helper to search up the VisualTree /// Helper to search up the VisualTree