Refactor new StatusBarViewModel

pull/5829/head
2dust 2024-10-12 15:45:21 +08:00
parent d7bde77977
commit 7618f9f7d4
14 changed files with 1095 additions and 869 deletions

View File

@ -1,41 +1,14 @@
using DynamicData.Binding;
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
using Splat;
using System.Diagnostics;
using System.Reactive;
using System.Reactive.Linq;
using System.Text;
namespace ServiceLib.ViewModels
{
public class MainWindowViewModel : MyReactiveObject
{
#region private prop
private bool _isAdministrator { get; set; }
#endregion private prop
#region ObservableCollection
private IObservableCollection<RoutingItem> _routingItems = new ObservableCollectionExtended<RoutingItem>();
public IObservableCollection<RoutingItem> RoutingItems => _routingItems;
private IObservableCollection<ComboItem> _servers = new ObservableCollectionExtended<ComboItem>();
public IObservableCollection<ComboItem> Servers => _servers;
[Reactive]
public RoutingItem SelectedRouting { get; set; }
[Reactive]
public ComboItem SelectedServer { get; set; }
[Reactive]
public bool BlServers { get; set; }
#endregion ObservableCollection
#region Menu
//servers
@ -76,120 +49,27 @@ namespace ServiceLib.ViewModels
[Reactive]
public bool BlReloadEnabled { get; set; }
public ReactiveCommand<Unit, Unit> NotifyLeftClickCmd { get; }
#endregion Menu
#region System Proxy
[Reactive]
public bool BlSystemProxyClear { get; set; }
[Reactive]
public bool BlSystemProxySet { get; set; }
[Reactive]
public bool BlSystemProxyNothing { get; set; }
[Reactive]
public bool BlSystemProxyPac { get; set; }
public ReactiveCommand<Unit, Unit> SystemProxyClearCmd { get; }
public ReactiveCommand<Unit, Unit> SystemProxySetCmd { get; }
public ReactiveCommand<Unit, Unit> SystemProxyNothingCmd { get; }
public ReactiveCommand<Unit, Unit> SystemProxyPacCmd { get; }
[Reactive]
public bool BlRouting { get; set; }
[Reactive]
public int SystemProxySelected { get; set; }
#endregion System Proxy
#region UI
[Reactive]
public string InboundDisplay { get; set; }
[Reactive]
public string InboundLanDisplay { get; set; }
[Reactive]
public string RunningServerDisplay { get; set; }
[Reactive]
public string RunningServerToolTipText { get; set; }
[Reactive]
public string RunningInfoDisplay { get; set; }
[Reactive]
public string SpeedProxyDisplay { get; set; }
[Reactive]
public string SpeedDirectDisplay { get; set; }
[Reactive]
public bool EnableTun { get; set; }
[Reactive]
public bool ShowClashUI { get; set; }
[Reactive]
public int TabMainSelectedIndex { get; set; }
#endregion UI
#endregion Menu
#region Init
public MainWindowViewModel(bool isAdministrator, Func<EViewAction, object?, Task<bool>>? updateView)
public MainWindowViewModel(Func<EViewAction, object?, Task<bool>>? updateView)
{
_config = AppHandler.Instance.Config;
_updateView = updateView;
_isAdministrator = isAdministrator;
MessageBus.Current.Listen<string>(EMsgCommand.RefreshProfiles.ToString()).Subscribe(async x => await _updateView?.Invoke(EViewAction.DispatcherRefreshServersBiz, null));
SelectedRouting = new();
SelectedServer = new();
Init();
_config.uiItem.showInTaskbar = true;
if (_config.tunModeItem.enableTun && _isAdministrator)
{
EnableTun = true;
}
else
{
_config.tunModeItem.enableTun = EnableTun = false;
}
#region WhenAnyValue && ReactiveCommand
this.WhenAnyValue(
x => x.SelectedRouting,
y => y != null && !y.remarks.IsNullOrEmpty())
.Subscribe(c => RoutingSelectedChangedAsync(c));
this.WhenAnyValue(
x => x.SelectedServer,
y => y != null && !y.Text.IsNullOrEmpty())
.Subscribe(c => ServerSelectedChanged(c));
SystemProxySelected = (int)_config.systemProxyItem.sysProxyType;
this.WhenAnyValue(
x => x.SystemProxySelected,
y => y >= 0)
.Subscribe(c => DoSystemProxySelected(c));
this.WhenAnyValue(
x => x.EnableTun,
y => y == true)
.Subscribe(c => DoEnableTun(c));
//servers
AddVmessServerCmd = ReactiveCommand.CreateFromTask(async () =>
{
@ -237,7 +117,7 @@ namespace ServiceLib.ViewModels
});
AddServerViaScanCmd = ReactiveCommand.CreateFromTask(async () =>
{
await _updateView?.Invoke(EViewAction.ScanScreenTask, null);
await AddServerViaScanTaskAsync();
});
//Subscription
@ -301,29 +181,6 @@ namespace ServiceLib.ViewModels
await Reload();
});
NotifyLeftClickCmd = ReactiveCommand.CreateFromTask(async () =>
{
await _updateView?.Invoke(EViewAction.ShowHideWindow, null);
});
//System proxy
SystemProxyClearCmd = ReactiveCommand.CreateFromTask(async () =>
{
await SetListenerType(ESysProxyType.ForcedClear);
});
SystemProxySetCmd = ReactiveCommand.CreateFromTask(async () =>
{
await SetListenerType(ESysProxyType.ForcedChange);
});
SystemProxyNothingCmd = ReactiveCommand.CreateFromTask(async () =>
{
await SetListenerType(ESysProxyType.Unchanged);
});
SystemProxyPacCmd = ReactiveCommand.CreateFromTask(async () =>
{
await SetListenerType(ESysProxyType.Pac);
});
#endregion WhenAnyValue && ReactiveCommand
AutoHideStartup();
@ -334,19 +191,14 @@ namespace ServiceLib.ViewModels
ConfigHandler.InitBuiltinRouting(_config);
ConfigHandler.InitBuiltinDNS(_config);
CoreHandler.Instance.Init(_config, UpdateHandler);
TaskHandler.Instance.RegUpdateTask(_config, UpdateTaskHandler);
if (_config.guiItem.enableStatistics)
{
StatisticsHandler.Instance.Init(_config, UpdateStatisticsHandler);
}
TaskHandler.Instance.RegUpdateTask(_config, UpdateTaskHandler);
RefreshRoutingsMenu();
InboundDisplayStaus();
//RefreshServers();
Reload();
ChangeSystemProxyStatusAsync(_config.systemProxyItem.sysProxyType, true);
}
#endregion Init
@ -393,9 +245,7 @@ namespace ServiceLib.ViewModels
{
try
{
SpeedProxyDisplay = string.Format(ResUI.SpeedDisplayText, Global.ProxyTag, Utils.HumanFy(update.proxyUp), Utils.HumanFy(update.proxyDown));
SpeedDirectDisplay = string.Format(ResUI.SpeedDisplayText, Global.DirectTag, Utils.HumanFy(update.directUp), Utils.HumanFy(update.directDown));
Locator.Current.GetService<StatusBarViewModel>()?.UpdateStatistics(update);
if ((update.proxyUp + update.proxyDown) > 0 && DateTime.Now.Second % 3 == 0)
{
Locator.Current.GetService<ProfilesViewModel>()?.UpdateStatistics(update);
@ -448,6 +298,11 @@ namespace ServiceLib.ViewModels
}
}
public void ShowHideWindow(bool? blShow)
{
_updateView?.Invoke(EViewAction.ShowHideWindow, blShow);
}
#endregion Actions
#region Servers && Groups
@ -457,50 +312,6 @@ namespace ServiceLib.ViewModels
MessageBus.Current.SendMessage("", EMsgCommand.RefreshProfiles.ToString());
}
public void RefreshServersBiz()
{
RefreshServersMenu();
//display running server
var running = ConfigHandler.GetDefaultServer(_config);
if (running != null)
{
RunningServerDisplay =
RunningServerToolTipText = running.GetSummary();
}
else
{
RunningServerDisplay =
RunningServerToolTipText = ResUI.CheckServerSettings;
}
}
private void RefreshServersMenu()
{
var lstModel = AppHandler.Instance.ProfileItems(_config.subIndexId, "");
_servers.Clear();
if (lstModel.Count > _config.guiItem.trayMenuServersLimit)
{
BlServers = false;
return;
}
BlServers = true;
for (int k = 0; k < lstModel.Count; k++)
{
ProfileItem it = lstModel[k];
string name = it.GetSummary();
var item = new ComboItem() { ID = it.indexId, Text = name };
_servers.Add(item);
if (_config.indexId == it.indexId)
{
SelectedServer = item;
}
}
}
private void RefreshSubscriptions()
{
Locator.Current.GetService<ProfilesViewModel>()?.RefreshSubscriptions();
@ -554,7 +365,12 @@ namespace ServiceLib.ViewModels
}
}
public void ScanScreenTaskAsync(string result)
public async Task AddServerViaScanTaskAsync()
{
_updateView?.Invoke(EViewAction.ScanScreenTask, null);
}
public void ScanScreenResult(string result)
{
if (Utils.IsNullOrEmpty(result))
{
@ -572,66 +388,6 @@ namespace ServiceLib.ViewModels
}
}
private void SetDefaultServer(string indexId)
{
if (Utils.IsNullOrEmpty(indexId))
{
return;
}
if (indexId == _config.indexId)
{
return;
}
var item = AppHandler.Instance.GetProfileItem(indexId);
if (item is null)
{
NoticeHandler.Instance.Enqueue(ResUI.PleaseSelectServer);
return;
}
if (ConfigHandler.SetDefaultServerIndex(_config, indexId) == 0)
{
RefreshServers();
Reload();
}
}
private void ServerSelectedChanged(bool c)
{
if (!c)
{
return;
}
if (SelectedServer == null)
{
return;
}
if (Utils.IsNullOrEmpty(SelectedServer.ID))
{
return;
}
SetDefaultServer(SelectedServer.ID);
}
public async Task TestServerAvailability()
{
var item = ConfigHandler.GetDefaultServer(_config);
if (item == null)
{
return;
}
await (new UpdateService()).RunAvailabilityCheck(async (bool success, string msg) =>
{
NoticeHandler.Instance.SendMessageEx(msg);
_updateView?.Invoke(EViewAction.DispatcherServerAvailability, msg);
});
}
public void TestServerAvailabilityResult(string msg)
{
RunningInfoDisplay = msg;
}
#endregion Add Servers
#region Subscription
@ -658,8 +414,7 @@ namespace ServiceLib.ViewModels
var ret = await _updateView?.Invoke(EViewAction.OptionSettingWindow, null);
if (ret == true)
{
InboundDisplayStaus();
//RefreshServers();
Locator.Current.GetService<StatusBarViewModel>()?.InboundDisplayStaus();
Reload();
}
}
@ -670,8 +425,7 @@ namespace ServiceLib.ViewModels
if (ret == true)
{
ConfigHandler.InitBuiltinRouting(_config);
RefreshRoutingsMenu();
//RefreshServers();
Locator.Current.GetService<StatusBarViewModel>()?.RefreshRoutingsMenu();
Reload();
}
}
@ -685,20 +439,20 @@ namespace ServiceLib.ViewModels
}
}
private async Task RebootAsAdmin()
public async Task RebootAsAdmin()
{
ProcessStartInfo startInfo = new()
{
UseShellExecute = true,
Arguments = Global.RebootAs,
WorkingDirectory = Utils.StartupPath(),
FileName = Utils.GetExePath().AppendQuotes(),
Verb = "runas",
};
try
{
ProcessStartInfo startInfo = new()
{
UseShellExecute = true,
Arguments = Global.RebootAs,
WorkingDirectory = Utils.StartupPath(),
FileName = Utils.GetExePath().AppendQuotes(),
Verb = "runas",
};
Process.Start(startInfo);
MyAppExitAsync(false);
await MyAppExitAsync(false);
}
catch { }
}
@ -730,13 +484,13 @@ namespace ServiceLib.ViewModels
BlReloadEnabled = false;
await LoadCore();
await TestServerAvailability();
Locator.Current.GetService<StatusBarViewModel>()?.TestServerAvailability();
_updateView?.Invoke(EViewAction.DispatcherReload, null);
}
public void ReloadResult()
{
ChangeSystemProxyStatusAsync(_config.systemProxyItem.sysProxyType, false);
//ChangeSystemProxyStatusAsync(_config.systemProxyItem.sysProxyType, false);
BlReloadEnabled = true;
ShowClashUI = _config.IsRunningCore(ECoreType.sing_box);
if (ShowClashUI)
@ -764,180 +518,22 @@ namespace ServiceLib.ViewModels
public void CloseCore()
{
ConfigHandler.SaveConfig(_config, false);
ChangeSystemProxyStatusAsync(ESysProxyType.ForcedClear, false);
CoreHandler.Instance.CoreStop();
}
#endregion core job
#region System proxy and Routings
public async Task SetListenerType(ESysProxyType type)
{
if (_config.systemProxyItem.sysProxyType == type)
{
return;
}
_config.systemProxyItem.sysProxyType = type;
ChangeSystemProxyStatusAsync(type, true);
SystemProxySelected = (int)_config.systemProxyItem.sysProxyType;
ConfigHandler.SaveConfig(_config, false);
}
private async Task ChangeSystemProxyStatusAsync(ESysProxyType type, bool blChange)
{
//await _updateView?.Invoke(EViewAction.UpdateSysProxy, _config.tunModeItem.enableTun ? true : false);
_updateView?.Invoke(EViewAction.UpdateSysProxy, false);
NoticeHandler.Instance.SendMessageEx($"{ResUI.TipChangeSystemProxy} - {_config.systemProxyItem.sysProxyType.ToString()}");
BlSystemProxyClear = (type == ESysProxyType.ForcedClear);
BlSystemProxySet = (type == ESysProxyType.ForcedChange);
BlSystemProxyNothing = (type == ESysProxyType.Unchanged);
BlSystemProxyPac = (type == ESysProxyType.Pac);
if (blChange)
{
_updateView?.Invoke(EViewAction.DispatcherRefreshIcon, null);
}
}
private void RefreshRoutingsMenu()
{
_routingItems.Clear();
if (!_config.routingBasicItem.enableRoutingAdvanced)
{
BlRouting = false;
return;
}
BlRouting = true;
var routings = AppHandler.Instance.RoutingItems();
foreach (var item in routings)
{
_routingItems.Add(item);
if (item.id == _config.routingBasicItem.routingIndexId)
{
SelectedRouting = item;
}
}
}
private async Task RoutingSelectedChangedAsync(bool c)
{
if (!c)
{
return;
}
if (SelectedRouting == null)
{
return;
}
var item = AppHandler.Instance.GetRoutingItem(SelectedRouting?.id);
if (item is null)
{
return;
}
if (_config.routingBasicItem.routingIndexId == item.id)
{
return;
}
if (ConfigHandler.SetDefaultRouting(_config, item) == 0)
{
NoticeHandler.Instance.SendMessageEx(ResUI.TipChangeRouting);
Reload();
_updateView?.Invoke(EViewAction.DispatcherRefreshIcon, null);
}
}
private void DoSystemProxySelected(bool c)
{
if (!c)
{
return;
}
if (_config.systemProxyItem.sysProxyType == (ESysProxyType)SystemProxySelected)
{
return;
}
SetListenerType((ESysProxyType)SystemProxySelected);
}
private void DoEnableTun(bool c)
{
if (_config.tunModeItem.enableTun != EnableTun)
{
_config.tunModeItem.enableTun = EnableTun;
// When running as a non-administrator, reboot to administrator mode
if (EnableTun && !_isAdministrator)
{
_config.tunModeItem.enableTun = false;
RebootAsAdmin();
return;
}
ConfigHandler.SaveConfig(_config);
Reload();
}
}
#endregion System proxy and Routings
#region UI
public void InboundDisplayStaus()
{
StringBuilder sb = new();
sb.Append($"[{EInboundProtocol.socks}:{AppHandler.Instance.GetLocalPort(EInboundProtocol.socks)}]");
sb.Append(" | ");
//if (_config.systemProxyItem.sysProxyType == ESysProxyType.ForcedChange)
//{
// sb.Append($"[{Global.InboundHttp}({ResUI.SystemProxy}):{LazyConfig.Instance.GetLocalPort(Global.InboundHttp)}]");
//}
//else
//{
sb.Append($"[{EInboundProtocol.http}:{AppHandler.Instance.GetLocalPort(EInboundProtocol.http)}]");
//}
InboundDisplay = $"{ResUI.LabLocal}:{sb}";
if (_config.inbound[0].allowLANConn)
{
if (_config.inbound[0].newPort4LAN)
{
StringBuilder sb2 = new();
sb2.Append($"[{EInboundProtocol.socks}:{AppHandler.Instance.GetLocalPort(EInboundProtocol.socks2)}]");
sb2.Append(" | ");
sb2.Append($"[{EInboundProtocol.http}:{AppHandler.Instance.GetLocalPort(EInboundProtocol.http2)}]");
InboundLanDisplay = $"{ResUI.LabLAN}:{sb2}";
}
else
{
InboundLanDisplay = $"{ResUI.LabLAN}:{sb}";
}
}
else
{
InboundLanDisplay = $"{ResUI.LabLAN}:None";
}
}
private void AutoHideStartup()
{
if (_config.uiItem.autoHideStartup)
{
Observable.Range(1, 1)
.Delay(TimeSpan.FromSeconds(1))
.Subscribe(async x =>
{
await _updateView?.Invoke(EViewAction.ShowHideWindow, false);
});
.Delay(TimeSpan.FromSeconds(1))
.Subscribe(async x =>
{
ShowHideWindow(false);
});
}
}
#endregion UI
#endregion core job
}
}

