mirror of https://github.com/2dust/v2rayN
Add functionality multi-server set as active
Implemented using sing-box Selector, which can be switched using the clash apipull/5377/head
parent
7c33c1c322
commit
c79e2e3ad4
|
@ -30,6 +30,7 @@ namespace v2rayN
|
||||||
public const string CoreConfigFileName = "config.json";
|
public const string CoreConfigFileName = "config.json";
|
||||||
public const string CorePreConfigFileName = "configPre.json";
|
public const string CorePreConfigFileName = "configPre.json";
|
||||||
public const string CoreSpeedtestConfigFileName = "configSpeedtest.json";
|
public const string CoreSpeedtestConfigFileName = "configSpeedtest.json";
|
||||||
|
public const string CoreMultipleLoadConfigFileName = "configMultipleLoad.json";
|
||||||
public const string ClashMixinConfigFileName = "Mixin.yaml";
|
public const string ClashMixinConfigFileName = "Mixin.yaml";
|
||||||
public const string V2raySampleClient = "v2rayN.Sample.SampleClientConfig";
|
public const string V2raySampleClient = "v2rayN.Sample.SampleClientConfig";
|
||||||
public const string SingboxSampleClient = "v2rayN.Sample.SingboxSampleClientConfig";
|
public const string SingboxSampleClient = "v2rayN.Sample.SingboxSampleClientConfig";
|
||||||
|
|
|
@ -3,6 +3,7 @@ using System.IO;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Web;
|
using System.Web;
|
||||||
using v2rayN.Enums;
|
using v2rayN.Enums;
|
||||||
|
using v2rayN.Handler.CoreConfig;
|
||||||
using v2rayN.Handler.Fmt;
|
using v2rayN.Handler.Fmt;
|
||||||
using v2rayN.Models;
|
using v2rayN.Models;
|
||||||
|
|
||||||
|
@ -1065,6 +1066,33 @@ namespace v2rayN.Handler
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static int AddCustomServer4Multiple(Config config, List<ProfileItem> selecteds, out string indexId)
|
||||||
|
{
|
||||||
|
indexId = Utils.GetMD5(Global.CoreMultipleLoadConfigFileName);
|
||||||
|
string configPath = Utils.GetConfigPath(Global.CoreMultipleLoadConfigFileName);
|
||||||
|
if (CoreConfigHandler.GenerateClientMultipleLoadConfig(config, configPath, selecteds, out string msg) != 0)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
var fileName = configPath;
|
||||||
|
if (!File.Exists(fileName))
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
var profileItem = LazyConfig.Instance.GetProfileItem(config.indexId) ?? new();
|
||||||
|
profileItem.indexId = indexId;
|
||||||
|
profileItem.remarks = "Multi-server Config";
|
||||||
|
profileItem.address = Global.CoreMultipleLoadConfigFileName;
|
||||||
|
profileItem.configType = EConfigType.Custom;
|
||||||
|
profileItem.coreType = ECoreType.sing_box;
|
||||||
|
|
||||||
|
AddServerCommon(config, profileItem, true);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
#endregion Server
|
#endregion Server
|
||||||
|
|
||||||
#region Batch add servers
|
#region Batch add servers
|
||||||
|
|
|
@ -144,5 +144,16 @@ namespace v2rayN.Handler.CoreConfig
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static int GenerateClientMultipleLoadConfig(Config config, string fileName, List<ProfileItem> selecteds, out string msg)
|
||||||
|
{
|
||||||
|
if (new CoreConfigSingbox(config).GenerateClientMultipleLoadConfig(selecteds, out SingboxConfig? singboxConfig, out msg) != 0)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
JsonUtils.ToFile(singboxConfig, fileName, false);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -804,7 +804,7 @@ namespace v2rayN.Handler.CoreConfig
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int GenDns(ProfileItem node, SingboxConfig singboxConfig)
|
private int GenDns(ProfileItem? node, SingboxConfig singboxConfig)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -853,6 +853,7 @@ namespace v2rayN.Handler.CoreConfig
|
||||||
var lstDomain = singboxConfig.outbounds
|
var lstDomain = singboxConfig.outbounds
|
||||||
.Where(t => !Utils.IsNullOrEmpty(t.server) && Utils.IsDomain(t.server))
|
.Where(t => !Utils.IsNullOrEmpty(t.server) && Utils.IsDomain(t.server))
|
||||||
.Select(t => t.server)
|
.Select(t => t.server)
|
||||||
|
.Distinct()
|
||||||
.ToList();
|
.ToList();
|
||||||
if (lstDomain != null && lstDomain.Count > 0)
|
if (lstDomain != null && lstDomain.Count > 0)
|
||||||
{
|
{
|
||||||
|
@ -1169,5 +1170,123 @@ namespace v2rayN.Handler.CoreConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion Gen speedtest config
|
#endregion Gen speedtest config
|
||||||
|
|
||||||
|
#region Gen Multiple Load config
|
||||||
|
|
||||||
|
public int GenerateClientMultipleLoadConfig(List<ProfileItem> selecteds, out SingboxConfig? singboxConfig, out string msg)
|
||||||
|
{
|
||||||
|
singboxConfig = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (_config == null)
|
||||||
|
{
|
||||||
|
msg = ResUI.CheckServerSettings;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
msg = ResUI.InitialConfiguration;
|
||||||
|
|
||||||
|
string result = Utils.GetEmbedText(Global.SingboxSampleClient);
|
||||||
|
string txtOutbound = Utils.GetEmbedText(Global.SingboxSampleOutbound);
|
||||||
|
if (Utils.IsNullOrEmpty(result) || txtOutbound.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
msg = ResUI.FailedGetDefaultConfiguration;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
singboxConfig = JsonUtils.Deserialize<SingboxConfig>(result);
|
||||||
|
if (singboxConfig == null)
|
||||||
|
{
|
||||||
|
msg = ResUI.FailedGenDefaultConfiguration;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
GenLog(singboxConfig);
|
||||||
|
GenInbounds(singboxConfig);
|
||||||
|
GenRouting(singboxConfig);
|
||||||
|
GenExperimental(singboxConfig);
|
||||||
|
singboxConfig.outbounds.RemoveAt(0);
|
||||||
|
|
||||||
|
var tagProxy = new List<string>();
|
||||||
|
foreach (var it in selecteds)
|
||||||
|
{
|
||||||
|
if (it.configType == EConfigType.Custom)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (it.port <= 0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
var item = LazyConfig.Instance.GetProfileItem(it.indexId);
|
||||||
|
if (item is null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (it.configType is EConfigType.VMess or EConfigType.VLESS)
|
||||||
|
{
|
||||||
|
if (Utils.IsNullOrEmpty(item.id) || !Utils.IsGuidByParse(item.id))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (item.configType == EConfigType.Shadowsocks
|
||||||
|
&& !Global.SsSecuritiesInSingbox.Contains(item.security))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (item.configType == EConfigType.VLESS && !Global.Flows.Contains(item.flow))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
//outbound
|
||||||
|
var outbound = JsonUtils.Deserialize<Outbound4Sbox>(txtOutbound);
|
||||||
|
GenOutbound(item, outbound);
|
||||||
|
outbound.tag = $"{Global.ProxyTag}-{tagProxy.Count + 1}";
|
||||||
|
singboxConfig.outbounds.Add(outbound);
|
||||||
|
tagProxy.Add(outbound.tag);
|
||||||
|
}
|
||||||
|
if (tagProxy.Count <= 0)
|
||||||
|
{
|
||||||
|
msg = ResUI.FailedGenDefaultConfiguration;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
GenDns(null, singboxConfig);
|
||||||
|
ConvertGeo2Ruleset(singboxConfig);
|
||||||
|
|
||||||
|
//add urltest outbound
|
||||||
|
var outUrltest = new Outbound4Sbox
|
||||||
|
{
|
||||||
|
type = "urltest",
|
||||||
|
tag = $"{Global.ProxyTag}-auto",
|
||||||
|
outbounds = tagProxy,
|
||||||
|
interrupt_exist_connections = false,
|
||||||
|
};
|
||||||
|
singboxConfig.outbounds.Add(outUrltest);
|
||||||
|
|
||||||
|
//add selector outbound
|
||||||
|
var outSelector = new Outbound4Sbox
|
||||||
|
{
|
||||||
|
type = "selector",
|
||||||
|
tag = Global.ProxyTag,
|
||||||
|
outbounds = JsonUtils.DeepCopy(tagProxy),
|
||||||
|
interrupt_exist_connections = false,
|
||||||
|
};
|
||||||
|
outSelector.outbounds.Insert(0, outUrltest.tag);
|
||||||
|
singboxConfig.outbounds.Add(outSelector);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logging.SaveLog(ex.Message, ex);
|
||||||
|
msg = ResUI.FailedGenDefaultConfiguration;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion Gen Multiple config
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -27,9 +27,8 @@ namespace v2rayN.Handler
|
||||||
Environment.SetEnvironmentVariable("xray.location.asset", Utils.GetBinPath(""), EnvironmentVariableTarget.Process);
|
Environment.SetEnvironmentVariable("xray.location.asset", Utils.GetBinPath(""), EnvironmentVariableTarget.Process);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void LoadCore()
|
public void LoadCore(ProfileItem? node) {
|
||||||
{
|
|
||||||
var node = ConfigHandler.GetDefaultServer(_config);
|
|
||||||
if (node == null)
|
if (node == null)
|
||||||
{
|
{
|
||||||
ShowMsg(false, ResUI.CheckServerSettings);
|
ShowMsg(false, ResUI.CheckServerSettings);
|
||||||
|
|
|
@ -130,6 +130,8 @@
|
||||||
public Multiplex4Sbox? multiplex { get; set; }
|
public Multiplex4Sbox? multiplex { get; set; }
|
||||||
public Transport4Sbox? transport { get; set; }
|
public Transport4Sbox? transport { get; set; }
|
||||||
public HyObfs4Sbox? obfs { get; set; }
|
public HyObfs4Sbox? obfs { get; set; }
|
||||||
|
public List<string>? outbounds { get; set; }
|
||||||
|
public bool? interrupt_exist_connections { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class Tls4Sbox
|
public class Tls4Sbox
|
||||||
|
|
|
@ -1078,7 +1078,7 @@ namespace v2rayN.Resx {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Refresh Proxies (F5) 的本地化字符串。
|
/// 查找类似 Refresh Proxies 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string menuProxiesReload {
|
public static string menuProxiesReload {
|
||||||
get {
|
get {
|
||||||
|
@ -1302,6 +1302,15 @@ namespace v2rayN.Resx {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 Multi-server set to active 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
public static string menuSetDefaultMultipleServer {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("menuSetDefaultMultipleServer", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Set as active server (Enter) 的本地化字符串。
|
/// 查找类似 Set as active server (Enter) 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -1066,9 +1066,6 @@
|
||||||
<data name="menuProxiesDelaytestPart" xml:space="preserve">
|
<data name="menuProxiesDelaytestPart" xml:space="preserve">
|
||||||
<value>Part Node Latency Test</value>
|
<value>Part Node Latency Test</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuProxiesReload" xml:space="preserve">
|
|
||||||
<value>Refresh Proxies (F5)</value>
|
|
||||||
</data>
|
|
||||||
<data name="menuProxiesSelectActivity" xml:space="preserve">
|
<data name="menuProxiesSelectActivity" xml:space="preserve">
|
||||||
<value>Select active node (Enter)</value>
|
<value>Select active node (Enter)</value>
|
||||||
</data>
|
</data>
|
||||||
|
|
|
@ -1292,7 +1292,7 @@
|
||||||
<value>Part Node Latency Test</value>
|
<value>Part Node Latency Test</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuProxiesReload" xml:space="preserve">
|
<data name="menuProxiesReload" xml:space="preserve">
|
||||||
<value>Refresh Proxies (F5)</value>
|
<value>Refresh Proxies</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuProxiesSelectActivity" xml:space="preserve">
|
<data name="menuProxiesSelectActivity" xml:space="preserve">
|
||||||
<value>Select active node (Enter)</value>
|
<value>Select active node (Enter)</value>
|
||||||
|
@ -1300,4 +1300,7 @@
|
||||||
<data name="TbSettingsDomainStrategy4Out" xml:space="preserve">
|
<data name="TbSettingsDomainStrategy4Out" xml:space="preserve">
|
||||||
<value>Default domain strategy for outbound</value>
|
<value>Default domain strategy for outbound</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="menuSetDefaultMultipleServer" xml:space="preserve">
|
||||||
|
<value>Multi-server set to active</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
|
@ -1289,7 +1289,7 @@
|
||||||
<value>当前部分节点延迟测试</value>
|
<value>当前部分节点延迟测试</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuProxiesReload" xml:space="preserve">
|
<data name="menuProxiesReload" xml:space="preserve">
|
||||||
<value>刷新 (F5)</value>
|
<value>刷新</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuProxiesSelectActivity" xml:space="preserve">
|
<data name="menuProxiesSelectActivity" xml:space="preserve">
|
||||||
<value>设为活动节点 (Enter)</value>
|
<value>设为活动节点 (Enter)</value>
|
||||||
|
@ -1297,4 +1297,7 @@
|
||||||
<data name="TbSettingsDomainStrategy4Out" xml:space="preserve">
|
<data name="TbSettingsDomainStrategy4Out" xml:space="preserve">
|
||||||
<value>Outbound默认解析策略</value>
|
<value>Outbound默认解析策略</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="menuSetDefaultMultipleServer" xml:space="preserve">
|
||||||
|
<value>多服务器设为活动(多选)</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
|
@ -106,6 +106,7 @@ namespace v2rayN.ViewModels
|
||||||
public ReactiveCommand<Unit, Unit> CopyServerCmd { get; }
|
public ReactiveCommand<Unit, Unit> CopyServerCmd { get; }
|
||||||
public ReactiveCommand<Unit, Unit> SetDefaultServerCmd { get; }
|
public ReactiveCommand<Unit, Unit> SetDefaultServerCmd { get; }
|
||||||
public ReactiveCommand<Unit, Unit> ShareServerCmd { get; }
|
public ReactiveCommand<Unit, Unit> ShareServerCmd { get; }
|
||||||
|
public ReactiveCommand<Unit, Unit> SetDefaultMultipleServerCmd { get; }
|
||||||
|
|
||||||
//servers move
|
//servers move
|
||||||
public ReactiveCommand<Unit, Unit> MoveTopCmd { get; }
|
public ReactiveCommand<Unit, Unit> MoveTopCmd { get; }
|
||||||
|
@ -398,6 +399,10 @@ namespace v2rayN.ViewModels
|
||||||
{
|
{
|
||||||
ShareServer();
|
ShareServer();
|
||||||
}, canEditRemove);
|
}, canEditRemove);
|
||||||
|
SetDefaultMultipleServerCmd = ReactiveCommand.Create(() =>
|
||||||
|
{
|
||||||
|
SetDefaultMultipleServer();
|
||||||
|
}, canEditRemove);
|
||||||
//servers move
|
//servers move
|
||||||
MoveTopCmd = ReactiveCommand.Create(() =>
|
MoveTopCmd = ReactiveCommand.Create(() =>
|
||||||
{
|
{
|
||||||
|
@ -1166,6 +1171,28 @@ namespace v2rayN.ViewModels
|
||||||
await DialogHost.Show(dialog, "RootDialog");
|
await DialogHost.Show(dialog, "RootDialog");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void SetDefaultMultipleServer()
|
||||||
|
{
|
||||||
|
if (GetProfileItems(out List<ProfileItem> lstSelecteds, true) < 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ConfigHandler.AddCustomServer4Multiple(_config, lstSelecteds, out string indexId) != 0)
|
||||||
|
{
|
||||||
|
_noticeHandler?.Enqueue(ResUI.OperationFailed);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (indexId == _config.indexId)
|
||||||
|
{
|
||||||
|
Reload();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SetDefaultServer(indexId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void SortServer(string colName)
|
public void SortServer(string colName)
|
||||||
{
|
{
|
||||||
if (Utils.IsNullOrEmpty(colName))
|
if (Utils.IsNullOrEmpty(colName))
|
||||||
|
@ -1519,7 +1546,7 @@ namespace v2rayN.ViewModels
|
||||||
Application.Current?.Dispatcher.Invoke((Action)(() =>
|
Application.Current?.Dispatcher.Invoke((Action)(() =>
|
||||||
{
|
{
|
||||||
BlReloadEnabled = true;
|
BlReloadEnabled = true;
|
||||||
ShowCalshUI = (_config.runningCoreType is ECoreType.clash or ECoreType.clash_meta or ECoreType.mihomo);
|
ShowCalshUI = (_config.runningCoreType is ECoreType.sing_box or ECoreType.clash or ECoreType.clash_meta or ECoreType.mihomo);
|
||||||
if (ShowCalshUI)
|
if (ShowCalshUI)
|
||||||
{
|
{
|
||||||
Locator.Current.GetService<ClashProxiesViewModel>()?.ProxiesReload();
|
Locator.Current.GetService<ClashProxiesViewModel>()?.ProxiesReload();
|
||||||
|
@ -1532,7 +1559,8 @@ namespace v2rayN.ViewModels
|
||||||
{
|
{
|
||||||
await Task.Run(() =>
|
await Task.Run(() =>
|
||||||
{
|
{
|
||||||
_coreHandler.LoadCore();
|
var node = ConfigHandler.GetDefaultServer(_config);
|
||||||
|
_coreHandler.LoadCore(node);
|
||||||
|
|
||||||
//ConfigHandler.SaveConfig(_config, false);
|
//ConfigHandler.SaveConfig(_config, false);
|
||||||
|
|
||||||
|
|
|
@ -581,6 +581,10 @@
|
||||||
x:Name="menuShareServer"
|
x:Name="menuShareServer"
|
||||||
Height="{StaticResource MenuItemHeight}"
|
Height="{StaticResource MenuItemHeight}"
|
||||||
Header="{x:Static resx:ResUI.menuShareServer}" />
|
Header="{x:Static resx:ResUI.menuShareServer}" />
|
||||||
|
<MenuItem
|
||||||
|
x:Name="menuSetDefaultMultipleServer"
|
||||||
|
Height="{StaticResource MenuItemHeight}"
|
||||||
|
Header="{x:Static resx:ResUI.menuSetDefaultMultipleServer}" />
|
||||||
<Separator />
|
<Separator />
|
||||||
<MenuItem
|
<MenuItem
|
||||||
x:Name="menuMixedTestServer"
|
x:Name="menuMixedTestServer"
|
||||||
|
|
|
@ -104,7 +104,8 @@ namespace v2rayN.Views
|
||||||
this.BindCommand(ViewModel, vm => vm.CopyServerCmd, v => v.menuCopyServer).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.CopyServerCmd, v => v.menuCopyServer).DisposeWith(disposables);
|
||||||
this.BindCommand(ViewModel, vm => vm.SetDefaultServerCmd, v => v.menuSetDefaultServer).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.SetDefaultServerCmd, v => v.menuSetDefaultServer).DisposeWith(disposables);
|
||||||
this.BindCommand(ViewModel, vm => vm.ShareServerCmd, v => v.menuShareServer).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.ShareServerCmd, v => v.menuShareServer).DisposeWith(disposables);
|
||||||
|
this.BindCommand(ViewModel, vm => vm.SetDefaultMultipleServerCmd, v => v.menuSetDefaultMultipleServer).DisposeWith(disposables);
|
||||||
|
|
||||||
//servers move
|
//servers move
|
||||||
this.OneWayBind(ViewModel, vm => vm.SubItems, v => v.cmbMoveToGroup.ItemsSource).DisposeWith(disposables);
|
this.OneWayBind(ViewModel, vm => vm.SubItems, v => v.cmbMoveToGroup.ItemsSource).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedMoveToGroup, v => v.cmbMoveToGroup.SelectedItem).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedMoveToGroup, v => v.cmbMoveToGroup.SelectedItem).DisposeWith(disposables);
|
||||||
|
|
Loading…
Reference in New Issue