Compare commits

..

13 Commits

Author SHA1 Message Date
2dust
ec627bdb82 up 7.14.10 2025-09-13 09:53:03 +08:00
2dust
4606e78570 Update Directory.Packages.props 2025-09-13 09:46:28 +08:00
2dust
f00e968b8f Bug fix
https://github.com/2dust/v2rayN/issues/7944
2025-09-13 09:41:34 +08:00
DHR60
a87a015c03 Fix some minor UI bugs (#7941) 2025-09-12 20:28:24 +08:00
2dust
c559914ff7 Fix
https://github.com/2dust/v2rayN/issues/7938
2025-09-12 17:01:53 +08:00
2dust
436d95576e Optimization and improvement JsonUtils 2025-09-12 16:45:55 +08:00
DHR60
54e83391d0 Pre-resolve to apply hosts (#7937) 2025-09-12 16:28:31 +08:00
JieXu
3e0578f775 Update CheckUpdateViewModel.cs (#7932)
* Update CheckUpdateViewModel.cs

* Update Utils.cs

* Update Utils.cs

* Update Utils.cs

* Update CheckUpdateViewModel.cs

* Update CheckUpdateViewModel.cs

* Update Utils.cs
2025-09-12 16:24:59 +08:00
2dust
29a5abf4d6 Optimization and improvement 2025-09-10 19:43:11 +08:00
2dust
b54c67d6f1 up 7.14.9 2025-09-09 20:18:55 +08:00
2dust
b49486cc23 Update ProfilesSelectWindow.axaml 2025-09-09 20:00:00 +08:00
JieXu
b95830b3d5 Update package-rhel.sh package-debian.sh MainWindowViewModel.cs (#7910)
* Update package-rhel.sh

* Update package-rhel.sh

* Update package-rhel.sh

* Update package-rhel.sh

* Update MainWindowViewModel.cs

* Update package-rhel.sh

* Update package-debian.sh
2025-09-09 19:51:10 +08:00
2dust
8e0c5cb9aa Bug fix
https://github.com/2dust/v2rayN/issues/7914
2025-09-09 17:55:15 +08:00
25 changed files with 218 additions and 117 deletions

View File

@@ -28,7 +28,7 @@ Package: v2rayN
Version: $Version Version: $Version
Architecture: $Arch2 Architecture: $Arch2
Maintainer: https://github.com/2dust/v2rayN Maintainer: https://github.com/2dust/v2rayN
Depends: desktop-file-utils Depends: desktop-file-utils, xdg-utils
Description: A GUI client for Windows and Linux, support Xray core and sing-box-core and others Description: A GUI client for Windows and Linux, support Xray core and sing-box-core and others
EOF EOF

View File

@@ -1,11 +1,11 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -euo pipefail set -euo pipefail
# ===== Require Red Hat Enterprise Linux/RockyLinux/AlmaLinux/CentOS OR Ubuntu/Debian ==== # == Require Red Hat Enterprise Linux/FedoraLinux/RockyLinux/AlmaLinux/CentOS OR Ubuntu/Debian ==
if [[ -r /etc/os-release ]]; then if [[ -r /etc/os-release ]]; then
. /etc/os-release . /etc/os-release
case "$ID" in case "$ID" in
rhel|rocky|almalinux|centos|ubuntu|debian) rhel|rocky|almalinux|fedora|centos|ubuntu|debian)
echo "[OK] Detected supported system: $NAME $VERSION_ID" echo "[OK] Detected supported system: $NAME $VERSION_ID"
;; ;;
*) *)
@@ -390,25 +390,30 @@ download_mihomo() {
chmod +x "$outroot/bin/mihomo/mihomo" || true chmod +x "$outroot/bin/mihomo/mihomo" || true
} }
# Move geo files to a unified path: outroot/bin/xray/ # Move geo files to a unified path: outroot/bin
unify_geo_layout() { unify_geo_layout() {
local outroot="$1" local outroot="$1"
mkdir -p "$outroot/bin/xray" mkdir -p "$outroot/bin"
local srcs=( \ local names=( \
"$outroot/bin/geosite.dat" \ "geosite.dat" \
"$outroot/bin/geoip.dat" \ "geoip.dat" \
"$outroot/bin/geoip-only-cn-private.dat" \ "geoip-only-cn-private.dat" \
"$outroot/bin/Country.mmdb" \ "Country.mmdb" \
"$outroot/bin/geoip.metadb" \ "geoip.metadb" \
) )
for s in "${srcs[@]}"; do for n in "${names[@]}"; do
if [[ -f "$s" ]]; then # If file exists under bin/xray/, move it up to bin/
mv -f "$s" "$outroot/bin/xray/$(basename "$s")" if [[ -f "$outroot/bin/xray/$n" ]]; then
mv -f "$outroot/bin/xray/$n" "$outroot/bin/$n"
fi
# If file already in bin/, leave it as-is
if [[ -f "$outroot/bin/$n" ]]; then
:
fi fi
done done
} }
# Download geo/rule assets; then unify to bin/xray/ # Download geo/rule assets; then unify to bin/
download_geo_assets() { download_geo_assets() {
local outroot="$1" local outroot="$1"
local bin_dir="$outroot/bin" local bin_dir="$outroot/bin"
@@ -442,7 +447,7 @@ download_geo_assets() {
"https://raw.githubusercontent.com/2dust/sing-box-rules/rule-set-geosite/$f" || true "https://raw.githubusercontent.com/2dust/sing-box-rules/rule-set-geosite/$f" || true
done done
# Unify to bin/xray/ # Unify to bin/
unify_geo_layout "$outroot" unify_geo_layout "$outroot"
} }
@@ -480,7 +485,7 @@ download_v2rayn_bundle() {
rm -rf "$nested_dir" rm -rf "$nested_dir"
fi fi
# Unify to bin/xray/ # Unify to bin/
unify_geo_layout "$outroot" unify_geo_layout "$outroot"
echo "[+] Bundle extracted to $outroot" echo "[+] Bundle extracted to $outroot"
@@ -610,7 +615,7 @@ Source0: __PKGROOT__.tar.gz
# Runtime dependencies (Avalonia / X11 / Fonts / GL) # Runtime dependencies (Avalonia / X11 / Fonts / GL)
Requires: libX11, libXrandr, libXcursor, libXi, libXext, libxcb, libXrender, libXfixes, libXinerama, libxkbcommon Requires: libX11, libXrandr, libXcursor, libXi, libXext, libxcb, libXrender, libXfixes, libXinerama, libxkbcommon
Requires: fontconfig, freetype, cairo, pango, mesa-libEGL, mesa-libGL Requires: fontconfig, freetype, cairo, pango, mesa-libEGL, mesa-libGL, xdg-utils
%description %description
v2rayN Linux for Red Hat Enterprise Linux v2rayN Linux for Red Hat Enterprise Linux
@@ -629,25 +634,13 @@ https://github.com/2dust/v2rayN
install -dm0755 %{buildroot}/opt/v2rayN install -dm0755 %{buildroot}/opt/v2rayN
cp -a * %{buildroot}/opt/v2rayN/ cp -a * %{buildroot}/opt/v2rayN/
# Launcher (prefer native ELF first, then DLL fallback; also create Geo symlinks for the user) # Launcher (prefer native ELF first, then DLL fallback)
install -dm0755 %{buildroot}%{_bindir} install -dm0755 %{buildroot}%{_bindir}
cat > %{buildroot}%{_bindir}/v2rayn << 'EOF' cat > %{buildroot}%{_bindir}/v2rayn << 'EOF'
#!/usr/bin/bash #!/usr/bin/bash
set -euo pipefail set -euo pipefail
DIR="/opt/v2rayN" DIR="/opt/v2rayN"
# --- Symlink GEO files into user's XDG dir (first-run convenience) ---
XDG_DATA_HOME="${XDG_DATA_HOME:-$HOME/.local/share}"
USR_GEO_DIR="$XDG_DATA_HOME/v2rayN/bin"
SYS_XRAY_DIR="$DIR/bin/xray"
mkdir -p "$USR_GEO_DIR"
for f in geosite.dat geoip.dat geoip-only-cn-private.dat Country.mmdb; do
if [[ -f "$SYS_XRAY_DIR/$f" && ! -e "$USR_GEO_DIR/$f" ]]; then
ln -s "$SYS_XRAY_DIR/$f" "$USR_GEO_DIR/$f" || true
fi
done
# --- end GEO ---
# Prefer native apphost # Prefer native apphost
if [[ -x "$DIR/v2rayN" ]]; then exec "$DIR/v2rayN" "$@"; fi if [[ -x "$DIR/v2rayN" ]]; then exec "$DIR/v2rayN" "$@"; fi

View File

@@ -1,7 +1,7 @@
<Project> <Project>
<PropertyGroup> <PropertyGroup>
<Version>7.14.8</Version> <Version>7.14.10</Version>
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>

View File

@@ -5,10 +5,10 @@
<CentralPackageVersionOverrideEnabled>false</CentralPackageVersionOverrideEnabled> <CentralPackageVersionOverrideEnabled>false</CentralPackageVersionOverrideEnabled>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.3.5" /> <PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.3.6" />
<PackageVersion Include="Avalonia.Desktop" Version="11.3.5" /> <PackageVersion Include="Avalonia.Desktop" Version="11.3.6" />
<PackageVersion Include="Avalonia.Diagnostics" Version="11.3.5" /> <PackageVersion Include="Avalonia.Diagnostics" Version="11.3.6" />
<PackageVersion Include="Avalonia.ReactiveUI" Version="11.3.5" /> <PackageVersion Include="Avalonia.ReactiveUI" Version="11.3.6" />
<PackageVersion Include="CliWrap" Version="3.9.0" /> <PackageVersion Include="CliWrap" Version="3.9.0" />
<PackageVersion Include="Downloader" Version="4.0.3" /> <PackageVersion Include="Downloader" Version="4.0.3" />
<PackageVersion Include="H.NotifyIcon.Wpf" Version="2.3.0" /> <PackageVersion Include="H.NotifyIcon.Wpf" Version="2.3.0" />

View File

@@ -9,6 +9,31 @@ public class JsonUtils
{ {
private static readonly string _tag = "JsonUtils"; private static readonly string _tag = "JsonUtils";
private static readonly JsonSerializerOptions _defaultDeserializeOptions = new()
{
PropertyNameCaseInsensitive = true,
ReadCommentHandling = JsonCommentHandling.Skip
};
private static readonly JsonSerializerOptions _defaultSerializeOptions = new()
{
WriteIndented = true,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
};
private static readonly JsonSerializerOptions _nullValueSerializeOptions = new()
{
WriteIndented = true,
DefaultIgnoreCondition = JsonIgnoreCondition.Never,
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
};
private static readonly JsonDocumentOptions _defaultDocumentOptions = new()
{
CommentHandling = JsonCommentHandling.Skip
};
/// <summary> /// <summary>
/// DeepCopy /// DeepCopy
/// </summary> /// </summary>
@@ -34,11 +59,7 @@ public class JsonUtils
{ {
return default; return default;
} }
var options = new JsonSerializerOptions return JsonSerializer.Deserialize<T>(strJson, _defaultDeserializeOptions);
{
PropertyNameCaseInsensitive = true
};
return JsonSerializer.Deserialize<T>(strJson, options);
} }
catch catch
{ {
@@ -59,7 +80,7 @@ public class JsonUtils
{ {
return null; return null;
} }
return JsonNode.Parse(strJson); return JsonNode.Parse(strJson, nodeOptions: null, _defaultDocumentOptions);
} }
catch catch
{ {
@@ -84,12 +105,7 @@ public class JsonUtils
{ {
return result; return result;
} }
var options = new JsonSerializerOptions var options = nullValue ? _nullValueSerializeOptions : _defaultSerializeOptions;
{
WriteIndented = indented,
DefaultIgnoreCondition = nullValue ? JsonIgnoreCondition.Never : JsonIgnoreCondition.WhenWritingNull,
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
};
result = JsonSerializer.Serialize(obj, options); result = JsonSerializer.Serialize(obj, options);
} }
catch (Exception ex) catch (Exception ex)

View File

@@ -331,6 +331,32 @@ public class Utils
.ToList(); .ToList();
} }
public static Dictionary<string, List<string>> ParseHostsToDictionary(string hostsContent)
{
var userHostsMap = hostsContent
.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries)
.Select(line => line.Trim())
// skip full-line comments
.Where(line => !string.IsNullOrWhiteSpace(line) && !line.StartsWith("#"))
// strip inline comments (truncate at '#')
.Select(line =>
{
var index = line.IndexOf('#');
return index >= 0 ? line.Substring(0, index).Trim() : line;
})
// ensure line still contains valid parts
.Where(line => !string.IsNullOrWhiteSpace(line) && line.Contains(' '))
.Select(line => line.Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries))
.Where(parts => parts.Length >= 2)
.GroupBy(parts => parts[0])
.ToDictionary(
group => group.Key,
group => group.SelectMany(parts => parts.Skip(1)).ToList()
);
return userHostsMap;
}
#endregion #endregion
#region #region
@@ -857,6 +883,55 @@ public class Utils
return false; return false;
} }
public static bool IsPackagedInstall()
{
try
{
if (IsWindows() || IsOSX())
{
return false;
}
if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("APPIMAGE")))
{
return true;
}
var exePath = GetExePath();
var baseDir = string.IsNullOrEmpty(exePath) ? StartupPath() : Path.GetDirectoryName(exePath) ?? "";
var p = baseDir.Replace('\\', '/');
if (string.IsNullOrEmpty(p))
{
return false;
}
if (p.Contains("/.mount_", StringComparison.Ordinal))
{
return true;
}
if (p.StartsWith("/opt/v2rayN", StringComparison.OrdinalIgnoreCase))
{
return true;
}
if (p.StartsWith("/usr/lib/v2rayN", StringComparison.OrdinalIgnoreCase))
{
return true;
}
if (p.StartsWith("/usr/share/v2rayN", StringComparison.OrdinalIgnoreCase))
{
return true;
}
}
catch
{
}
return false;
}
private static async Task<string?> GetLinuxUserId() private static async Task<string?> GetLinuxUserId()
{ {
var arg = new List<string>() { "-c", "id -u" }; var arg = new List<string>() { "-c", "id -u" };

View File

@@ -4,7 +4,7 @@ public class ClashFmt : BaseFmt
{ {
public static ProfileItem? ResolveFull(string strData, string? subRemarks) public static ProfileItem? ResolveFull(string strData, string? subRemarks)
{ {
if (Contains(strData, "port", "socks-port", "proxies")) if (Contains(strData, "external-controller", "-port", "proxies"))
{ {
var fileName = WriteAllText(strData, "yaml"); var fileName = WriteAllText(strData, "yaml");

View File

@@ -1,10 +1,13 @@
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
namespace ServiceLib.Models; namespace ServiceLib.Models;
public class CheckUpdateModel public class CheckUpdateModel : ReactiveObject
{ {
public bool? IsSelected { get; set; } public bool? IsSelected { get; set; }
public string? CoreType { get; set; } public string? CoreType { get; set; }
public string? Remarks { get; set; } [Reactive] public string? Remarks { get; set; }
public string? FileName { get; set; } public string? FileName { get; set; }
public bool? IsFinished { get; set; } public bool? IsFinished { get; set; }
} }

View File

@@ -1,7 +1,10 @@
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
namespace ServiceLib.Models; namespace ServiceLib.Models;
[Serializable] [Serializable]
public class ClashProxyModel public class ClashProxyModel : ReactiveObject
{ {
public string? Name { get; set; } public string? Name { get; set; }
@@ -9,9 +12,9 @@ public class ClashProxyModel
public string? Now { get; set; } public string? Now { get; set; }
public int Delay { get; set; } [Reactive] public int Delay { get; set; }
public string? DelayName { get; set; } [Reactive] public string? DelayName { get; set; }
public bool IsActive { get; set; } public bool IsActive { get; set; }
} }

View File

@@ -94,17 +94,7 @@ public partial class CoreConfigSingboxService
if (!simpleDNSItem.Hosts.IsNullOrEmpty()) if (!simpleDNSItem.Hosts.IsNullOrEmpty())
{ {
var userHostsMap = simpleDNSItem.Hosts var userHostsMap = Utils.ParseHostsToDictionary(simpleDNSItem.Hosts);
.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries)
.Select(line => line.Trim())
.Where(line => !string.IsNullOrWhiteSpace(line) && line.Contains(' '))
.Select(line => line.Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries))
.Where(parts => parts.Length >= 2)
.GroupBy(parts => parts[0])
.ToDictionary(
group => group.Key,
group => group.SelectMany(parts => parts.Skip(1)).ToList()
);
foreach (var kvp in userHostsMap) foreach (var kvp in userHostsMap)
{ {

View File

@@ -71,6 +71,31 @@ public partial class CoreConfigSingboxService
}); });
} }
var hostsDomains = new List<string>();
var systemHostsMap = Utils.GetSystemHosts();
foreach (var kvp in systemHostsMap)
{
hostsDomains.Add(kvp.Key);
}
var dnsItem = await AppManager.Instance.GetDNSItem(ECoreType.sing_box);
if (dnsItem == null || dnsItem.Enabled == false)
{
var simpleDNSItem = _config.SimpleDNSItem;
if (!simpleDNSItem.Hosts.IsNullOrEmpty())
{
var userHostsMap = Utils.ParseHostsToDictionary(simpleDNSItem.Hosts);
foreach (var kvp in userHostsMap)
{
hostsDomains.Add(kvp.Key);
}
}
}
singboxConfig.route.rules.Add(new()
{
action = "resolve",
domain = hostsDomains,
});
singboxConfig.route.rules.Add(new() singboxConfig.route.rules.Add(new()
{ {
outbound = Global.DirectTag, outbound = Global.DirectTag,
@@ -343,6 +368,13 @@ public partial class CoreConfigSingboxService
return Global.ProxyTag; return Global.ProxyTag;
} }
var tag = Global.ProxyTag + node.IndexId.ToString();
if (singboxConfig.outbounds.Any(o => o.tag == tag)
|| (singboxConfig.endpoints != null && singboxConfig.endpoints.Any(e => e.tag == tag)))
{
return tag;
}
var server = await GenServer(node); var server = await GenServer(node);
if (server is null) if (server is null)
{ {

View File

@@ -261,17 +261,7 @@ public partial class CoreConfigV2rayService
if (!simpleDNSItem.Hosts.IsNullOrEmpty()) if (!simpleDNSItem.Hosts.IsNullOrEmpty())
{ {
var userHostsMap = simpleDNSItem.Hosts var userHostsMap = Utils.ParseHostsToDictionary(simpleDNSItem.Hosts);
.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries)
.Select(line => line.Trim())
.Where(line => !string.IsNullOrWhiteSpace(line) && line.Contains(' '))
.Select(line => line.Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries))
.Where(parts => parts.Length >= 2)
.GroupBy(parts => parts[0])
.ToDictionary(
group => group.Key,
group => group.SelectMany(parts => parts.Skip(1)).ToList()
);
foreach (var kvp in userHostsMap) foreach (var kvp in userHostsMap)
{ {

View File

@@ -131,10 +131,16 @@ public partial class CoreConfigV2rayService
return Global.ProxyTag; return Global.ProxyTag;
} }
var tag = Global.ProxyTag + node.IndexId.ToString();
if (v2rayConfig.outbounds.Any(p => p.tag == tag))
{
return tag;
}
var txtOutbound = EmbedUtils.GetEmbedText(Global.V2raySampleOutbound); var txtOutbound = EmbedUtils.GetEmbedText(Global.V2raySampleOutbound);
var outbound = JsonUtils.Deserialize<Outbounds4Ray>(txtOutbound); var outbound = JsonUtils.Deserialize<Outbounds4Ray>(txtOutbound);
await GenOutbound(node, outbound); await GenOutbound(node, outbound);
outbound.tag = Global.ProxyTag + node.IndexId.ToString(); outbound.tag = tag;
v2rayConfig.outbounds.Add(outbound); v2rayConfig.outbounds.Add(outbound);
return outbound.tag; return outbound.tag;

View File

@@ -63,6 +63,16 @@ public class CheckUpdateViewModel : MyReactiveObject
private CheckUpdateModel GetCheckUpdateModel(string coreType) private CheckUpdateModel GetCheckUpdateModel(string coreType)
{ {
if (coreType == _v2rayN && Utils.IsPackagedInstall())
{
return new()
{
IsSelected = false,
CoreType = coreType,
Remarks = ResUI.menuCheckUpdate + " (Not Support)",
};
}
return new() return new()
{ {
IsSelected = _config.CheckUpdateItem.SelectedCoreTypes?.Contains(coreType) ?? true, IsSelected = _config.CheckUpdateItem.SelectedCoreTypes?.Contains(coreType) ?? true,
@@ -104,6 +114,11 @@ public class CheckUpdateViewModel : MyReactiveObject
} }
else if (item.CoreType == _v2rayN) else if (item.CoreType == _v2rayN)
{ {
if (Utils.IsPackagedInstall())
{
await UpdateView(_v2rayN, "Not Support");
continue;
}
await CheckUpdateN(EnableCheckPreReleaseUpdate); await CheckUpdateN(EnableCheckPreReleaseUpdate);
} }
else if (item.CoreType == ECoreType.Xray.ToString()) else if (item.CoreType == ECoreType.Xray.ToString())
@@ -334,9 +349,6 @@ public class CheckUpdateViewModel : MyReactiveObject
{ {
return; return;
} }
found.Remarks = model.Remarks;
var itemCopy = JsonUtils.DeepCopy(found);
itemCopy.Remarks = model.Remarks;
CheckUpdateModels.Replace(found, itemCopy);
} }
} }

View File

@@ -391,7 +391,6 @@ public class ClashProxiesViewModel : MyReactiveObject
public async Task ProxiesDelayTestResult(SpeedTestResult result) public async Task ProxiesDelayTestResult(SpeedTestResult result)
{ {
//UpdateHandler(false, $"{item.name}={result}");
var detail = ProxyDetails.FirstOrDefault(it => it.Name == result.IndexId); var detail = ProxyDetails.FirstOrDefault(it => it.Name == result.IndexId);
if (detail == null) if (detail == null)
{ {
@@ -414,7 +413,6 @@ public class ClashProxiesViewModel : MyReactiveObject
detail.Delay = _delayTimeout; detail.Delay = _delayTimeout;
detail.DelayName = string.Empty; detail.DelayName = string.Empty;
} }
ProxyDetails.Replace(detail, JsonUtils.DeepCopy(detail));
} }
#endregion proxy function #endregion proxy function

View File

@@ -235,6 +235,7 @@ public class MainWindowViewModel : MyReactiveObject
{ {
await StatisticsManager.Instance.Init(_config, UpdateStatisticsHandler); await StatisticsManager.Instance.Init(_config, UpdateStatisticsHandler);
} }
await RefreshServers();
BlReloadEnabled = true; BlReloadEnabled = true;
await Reload(); await Reload();
@@ -487,7 +488,7 @@ public class MainWindowViewModel : MyReactiveObject
} }
else if (Utils.IsLinux()) else if (Utils.IsLinux())
{ {
ProcUtils.ProcessStart("nautilus", path); ProcUtils.ProcessStart("xdg-open", path);
} }
else if (Utils.IsOSX()) else if (Utils.IsOSX())
{ {

View File

@@ -257,7 +257,7 @@ public class ProfilesViewModel : MyReactiveObject
SelectedMoveToGroup = new(); SelectedMoveToGroup = new();
await RefreshSubscriptions(); await RefreshSubscriptions();
await RefreshServers(); //await RefreshServers();
} }
#endregion Init #endregion Init
@@ -293,7 +293,6 @@ public class ProfilesViewModel : MyReactiveObject
{ {
item.SpeedVal = result.Speed ?? string.Empty; item.SpeedVal = result.Speed ?? string.Empty;
} }
//_profileItems.Replace(item, JsonUtils.DeepCopy(item));
} }
public async Task UpdateStatistics(ServerSpeedItem update) public async Task UpdateStatistics(ServerSpeedItem update)
@@ -314,17 +313,6 @@ public class ProfilesViewModel : MyReactiveObject
item.TodayUp = Utils.HumanFy(update.TodayUp); item.TodayUp = Utils.HumanFy(update.TodayUp);
item.TotalDown = Utils.HumanFy(update.TotalDown); item.TotalDown = Utils.HumanFy(update.TotalDown);
item.TotalUp = Utils.HumanFy(update.TotalUp); item.TotalUp = Utils.HumanFy(update.TotalUp);
//if (SelectedProfile?.IndexId == item.IndexId)
//{
// var temp = JsonUtils.DeepCopy(item);
// _profileItems.Replace(item, temp);
// SelectedProfile = temp;
//}
//else
//{
// _profileItems.Replace(item, JsonUtils.DeepCopy(item));
//}
} }
} }
catch catch