View File

@ -15,7 +15,6 @@ namespace ServiceLib.ViewModels
private List<ProfileItem> _lstProfile;
private string _serverFilter = string.Empty;
private Dictionary<string, bool> _dicHeaderSort = new();
private SpeedtestService? _speedtestHandler;
@ -57,7 +56,6 @@ namespace ServiceLib.ViewModels
//servers delete
public ReactiveCommand<Unit, Unit> EditServerCmd { get; }
public ReactiveCommand<Unit, Unit> RemoveServerCmd { get; }
public ReactiveCommand<Unit, Unit> RemoveDuplicateServerCmd { get; }
public ReactiveCommand<Unit, Unit> CopyServerCmd { get; }
@ -68,14 +66,12 @@ namespace ServiceLib.ViewModels
//servers move
public ReactiveCommand<Unit, Unit> MoveTopCmd { get; }
public ReactiveCommand<Unit, Unit> MoveUpCmd { get; }
public ReactiveCommand<Unit, Unit> MoveDownCmd { get; }
public ReactiveCommand<Unit, Unit> MoveBottomCmd { get; }
//servers ping
public ReactiveCommand<Unit, Unit> MixedTestServerCmd { get; }
public ReactiveCommand<Unit, Unit> TcpingServerCmd { get; }
public ReactiveCommand<Unit, Unit> RealPingServerCmd { get; }
public ReactiveCommand<Unit, Unit> SpeedServerCmd { get; }
@ -83,7 +79,6 @@ namespace ServiceLib.ViewModels
//servers export
public ReactiveCommand<Unit, Unit> Export2ClientConfigCmd { get; }
public ReactiveCommand<Unit, Unit> Export2ClientConfigClipboardCmd { get; }
public ReactiveCommand<Unit, Unit> Export2ShareUrlCmd { get; }
public ReactiveCommand<Unit, Unit> Export2ShareUrlBase64Cmd { get; }
@ -98,7 +93,6 @@ namespace ServiceLib.ViewModels
public ProfilesViewModel(Func<EViewAction, object?, Task<bool>>? updateView)
{
_config = AppHandler.Instance.Config;
_updateView = updateView;
MessageBus.Current.Listen<string>(EMsgCommand.RefreshProfiles.ToString()).Subscribe(async x => await _updateView?.Invoke(EViewAction.DispatcherRefreshServersBiz, null));
@ -505,7 +499,7 @@ namespace ServiceLib.ViewModels
SetDefaultServer(SelectedProfile.indexId);
}
private async Task SetDefaultServer(string indexId)
public async Task SetDefaultServer(string indexId)
{
if (Utils.IsNullOrEmpty(indexId))
{

View File

@ -0,0 +1,459 @@
using DynamicData.Binding;
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
using Splat;
using System.Reactive;
using System.Reactive.Linq;
using System.Text;
namespace ServiceLib.ViewModels
{
public class StatusBarViewModel : MyReactiveObject
{
private bool _isAdministrator { get; set; }
#region ObservableCollection
private IObservableCollection<RoutingItem> _routingItems = new ObservableCollectionExtended<RoutingItem>();
public IObservableCollection<RoutingItem> RoutingItems => _routingItems;
private IObservableCollection<ComboItem> _servers = new ObservableCollectionExtended<ComboItem>();
public IObservableCollection<ComboItem> Servers => _servers;
[Reactive]
public RoutingItem SelectedRouting { get; set; }
[Reactive]
public ComboItem SelectedServer { get; set; }
[Reactive]
public bool BlServers { get; set; }
#endregion ObservableCollection
public ReactiveCommand<Unit, Unit> AddServerViaClipboardCmd { get; }
public ReactiveCommand<Unit, Unit> AddServerViaScanCmd { get; }
public ReactiveCommand<Unit, Unit> SubUpdateCmd { get; }
public ReactiveCommand<Unit, Unit> SubUpdateViaProxyCmd { get; }
public ReactiveCommand<Unit, Unit> NotifyLeftClickCmd { get; }
public ReactiveCommand<Unit, Unit> ExitCmd { get; }
#region System Proxy
[Reactive]
public bool BlSystemProxyClear { get; set; }
[Reactive]
public bool BlSystemProxySet { get; set; }
[Reactive]
public bool BlSystemProxyNothing { get; set; }
[Reactive]
public bool BlSystemProxyPac { get; set; }
public ReactiveCommand<Unit, Unit> SystemProxyClearCmd { get; }
public ReactiveCommand<Unit, Unit> SystemProxySetCmd { get; }
public ReactiveCommand<Unit, Unit> SystemProxyNothingCmd { get; }
public ReactiveCommand<Unit, Unit> SystemProxyPacCmd { get; }
[Reactive]
public bool BlRouting { get; set; }
[Reactive]
public int SystemProxySelected { get; set; }
#endregion System Proxy
#region UI
[Reactive]
public string InboundDisplay { get; set; }
[Reactive]
public string InboundLanDisplay { get; set; }
[Reactive]
public string RunningServerDisplay { get; set; }
[Reactive]
public string RunningServerToolTipText { get; set; }
[Reactive]
public string RunningInfoDisplay { get; set; }
[Reactive]
public string SpeedProxyDisplay { get; set; }
[Reactive]
public string SpeedDirectDisplay { get; set; }
[Reactive]
public bool EnableTun { get; set; }
#endregion UI
public StatusBarViewModel(Func<EViewAction, object?, Task<bool>>? updateView)
{
_config = AppHandler.Instance.Config;
_updateView = updateView;
if (_updateView != null)
{
MessageBus.Current.Listen<string>(EMsgCommand.RefreshProfiles.ToString())
.Subscribe(async x => await _updateView?.Invoke(EViewAction.DispatcherRefreshServersBiz, null));
}
SelectedRouting = new();
SelectedServer = new();
_isAdministrator = Utils.IsAdministrator();
if (_config.tunModeItem.enableTun && _isAdministrator)
{
EnableTun = true;
}
else
{
_config.tunModeItem.enableTun = EnableTun = false;
}
RefreshRoutingsMenu();
InboundDisplayStaus();
ChangeSystemProxyAsync(_config.systemProxyItem.sysProxyType, true);
#region WhenAnyValue && ReactiveCommand
this.WhenAnyValue(
x => x.SelectedRouting,
y => y != null && !y.remarks.IsNullOrEmpty())
.Subscribe(c => RoutingSelectedChangedAsync(c));
this.WhenAnyValue(
x => x.SelectedServer,
y => y != null && !y.Text.IsNullOrEmpty())
.Subscribe(c => ServerSelectedChanged(c));
SystemProxySelected = (int)_config.systemProxyItem.sysProxyType;
this.WhenAnyValue(
x => x.SystemProxySelected,
y => y >= 0)
.Subscribe(c => DoSystemProxySelected(c));
this.WhenAnyValue(
x => x.EnableTun,
y => y == true)
.Subscribe(c => DoEnableTun(c));
NotifyLeftClickCmd = ReactiveCommand.CreateFromTask(async () =>
{
Locator.Current.GetService<MainWindowViewModel>()?.ShowHideWindow(null);
});
AddServerViaClipboardCmd = ReactiveCommand.CreateFromTask(async () =>
{
await AddServerViaClipboard();
});
AddServerViaScanCmd = ReactiveCommand.CreateFromTask(async () =>
{
await AddServerViaScan();
});
SubUpdateCmd = ReactiveCommand.CreateFromTask(async () =>
{
await UpdateSubscriptionProcess(false);
});
SubUpdateViaProxyCmd = ReactiveCommand.CreateFromTask(async () =>
{
await UpdateSubscriptionProcess(true);
});
//System proxy
SystemProxyClearCmd = ReactiveCommand.CreateFromTask(async () =>
{
await SetListenerType(ESysProxyType.ForcedClear);
});
SystemProxySetCmd = ReactiveCommand.CreateFromTask(async () =>
{
await SetListenerType(ESysProxyType.ForcedChange);
});
SystemProxyNothingCmd = ReactiveCommand.CreateFromTask(async () =>
{
await SetListenerType(ESysProxyType.Unchanged);
});
SystemProxyPacCmd = ReactiveCommand.CreateFromTask(async () =>
{
await SetListenerType(ESysProxyType.Pac);
});
#endregion WhenAnyValue && ReactiveCommand
}
public void Init(Func<EViewAction, object?, Task<bool>>? updateView)
{
_updateView = updateView;
if (_updateView != null)
{
MessageBus.Current.Listen<string>(EMsgCommand.RefreshProfiles.ToString())
.Subscribe(async x => await _updateView?.Invoke(EViewAction.DispatcherRefreshServersBiz, null));
}
}
private async Task AddServerViaClipboard()
{
var service = Locator.Current.GetService<MainWindowViewModel>();
if (service != null) await service.AddServerViaClipboardAsync(null);
}
private async Task AddServerViaScan()
{
var service = Locator.Current.GetService<MainWindowViewModel>();
if (service != null) await service.AddServerViaScanTaskAsync();
}
private async Task UpdateSubscriptionProcess(bool blProxy)
{
var service = Locator.Current.GetService<MainWindowViewModel>();
if (service != null) await service.UpdateSubscriptionProcess("", blProxy);
}
public void RefreshServersBiz()
{
RefreshServersMenu();
//display running server
var running = ConfigHandler.GetDefaultServer(_config);
if (running != null)
{
RunningServerDisplay =
RunningServerToolTipText = running.GetSummary();
}
else
{
RunningServerDisplay =
RunningServerToolTipText = ResUI.CheckServerSettings;
}
}
private void RefreshServersMenu()
{
var lstModel = AppHandler.Instance.ProfileItems(_config.subIndexId, "");
_servers.Clear();
if (lstModel.Count > _config.guiItem.trayMenuServersLimit)
{
BlServers = false;
return;
}
BlServers = true;
for (int k = 0; k < lstModel.Count; k++)
{
ProfileItem it = lstModel[k];
string name = it.GetSummary();
var item = new ComboItem() { ID = it.indexId, Text = name };
_servers.Add(item);
if (_config.indexId == it.indexId)
{
SelectedServer = item;
}
}
}
private void ServerSelectedChanged(bool c)
{
if (!c)
{
return;
}
if (SelectedServer == null)
{
return;
}
if (Utils.IsNullOrEmpty(SelectedServer.ID))
{
return;
}
Locator.Current.GetService<ProfilesViewModel>()?.SetDefaultServer(SelectedServer.ID);
}
public async Task TestServerAvailability()
{
var item = ConfigHandler.GetDefaultServer(_config);
if (item == null)
{
return;
}
await (new UpdateService()).RunAvailabilityCheck(async (bool success, string msg) =>
{
NoticeHandler.Instance.SendMessageEx(msg);
_updateView?.Invoke(EViewAction.DispatcherServerAvailability, msg);
});
}
public void TestServerAvailabilityResult(string msg)
{
RunningInfoDisplay = msg;
}
#region System proxy and Routings
public async Task SetListenerType(ESysProxyType type)
{
if (_config.systemProxyItem.sysProxyType == type)
{
return;
}
_config.systemProxyItem.sysProxyType = type;
ChangeSystemProxyAsync(type, true);
NoticeHandler.Instance.SendMessageEx($"{ResUI.TipChangeSystemProxy} - {_config.systemProxyItem.sysProxyType.ToString()}");
SystemProxySelected = (int)_config.systemProxyItem.sysProxyType;
ConfigHandler.SaveConfig(_config, false);
}
private async Task ChangeSystemProxyAsync(ESysProxyType type, bool blChange)
{
//await _updateView?.Invoke(EViewAction.UpdateSysProxy, _config.tunModeItem.enableTun ? true : false);
_updateView?.Invoke(EViewAction.UpdateSysProxy, false);
BlSystemProxyClear = (type == ESysProxyType.ForcedClear);
BlSystemProxySet = (type == ESysProxyType.ForcedChange);
BlSystemProxyNothing = (type == ESysProxyType.Unchanged);
BlSystemProxyPac = (type == ESysProxyType.Pac);
if (blChange)
{
_updateView?.Invoke(EViewAction.DispatcherRefreshIcon, null);
}
}
public void RefreshRoutingsMenu()
{
_routingItems.Clear();
if (!_config.routingBasicItem.enableRoutingAdvanced)
{
BlRouting = false;
return;
}
BlRouting = true;
var routings = AppHandler.Instance.RoutingItems();
foreach (var item in routings)
{
_routingItems.Add(item);
if (item.id == _config.routingBasicItem.routingIndexId)
{
SelectedRouting = item;
}
}
}
private async Task RoutingSelectedChangedAsync(bool c)
{
if (!c)
{
return;
}
if (SelectedRouting == null)
{
return;
}
var item = AppHandler.Instance.GetRoutingItem(SelectedRouting?.id);
if (item is null)
{
return;
}
if (_config.routingBasicItem.routingIndexId == item.id)
{
return;
}
if (ConfigHandler.SetDefaultRouting(_config, item) == 0)
{
NoticeHandler.Instance.SendMessageEx(ResUI.TipChangeRouting);
Locator.Current.GetService<MainWindowViewModel>()?.Reload();
_updateView?.Invoke(EViewAction.DispatcherRefreshIcon, null);
}
}
private void DoSystemProxySelected(bool c)
{
if (!c)
{
return;
}
if (_config.systemProxyItem.sysProxyType == (ESysProxyType)SystemProxySelected)
{
return;
}
SetListenerType((ESysProxyType)SystemProxySelected);
}
private void DoEnableTun(bool c)
{
if (_config.tunModeItem.enableTun != EnableTun)
{
_config.tunModeItem.enableTun = EnableTun;
// When running as a non-administrator, reboot to administrator mode
if (EnableTun && !_isAdministrator)
{
_config.tunModeItem.enableTun = false;
Locator.Current.GetService<MainWindowViewModel>()?.RebootAsAdmin();
return;
}
ConfigHandler.SaveConfig(_config);
Locator.Current.GetService<MainWindowViewModel>()?.Reload();
}
}
#endregion System proxy and Routings
#region UI
public void InboundDisplayStaus()
{
StringBuilder sb = new();
sb.Append($"[{EInboundProtocol.socks}:{AppHandler.Instance.GetLocalPort(EInboundProtocol.socks)}]");
sb.Append(" | ");
//if (_config.systemProxyItem.sysProxyType == ESysProxyType.ForcedChange)
//{
// sb.Append($"[{Global.InboundHttp}({ResUI.SystemProxy}):{LazyConfig.Instance.GetLocalPort(Global.InboundHttp)}]");
//}
//else
//{
sb.Append($"[{EInboundProtocol.http}:{AppHandler.Instance.GetLocalPort(EInboundProtocol.http)}]");
//}
InboundDisplay = $"{ResUI.LabLocal}:{sb}";
if (_config.inbound[0].allowLANConn)
{
if (_config.inbound[0].newPort4LAN)
{
StringBuilder sb2 = new();
sb2.Append($"[{EInboundProtocol.socks}:{AppHandler.Instance.GetLocalPort(EInboundProtocol.socks2)}]");
sb2.Append(" | ");
sb2.Append($"[{EInboundProtocol.http}:{AppHandler.Instance.GetLocalPort(EInboundProtocol.http2)}]");
InboundLanDisplay = $"{ResUI.LabLAN}:{sb2}";
}
else
{
InboundLanDisplay = $"{ResUI.LabLAN}:{sb}";
}
}
else
{
InboundLanDisplay = $"{ResUI.LabLAN}:None";
}
}
public void UpdateStatistics(ServerSpeedItem update)
{
SpeedProxyDisplay = string.Format(ResUI.SpeedDisplayText, Global.ProxyTag, Utils.HumanFy(update.proxyUp), Utils.HumanFy(update.proxyDown));
SpeedDirectDisplay = string.Format(ResUI.SpeedDisplayText, Global.DirectTag, Utils.HumanFy(update.directUp), Utils.HumanFy(update.directDown));
}
#endregion UI
}
}

View File

@ -2,9 +2,9 @@
x:Class="v2rayN.Desktop.App"
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:v2rayN.Desktop.ViewModels"
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
x:DataType="local:AppViewModel"
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
x:DataType="vms:StatusBarViewModel"
RequestedThemeVariant="Default">
<Application.Styles>
<StyleInclude Source="Styles/GlobalStyles.axaml" />
@ -37,13 +37,13 @@
<NativeMenuItem Command="{Binding SystemProxySetCmd}" Header="{x:Static resx:ResUI.menuSystemProxySet}" />
<NativeMenuItem Command="{Binding SystemProxyNothingCmd}" Header="{x:Static resx:ResUI.menuSystemProxyNothing}" />
<NativeMenuItemSeparator />
<NativeMenuItem Command="{Binding AddServerViaClipboardCmd}" Header="{x:Static resx:ResUI.menuAddServerViaClipboard}" />
<NativeMenuItem Click="MenuAddServerViaClipboardClick" Header="{x:Static resx:ResUI.menuAddServerViaClipboard}" />
<NativeMenuItem Header="{x:Static resx:ResUI.menuAddServerViaScan}" IsVisible="False" />
<NativeMenuItem Command="{Binding SubUpdateCmd}" Header="{x:Static resx:ResUI.menuSubUpdate}" />
<NativeMenuItem Command="{Binding SubUpdateViaProxyCmd}" Header="{x:Static resx:ResUI.menuSubUpdateViaProxy}" />
<NativeMenuItemSeparator />
<NativeMenuItem Click="TrayIcon_Clicked" Header="{x:Static resx:ResUI.menuShowOrHideMainWindow}" />
<NativeMenuItem Command="{Binding ExitCmd}" Header="{x:Static resx:ResUI.menuExit}" />
<NativeMenuItem Click="MenuExit_Click" Header="{x:Static resx:ResUI.menuExit}" />
</NativeMenu>
</TrayIcon.Menu>
</TrayIcon>

View File

@ -1,7 +1,8 @@
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;
using v2rayN.Desktop.ViewModels;
using Splat;
using v2rayN.Desktop.Common;
using v2rayN.Desktop.Views;
namespace v2rayN.Desktop;
@ -22,7 +23,9 @@ public partial class App : Application
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;
this.DataContext = new AppViewModel();
var ViewModel = new StatusBarViewModel(null);
Locator.CurrentMutable.RegisterLazySingleton(() => ViewModel, typeof(StatusBarViewModel));
this.DataContext = ViewModel;
}
public override void OnFrameworkInitializationCompleted()
@ -85,4 +88,27 @@ public partial class App : Application
}
}
}
private void MenuAddServerViaClipboardClick(object? sender, EventArgs e)
{
if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
if (desktop.MainWindow != null)
{
var clipboardData = AvaUtils.GetClipboardData(desktop.MainWindow).Result;
var service = Locator.Current.GetService<MainWindowViewModel>();
if (service != null) _ = service.AddServerViaClipboardAsync(clipboardData);
}
}
}
private void MenuExit_Click(object? sender, EventArgs e)
{
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
Locator.Current.GetService<MainWindowViewModel>()?.MyAppExitAsync(false);
desktop.Shutdown();
}
}
}

