Compare commits
2
Commits
v1.0.1
...
v2.0.0-rc.2
@@ -11,7 +11,7 @@ permissions:
|
||||
|
||||
jobs:
|
||||
release:
|
||||
name: Publish Windows x64
|
||||
name: Publish Windows, Linux and macOS
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
@@ -23,7 +23,8 @@ jobs:
|
||||
with:
|
||||
dotnet-version: "8.0.x"
|
||||
|
||||
- name: Build Windows executable
|
||||
- name: Read release metadata
|
||||
id: metadata
|
||||
shell: bash
|
||||
env:
|
||||
TAG_NAME: ${{ gitea.ref_name }}
|
||||
@@ -36,20 +37,51 @@ jobs:
|
||||
exit 1
|
||||
fi
|
||||
|
||||
dotnet publish ./kcsj.csproj \
|
||||
--configuration Release \
|
||||
--runtime win-x64 \
|
||||
--self-contained true \
|
||||
--output ./publish \
|
||||
-p:EnableWindowsTargeting=true \
|
||||
-p:PublishSingleFile=true \
|
||||
-p:EnableCompressionInSingleFile=true \
|
||||
-p:IncludeNativeLibrariesForSelfExtract=true \
|
||||
-p:DebugType=None \
|
||||
-p:Version="${version}"
|
||||
prerelease=false
|
||||
if [[ "${version}" == *-* ]]; then
|
||||
prerelease=true
|
||||
fi
|
||||
|
||||
echo "version=${version}" >> "${GITEA_OUTPUT}"
|
||||
echo "prerelease=${prerelease}" >> "${GITEA_OUTPUT}"
|
||||
|
||||
- name: Publish all desktop platforms
|
||||
shell: bash
|
||||
env:
|
||||
TAG_NAME: ${{ gitea.ref_name }}
|
||||
VERSION: ${{ steps.metadata.outputs.version }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
runtimes=(win-x64 linux-x64 osx-x64)
|
||||
mkdir -p ./dist
|
||||
mv ./publish/kcsj.exe "./dist/kcsj-${TAG_NAME}-win-x64.exe"
|
||||
|
||||
for runtime in "${runtimes[@]}"; do
|
||||
publish_dir="./publish/${runtime}"
|
||||
|
||||
dotnet publish ./kcsj.csproj \
|
||||
--configuration Release \
|
||||
--runtime "${runtime}" \
|
||||
--self-contained true \
|
||||
--output "${publish_dir}" \
|
||||
-p:PublishSingleFile=true \
|
||||
-p:EnableCompressionInSingleFile=true \
|
||||
-p:IncludeNativeLibrariesForSelfExtract=true \
|
||||
-p:DebugType=None \
|
||||
-p:Version="${VERSION}"
|
||||
|
||||
if [[ "${runtime}" == "win-x64" ]]; then
|
||||
cp "${publish_dir}/kcsj.exe" "./dist/kcsj-${TAG_NAME}-${runtime}.exe"
|
||||
else
|
||||
tar -czf "./dist/kcsj-${TAG_NAME}-${runtime}.tar.gz" \
|
||||
-C "${publish_dir}" .
|
||||
fi
|
||||
done
|
||||
|
||||
(
|
||||
cd ./dist
|
||||
sha256sum kcsj-* > SHA256SUMS.txt
|
||||
)
|
||||
|
||||
# Gitea's release action is written in Go and is compiled by the runner.
|
||||
- name: Set up Go for the release action
|
||||
@@ -61,6 +93,7 @@ jobs:
|
||||
uses: https://gitea.com/actions/release-action@main
|
||||
with:
|
||||
title: ${{ gitea.ref_name }}
|
||||
pre_release: ${{ steps.metadata.outputs.prerelease }}
|
||||
files: |-
|
||||
dist/*
|
||||
api_key: ${{ secrets.GITEA_TOKEN }}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
<Application xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
x:Class="kcsj.App"
|
||||
RequestedThemeVariant="Default">
|
||||
<Application.Styles>
|
||||
<FluentTheme />
|
||||
</Application.Styles>
|
||||
</Application>
|
||||
@@ -0,0 +1,24 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls.ApplicationLifetimes;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using kcsj.Views;
|
||||
|
||||
namespace kcsj;
|
||||
|
||||
public partial class App : Application
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
|
||||
public override void OnFrameworkInitializationCompleted()
|
||||
{
|
||||
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
||||
{
|
||||
desktop.MainWindow = new MainWindow();
|
||||
}
|
||||
|
||||
base.OnFrameworkInitializationCompleted();
|
||||
}
|
||||
}
|
||||
+14
-15
@@ -1,19 +1,18 @@
|
||||
using kcsj.Forms;
|
||||
using Avalonia;
|
||||
|
||||
namespace kcsj
|
||||
namespace kcsj;
|
||||
|
||||
internal static class Program
|
||||
{
|
||||
internal static class Program
|
||||
[STAThread]
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
/// <summary>
|
||||
/// The main entry point for the application.
|
||||
/// </summary>
|
||||
[STAThread]
|
||||
static void Main()
|
||||
{
|
||||
// To customize application configuration such as set high DPI settings or default font,
|
||||
// see https://aka.ms/applicationconfiguration.
|
||||
ApplicationConfiguration.Initialize();
|
||||
Application.Run(new Forms.MainForm());
|
||||
}
|
||||
BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
|
||||
}
|
||||
}
|
||||
|
||||
public static AppBuilder BuildAvaloniaApp() =>
|
||||
AppBuilder.Configure<App>()
|
||||
.UsePlatformDetect()
|
||||
.WithInterFont()
|
||||
.LogToTrace();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
# 水准网平差程序
|
||||
|
||||
基于 .NET 8 与 Avalonia 12 的跨平台桌面应用,可在 Windows、macOS 和 Linux 上运行。
|
||||
|
||||
## 运行
|
||||
|
||||
```powershell
|
||||
dotnet run --project kcsj.csproj
|
||||
```
|
||||
|
||||
## 发布
|
||||
|
||||
以下命令生成依赖目标计算机 .NET 8 运行时的发布包:
|
||||
|
||||
```powershell
|
||||
dotnet publish kcsj.csproj -c Release -r win-x64 --self-contained false
|
||||
dotnet publish kcsj.csproj -c Release -r linux-x64 --self-contained false
|
||||
dotnet publish kcsj.csproj -c Release -r osx-x64 --self-contained false
|
||||
```
|
||||
|
||||
如需不依赖目标计算机预装 .NET,将 `--self-contained false` 改为 `--self-contained true`。
|
||||
|
||||
## 自动发布
|
||||
|
||||
推送以 `v` 开头的版本标签会触发 Gitea Actions,自动生成 Windows、Linux 和 macOS 的 x64 自包含发布包及 SHA-256 校验文件。
|
||||
|
||||
正式版本使用不带预发布后缀的标签:
|
||||
|
||||
```powershell
|
||||
git tag v1.2.3
|
||||
git push origin v1.2.3
|
||||
```
|
||||
|
||||
包含 `-` 后缀的 SemVer 标签会自动创建 Pre-release:
|
||||
|
||||
```powershell
|
||||
git tag v1.3.0-rc.1
|
||||
git push origin v1.3.0-rc.1
|
||||
```
|
||||
|
||||
## 输入格式
|
||||
|
||||
已知点文件每行包含点名和高程:
|
||||
|
||||
```text
|
||||
BM1 100.0000
|
||||
```
|
||||
|
||||
观测文件每行包含起点、终点、高差和距离:
|
||||
|
||||
```text
|
||||
BM1 P1 1.2345 2.5
|
||||
```
|
||||
|
||||
字段可以使用空格、Tab、英文逗号或中文逗号分隔。空行以及以 `#` 或 `//` 开头的行会被忽略。
|
||||
@@ -71,10 +71,10 @@ namespace kcsj.Services
|
||||
return result;
|
||||
}
|
||||
|
||||
CheckKnownPoints(knownPoints, result);
|
||||
CheckObservations(observations, result);
|
||||
CheckNetwork(knownPoints, observations, result);
|
||||
CheckRedundancy(knownPoints, observations, result);
|
||||
CheckKnownPoints(knownPoints!, result);
|
||||
CheckObservations(observations!, result);
|
||||
CheckNetwork(knownPoints!, observations!, result);
|
||||
CheckRedundancy(knownPoints!, observations!, result);
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -293,4 +293,4 @@ namespace kcsj.Services
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,150 @@
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
using kcsj.Models;
|
||||
|
||||
namespace kcsj.Services;
|
||||
|
||||
public static class InputParser
|
||||
{
|
||||
private static readonly char[] Separators = [' ', '\t', ',', ','];
|
||||
|
||||
public static List<KnownPoint> ReadKnownPointsFile(string filePath) =>
|
||||
ParseKnownPoints(File.ReadLines(filePath, Encoding.UTF8), Path.GetFileName(filePath));
|
||||
|
||||
public static List<Observation> ReadObservationsFile(string filePath) =>
|
||||
ParseObservations(File.ReadLines(filePath, Encoding.UTF8), Path.GetFileName(filePath));
|
||||
|
||||
public static List<KnownPoint> ParseKnownPointsText(string text) =>
|
||||
ParseKnownPoints(SplitText(text), "手动输入");
|
||||
|
||||
public static List<Observation> ParseObservationsText(string text) =>
|
||||
ParseObservations(SplitText(text), "手动输入");
|
||||
|
||||
public static string BuildKnownPointsPreview(IEnumerable<KnownPoint> points)
|
||||
{
|
||||
StringBuilder text = new("点名\t高程(m)\n");
|
||||
foreach (KnownPoint point in points)
|
||||
{
|
||||
text.AppendLine($"{point.Name}\t{point.Elevation:F4}");
|
||||
}
|
||||
|
||||
return text.ToString();
|
||||
}
|
||||
|
||||
public static string BuildObservationsPreview(IEnumerable<Observation> observations)
|
||||
{
|
||||
StringBuilder text = new("起点\t终点\t高差(m)\t距离(km)\n");
|
||||
foreach (Observation observation in observations)
|
||||
{
|
||||
text.AppendLine(
|
||||
$"{observation.FromPoint}\t{observation.ToPoint}\t" +
|
||||
$"{observation.HeightDiff:F6}\t{observation.Distance:F4}");
|
||||
}
|
||||
|
||||
return text.ToString();
|
||||
}
|
||||
|
||||
private static List<KnownPoint> ParseKnownPoints(IEnumerable<string> lines, string sourceName)
|
||||
{
|
||||
List<KnownPoint> result = [];
|
||||
HashSet<string> names = new(StringComparer.OrdinalIgnoreCase);
|
||||
int lineNumber = 0;
|
||||
|
||||
foreach (string rawLine in lines)
|
||||
{
|
||||
lineNumber++;
|
||||
string line = rawLine.Trim();
|
||||
if (ShouldSkip(line))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
string[] parts = SplitLine(line);
|
||||
if (parts.Length != 2)
|
||||
{
|
||||
throw new FormatException($"{sourceName} 第 {lineNumber} 行格式错误,应为:点名 高程");
|
||||
}
|
||||
|
||||
string name = parts[0].Trim();
|
||||
if (!names.Add(name))
|
||||
{
|
||||
throw new FormatException($"{sourceName} 第 {lineNumber} 行点名重复:{name}");
|
||||
}
|
||||
|
||||
if (!TryParseNumber(parts[1], out double elevation))
|
||||
{
|
||||
throw new FormatException($"{sourceName} 第 {lineNumber} 行高程不是有效数字");
|
||||
}
|
||||
|
||||
result.Add(new KnownPoint(name, elevation));
|
||||
}
|
||||
|
||||
if (result.Count == 0)
|
||||
{
|
||||
throw new FormatException($"{sourceName} 中没有有效的已知点数据");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static List<Observation> ParseObservations(IEnumerable<string> lines, string sourceName)
|
||||
{
|
||||
List<Observation> result = [];
|
||||
int lineNumber = 0;
|
||||
|
||||
foreach (string rawLine in lines)
|
||||
{
|
||||
lineNumber++;
|
||||
string line = rawLine.Trim();
|
||||
if (ShouldSkip(line))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
string[] parts = SplitLine(line);
|
||||
if (parts.Length != 4)
|
||||
{
|
||||
throw new FormatException($"{sourceName} 第 {lineNumber} 行格式错误,应为:起点 终点 高差 距离");
|
||||
}
|
||||
|
||||
string from = parts[0].Trim();
|
||||
string to = parts[1].Trim();
|
||||
if (string.Equals(from, to, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
throw new FormatException($"{sourceName} 第 {lineNumber} 行起点和终点不能相同");
|
||||
}
|
||||
|
||||
if (!TryParseNumber(parts[2], out double heightDiff))
|
||||
{
|
||||
throw new FormatException($"{sourceName} 第 {lineNumber} 行高差不是有效数字");
|
||||
}
|
||||
|
||||
if (!TryParseNumber(parts[3], out double distance) || distance <= 0)
|
||||
{
|
||||
throw new FormatException($"{sourceName} 第 {lineNumber} 行距离必须是大于 0 的数字");
|
||||
}
|
||||
|
||||
result.Add(new Observation(from, to, heightDiff, distance));
|
||||
}
|
||||
|
||||
if (result.Count == 0)
|
||||
{
|
||||
throw new FormatException($"{sourceName} 中没有有效的观测数据");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static string[] SplitText(string text) =>
|
||||
text.Replace("\r\n", "\n", StringComparison.Ordinal).Replace('\r', '\n').Split('\n');
|
||||
|
||||
private static string[] SplitLine(string line) =>
|
||||
line.Split(Separators, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
|
||||
|
||||
private static bool ShouldSkip(string line) =>
|
||||
string.IsNullOrWhiteSpace(line) || line.StartsWith('#') || line.StartsWith("//", StringComparison.Ordinal);
|
||||
|
||||
private static bool TryParseNumber(string text, out double value) =>
|
||||
double.TryParse(text, NumberStyles.Float, CultureInfo.InvariantCulture, out value) ||
|
||||
double.TryParse(text, NumberStyles.Float, CultureInfo.CurrentCulture, out value);
|
||||
}
|
||||
@@ -8,7 +8,7 @@ namespace kcsj.Services
|
||||
{
|
||||
public static class LogService
|
||||
{
|
||||
public static event Action<string> OnLog;
|
||||
public static event Action<string>? OnLog;
|
||||
public static void AddLog(string message)
|
||||
{
|
||||
string time = DateTime.Now.ToString("HH:mm:ss");
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace kcsj.Services;
|
||||
|
||||
public static class PlatformLauncher
|
||||
{
|
||||
public static void OpenFolder(string folderPath)
|
||||
{
|
||||
ProcessStartInfo startInfo;
|
||||
|
||||
if (OperatingSystem.IsWindows())
|
||||
{
|
||||
startInfo = new ProcessStartInfo
|
||||
{
|
||||
FileName = folderPath,
|
||||
UseShellExecute = true
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
startInfo = new ProcessStartInfo
|
||||
{
|
||||
FileName = OperatingSystem.IsMacOS() ? "open" : "xdg-open",
|
||||
UseShellExecute = false
|
||||
};
|
||||
startInfo.ArgumentList.Add(folderPath);
|
||||
}
|
||||
|
||||
Process.Start(startInfo);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
<Window xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
x:Class="kcsj.Views.DataInputWindow"
|
||||
Title="数据输入"
|
||||
Width="900"
|
||||
Height="700"
|
||||
MinWidth="720"
|
||||
MinHeight="580"
|
||||
WindowStartupLocation="CenterOwner">
|
||||
<DockPanel Margin="24">
|
||||
<Grid DockPanel.Dock="Bottom" ColumnDefinitions="*,Auto" Margin="0,18,0,0">
|
||||
<TextBlock x:Name="InputStatusText"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}"
|
||||
Text="请选择文件或切换到手动输入。" />
|
||||
<Button Grid.Column="1" MinWidth="92" Content="关闭" Click="Cancel_Click" />
|
||||
</Grid>
|
||||
|
||||
<TabControl x:Name="DataTabs">
|
||||
<TabItem Header="从文件导入">
|
||||
<Grid Margin="18" RowDefinitions="Auto,Auto,Auto,*" RowSpacing="14">
|
||||
<Grid ColumnDefinitions="120,*,Auto" ColumnSpacing="10">
|
||||
<TextBlock VerticalAlignment="Center" Text="已知点文件" />
|
||||
<TextBox x:Name="KnownPathTextBox" Grid.Column="1" IsReadOnly="True" PlaceholderText="请选择 TXT 文件" />
|
||||
<Button Grid.Column="2" Content="浏览…" Click="BrowseKnownFile_Click" />
|
||||
</Grid>
|
||||
|
||||
<Grid Grid.Row="1" ColumnDefinitions="120,*,Auto" ColumnSpacing="10">
|
||||
<TextBlock VerticalAlignment="Center" Text="观测数据文件" />
|
||||
<TextBox x:Name="ObservationPathTextBox" Grid.Column="1" IsReadOnly="True" PlaceholderText="请选择 TXT 文件" />
|
||||
<Button Grid.Column="2" Content="浏览…" Click="BrowseObservationFile_Click" />
|
||||
</Grid>
|
||||
|
||||
<StackPanel Grid.Row="2" Orientation="Horizontal" Spacing="10">
|
||||
<Button MinWidth="110" Content="预览数据" Click="PreviewFiles_Click" />
|
||||
<Button MinWidth="110" Content="导入数据" Click="ImportFiles_Click" />
|
||||
</StackPanel>
|
||||
|
||||
<Grid Grid.Row="3" ColumnDefinitions="*,*" ColumnSpacing="14">
|
||||
<Grid RowDefinitions="Auto,*">
|
||||
<TextBlock Margin="0,0,0,8" FontWeight="SemiBold" Text="已知点预览" />
|
||||
<TextBox x:Name="KnownPreviewTextBox"
|
||||
Grid.Row="1"
|
||||
IsReadOnly="True"
|
||||
AcceptsReturn="True"
|
||||
TextWrapping="NoWrap"
|
||||
ScrollViewer.HorizontalScrollBarVisibility="Auto"
|
||||
ScrollViewer.VerticalScrollBarVisibility="Auto"
|
||||
FontFamily="fonts:Inter#Inter" />
|
||||
</Grid>
|
||||
<Grid Grid.Column="1" RowDefinitions="Auto,*">
|
||||
<TextBlock Margin="0,0,0,8" FontWeight="SemiBold" Text="观测数据预览" />
|
||||
<TextBox x:Name="ObservationPreviewTextBox"
|
||||
Grid.Row="1"
|
||||
IsReadOnly="True"
|
||||
AcceptsReturn="True"
|
||||
TextWrapping="NoWrap"
|
||||
ScrollViewer.HorizontalScrollBarVisibility="Auto"
|
||||
ScrollViewer.VerticalScrollBarVisibility="Auto"
|
||||
FontFamily="fonts:Inter#Inter" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</TabItem>
|
||||
|
||||
<TabItem Header="手动输入">
|
||||
<Grid Margin="18" RowDefinitions="Auto,*,Auto" RowSpacing="14">
|
||||
<Border Padding="12"
|
||||
Background="{DynamicResource SystemControlBackgroundBaseLowBrush}"
|
||||
CornerRadius="6">
|
||||
<TextBlock TextWrapping="Wrap"
|
||||
Text="每行一条数据,可使用空格、Tab 或逗号分隔;空行以及以 # 或 // 开头的行会被忽略。" />
|
||||
</Border>
|
||||
|
||||
<Grid Grid.Row="1" ColumnDefinitions="*,*" ColumnSpacing="14">
|
||||
<Grid RowDefinitions="Auto,*">
|
||||
<TextBlock Margin="0,0,0,8" FontWeight="SemiBold" Text="已知点(点名 高程)" />
|
||||
<TextBox x:Name="ManualKnownTextBox"
|
||||
Grid.Row="1"
|
||||
AcceptsReturn="True"
|
||||
TextWrapping="NoWrap"
|
||||
ScrollViewer.HorizontalScrollBarVisibility="Auto"
|
||||
ScrollViewer.VerticalScrollBarVisibility="Auto"
|
||||
PlaceholderText="例如: BM1 100.0000"
|
||||
FontFamily="fonts:Inter#Inter" />
|
||||
</Grid>
|
||||
<Grid Grid.Column="1" RowDefinitions="Auto,*">
|
||||
<TextBlock Margin="0,0,0,8" FontWeight="SemiBold" Text="观测数据(起点 终点 高差 距离)" />
|
||||
<TextBox x:Name="ManualObservationTextBox"
|
||||
Grid.Row="1"
|
||||
AcceptsReturn="True"
|
||||
TextWrapping="NoWrap"
|
||||
ScrollViewer.HorizontalScrollBarVisibility="Auto"
|
||||
ScrollViewer.VerticalScrollBarVisibility="Auto"
|
||||
PlaceholderText="例如: BM1 P1 1.2345 2.5"
|
||||
FontFamily="fonts:Inter#Inter" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<StackPanel Grid.Row="2" Orientation="Horizontal" Spacing="10">
|
||||
<Button MinWidth="110" Content="检查并预览" Click="PreviewManual_Click" />
|
||||
<Button MinWidth="110" Content="保存数据" Click="SaveManual_Click" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</TabItem>
|
||||
</TabControl>
|
||||
</DockPanel>
|
||||
</Window>
|
||||
@@ -0,0 +1,182 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Avalonia.Platform.Storage;
|
||||
using kcsj.Models;
|
||||
using kcsj.Services;
|
||||
|
||||
namespace kcsj.Views;
|
||||
|
||||
public partial class DataInputWindow : Window
|
||||
{
|
||||
private static readonly FilePickerFileType TextFileType = new("文本文件")
|
||||
{
|
||||
Patterns = ["*.txt"],
|
||||
MimeTypes = ["text/plain"]
|
||||
};
|
||||
|
||||
private TabControl TabsControl => this.FindControl<TabControl>("DataTabs")!;
|
||||
private TextBlock StatusControl => this.FindControl<TextBlock>("InputStatusText")!;
|
||||
private TextBox KnownPathControl => this.FindControl<TextBox>("KnownPathTextBox")!;
|
||||
private TextBox ObservationPathControl => this.FindControl<TextBox>("ObservationPathTextBox")!;
|
||||
private TextBox KnownPreviewControl => this.FindControl<TextBox>("KnownPreviewTextBox")!;
|
||||
private TextBox ObservationPreviewControl => this.FindControl<TextBox>("ObservationPreviewTextBox")!;
|
||||
private TextBox ManualKnownControl => this.FindControl<TextBox>("ManualKnownTextBox")!;
|
||||
private TextBox ManualObservationControl => this.FindControl<TextBox>("ManualObservationTextBox")!;
|
||||
|
||||
private List<KnownPoint> _fileKnownPoints = [];
|
||||
private List<Observation> _fileObservations = [];
|
||||
|
||||
public DataInputWindow()
|
||||
: this(0)
|
||||
{
|
||||
}
|
||||
|
||||
public DataInputWindow(int selectedTab)
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
TabsControl.SelectedIndex = Math.Clamp(selectedTab, 0, 1);
|
||||
}
|
||||
|
||||
private async void BrowseKnownFile_Click(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
string? path = await PickTextFileAsync("选择已知点高程文件");
|
||||
if (path is not null)
|
||||
{
|
||||
KnownPathControl.Text = path;
|
||||
_fileKnownPoints = [];
|
||||
}
|
||||
}
|
||||
|
||||
private async void BrowseObservationFile_Click(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
string? path = await PickTextFileAsync("选择观测数据文件");
|
||||
if (path is not null)
|
||||
{
|
||||
ObservationPathControl.Text = path;
|
||||
_fileObservations = [];
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<string?> PickTextFileAsync(string title)
|
||||
{
|
||||
IReadOnlyList<IStorageFile> files = await StorageProvider.OpenFilePickerAsync(
|
||||
new FilePickerOpenOptions
|
||||
{
|
||||
Title = title,
|
||||
AllowMultiple = false,
|
||||
FileTypeFilter = [TextFileType, FilePickerFileTypes.All]
|
||||
});
|
||||
|
||||
return files.FirstOrDefault()?.Path.LocalPath;
|
||||
}
|
||||
|
||||
private async void PreviewFiles_Click(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
LoadFileData();
|
||||
ShowFilePreview();
|
||||
StatusControl.Text = $"预览完成:{_fileKnownPoints.Count} 个已知点,{_fileObservations.Count} 条观测。";
|
||||
LogService.AddLog("文件数据预览成功。");
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
ClearFilePreview();
|
||||
LogService.AddLog("数据预览失败:" + exception.Message);
|
||||
await DialogWindow.ShowMessageAsync(this, "数据预览失败", exception.Message);
|
||||
}
|
||||
}
|
||||
|
||||
private async void ImportFiles_Click(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
LoadFileData();
|
||||
DataStore.SetData(_fileKnownPoints, _fileObservations, "文件导入");
|
||||
LogService.AddLog("文件数据导入成功。");
|
||||
Close(true);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
LogService.AddLog("导入失败:" + exception.Message);
|
||||
await DialogWindow.ShowMessageAsync(this, "导入失败", exception.Message);
|
||||
}
|
||||
}
|
||||
|
||||
private async void PreviewManual_Click(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
(List<KnownPoint> knownPoints, List<Observation> observations) = ParseManualData();
|
||||
StatusControl.Text = $"手动输入检查通过:{knownPoints.Count} 个已知点,{observations.Count} 条观测。";
|
||||
await DialogWindow.ShowMessageAsync(
|
||||
this,
|
||||
"手动输入预览",
|
||||
InputParser.BuildKnownPointsPreview(knownPoints) + "\n" +
|
||||
InputParser.BuildObservationsPreview(observations));
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
LogService.AddLog("手动输入检查失败:" + exception.Message);
|
||||
await DialogWindow.ShowMessageAsync(this, "输入错误", exception.Message);
|
||||
}
|
||||
}
|
||||
|
||||
private async void SaveManual_Click(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
(List<KnownPoint> knownPoints, List<Observation> observations) = ParseManualData();
|
||||
DataStore.SetData(knownPoints, observations, "手动输入");
|
||||
LogService.AddLog("手动输入数据已保存。");
|
||||
Close(true);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
LogService.AddLog("手动输入数据时发生错误:" + exception.Message);
|
||||
await DialogWindow.ShowMessageAsync(this, "输入错误", exception.Message);
|
||||
}
|
||||
}
|
||||
|
||||
private void LoadFileData()
|
||||
{
|
||||
string knownPath = KnownPathControl.Text?.Trim() ?? string.Empty;
|
||||
string observationPath = ObservationPathControl.Text?.Trim() ?? string.Empty;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(knownPath))
|
||||
{
|
||||
throw new InvalidOperationException("请先选择已知点高程文件。");
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(observationPath))
|
||||
{
|
||||
throw new InvalidOperationException("请先选择观测数据文件。");
|
||||
}
|
||||
|
||||
_fileKnownPoints = InputParser.ReadKnownPointsFile(knownPath);
|
||||
_fileObservations = InputParser.ReadObservationsFile(observationPath);
|
||||
}
|
||||
|
||||
private void ShowFilePreview()
|
||||
{
|
||||
KnownPreviewControl.Text = InputParser.BuildKnownPointsPreview(_fileKnownPoints);
|
||||
ObservationPreviewControl.Text = InputParser.BuildObservationsPreview(_fileObservations);
|
||||
}
|
||||
|
||||
private void ClearFilePreview()
|
||||
{
|
||||
_fileKnownPoints = [];
|
||||
_fileObservations = [];
|
||||
KnownPreviewControl.Text = string.Empty;
|
||||
ObservationPreviewControl.Text = string.Empty;
|
||||
}
|
||||
|
||||
private (List<KnownPoint> KnownPoints, List<Observation> Observations) ParseManualData() =>
|
||||
(
|
||||
InputParser.ParseKnownPointsText(ManualKnownControl.Text ?? string.Empty),
|
||||
InputParser.ParseObservationsText(ManualObservationControl.Text ?? string.Empty)
|
||||
);
|
||||
|
||||
private void Cancel_Click(object? sender, RoutedEventArgs e) => Close(false);
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Layout;
|
||||
using Avalonia.Media;
|
||||
|
||||
namespace kcsj.Views;
|
||||
|
||||
public sealed class DialogWindow : Window
|
||||
{
|
||||
private DialogWindow(string title, string message, bool showCancel)
|
||||
{
|
||||
Title = title;
|
||||
CanResize = false;
|
||||
SizeToContent = SizeToContent.WidthAndHeight;
|
||||
WindowStartupLocation = WindowStartupLocation.CenterOwner;
|
||||
MinWidth = 360;
|
||||
MaxWidth = 560;
|
||||
|
||||
TextBlock messageText = new()
|
||||
{
|
||||
Text = message,
|
||||
TextWrapping = TextWrapping.Wrap,
|
||||
MaxWidth = 500,
|
||||
FontSize = 15
|
||||
};
|
||||
|
||||
Button confirmButton = new()
|
||||
{
|
||||
Content = "确定",
|
||||
MinWidth = 88,
|
||||
HorizontalContentAlignment = HorizontalAlignment.Center
|
||||
};
|
||||
confirmButton.Click += (_, _) => Close(true);
|
||||
|
||||
StackPanel buttons = new()
|
||||
{
|
||||
Orientation = Orientation.Horizontal,
|
||||
HorizontalAlignment = HorizontalAlignment.Right,
|
||||
Spacing = 10
|
||||
};
|
||||
|
||||
if (showCancel)
|
||||
{
|
||||
Button cancelButton = new()
|
||||
{
|
||||
Content = "取消",
|
||||
MinWidth = 88,
|
||||
HorizontalContentAlignment = HorizontalAlignment.Center
|
||||
};
|
||||
cancelButton.Click += (_, _) => Close(false);
|
||||
buttons.Children.Add(cancelButton);
|
||||
}
|
||||
|
||||
buttons.Children.Add(confirmButton);
|
||||
|
||||
StackPanel content = new() { Spacing = 24 };
|
||||
content.Children.Add(messageText);
|
||||
content.Children.Add(buttons);
|
||||
|
||||
Content = new Border
|
||||
{
|
||||
Padding = new Thickness(24),
|
||||
Background = Brushes.Transparent,
|
||||
Child = content
|
||||
};
|
||||
}
|
||||
|
||||
public static Task ShowMessageAsync(Window owner, string title, string message) =>
|
||||
new DialogWindow(title, message, false).ShowDialog<bool>(owner);
|
||||
|
||||
public static Task<bool> ConfirmAsync(Window owner, string title, string message) =>
|
||||
new DialogWindow(title, message, true).ShowDialog<bool>(owner);
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
<Window xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
x:Class="kcsj.Views.MainWindow"
|
||||
Title="水准网平差程序"
|
||||
Width="980"
|
||||
Height="680"
|
||||
MinWidth="780"
|
||||
MinHeight="560"
|
||||
WindowStartupLocation="CenterScreen">
|
||||
<DockPanel>
|
||||
<Menu DockPanel.Dock="Top">
|
||||
<MenuItem Header="数据管理">
|
||||
<MenuItem Header="从文件导入" Click="OpenFileInput_Click" />
|
||||
<MenuItem Header="手动输入" Click="OpenManualInput_Click" />
|
||||
<Separator />
|
||||
<MenuItem Header="删除当前数据" Click="ClearData_Click" />
|
||||
<MenuItem Header="数据检查" Click="CheckData_Click" />
|
||||
</MenuItem>
|
||||
<MenuItem Header="开始平差" Click="RunAdjustment_Click" />
|
||||
<MenuItem Header="查看结果" Click="ShowResult_Click" />
|
||||
<MenuItem Header="输出报告" Click="ExportReport_Click" />
|
||||
<MenuItem Header="关于" Click="About_Click" />
|
||||
</Menu>
|
||||
|
||||
<Border DockPanel.Dock="Bottom"
|
||||
Padding="12,7"
|
||||
Background="{DynamicResource SystemControlBackgroundBaseLowBrush}">
|
||||
<TextBlock x:Name="DataStatusText" Text="当前未加载数据" />
|
||||
</Border>
|
||||
|
||||
<Grid Margin="28" ColumnDefinitions="220,24,*" RowDefinitions="Auto,*">
|
||||
<StackPanel Grid.ColumnSpan="3" Margin="0,0,0,24">
|
||||
<TextBlock Text="水准网平差程序" FontSize="28" FontWeight="SemiBold" />
|
||||
<TextBlock Margin="0,6,0,0"
|
||||
Text="基于 Avalonia 的跨平台水准测量间接平差工具"
|
||||
Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Grid.Row="1" Grid.Column="0" Spacing="12">
|
||||
<Button Height="52" Content="数据输入" Click="OpenDataInput_Click" />
|
||||
<Button Height="52" Content="数据检查" Click="CheckData_Click" />
|
||||
<Button Height="52" Content="开始平差" Click="RunAdjustment_Click" />
|
||||
<Button Height="52" Content="查看结果" Click="ShowResult_Click" />
|
||||
<Button Height="52" Content="输出报告" Click="ExportReport_Click" />
|
||||
</StackPanel>
|
||||
|
||||
<Border Grid.Row="1"
|
||||
Grid.Column="2"
|
||||
Padding="20"
|
||||
BorderThickness="1"
|
||||
BorderBrush="{DynamicResource SystemControlForegroundBaseLowBrush}"
|
||||
CornerRadius="8">
|
||||
<Grid RowDefinitions="Auto,*">
|
||||
<Grid ColumnDefinitions="*,Auto" Margin="0,0,0,12">
|
||||
<TextBlock Text="运行日志" FontSize="18" FontWeight="SemiBold" VerticalAlignment="Center" />
|
||||
<Button Grid.Column="1" Content="清除日志" Click="ClearLog_Click" />
|
||||
</Grid>
|
||||
<TextBox x:Name="LogTextBox"
|
||||
Grid.Row="1"
|
||||
IsReadOnly="True"
|
||||
AcceptsReturn="True"
|
||||
TextWrapping="Wrap"
|
||||
ScrollViewer.VerticalScrollBarVisibility="Auto"
|
||||
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
|
||||
FontFamily="fonts:Inter#Inter" />
|
||||
</Grid>
|
||||
</Border>
|
||||
</Grid>
|
||||
</DockPanel>
|
||||
</Window>
|
||||
@@ -0,0 +1,221 @@
|
||||
using System.Text;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Avalonia.Platform.Storage;
|
||||
using Avalonia.Threading;
|
||||
using kcsj.Models;
|
||||
using kcsj.Services;
|
||||
|
||||
namespace kcsj.Views;
|
||||
|
||||
public partial class MainWindow : Window
|
||||
{
|
||||
private TextBox LogControl => this.FindControl<TextBox>("LogTextBox")!;
|
||||
private TextBlock DataStatusControl => this.FindControl<TextBlock>("DataStatusText")!;
|
||||
private LeastSquaresResult? _lastResult;
|
||||
|
||||
public MainWindow()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
LogService.OnLog += ShowLog;
|
||||
Closed += (_, _) => LogService.OnLog -= ShowLog;
|
||||
LogService.AddLog("程序启动成功。当前界面框架:Avalonia。");
|
||||
UpdateDataStatus();
|
||||
}
|
||||
|
||||
private async void OpenDataInput_Click(object? sender, RoutedEventArgs e) =>
|
||||
await OpenDataInputAsync(0);
|
||||
|
||||
private async void OpenFileInput_Click(object? sender, RoutedEventArgs e) =>
|
||||
await OpenDataInputAsync(0);
|
||||
|
||||
private async void OpenManualInput_Click(object? sender, RoutedEventArgs e) =>
|
||||
await OpenDataInputAsync(1);
|
||||
|
||||
private async Task OpenDataInputAsync(int selectedTab)
|
||||
{
|
||||
DataInputWindow window = new(selectedTab);
|
||||
bool saved = await window.ShowDialog<bool>(this);
|
||||
if (saved)
|
||||
{
|
||||
_lastResult = null;
|
||||
UpdateDataStatus();
|
||||
}
|
||||
}
|
||||
|
||||
private async void CheckData_Click(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
DataCheckResult result = DataCheck.Check(DataStore.KnownPoints, DataStore.Observations);
|
||||
LogService.AddLog(result.ToLogText());
|
||||
|
||||
string message = result.IsValid
|
||||
? "数据检查通过,可以进行平差计算。"
|
||||
: "数据检查未通过,请查看日志信息。";
|
||||
await DialogWindow.ShowMessageAsync(this, "数据检查", message);
|
||||
}
|
||||
|
||||
private async void RunAdjustment_Click(object? sender, RoutedEventArgs e) =>
|
||||
await RunAdjustmentAsync();
|
||||
|
||||
private async Task<bool> RunAdjustmentAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!DataStore.HasData)
|
||||
{
|
||||
await DialogWindow.ShowMessageAsync(this, "开始平差", "请先导入或输入已知点和观测数据。");
|
||||
return false;
|
||||
}
|
||||
|
||||
_lastResult = LeastSquaresAdjustmentService.Adjust(
|
||||
DataStore.KnownPoints,
|
||||
DataStore.Observations);
|
||||
|
||||
foreach ((string pointName, double elevation) in _lastResult.UnknownElevations)
|
||||
{
|
||||
double error = _lastResult.UnknownElevationErrors[pointName];
|
||||
LogService.AddLog(
|
||||
$"{pointName} 平差高程 = {elevation:F4} m,中误差 = ±{FormatValue(error, 4)} m");
|
||||
}
|
||||
|
||||
foreach (ObservationAdjustmentResult item in _lastResult.ObservationResults)
|
||||
{
|
||||
LogService.AddLog(
|
||||
$"第{item.Index}段 {item.FromPoint}->{item.ToPoint}:" +
|
||||
$"v = {item.Residual:F6},平差后高差 = {item.AdjustedHeightDiff:F6}," +
|
||||
$"中误差 = ±{FormatValue(item.AdjustedHeightDiffError, 6)} m");
|
||||
}
|
||||
|
||||
await DialogWindow.ShowMessageAsync(this, "开始平差", "间接平差计算完成。");
|
||||
return true;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
LogService.AddLog("平差失败:" + exception.Message);
|
||||
await DialogWindow.ShowMessageAsync(this, "平差失败", exception.Message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private async void ShowResult_Click(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
LeastSquaresResult? result = await GetResultOrRunAdjustmentAsync();
|
||||
if (result is not null)
|
||||
{
|
||||
await new ResultWindow(result).ShowDialog(this);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<LeastSquaresResult?> GetResultOrRunAdjustmentAsync()
|
||||
{
|
||||
if (_lastResult is not null)
|
||||
{
|
||||
return _lastResult;
|
||||
}
|
||||
|
||||
bool shouldRun = await DialogWindow.ConfirmAsync(
|
||||
this,
|
||||
"平差结果",
|
||||
"当前还没有平差结果,是否立即进行平差计算?");
|
||||
|
||||
return shouldRun && await RunAdjustmentAsync() ? _lastResult : null;
|
||||
}
|
||||
|
||||
private async void ExportReport_Click(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
LeastSquaresResult? result = await GetResultOrRunAdjustmentAsync();
|
||||
if (result is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
IReadOnlyList<IStorageFolder> folders = await StorageProvider.OpenFolderPickerAsync(
|
||||
new FolderPickerOpenOptions
|
||||
{
|
||||
Title = "选择成果文件和报告文件的输出目录",
|
||||
AllowMultiple = false
|
||||
});
|
||||
|
||||
string? outputFolder = folders.FirstOrDefault()?.Path.LocalPath;
|
||||
if (string.IsNullOrWhiteSpace(outputFolder))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
string heightPath = Path.Combine(outputFolder, "height.txt");
|
||||
string surveyPath = Path.Combine(outputFolder, "survey.txt");
|
||||
string reportPath = Path.Combine(outputFolder, "report.txt");
|
||||
|
||||
await File.WriteAllTextAsync(heightPath, Report.BuildHeightText(result), Encoding.UTF8);
|
||||
await File.WriteAllTextAsync(surveyPath, Report.BuildSurveyText(result), Encoding.UTF8);
|
||||
await File.WriteAllTextAsync(
|
||||
reportPath,
|
||||
Report.BuildReportText(DataStore.KnownPoints, DataStore.Observations, result),
|
||||
Encoding.UTF8);
|
||||
|
||||
LogService.AddLog($"高程成果文件已输出:{heightPath}");
|
||||
LogService.AddLog($"高差成果文件已输出:{surveyPath}");
|
||||
LogService.AddLog($"平差报告已输出:{reportPath}");
|
||||
|
||||
bool shouldOpen = await DialogWindow.ConfirmAsync(
|
||||
this,
|
||||
"输出报告",
|
||||
"输出完成,是否打开输出目录?");
|
||||
if (shouldOpen)
|
||||
{
|
||||
PlatformLauncher.OpenFolder(outputFolder);
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
LogService.AddLog("输出报告失败:" + exception.Message);
|
||||
await DialogWindow.ShowMessageAsync(this, "输出报告失败", exception.Message);
|
||||
}
|
||||
}
|
||||
|
||||
private async void ClearData_Click(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
bool confirmed = await DialogWindow.ConfirmAsync(this, "二次确认", "确定要清空当前数据吗?");
|
||||
if (!confirmed)
|
||||
{
|
||||
LogService.AddLog("用户取消清空数据。");
|
||||
return;
|
||||
}
|
||||
|
||||
DataStore.Clear();
|
||||
_lastResult = null;
|
||||
UpdateDataStatus();
|
||||
}
|
||||
|
||||
private async void About_Click(object? sender, RoutedEventArgs e) =>
|
||||
await DialogWindow.ShowMessageAsync(
|
||||
this,
|
||||
"关于",
|
||||
"水准测量间接平差程序\n使用 Avalonia 构建,支持 Windows、macOS 和 Linux。\n运行时:.NET 8");
|
||||
|
||||
private void ClearLog_Click(object? sender, RoutedEventArgs e) => LogControl.Text = string.Empty;
|
||||
|
||||
private void ShowLog(string message)
|
||||
{
|
||||
Dispatcher.UIThread.Post(() =>
|
||||
{
|
||||
LogControl.Text = string.IsNullOrEmpty(LogControl.Text)
|
||||
? message
|
||||
: LogControl.Text + Environment.NewLine + message;
|
||||
LogControl.CaretIndex = LogControl.Text?.Length ?? 0;
|
||||
});
|
||||
}
|
||||
|
||||
private void UpdateDataStatus()
|
||||
{
|
||||
DataStatusControl.Text = DataStore.HasData
|
||||
? $"数据来源:{DataStore.DataSource} 已知点:{DataStore.KnownPoints.Count} 观测:{DataStore.Observations.Count}"
|
||||
: "当前未加载数据";
|
||||
}
|
||||
|
||||
private static string FormatValue(double value, int digits) =>
|
||||
double.IsFinite(value) ? value.ToString($"F{digits}") : "无法计算";
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<Window xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
x:Class="kcsj.Views.ResultWindow"
|
||||
Title="平差结果"
|
||||
Width="860"
|
||||
Height="600"
|
||||
MinWidth="680"
|
||||
MinHeight="440"
|
||||
WindowStartupLocation="CenterOwner">
|
||||
<Grid Margin="20" RowDefinitions="*,Auto" RowSpacing="14">
|
||||
<TextBox x:Name="ResultTextBox"
|
||||
IsReadOnly="True"
|
||||
AcceptsReturn="True"
|
||||
TextWrapping="NoWrap"
|
||||
ScrollViewer.HorizontalScrollBarVisibility="Auto"
|
||||
ScrollViewer.VerticalScrollBarVisibility="Auto"
|
||||
FontFamily="fonts:Inter#Inter" />
|
||||
<Button Grid.Row="1"
|
||||
HorizontalAlignment="Right"
|
||||
MinWidth="92"
|
||||
Content="关闭"
|
||||
Click="Close_Click" />
|
||||
</Grid>
|
||||
</Window>
|
||||
@@ -0,0 +1,23 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using kcsj.Models;
|
||||
using kcsj.Services;
|
||||
|
||||
namespace kcsj.Views;
|
||||
|
||||
public partial class ResultWindow : Window
|
||||
{
|
||||
public ResultWindow()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
|
||||
public ResultWindow(LeastSquaresResult result)
|
||||
: this()
|
||||
{
|
||||
this.FindControl<TextBox>("ResultTextBox")!.Text = Report.BuildResultText(result);
|
||||
}
|
||||
|
||||
private void Close_Click(object? sender, RoutedEventArgs e) => Close();
|
||||
}
|
||||
+15
-5
@@ -1,11 +1,21 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>net8.0-windows</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<BuiltInComInteropSupport>true</BuiltInComInteropSupport>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
<ItemGroup>
|
||||
<Compile Remove="Forms\**\*.cs" />
|
||||
<EmbeddedResource Remove="Forms\**\*.resx" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Avalonia" Version="12.1.0" />
|
||||
<PackageReference Include="Avalonia.Desktop" Version="12.1.0" />
|
||||
<PackageReference Include="Avalonia.Fonts.Inter" Version="12.1.0" />
|
||||
<PackageReference Include="Avalonia.Themes.Fluent" Version="12.1.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
Reference in New Issue
Block a user