View File

@@ -400,7 +400,7 @@
Grid.Column="1" Grid.Column="1"
Width="400" Width="400"
Margin="{StaticResource Margin4}" Margin="{StaticResource Margin4}"
Watermark="1000:2000,3000:4000" /> Watermark="1000-2000,3000,4000" />
<TextBlock <TextBlock
Grid.Row="3" Grid.Row="3"
Grid.Column="2" Grid.Column="2"

View File

@@ -38,9 +38,10 @@
<WrapPanel Margin="4" DockPanel.Dock="Top"> <WrapPanel Margin="4" DockPanel.Dock="Top">
<ListBox <ListBox
x:Name="lstGroup" x:Name="lstGroup"
Margin="4,0" Margin="{StaticResource MarginLr4}"
DisplayMemberBinding="{Binding Remarks}" DisplayMemberBinding="{Binding Remarks}"
ItemsSource="{Binding SubItems}"> ItemsSource="{Binding SubItems}"
Theme="{DynamicResource ButtonRadioGroupListBox}">
<ListBox.ItemsPanel> <ListBox.ItemsPanel>
<ItemsPanelTemplate> <ItemsPanelTemplate>
<WrapPanel /> <WrapPanel />
@@ -89,20 +90,11 @@
Binding="{Binding ConfigType}" Binding="{Binding ConfigType}"
Header="{x:Static resx:ResUI.LvServiceType}" Header="{x:Static resx:ResUI.LvServiceType}"
Tag="ConfigType" /> Tag="ConfigType" />
<DataGridTextColumn
<DataGridTemplateColumn SortMemberPath="Remarks" Tag="Remarks"> Width="120"
<DataGridTemplateColumn.Header> Binding="{Binding Remarks}"
<TextBlock Text="{x:Static resx:ResUI.LvRemarks}" /> Header="{x:Static resx:ResUI.LvRemarks}"
</DataGridTemplateColumn.Header> Tag="Remarks" />
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel Margin="8,0" Orientation="Horizontal">
<TextBlock Text="{Binding Remarks}" />
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn <DataGridTextColumn
Width="120" Width="120"
Binding="{Binding Address}" Binding="{Binding Address}"