View File

@ -61,7 +61,7 @@ namespace v2rayN.Desktop.ViewModels
return;
}
var service = Locator.Current.GetService<MainWindowViewModel>();
var service = Locator.Current.GetService<StatusBarViewModel>();
if (service != null) await service.SetListenerType(type);
}

View File

@ -6,6 +6,7 @@
xmlns:dialogHost="clr-namespace:DialogHostAvalonia;assembly=DialogHost.Avalonia"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
xmlns:view="using:v2rayN.Desktop.Views"
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
Title="v2rayN"
Width="900"
@ -118,72 +119,7 @@
</Menu>
</DockPanel>
<StackPanel Height="50" DockPanel.Dock="Bottom">
<DockPanel>
<StackPanel
Margin="8,0"
VerticalAlignment="Center"
DockPanel.Dock="Right">
<TextBlock x:Name="txtSpeedProxyDisplay" />
<Border Margin="2" />
<TextBlock x:Name="txtSpeedDirectDisplay" />
</StackPanel>
<StackPanel
Width="240"
Margin="8,0"
VerticalAlignment="Center"
DockPanel.Dock="Left">
<TextBlock x:Name="txtInboundDisplay" />
<Border Margin="2" />
<TextBlock x:Name="txtInboundLanDisplay" />
</StackPanel>
<StackPanel
x:Name="spEnableTun"
Width="100"
Margin="8,0"
VerticalAlignment="Center"
DockPanel.Dock="Left">
<TextBlock Text="{x:Static resx:ResUI.TbEnableTunAs}" />
<ToggleSwitch
x:Name="togEnableTun"
HorizontalAlignment="Left"
Classes="Margin8"
Theme="{StaticResource SimpleToggleSwitch}" />
</StackPanel>
<StackPanel
Margin="8,0"
VerticalAlignment="Center"
DockPanel.Dock="Left"
Orientation="Horizontal">
<ComboBox
x:Name="cmbSystemProxy"
Width="120"
Margin="8,0"
ToolTip.Tip="{x:Static resx:ResUI.menuSystemproxy}">
<ComboBoxItem Content="{x:Static resx:ResUI.menuSystemProxyClear}" />
<ComboBoxItem Content="{x:Static resx:ResUI.menuSystemProxySet}" />
<ComboBoxItem Content="{x:Static resx:ResUI.menuSystemProxyNothing}" />
</ComboBox>
<ComboBox
x:Name="cmbRoutings2"
Width="150"
Margin="8,0"
DisplayMemberBinding="{Binding remarks}"
ItemsSource="{Binding RoutingItems}"
ToolTip.Tip="{x:Static resx:ResUI.menuRouting}" />
</StackPanel>
<StackPanel Margin="8,0" VerticalAlignment="Center">
<TextBlock x:Name="txtRunningServerDisplay" Tapped="TxtRunningServerDisplay_Tapped" />
<Border Margin="2" />
<TextBlock x:Name="txtRunningInfoDisplay" Tapped="TxtRunningServerDisplay_Tapped" />
</StackPanel>
</DockPanel>
</StackPanel>
<view:StatusBarView DockPanel.Dock="Bottom" />
<Grid>
<Grid x:Name="gridMain" IsVisible="False">

