From 230a2f6773d09a12ce4130404aa5571b20de63a2 Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Fri, 14 Aug 2026 12:08:43 +0800 Subject: [PATCH] Bug fix https://github.com/2dust/v2rayN/issues/9936 --- .../ViewModels/RoutingRuleDetailsViewModel.cs | 28 +++++++++++++++++-- .../ServiceLib/ViewModels/SubEditViewModel.cs | 15 +++++++--- .../Views/RoutingRuleDetailsWindow.axaml.cs | 12 ++++---- .../Views/SubEditWindow.axaml.cs | 4 +-- .../Views/RoutingRuleDetailsWindow.xaml.cs | 10 +++---- v2rayN/v2rayN/Views/SubEditWindow.xaml.cs | 4 +-- 6 files changed, 52 insertions(+), 21 deletions(-) diff --git a/v2rayN/ServiceLib/ViewModels/RoutingRuleDetailsViewModel.cs b/v2rayN/ServiceLib/ViewModels/RoutingRuleDetailsViewModel.cs index 593ffc60..ee6826bc 100644 --- a/v2rayN/ServiceLib/ViewModels/RoutingRuleDetailsViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/RoutingRuleDetailsViewModel.cs @@ -25,6 +25,21 @@ public partial class RoutingRuleDetailsViewModel : MyReactiveObject, ICloseable [Reactive] public partial bool AutoSort { get; set; } + [Reactive] + public partial string OutboundTag { get; set; } + + [Reactive] + public partial string Remarks { get; set; } + + [Reactive] + public partial string Port { get; set; } + + [Reactive] + public partial string Network { get; set; } + + [Reactive] + public partial bool Enabled { get; set; } + public ReactiveCommand SelectProfileCmd { get; } public ReactiveCommand SaveCmd { get; } @@ -57,6 +72,11 @@ public partial class RoutingRuleDetailsViewModel : MyReactiveObject, ICloseable IP = Utils.List2String(SelectedSource.Ip, true); Process = Utils.List2String(SelectedSource.Process, true); RuleType = SelectedSource.RuleType?.ToString(); + OutboundTag = SelectedSource.OutboundTag; + Remarks = SelectedSource.Remarks; + Port = SelectedSource.Port; + Network = SelectedSource.Network; + Enabled = SelectedSource.Enabled; } private async Task SaveRulesAsync() @@ -80,6 +100,11 @@ public partial class RoutingRuleDetailsViewModel : MyReactiveObject, ICloseable SelectedSource.Protocol = ProtocolItems?.ToList(); SelectedSource.InboundTag = InboundTagItems?.ToList(); SelectedSource.RuleType = RuleType.IsNullOrEmpty() ? null : Enum.Parse(RuleType); + SelectedSource.OutboundTag = OutboundTag; + SelectedSource.Remarks = Remarks; + SelectedSource.Port = Port; + SelectedSource.Network = Network; + SelectedSource.Enabled = Enabled; var hasRule = SelectedSource.Domain?.Count > 0 || SelectedSource.Ip?.Count > 0 @@ -110,8 +135,7 @@ public partial class RoutingRuleDetailsViewModel : MyReactiveObject, ICloseable var profileItem = await profileSelectViewModel.GetProfileItem(); if (profileItem != null) { - SelectedSource.OutboundTag = profileItem.Remarks; - SelectedSource = JsonUtils.DeepCopy(SelectedSource); + OutboundTag = profileItem.Remarks; } } } diff --git a/v2rayN/ServiceLib/ViewModels/SubEditViewModel.cs b/v2rayN/ServiceLib/ViewModels/SubEditViewModel.cs index 5db7f195..da97e748 100644 --- a/v2rayN/ServiceLib/ViewModels/SubEditViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/SubEditViewModel.cs @@ -9,6 +9,11 @@ public partial class SubEditViewModel : MyReactiveObject, ICloseable [Reactive] public partial string CustomCoreType { get; set; } + [Reactive] + public partial string PrevProfile { get; set; } + + [Reactive] + public partial string NextProfile { get; set; } public ReactiveCommand SelectPrevProfileCmd { get; } public ReactiveCommand SelectNextProfileCmd { get; } @@ -23,8 +28,7 @@ public partial class SubEditViewModel : MyReactiveObject, ICloseable var profileItem = await SelectProfileAsync(); if (profileItem != null) { - SelectedSource?.PrevProfile = profileItem.Remarks; - SelectedSource = JsonUtils.DeepCopy(SelectedSource); + PrevProfile = profileItem.Remarks; } }); SelectNextProfileCmd = ReactiveCommand.CreateFromTask(async () => @@ -32,8 +36,7 @@ public partial class SubEditViewModel : MyReactiveObject, ICloseable var profileItem = await SelectProfileAsync(); if (profileItem != null) { - SelectedSource?.NextProfile = profileItem.Remarks; - SelectedSource = JsonUtils.DeepCopy(SelectedSource); + NextProfile = profileItem.Remarks; } }); SaveCmd = ReactiveCommand.CreateFromTask(async () => @@ -43,6 +46,8 @@ public partial class SubEditViewModel : MyReactiveObject, ICloseable SelectedSource = subItem.Id.IsNullOrEmpty() ? subItem : JsonUtils.DeepCopy(subItem); CustomCoreType = SelectedSource.CustomCoreType?.ToString() ?? string.Empty; + PrevProfile = SelectedSource.PrevProfile; + NextProfile = SelectedSource.NextProfile; } private async Task SaveSubAsync() @@ -72,6 +77,8 @@ public partial class SubEditViewModel : MyReactiveObject, ICloseable } SelectedSource.CustomCoreType = Enum.TryParse(CustomCoreType, out var coreType) ? coreType : null; + SelectedSource.PrevProfile = PrevProfile; + SelectedSource.NextProfile = NextProfile; if (await ConfigHandler.AddSubItem(_config, SelectedSource) == 0) { diff --git a/v2rayN/v2rayN.Desktop/Views/RoutingRuleDetailsWindow.axaml.cs b/v2rayN/v2rayN.Desktop/Views/RoutingRuleDetailsWindow.axaml.cs index bbd3eae2..f6df8a10 100644 --- a/v2rayN/v2rayN.Desktop/Views/RoutingRuleDetailsWindow.axaml.cs +++ b/v2rayN/v2rayN.Desktop/Views/RoutingRuleDetailsWindow.axaml.cs @@ -26,12 +26,12 @@ public partial class RoutingRuleDetailsWindow : WindowBase vm.SelectedSource.OutboundTag, v => v.cmbOutboundTag.Text).DisposeWith(disposables); - this.Bind(ViewModel, vm => vm.SelectedSource.Remarks, v => v.txtRemarks.Text).DisposeWith(disposables); - this.Bind(ViewModel, vm => vm.SelectedSource.OutboundTag, v => v.cmbOutboundTag.Text).DisposeWith(disposables); - this.Bind(ViewModel, vm => vm.SelectedSource.Port, v => v.txtPort.Text).DisposeWith(disposables); - this.Bind(ViewModel, vm => vm.SelectedSource.Network, v => v.cmbNetwork.SelectedValue).DisposeWith(disposables); - this.Bind(ViewModel, vm => vm.SelectedSource.Enabled, v => v.togEnabled.IsChecked).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.OutboundTag, v => v.cmbOutboundTag.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.Remarks, v => v.txtRemarks.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.OutboundTag, v => v.cmbOutboundTag.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.Port, v => v.txtPort.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.Network, v => v.cmbNetwork.SelectedValue).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.Enabled, v => v.togEnabled.IsChecked).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.Domain, v => v.txtDomain.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.IP, v => v.txtIP.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.Process, v => v.txtProcess.Text).DisposeWith(disposables); diff --git a/v2rayN/v2rayN.Desktop/Views/SubEditWindow.axaml.cs b/v2rayN/v2rayN.Desktop/Views/SubEditWindow.axaml.cs index 78a730a8..b7267077 100644 --- a/v2rayN/v2rayN.Desktop/Views/SubEditWindow.axaml.cs +++ b/v2rayN/v2rayN.Desktop/Views/SubEditWindow.axaml.cs @@ -25,8 +25,8 @@ public partial class SubEditWindow : WindowBase this.Bind(ViewModel, vm => vm.SelectedSource.Sort, v => v.txtSort.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SelectedSource.Filter, v => v.txtFilter.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SelectedSource.ConvertTarget, v => v.cmbConvertTarget.SelectedValue).DisposeWith(disposables); - this.Bind(ViewModel, vm => vm.SelectedSource.PrevProfile, v => v.txtPrevProfile.Text).DisposeWith(disposables); - this.Bind(ViewModel, vm => vm.SelectedSource.NextProfile, v => v.txtNextProfile.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.PrevProfile, v => v.txtPrevProfile.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.NextProfile, v => v.txtNextProfile.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SelectedSource.PreSocksPort, v => v.txtPreSocksPort.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SelectedSource.Memo, v => v.txtMemo.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.CustomCoreType, v => v.cmbCustomCoreType.SelectedValue).DisposeWith(disposables); diff --git a/v2rayN/v2rayN/Views/RoutingRuleDetailsWindow.xaml.cs b/v2rayN/v2rayN/Views/RoutingRuleDetailsWindow.xaml.cs index 0df2007c..6118045b 100644 --- a/v2rayN/v2rayN/Views/RoutingRuleDetailsWindow.xaml.cs +++ b/v2rayN/v2rayN/Views/RoutingRuleDetailsWindow.xaml.cs @@ -23,11 +23,11 @@ public partial class RoutingRuleDetailsWindow .Subscribe(InitializeData) .DisposeWith(disposables); - this.Bind(ViewModel, vm => vm.SelectedSource.Remarks, v => v.txtRemarks.Text).DisposeWith(disposables); - this.Bind(ViewModel, vm => vm.SelectedSource.OutboundTag, v => v.cmbOutboundTag.Text).DisposeWith(disposables); - this.Bind(ViewModel, vm => vm.SelectedSource.Port, v => v.txtPort.Text).DisposeWith(disposables); - this.Bind(ViewModel, vm => vm.SelectedSource.Network, v => v.cmbNetwork.Text).DisposeWith(disposables); - this.Bind(ViewModel, vm => vm.SelectedSource.Enabled, v => v.togEnabled.IsChecked).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.Remarks, v => v.txtRemarks.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.OutboundTag, v => v.cmbOutboundTag.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.Port, v => v.txtPort.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.Network, v => v.cmbNetwork.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.Enabled, v => v.togEnabled.IsChecked).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.Domain, v => v.txtDomain.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.IP, v => v.txtIP.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.Process, v => v.txtProcess.Text).DisposeWith(disposables); diff --git a/v2rayN/v2rayN/Views/SubEditWindow.xaml.cs b/v2rayN/v2rayN/Views/SubEditWindow.xaml.cs index 54c2c8fc..2d7ec592 100644 --- a/v2rayN/v2rayN/Views/SubEditWindow.xaml.cs +++ b/v2rayN/v2rayN/Views/SubEditWindow.xaml.cs @@ -22,8 +22,8 @@ public partial class SubEditWindow this.Bind(ViewModel, vm => vm.SelectedSource.Sort, v => v.txtSort.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SelectedSource.Filter, v => v.txtFilter.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SelectedSource.ConvertTarget, v => v.cmbConvertTarget.Text).DisposeWith(disposables); - this.Bind(ViewModel, vm => vm.SelectedSource.PrevProfile, v => v.txtPrevProfile.Text).DisposeWith(disposables); - this.Bind(ViewModel, vm => vm.SelectedSource.NextProfile, v => v.txtNextProfile.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.PrevProfile, v => v.txtPrevProfile.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.NextProfile, v => v.txtNextProfile.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SelectedSource.PreSocksPort, v => v.txtPreSocksPort.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SelectedSource.Memo, v => v.txtMemo.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.CustomCoreType, v => v.cmbCustomCoreType.Text).DisposeWith(disposables);