View File

@@ -112,7 +112,6 @@ public partial class ProfilesView : ReactiveUserControl<ProfilesViewModel>
}); });
RestoreUI(); RestoreUI();
ViewModel?.RefreshServers();
} }
private async void LstProfiles_Sorting(object? sender, DataGridColumnEventArgs e) private async void LstProfiles_Sorting(object? sender, DataGridColumnEventArgs e)

View File

@@ -176,6 +176,7 @@
<DataGrid <DataGrid
x:Name="lstRules" x:Name="lstRules"
AutoGenerateColumns="False" AutoGenerateColumns="False"
Background="Transparent"
BorderThickness="1" BorderThickness="1"
CanUserResizeColumns="True" CanUserResizeColumns="True"
GridLinesVisibility="All" GridLinesVisibility="All"

View File

@@ -92,6 +92,7 @@
<DataGrid <DataGrid
x:Name="lstRoutings" x:Name="lstRoutings"
AutoGenerateColumns="False" AutoGenerateColumns="False"
Background="Transparent"
BorderThickness="1" BorderThickness="1"
CanUserResizeColumns="True" CanUserResizeColumns="True"
GridLinesVisibility="All" GridLinesVisibility="All"

View File

@@ -538,7 +538,7 @@
Width="400" Width="400"
Margin="{StaticResource Margin4}" Margin="{StaticResource Margin4}"
HorizontalAlignment="Left" HorizontalAlignment="Left"
materialDesign:HintAssist.Hint="1000:2000,3000:4000" materialDesign:HintAssist.Hint="1000-2000,3000,4000"
Style="{StaticResource DefTextBox}" /> Style="{StaticResource DefTextBox}" />
<TextBlock <TextBlock
Grid.Row="3" Grid.Row="3"

View File

@@ -106,7 +106,6 @@ public partial class ProfilesView
}); });
RestoreUI(); RestoreUI();
ViewModel?.RefreshServers();
} }
#region Event #region Event

View File

@@ -122,6 +122,8 @@ public partial class RoutingRuleSettingWindow
private void RoutingRuleSettingWindow_PreviewKeyDown(object sender, KeyEventArgs e) private void RoutingRuleSettingWindow_PreviewKeyDown(object sender, KeyEventArgs e)
{ {
if (!lstRules.IsKeyboardFocusWithin)
return;
if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl)) if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl))
{ {
if (e.Key == Key.A) if (e.Key == Key.A)