View File

@ -12,7 +12,6 @@ using Splat;
using System.ComponentModel;
using System.Reactive.Disposables;
using v2rayN.Desktop.Common;
using v2rayN.Desktop.Handler;
namespace v2rayN.Desktop.Views
{
@ -40,9 +39,8 @@ namespace v2rayN.Desktop.Views
menuCheckUpdate.Click += MenuCheckUpdate_Click;
menuBackupAndRestore.Click += MenuBackupAndRestore_Click;
var IsAdministrator = Utils.IsAdministrator();
MessageBus.Current.Listen<string>(EMsgCommand.SendSnackMsg.ToString()).Subscribe(x => DelegateSnackMsg(x));
ViewModel = new MainWindowViewModel(IsAdministrator, UpdateViewHandler);
ViewModel = new MainWindowViewModel(UpdateViewHandler);
Locator.CurrentMutable.RegisterLazySingleton(() => ViewModel, typeof(MainWindowViewModel));
//WindowsHandler.Instance.RegisterGlobalHotkey(_config, OnHotkeyHandler, null);
@ -82,19 +80,6 @@ namespace v2rayN.Desktop.Views
this.BindCommand(ViewModel, vm => vm.ReloadCmd, v => v.menuReload).DisposeWith(disposables);
this.OneWayBind(ViewModel, vm => vm.BlReloadEnabled, v => v.menuReload.IsEnabled).DisposeWith(disposables);
//status bar
this.OneWayBind(ViewModel, vm => vm.InboundDisplay, v => v.txtInboundDisplay.Text).DisposeWith(disposables);
this.OneWayBind(ViewModel, vm => vm.InboundLanDisplay, v => v.txtInboundLanDisplay.Text).DisposeWith(disposables);
this.OneWayBind(ViewModel, vm => vm.RunningServerDisplay, v => v.txtRunningServerDisplay.Text).DisposeWith(disposables);
this.OneWayBind(ViewModel, vm => vm.RunningInfoDisplay, v => v.txtRunningInfoDisplay.Text).DisposeWith(disposables);
this.OneWayBind(ViewModel, vm => vm.SpeedProxyDisplay, v => v.txtSpeedProxyDisplay.Text).DisposeWith(disposables);
this.OneWayBind(ViewModel, vm => vm.SpeedDirectDisplay, v => v.txtSpeedDirectDisplay.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.EnableTun, v => v.togEnableTun.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SystemProxySelected, v => v.cmbSystemProxy.SelectedIndex).DisposeWith(disposables);
//this.OneWayBind(ViewModel, vm => vm.RoutingItems, v => v.cmbRoutings2.ItemsSource).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedRouting, v => v.cmbRoutings2.SelectedItem).DisposeWith(disposables);
if (_config.uiItem.mainGirdOrientation == EGirdOrientation.Horizontal)
{
gridMain.IsVisible = true;
@ -118,7 +103,7 @@ namespace v2rayN.Desktop.Views
}
});
this.Title = $"{Utils.GetVersion()} - {(IsAdministrator ? ResUI.RunAsAdmin : ResUI.NotRunAsAdmin)}";
this.Title = $"{Utils.GetVersion()} - {(Utils.IsAdministrator() ? ResUI.RunAsAdmin : ResUI.NotRunAsAdmin)}";
if (Utils.IsWindows())
{
menuGlobalHotkeySetting.IsVisible = false;
@ -128,10 +113,6 @@ namespace v2rayN.Desktop.Views
menuRebootAsAdmin.IsVisible = false;
menuSettingsSetUWP.IsVisible = false;
menuGlobalHotkeySetting.IsVisible = false;
if (_config.tunModeItem.enableTun)
{
ViewModel.EnableTun = true;
}
}
if (_config.uiItem.mainGirdOrientation == EGirdOrientation.Horizontal)
@ -215,33 +196,12 @@ namespace v2rayN.Desktop.Views
DispatcherPriority.Default);
break;
case EViewAction.DispatcherServerAvailability:
if (obj is null) return false;
Dispatcher.UIThread.Post(() =>
ViewModel?.TestServerAvailabilityResult((string)obj),
DispatcherPriority.Default);
break;
case EViewAction.DispatcherReload:
Dispatcher.UIThread.Post(() =>
ViewModel?.ReloadResult(),
DispatcherPriority.Default);
break;
case EViewAction.DispatcherRefreshServersBiz:
Dispatcher.UIThread.Post(() =>
ViewModel?.RefreshServersBiz(),
DispatcherPriority.Default);
break;
case EViewAction.DispatcherRefreshIcon:
Dispatcher.UIThread.Post(() =>
{
this.Icon = AvaUtils.GetAppIcon(_config.systemProxyItem.sysProxyType);
},
DispatcherPriority.Default);
break;
case EViewAction.Shutdown:
StorageUI();
if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
@ -251,12 +211,7 @@ namespace v2rayN.Desktop.Views
break;
case EViewAction.ScanScreenTask:
ScanScreenTaskAsync().ContinueWith(_ => { });
break;
case EViewAction.UpdateSysProxy:
if (obj is null) return false;
await SysProxyHandler.UpdateSysProxy(_config, (bool)obj);
await ScanScreenTaskAsync();
break;
case EViewAction.AddServerViaClipboard:
@ -282,21 +237,21 @@ namespace v2rayN.Desktop.Views
ShowHideWindow(null);
break;
case EGlobalHotkey.SystemProxyClear:
ViewModel?.SetListenerType(ESysProxyType.ForcedClear);
break;
//case EGlobalHotkey.SystemProxyClear:
// ViewModel?.SetListenerType(ESysProxyType.ForcedClear);
// break;
case EGlobalHotkey.SystemProxySet:
ViewModel?.SetListenerType(ESysProxyType.ForcedChange);
break;
//case EGlobalHotkey.SystemProxySet:
// ViewModel?.SetListenerType(ESysProxyType.ForcedChange);
// break;
case EGlobalHotkey.SystemProxyUnchanged:
ViewModel?.SetListenerType(ESysProxyType.Unchanged);
break;
//case EGlobalHotkey.SystemProxyUnchanged:
// ViewModel?.SetListenerType(ESysProxyType.Unchanged);
// break;
case EGlobalHotkey.SystemProxyPac:
ViewModel?.SetListenerType(ESysProxyType.Pac);
break;
//case EGlobalHotkey.SystemProxyPac:
// ViewModel?.SetListenerType(ESysProxyType.Pac);
// break;
}
}
@ -342,11 +297,6 @@ namespace v2rayN.Desktop.Views
Utils.ProcessStart($"{Utils.Base64Decode(Global.PromotionUrl)}?t={DateTime.Now.Ticks}");
}
private void TxtRunningServerDisplay_Tapped(object? sender, Avalonia.Input.TappedEventArgs e)
{
ViewModel?.TestServerAvailability();
}
private void menuSettingsSetUWP_Click(object? sender, RoutedEventArgs e)
{
Utils.ProcessStart(Utils.GetBinPath("EnableLoopback.exe"));

View File

@ -0,0 +1,81 @@
<UserControl
x:Class="v2rayN.Desktop.Views.StatusBarView"
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
d:DesignHeight="450"
d:DesignWidth="800"
x:DataType="vms:StatusBarViewModel"
mc:Ignorable="d">
<Grid>
<StackPanel Height="50">
<DockPanel>
<StackPanel
Margin="8,0"
VerticalAlignment="Center"
DockPanel.Dock="Right">
<TextBlock x:Name="txtSpeedProxyDisplay" />
<Border Margin="2" />
<TextBlock x:Name="txtSpeedDirectDisplay" />
</StackPanel>
<StackPanel
Width="240"
Margin="8,0"
VerticalAlignment="Center"
DockPanel.Dock="Left">
<TextBlock x:Name="txtInboundDisplay" />
<Border Margin="2" />
<TextBlock x:Name="txtInboundLanDisplay" />
</StackPanel>
<StackPanel
x:Name="spEnableTun"
Width="100"
Margin="8,0"
VerticalAlignment="Center"
DockPanel.Dock="Left">
<TextBlock Text="{x:Static resx:ResUI.TbEnableTunAs}" />
<ToggleSwitch
x:Name="togEnableTun"
HorizontalAlignment="Left"
Classes="Margin8"
Theme="{StaticResource SimpleToggleSwitch}" />
</StackPanel>
<StackPanel
Margin="8,0"
VerticalAlignment="Center"
DockPanel.Dock="Left"
Orientation="Horizontal">
<ComboBox
x:Name="cmbSystemProxy"
Width="120"
Margin="8,0"
ToolTip.Tip="{x:Static resx:ResUI.menuSystemproxy}">
<ComboBoxItem Content="{x:Static resx:ResUI.menuSystemProxyClear}" />
<ComboBoxItem Content="{x:Static resx:ResUI.menuSystemProxySet}" />
<ComboBoxItem Content="{x:Static resx:ResUI.menuSystemProxyNothing}" />
</ComboBox>
<ComboBox
x:Name="cmbRoutings2"
Width="150"
Margin="8,0"
DisplayMemberBinding="{Binding remarks}"
ItemsSource="{Binding RoutingItems}"
ToolTip.Tip="{x:Static resx:ResUI.menuRouting}" />
</StackPanel>
<StackPanel Margin="8,0" VerticalAlignment="Center">
<TextBlock x:Name="txtRunningServerDisplay" />
<Border Margin="2" />
<TextBlock x:Name="txtRunningInfoDisplay" />
</StackPanel>
</DockPanel>
</StackPanel>
</Grid>
</UserControl>

View File

@ -0,0 +1,136 @@
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.ReactiveUI;
using Avalonia.Threading;
using ReactiveUI;
using Splat;
using System.Reactive.Disposables;
using v2rayN.Desktop.Common;
using v2rayN.Desktop.Handler;
namespace v2rayN.Desktop.Views
{
public partial class StatusBarView : ReactiveUserControl<StatusBarViewModel>
{
private static Config _config;
public StatusBarView()
{
InitializeComponent();
_config = AppHandler.Instance.Config;
//ViewModel = new StatusBarViewModel(UpdateViewHandler);
//Locator.CurrentMutable.RegisterLazySingleton(() => ViewModel, typeof(StatusBarViewModel));
ViewModel = Locator.Current.GetService<StatusBarViewModel>();
ViewModel?.Init(UpdateViewHandler);
txtRunningServerDisplay.Tapped += TxtRunningServerDisplay_Tapped;
txtRunningInfoDisplay.Tapped += TxtRunningServerDisplay_Tapped;
this.WhenActivated(disposables =>
{
//status bar
this.OneWayBind(ViewModel, vm => vm.InboundDisplay, v => v.txtInboundDisplay.Text).DisposeWith(disposables);
this.OneWayBind(ViewModel, vm => vm.InboundLanDisplay, v => v.txtInboundLanDisplay.Text).DisposeWith(disposables);
this.OneWayBind(ViewModel, vm => vm.RunningServerDisplay, v => v.txtRunningServerDisplay.Text).DisposeWith(disposables);
this.OneWayBind(ViewModel, vm => vm.RunningInfoDisplay, v => v.txtRunningInfoDisplay.Text).DisposeWith(disposables);
this.OneWayBind(ViewModel, vm => vm.SpeedProxyDisplay, v => v.txtSpeedProxyDisplay.Text).DisposeWith(disposables);
this.OneWayBind(ViewModel, vm => vm.SpeedDirectDisplay, v => v.txtSpeedDirectDisplay.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.EnableTun, v => v.togEnableTun.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SystemProxySelected, v => v.cmbSystemProxy.SelectedIndex).DisposeWith(disposables);
//this.OneWayBind(ViewModel, vm => vm.RoutingItems, v => v.cmbRoutings2.ItemsSource).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedRouting, v => v.cmbRoutings2.SelectedItem).DisposeWith(disposables);
////system proxy
//this.OneWayBind(ViewModel, vm => vm.BlSystemProxyClear, v => v.menuSystemProxyClear2.Visibility, conversionHint: BooleanToVisibilityHint.UseHidden, vmToViewConverterOverride: new BooleanToVisibilityTypeConverter()).DisposeWith(disposables);
//this.OneWayBind(ViewModel, vm => vm.BlSystemProxySet, v => v.menuSystemProxySet2.Visibility, conversionHint: BooleanToVisibilityHint.UseHidden, vmToViewConverterOverride: new BooleanToVisibilityTypeConverter()).DisposeWith(disposables);
//this.OneWayBind(ViewModel, vm => vm.BlSystemProxyNothing, v => v.menuSystemProxyNothing2.Visibility, conversionHint: BooleanToVisibilityHint.UseHidden, vmToViewConverterOverride: new BooleanToVisibilityTypeConverter()).DisposeWith(disposables);
//this.OneWayBind(ViewModel, vm => vm.BlSystemProxyPac, v => v.menuSystemProxyPac2.Visibility, conversionHint: BooleanToVisibilityHint.UseHidden, vmToViewConverterOverride: new BooleanToVisibilityTypeConverter()).DisposeWith(disposables);
//this.BindCommand(ViewModel, vm => vm.SystemProxyClearCmd, v => v.menuSystemProxyClear).DisposeWith(disposables);
//this.BindCommand(ViewModel, vm => vm.SystemProxySetCmd, v => v.menuSystemProxySet).DisposeWith(disposables);
//this.BindCommand(ViewModel, vm => vm.SystemProxyPacCmd, v => v.menuSystemProxyPac).DisposeWith(disposables);
//this.BindCommand(ViewModel, vm => vm.SystemProxyNothingCmd, v => v.menuSystemProxyNothing).DisposeWith(disposables);
////routings and servers
//this.OneWayBind(ViewModel, vm => vm.RoutingItems, v => v.cmbRoutings.ItemsSource).DisposeWith(disposables);
//this.Bind(ViewModel, vm => vm.SelectedRouting, v => v.cmbRoutings.SelectedItem).DisposeWith(disposables);
//this.OneWayBind(ViewModel, vm => vm.BlRouting, v => v.menuRoutings.Visibility).DisposeWith(disposables);
//this.OneWayBind(ViewModel, vm => vm.BlRouting, v => v.sepRoutings.Visibility).DisposeWith(disposables);
//this.OneWayBind(ViewModel, vm => vm.Servers, v => v.cmbServers.ItemsSource).DisposeWith(disposables);
//this.Bind(ViewModel, vm => vm.SelectedServer, v => v.cmbServers.SelectedItem).DisposeWith(disposables);
//this.OneWayBind(ViewModel, vm => vm.BlServers, v => v.cmbServers.Visibility).DisposeWith(disposables);
////tray menu
//this.BindCommand(ViewModel, vm => vm.AddServerViaClipboardCmd, v => v.menuAddServerViaClipboard2).DisposeWith(disposables);
//this.BindCommand(ViewModel, vm => vm.AddServerViaScanCmd, v => v.menuAddServerViaScan2).DisposeWith(disposables);
//this.BindCommand(ViewModel, vm => vm.SubUpdateCmd, v => v.menuSubUpdate2).DisposeWith(disposables);
//this.BindCommand(ViewModel, vm => vm.SubUpdateViaProxyCmd, v => v.menuSubUpdateViaProxy2).DisposeWith(disposables);
//this.OneWayBind(ViewModel, vm => vm.RunningServerToolTipText, v => v.tbNotify.ToolTipText).DisposeWith(disposables);
//this.OneWayBind(ViewModel, vm => vm.NotifyLeftClickCmd, v => v.tbNotify.LeftClickCommand).DisposeWith(disposables);
////status bar
//this.OneWayBind(ViewModel, vm => vm.InboundDisplay, v => v.txtInboundDisplay.Text).DisposeWith(disposables);
//this.OneWayBind(ViewModel, vm => vm.InboundLanDisplay, v => v.txtInboundLanDisplay.Text).DisposeWith(disposables);
//this.OneWayBind(ViewModel, vm => vm.RunningServerDisplay, v => v.txtRunningServerDisplay.Text).DisposeWith(disposables);
//this.OneWayBind(ViewModel, vm => vm.RunningInfoDisplay, v => v.txtRunningInfoDisplay.Text).DisposeWith(disposables);
//this.OneWayBind(ViewModel, vm => vm.SpeedProxyDisplay, v => v.txtSpeedProxyDisplay.Text).DisposeWith(disposables);
//this.OneWayBind(ViewModel, vm => vm.SpeedDirectDisplay, v => v.txtSpeedDirectDisplay.Text).DisposeWith(disposables);
//this.Bind(ViewModel, vm => vm.EnableTun, v => v.togEnableTun.IsChecked).DisposeWith(disposables);
//this.Bind(ViewModel, vm => vm.SystemProxySelected, v => v.cmbSystemProxy.SelectedIndex).DisposeWith(disposables);
//this.OneWayBind(ViewModel, vm => vm.RoutingItems, v => v.cmbRoutings2.ItemsSource).DisposeWith(disposables);
//this.Bind(ViewModel, vm => vm.SelectedRouting, v => v.cmbRoutings2.SelectedItem).DisposeWith(disposables);
//this.OneWayBind(ViewModel, vm => vm.BlRouting, v => v.cmbRoutings2.Visibility).DisposeWith(disposables);
});
}
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
{
switch (action)
{
case EViewAction.UpdateSysProxy:
if (obj is null) return false;
await SysProxyHandler.UpdateSysProxy(_config, (bool)obj);
break;
case EViewAction.DispatcherServerAvailability:
if (obj is null) return false;
Dispatcher.UIThread.Post(() =>
ViewModel?.TestServerAvailabilityResult((string)obj),
DispatcherPriority.Default);
break;
case EViewAction.DispatcherRefreshServersBiz:
Dispatcher.UIThread.Post(() =>
ViewModel?.RefreshServersBiz(),
DispatcherPriority.Default);
break;
case EViewAction.DispatcherRefreshIcon:
Dispatcher.UIThread.Post(() =>
{
RefreshIcon();
},
DispatcherPriority.Default);
break;
}
return await Task.FromResult(true);
}
private void RefreshIcon()
{
if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
desktop.MainWindow.Icon = AvaUtils.GetAppIcon(_config.systemProxyItem.sysProxyType);
}
}
private void TxtRunningServerDisplay_Tapped(object? sender, Avalonia.Input.TappedEventArgs e)
{
ViewModel?.TestServerAvailability();
}
}
}

View File

@ -8,7 +8,7 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:reactiveui="http://reactiveui.net"
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
xmlns:tb="clr-namespace:H.NotifyIcon;assembly=H.NotifyIcon.Wpf"
xmlns:view="clr-namespace:v2rayN.Views"
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
Title="v2rayN"
Width="900"
@ -276,84 +276,7 @@
</ToolBar>
</ToolBarTray>
<materialDesign:ColorZone
Height="50"
DockPanel.Dock="Bottom"
Mode="Standard">
<DockPanel>
<StackPanel
Margin="8,0"
VerticalAlignment="Center"
DockPanel.Dock="Right">
<TextBlock x:Name="txtSpeedProxyDisplay" Style="{StaticResource StatusbarItem}" />
<Border Margin="2" />
<TextBlock x:Name="txtSpeedDirectDisplay" Style="{StaticResource StatusbarItem}" />
</StackPanel>
<StackPanel
Width="240"
Margin="8,0"
VerticalAlignment="Center"
DockPanel.Dock="Left">
<TextBlock x:Name="txtInboundDisplay" Style="{StaticResource StatusbarItem}" />
<Border Margin="2" />
<TextBlock x:Name="txtInboundLanDisplay" Style="{StaticResource StatusbarItem}" />
</StackPanel>
<StackPanel
x:Name="spEnableTun"
Width="auto"
Margin="8,0"
VerticalAlignment="Center"
DockPanel.Dock="Left">
<TextBlock Text="{x:Static resx:ResUI.TbEnableTunAs}" />
<ToggleButton
x:Name="togEnableTun"
Margin="4"
HorizontalAlignment="Left" />
</StackPanel>
<StackPanel
Margin="8,0"
VerticalAlignment="Center"
DockPanel.Dock="Left"
Orientation="Horizontal">
<ComboBox
x:Name="cmbSystemProxy"
Width="120"
Margin="8,0"
materialDesign:HintAssist.Hint="{x:Static resx:ResUI.menuSystemproxy}"
FontSize="{DynamicResource StdFontSize}"
Style="{StaticResource MaterialDesignFloatingHintComboBox}">
<ComboBoxItem Content="{x:Static resx:ResUI.menuSystemProxyClear}" />
<ComboBoxItem Content="{x:Static resx:ResUI.menuSystemProxySet}" />
<ComboBoxItem Content="{x:Static resx:ResUI.menuSystemProxyNothing}" />
<ComboBoxItem Content="{x:Static resx:ResUI.menuSystemProxyPac}" />
</ComboBox>
<ComboBox
x:Name="cmbRoutings2"
Width="150"
Margin="8,0"
materialDesign:HintAssist.Hint="{x:Static resx:ResUI.menuRouting}"
DisplayMemberPath="remarks"
FontSize="{DynamicResource StdFontSize}"
Style="{StaticResource MaterialDesignFloatingHintComboBox}" />
</StackPanel>
<StackPanel Margin="8,0" VerticalAlignment="Center">
<TextBlock
x:Name="txtRunningServerDisplay"
PreviewMouseDown="txtRunningInfoDisplay_MouseDoubleClick"
Style="{StaticResource StatusbarItem}" />
<Border Margin="2" />
<TextBlock
x:Name="txtRunningInfoDisplay"
PreviewMouseDown="txtRunningInfoDisplay_MouseDoubleClick"
Style="{StaticResource StatusbarItem}" />
</StackPanel>
</DockPanel>
</materialDesign:ColorZone>
<view:StatusBarView DockPanel.Dock="Bottom" />
<Grid>
<Grid x:Name="gridMain" Visibility="Collapsed">
@ -505,113 +428,6 @@
<materialDesign:Snackbar x:Name="MainSnackbar" MessageQueue="{materialDesign:MessageQueue}" />
</Grid>
</DockPanel>
<tb:TaskbarIcon
x:Name="tbNotify"
IconSource="/v2rayN.ico"
NoLeftClickDelay="True"
ToolTipText="v2rayN">
<tb:TaskbarIcon.ContextMenu>
<ContextMenu Style="{StaticResource DefContextMenu}">
<MenuItem x:Name="menuSystemProxyClear" Height="{StaticResource MenuItemHeight}">
<MenuItem.Header>
<StackPanel Orientation="Horizontal">
<materialDesign:PackIcon
x:Name="menuSystemProxyClear2"
Margin="0,0,8,0"
VerticalAlignment="Center"
Kind="Check" />
<TextBlock Text="{x:Static resx:ResUI.menuSystemProxyClear}" />
</StackPanel>
</MenuItem.Header>
</MenuItem>
<MenuItem x:Name="menuSystemProxySet" Height="{StaticResource MenuItemHeight}">
<MenuItem.Header>
<StackPanel Orientation="Horizontal">
<materialDesign:PackIcon
x:Name="menuSystemProxySet2"
Margin="0,0,8,0"
VerticalAlignment="Center"
Kind="Check" />
<TextBlock Text="{x:Static resx:ResUI.menuSystemProxySet}" />
</StackPanel>
</MenuItem.Header>
</MenuItem>
<MenuItem x:Name="menuSystemProxyNothing" Height="{StaticResource MenuItemHeight}">
<MenuItem.Header>
<StackPanel Orientation="Horizontal">
<materialDesign:PackIcon
x:Name="menuSystemProxyNothing2"
Margin="0,0,8,0"
VerticalAlignment="Center"
Kind="Check" />
<TextBlock Text="{x:Static resx:ResUI.menuSystemProxyNothing}" />
</StackPanel>
</MenuItem.Header>
</MenuItem>
<MenuItem x:Name="menuSystemProxyPac" Height="{StaticResource MenuItemHeight}">
<MenuItem.Header>
<StackPanel Orientation="Horizontal">
<materialDesign:PackIcon
x:Name="menuSystemProxyPac2"
Margin="0,0,8,0"
VerticalAlignment="Center"
Kind="Check" />
<TextBlock Text="{x:Static resx:ResUI.menuSystemProxyPac}" />
</StackPanel>
</MenuItem.Header>
</MenuItem>
<Separator x:Name="sepRoutings" />
<MenuItem x:Name="menuRoutings" Height="Auto">
<MenuItem.Header>
<DockPanel>
<ComboBox
x:Name="cmbRoutings"
MaxWidth="300"
materialDesign:HintAssist.Hint="{x:Static resx:ResUI.menuRouting}"
DisplayMemberPath="remarks"
FontSize="{DynamicResource StdFontSize}"
Style="{StaticResource MaterialDesignFilledComboBox}" />
</DockPanel>
</MenuItem.Header>
</MenuItem>
<MenuItem Height="Auto">
<MenuItem.Header>
<DockPanel>
<ComboBox
x:Name="cmbServers"
MaxWidth="300"
materialDesign:HintAssist.Hint="{x:Static resx:ResUI.menuServers}"
DisplayMemberPath="Text"
FontSize="{DynamicResource StdFontSize}"
Style="{StaticResource MaterialDesignFilledComboBox}" />
</DockPanel>
</MenuItem.Header>
</MenuItem>
<Separator />
<MenuItem
x:Name="menuAddServerViaClipboard2"
Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuAddServerViaClipboard}" />
<MenuItem
x:Name="menuAddServerViaScan2"
Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuAddServerViaScan}" />
<MenuItem
x:Name="menuSubUpdate2"
Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuSubUpdate}" />
<MenuItem
x:Name="menuSubUpdateViaProxy2"
Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuSubUpdateViaProxy}" />
<Separator />
<MenuItem
x:Name="menuExit"
Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuExit}" />
</ContextMenu>
</tb:TaskbarIcon.ContextMenu>
</tb:TaskbarIcon>
</Grid>
</materialDesign:DialogHost>
</reactiveui:ReactiveWindow>

View File

@ -26,19 +26,18 @@ namespace v2rayN.Views
_config = AppHandler.Instance.Config;
ThreadPool.RegisterWaitForSingleObject(App.ProgramStarted, OnProgramStarted, null, -1, false);
Application.Current.Exit += Current_Exit;
App.Current.SessionEnding += Current_SessionEnding;
this.Closing += MainWindow_Closing;
this.PreviewKeyDown += MainWindow_PreviewKeyDown;
menuSettingsSetUWP.Click += menuSettingsSetUWP_Click;
menuPromotion.Click += menuPromotion_Click;
menuClose.Click += menuClose_Click;
menuExit.Click += menuExit_Click;
menuCheckUpdate.Click += MenuCheckUpdate_Click;
menuBackupAndRestore.Click += MenuBackupAndRestore_Click;
var IsAdministrator = Utils.IsAdministrator();
MessageBus.Current.Listen<string>(EMsgCommand.SendSnackMsg.ToString()).Subscribe(x => DelegateSnackMsg(x));
ViewModel = new MainWindowViewModel(IsAdministrator, UpdateViewHandler);
ViewModel = new MainWindowViewModel(UpdateViewHandler);
Locator.CurrentMutable.RegisterLazySingleton(() => ViewModel, typeof(MainWindowViewModel));
WindowsHandler.Instance.RegisterGlobalHotkey(_config, OnHotkeyHandler, null);
@ -100,49 +99,6 @@ namespace v2rayN.Views
this.BindCommand(ViewModel, vm => vm.ReloadCmd, v => v.menuReload).DisposeWith(disposables);
this.OneWayBind(ViewModel, vm => vm.BlReloadEnabled, v => v.menuReload.IsEnabled).DisposeWith(disposables);
//system proxy
this.OneWayBind(ViewModel, vm => vm.BlSystemProxyClear, v => v.menuSystemProxyClear2.Visibility, conversionHint: BooleanToVisibilityHint.UseHidden, vmToViewConverterOverride: new BooleanToVisibilityTypeConverter()).DisposeWith(disposables);
this.OneWayBind(ViewModel, vm => vm.BlSystemProxySet, v => v.menuSystemProxySet2.Visibility, conversionHint: BooleanToVisibilityHint.UseHidden, vmToViewConverterOverride: new BooleanToVisibilityTypeConverter()).DisposeWith(disposables);
this.OneWayBind(ViewModel, vm => vm.BlSystemProxyNothing, v => v.menuSystemProxyNothing2.Visibility, conversionHint: BooleanToVisibilityHint.UseHidden, vmToViewConverterOverride: new BooleanToVisibilityTypeConverter()).DisposeWith(disposables);
this.OneWayBind(ViewModel, vm => vm.BlSystemProxyPac, v => v.menuSystemProxyPac2.Visibility, conversionHint: BooleanToVisibilityHint.UseHidden, vmToViewConverterOverride: new BooleanToVisibilityTypeConverter()).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.SystemProxyClearCmd, v => v.menuSystemProxyClear).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.SystemProxySetCmd, v => v.menuSystemProxySet).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.SystemProxyPacCmd, v => v.menuSystemProxyPac).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.SystemProxyNothingCmd, v => v.menuSystemProxyNothing).DisposeWith(disposables);
//routings and servers
this.OneWayBind(ViewModel, vm => vm.RoutingItems, v => v.cmbRoutings.ItemsSource).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedRouting, v => v.cmbRoutings.SelectedItem).DisposeWith(disposables);
this.OneWayBind(ViewModel, vm => vm.BlRouting, v => v.menuRoutings.Visibility).DisposeWith(disposables);
this.OneWayBind(ViewModel, vm => vm.BlRouting, v => v.sepRoutings.Visibility).DisposeWith(disposables);
this.OneWayBind(ViewModel, vm => vm.Servers, v => v.cmbServers.ItemsSource).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedServer, v => v.cmbServers.SelectedItem).DisposeWith(disposables);
this.OneWayBind(ViewModel, vm => vm.BlServers, v => v.cmbServers.Visibility).DisposeWith(disposables);
//tray menu
this.BindCommand(ViewModel, vm => vm.AddServerViaClipboardCmd, v => v.menuAddServerViaClipboard2).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.AddServerViaScanCmd, v => v.menuAddServerViaScan2).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.SubUpdateCmd, v => v.menuSubUpdate2).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.SubUpdateViaProxyCmd, v => v.menuSubUpdateViaProxy2).DisposeWith(disposables);
this.OneWayBind(ViewModel, vm => vm.RunningServerToolTipText, v => v.tbNotify.ToolTipText).DisposeWith(disposables);
this.OneWayBind(ViewModel, vm => vm.NotifyLeftClickCmd, v => v.tbNotify.LeftClickCommand).DisposeWith(disposables);
//status bar
this.OneWayBind(ViewModel, vm => vm.InboundDisplay, v => v.txtInboundDisplay.Text).DisposeWith(disposables);
this.OneWayBind(ViewModel, vm => vm.InboundLanDisplay, v => v.txtInboundLanDisplay.Text).DisposeWith(disposables);
this.OneWayBind(ViewModel, vm => vm.RunningServerDisplay, v => v.txtRunningServerDisplay.Text).DisposeWith(disposables);
this.OneWayBind(ViewModel, vm => vm.RunningInfoDisplay, v => v.txtRunningInfoDisplay.Text).DisposeWith(disposables);
this.OneWayBind(ViewModel, vm => vm.SpeedProxyDisplay, v => v.txtSpeedProxyDisplay.Text).DisposeWith(disposables);
this.OneWayBind(ViewModel, vm => vm.SpeedDirectDisplay, v => v.txtSpeedDirectDisplay.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.EnableTun, v => v.togEnableTun.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SystemProxySelected, v => v.cmbSystemProxy.SelectedIndex).DisposeWith(disposables);
this.OneWayBind(ViewModel, vm => vm.RoutingItems, v => v.cmbRoutings2.ItemsSource).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedRouting, v => v.cmbRoutings2.SelectedItem).DisposeWith(disposables);
this.OneWayBind(ViewModel, vm => vm.BlRouting, v => v.cmbRoutings2.Visibility).DisposeWith(disposables);
if (_config.uiItem.mainGirdOrientation == EGirdOrientation.Horizontal)
{
gridMain.Visibility = Visibility.Visible;
@ -166,7 +122,7 @@ namespace v2rayN.Views
}
});
this.Title = $"{Utils.GetVersion()} - {(IsAdministrator ? ResUI.RunAsAdmin : ResUI.NotRunAsAdmin)}";
this.Title = $"{Utils.GetVersion()} - {(Utils.IsAdministrator() ? ResUI.RunAsAdmin : ResUI.NotRunAsAdmin)}";
if (!_config.guiItem.enableHWA)
{
@ -237,14 +193,6 @@ namespace v2rayN.Views
}), DispatcherPriority.Normal);
break;
case EViewAction.DispatcherServerAvailability:
if (obj is null) return false;
Application.Current?.Dispatcher.Invoke((() =>
{
ViewModel?.TestServerAvailabilityResult((string)obj);
}), DispatcherPriority.Normal);
break;
case EViewAction.DispatcherReload:
Application.Current?.Dispatcher.Invoke((() =>
{
@ -252,21 +200,6 @@ namespace v2rayN.Views
}), DispatcherPriority.Normal);
break;
case EViewAction.DispatcherRefreshServersBiz:
Application.Current?.Dispatcher.Invoke((() =>
{
ViewModel?.RefreshServersBiz();
}), DispatcherPriority.Normal);
break;
case EViewAction.DispatcherRefreshIcon:
Application.Current?.Dispatcher.Invoke((() =>
{
tbNotify.Icon = WindowsHandler.Instance.GetNotifyIcon(_config);
this.Icon = WindowsHandler.Instance.GetAppIcon(_config);
}), DispatcherPriority.Normal);
break;
case EViewAction.Shutdown:
Application.Current?.Dispatcher.Invoke((() =>
{
@ -275,12 +208,7 @@ namespace v2rayN.Views
break;
case EViewAction.ScanScreenTask:
ScanScreenTaskAsync().ContinueWith(_ => { });
break;
case EViewAction.UpdateSysProxy:
if (obj is null) return false;
SysProxyHandler.UpdateSysProxy(_config, (bool)obj);
await ScanScreenTaskAsync();
break;
case EViewAction.AddServerViaClipboard:
@ -308,19 +236,10 @@ namespace v2rayN.Views
break;
case EGlobalHotkey.SystemProxyClear:
ViewModel?.SetListenerType(ESysProxyType.ForcedClear);
break;
case EGlobalHotkey.SystemProxySet:
ViewModel?.SetListenerType(ESysProxyType.ForcedChange);
break;
case EGlobalHotkey.SystemProxyUnchanged:
ViewModel?.SetListenerType(ESysProxyType.Unchanged);
break;
case EGlobalHotkey.SystemProxyPac:
ViewModel?.SetListenerType(ESysProxyType.Pac);
Locator.Current.GetService<StatusBarViewModel>()?.SetListenerType((ESysProxyType)((int)e - 1));
break;
}
}
@ -331,13 +250,9 @@ namespace v2rayN.Views
ShowHideWindow(false);
}
private void menuExit_Click(object sender, RoutedEventArgs e)
private void Current_Exit(object sender, ExitEventArgs e)
{
tabProfiles = null;
tbNotify.Dispose();
StorageUI();
ViewModel?.MyAppExitAsync(false);
}
private void Current_SessionEnding(object sender, SessionEndingCancelEventArgs e)
@ -383,17 +298,12 @@ namespace v2rayN.Views
Utils.ProcessStart($"{Utils.Base64Decode(Global.PromotionUrl)}?t={DateTime.Now.Ticks}");
}
private void txtRunningInfoDisplay_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
ViewModel?.TestServerAvailability();
}
private void menuSettingsSetUWP_Click(object sender, RoutedEventArgs e)
{
Utils.ProcessStart(Utils.GetBinPath("EnableLoopback.exe"));
}
public async Task ScanScreenTaskAsync()
private async Task ScanScreenTaskAsync()
{
ShowHideWindow(false);
@ -405,7 +315,7 @@ namespace v2rayN.Views
ShowHideWindow(true);
ViewModel?.ScanScreenTaskAsync(result);
ViewModel?.ScanScreenResult(result);
}
private void MenuCheckUpdate_Click(object sender, RoutedEventArgs e)

View File

@ -0,0 +1,198 @@
<reactiveui:ReactiveUserControl
x:Class="v2rayN.Views.StatusBarView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:reactiveui="http://reactiveui.net"
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
xmlns:tb="clr-namespace:H.NotifyIcon;assembly=H.NotifyIcon.Wpf"
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
d:DesignHeight="450"
d:DesignWidth="800"
x:TypeArguments="vms:StatusBarViewModel"
Style="{StaticResource ViewGlobal}"
mc:Ignorable="d">
<Grid>
<materialDesign:ColorZone Height="50" Mode="Standard">
<DockPanel>
<StackPanel
Margin="8,0"
VerticalAlignment="Center"
DockPanel.Dock="Right">
<TextBlock x:Name="txtSpeedProxyDisplay" Style="{StaticResource StatusbarItem}" />
<Border Margin="2" />
<TextBlock x:Name="txtSpeedDirectDisplay" Style="{StaticResource StatusbarItem}" />
</StackPanel>
<StackPanel
Width="240"
Margin="8,0"
VerticalAlignment="Center"
DockPanel.Dock="Left">
<TextBlock x:Name="txtInboundDisplay" Style="{StaticResource StatusbarItem}" />
<Border Margin="2" />
<TextBlock x:Name="txtInboundLanDisplay" Style="{StaticResource StatusbarItem}" />
</StackPanel>
<StackPanel
x:Name="spEnableTun"
Width="auto"
Margin="8,0"
VerticalAlignment="Center"
DockPanel.Dock="Left">
<TextBlock Text="{x:Static resx:ResUI.TbEnableTunAs}" />
<ToggleButton
x:Name="togEnableTun"
Margin="4"
HorizontalAlignment="Left" />
</StackPanel>
<StackPanel
Margin="8,0"
VerticalAlignment="Center"
DockPanel.Dock="Left"
Orientation="Horizontal">
<ComboBox
x:Name="cmbSystemProxy"
Width="120"
Margin="8,0"
materialDesign:HintAssist.Hint="{x:Static resx:ResUI.menuSystemproxy}"
FontSize="{DynamicResource StdFontSize}"
Style="{StaticResource MaterialDesignFloatingHintComboBox}">
<ComboBoxItem Content="{x:Static resx:ResUI.menuSystemProxyClear}" />
<ComboBoxItem Content="{x:Static resx:ResUI.menuSystemProxySet}" />
<ComboBoxItem Content="{x:Static resx:ResUI.menuSystemProxyNothing}" />
<ComboBoxItem Content="{x:Static resx:ResUI.menuSystemProxyPac}" />
</ComboBox>
<ComboBox
x:Name="cmbRoutings2"
Width="150"
Margin="8,0"
materialDesign:HintAssist.Hint="{x:Static resx:ResUI.menuRouting}"
DisplayMemberPath="remarks"
FontSize="{DynamicResource StdFontSize}"
Style="{StaticResource MaterialDesignFloatingHintComboBox}" />
</StackPanel>
<StackPanel Margin="8,0" VerticalAlignment="Center">
<TextBlock x:Name="txtRunningServerDisplay" Style="{StaticResource StatusbarItem}" />
<Border Margin="2" />
<TextBlock x:Name="txtRunningInfoDisplay" Style="{StaticResource StatusbarItem}" />
</StackPanel>
</DockPanel>
</materialDesign:ColorZone>
<tb:TaskbarIcon
x:Name="tbNotify"
NoLeftClickDelay="True"
ToolTipText="v2rayN">
<tb:TaskbarIcon.IconSource>
<tb:GeneratedIconSource Foreground="Red" Text="❤️" />
</tb:TaskbarIcon.IconSource>
<tb:TaskbarIcon.ContextMenu>
<ContextMenu Style="{StaticResource DefContextMenu}">
<MenuItem x:Name="menuSystemProxyClear" Height="{StaticResource MenuItemHeight}">
<MenuItem.Header>
<StackPanel Orientation="Horizontal">
<materialDesign:PackIcon
x:Name="menuSystemProxyClear2"
Margin="0,0,8,0"
VerticalAlignment="Center"
Kind="Check" />
<TextBlock Text="{x:Static resx:ResUI.menuSystemProxyClear}" />
</StackPanel>
</MenuItem.Header>
</MenuItem>
<MenuItem x:Name="menuSystemProxySet" Height="{StaticResource MenuItemHeight}">
<MenuItem.Header>
<StackPanel Orientation="Horizontal">
<materialDesign:PackIcon
x:Name="menuSystemProxySet2"
Margin="0,0,8,0"
VerticalAlignment="Center"
Kind="Check" />
<TextBlock Text="{x:Static resx:ResUI.menuSystemProxySet}" />
</StackPanel>
</MenuItem.Header>
</MenuItem>
<MenuItem x:Name="menuSystemProxyNothing" Height="{StaticResource MenuItemHeight}">
<MenuItem.Header>
<StackPanel Orientation="Horizontal">
<materialDesign:PackIcon
x:Name="menuSystemProxyNothing2"
Margin="0,0,8,0"
VerticalAlignment="Center"
Kind="Check" />
<TextBlock Text="{x:Static resx:ResUI.menuSystemProxyNothing}" />
</StackPanel>
</MenuItem.Header>
</MenuItem>
<MenuItem x:Name="menuSystemProxyPac" Height="{StaticResource MenuItemHeight}">
<MenuItem.Header>
<StackPanel Orientation="Horizontal">
<materialDesign:PackIcon
x:Name="menuSystemProxyPac2"
Margin="0,0,8,0"
VerticalAlignment="Center"
Kind="Check" />
<TextBlock Text="{x:Static resx:ResUI.menuSystemProxyPac}" />
</StackPanel>
</MenuItem.Header>
</MenuItem>
<Separator x:Name="sepRoutings" />
<MenuItem x:Name="menuRoutings" Height="Auto">
<MenuItem.Header>
<DockPanel>
<ComboBox
x:Name="cmbRoutings"
MaxWidth="300"
materialDesign:HintAssist.Hint="{x:Static resx:ResUI.menuRouting}"
DisplayMemberPath="remarks"
FontSize="{DynamicResource StdFontSize}"
Style="{StaticResource MaterialDesignFilledComboBox}" />
</DockPanel>
</MenuItem.Header>
</MenuItem>
<MenuItem Height="Auto">
<MenuItem.Header>
<DockPanel>
<ComboBox
x:Name="cmbServers"
MaxWidth="300"
materialDesign:HintAssist.Hint="{x:Static resx:ResUI.menuServers}"
DisplayMemberPath="Text"
FontSize="{DynamicResource StdFontSize}"
Style="{StaticResource MaterialDesignFilledComboBox}" />
</DockPanel>
</MenuItem.Header>
</MenuItem>
<Separator />
<MenuItem
x:Name="menuAddServerViaClipboard2"
Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuAddServerViaClipboard}" />
<MenuItem
x:Name="menuAddServerViaScan2"
Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuAddServerViaScan}" />
<MenuItem
x:Name="menuSubUpdate2"
Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuSubUpdate}" />
<MenuItem
x:Name="menuSubUpdateViaProxy2"
Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuSubUpdateViaProxy}" />
<Separator />
<MenuItem
x:Name="menuExit"
Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuExit}" />
</ContextMenu>
</tb:TaskbarIcon.ContextMenu>
</tb:TaskbarIcon>
</Grid>
</reactiveui:ReactiveUserControl>

View File

@ -0,0 +1,124 @@
using ReactiveUI;
using Splat;
using System.Reactive.Disposables;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Threading;
using v2rayN.Base;
using v2rayN.Handler;
namespace v2rayN.Views
{
public partial class StatusBarView
{
private static Config _config;
public StatusBarView()
{
InitializeComponent();
_config = AppHandler.Instance.Config;
ViewModel = new StatusBarViewModel(UpdateViewHandler);
Locator.CurrentMutable.RegisterLazySingleton(() => ViewModel, typeof(StatusBarViewModel));
menuExit.Click += menuExit_Click;
txtRunningServerDisplay.PreviewMouseDown += txtRunningInfoDisplay_MouseDoubleClick;
txtRunningInfoDisplay.PreviewMouseDown += txtRunningInfoDisplay_MouseDoubleClick;
this.WhenActivated(disposables =>
{
//system proxy
this.OneWayBind(ViewModel, vm => vm.BlSystemProxyClear, v => v.menuSystemProxyClear2.Visibility, conversionHint: BooleanToVisibilityHint.UseHidden, vmToViewConverterOverride: new BooleanToVisibilityTypeConverter()).DisposeWith(disposables);
this.OneWayBind(ViewModel, vm => vm.BlSystemProxySet, v => v.menuSystemProxySet2.Visibility, conversionHint: BooleanToVisibilityHint.UseHidden, vmToViewConverterOverride: new BooleanToVisibilityTypeConverter()).DisposeWith(disposables);
this.OneWayBind(ViewModel, vm => vm.BlSystemProxyNothing, v => v.menuSystemProxyNothing2.Visibility, conversionHint: BooleanToVisibilityHint.UseHidden, vmToViewConverterOverride: new BooleanToVisibilityTypeConverter()).DisposeWith(disposables);
this.OneWayBind(ViewModel, vm => vm.BlSystemProxyPac, v => v.menuSystemProxyPac2.Visibility, conversionHint: BooleanToVisibilityHint.UseHidden, vmToViewConverterOverride: new BooleanToVisibilityTypeConverter()).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.SystemProxyClearCmd, v => v.menuSystemProxyClear).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.SystemProxySetCmd, v => v.menuSystemProxySet).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.SystemProxyPacCmd, v => v.menuSystemProxyPac).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.SystemProxyNothingCmd, v => v.menuSystemProxyNothing).DisposeWith(disposables);
//routings and servers
this.OneWayBind(ViewModel, vm => vm.RoutingItems, v => v.cmbRoutings.ItemsSource).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedRouting, v => v.cmbRoutings.SelectedItem).DisposeWith(disposables);
this.OneWayBind(ViewModel, vm => vm.BlRouting, v => v.menuRoutings.Visibility).DisposeWith(disposables);
this.OneWayBind(ViewModel, vm => vm.BlRouting, v => v.sepRoutings.Visibility).DisposeWith(disposables);
this.OneWayBind(ViewModel, vm => vm.Servers, v => v.cmbServers.ItemsSource).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedServer, v => v.cmbServers.SelectedItem).DisposeWith(disposables);
this.OneWayBind(ViewModel, vm => vm.BlServers, v => v.cmbServers.Visibility).DisposeWith(disposables);
//tray menu
this.BindCommand(ViewModel, vm => vm.AddServerViaClipboardCmd, v => v.menuAddServerViaClipboard2).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.AddServerViaScanCmd, v => v.menuAddServerViaScan2).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.SubUpdateCmd, v => v.menuSubUpdate2).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.SubUpdateViaProxyCmd, v => v.menuSubUpdateViaProxy2).DisposeWith(disposables);
this.OneWayBind(ViewModel, vm => vm.RunningServerToolTipText, v => v.tbNotify.ToolTipText).DisposeWith(disposables);
this.OneWayBind(ViewModel, vm => vm.NotifyLeftClickCmd, v => v.tbNotify.LeftClickCommand).DisposeWith(disposables);
//status bar
this.OneWayBind(ViewModel, vm => vm.InboundDisplay, v => v.txtInboundDisplay.Text).DisposeWith(disposables);
this.OneWayBind(ViewModel, vm => vm.InboundLanDisplay, v => v.txtInboundLanDisplay.Text).DisposeWith(disposables);
this.OneWayBind(ViewModel, vm => vm.RunningServerDisplay, v => v.txtRunningServerDisplay.Text).DisposeWith(disposables);
this.OneWayBind(ViewModel, vm => vm.RunningInfoDisplay, v => v.txtRunningInfoDisplay.Text).DisposeWith(disposables);
this.OneWayBind(ViewModel, vm => vm.SpeedProxyDisplay, v => v.txtSpeedProxyDisplay.Text).DisposeWith(disposables);
this.OneWayBind(ViewModel, vm => vm.SpeedDirectDisplay, v => v.txtSpeedDirectDisplay.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.EnableTun, v => v.togEnableTun.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SystemProxySelected, v => v.cmbSystemProxy.SelectedIndex).DisposeWith(disposables);
this.OneWayBind(ViewModel, vm => vm.RoutingItems, v => v.cmbRoutings2.ItemsSource).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedRouting, v => v.cmbRoutings2.SelectedItem).DisposeWith(disposables);
this.OneWayBind(ViewModel, vm => vm.BlRouting, v => v.cmbRoutings2.Visibility).DisposeWith(disposables);
});
}
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
{
switch (action)
{
case EViewAction.DispatcherServerAvailability:
if (obj is null) return false;
Application.Current?.Dispatcher.Invoke((() =>
{
ViewModel?.TestServerAvailabilityResult((string)obj);
}), DispatcherPriority.Normal);
break;
case EViewAction.DispatcherRefreshServersBiz:
Application.Current?.Dispatcher.Invoke((() =>
{
ViewModel?.RefreshServersBiz();
}), DispatcherPriority.Normal);
break;
case EViewAction.DispatcherRefreshIcon:
Application.Current?.Dispatcher.Invoke((() =>
{
tbNotify.Icon = WindowsHandler.Instance.GetNotifyIcon(_config);
Application.Current.MainWindow.Icon = WindowsHandler.Instance.GetAppIcon(_config);
}), DispatcherPriority.Normal);
break;
case EViewAction.UpdateSysProxy:
if (obj is null) return false;
SysProxyHandler.UpdateSysProxy(_config, (bool)obj);
break;
}
return await Task.FromResult(true);
}
private void menuExit_Click(object sender, RoutedEventArgs e)
{
tbNotify.Dispose();
var service = Locator.Current.GetService<MainWindowViewModel>();
if (service != null) service.MyAppExitAsync(false);
}
private void txtRunningInfoDisplay_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
ViewModel?.TestServerAvailability();
}
}
}