添加项目文件。

This commit is contained in:
qianhao.xu 2025-04-03 12:53:43 +08:00
parent d9c9687a8b
commit 925dc6155d
25 changed files with 872 additions and 0 deletions

View File

@ -0,0 +1,67 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.13.35919.96 d17.13
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "linesider_screen_bankend.Modules.ModuleName.Tests", "linesider_screen_bankend\Tests\linesider_screen_bankend.Modules.ModuleName.Tests\linesider_screen_bankend.Modules.ModuleName.Tests.csproj", "{35E456EA-B00D-836A-01DD-847FD5160483}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Modules", "Modules", "{C9AD4566-5B66-40D2-B6C0-7CE2C1434FFA}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "linesider_screen_bankend.Modules.ModuleName", "linesider_screen_bankend\Modules\linesider_screen_bankend.Modules.ModuleName\linesider_screen_bankend.Modules.ModuleName.csproj", "{D6FC7446-D621-6607-8B56-DBFF09E12B6D}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Services", "Services", "{FD560FA8-19E9-42DF-AFAC-D8D439F16916}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "linesider_screen_bankend.Services", "linesider_screen_bankend\Services\linesider_screen_bankend.Services\linesider_screen_bankend.Services.csproj", "{D9EC6920-FC64-BE4A-ECC0-BFD93DA3FEC1}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "linesider_screen_bankend.Services.Interfaces", "linesider_screen_bankend\Services\linesider_screen_bankend.Services.Interfaces\linesider_screen_bankend.Services.Interfaces.csproj", "{2D870641-D0E0-20A4-F37D-15DA8673ABCB}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "linesider_screen_bankend", "linesider_screen_bankend\linesider_screen_bankend\linesider_screen_bankend.csproj", "{2CA9B9D5-2DC8-260D-0931-3AA8F065172C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "linesider_screen_bankend.Core", "linesider_screen_bankend\linesider_screen_bankend.Core\linesider_screen_bankend.Core.csproj", "{3153BFCD-9C29-CFC5-A014-A425BB1CF856}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{35E456EA-B00D-836A-01DD-847FD5160483}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{35E456EA-B00D-836A-01DD-847FD5160483}.Debug|Any CPU.Build.0 = Debug|Any CPU
{35E456EA-B00D-836A-01DD-847FD5160483}.Release|Any CPU.ActiveCfg = Release|Any CPU
{35E456EA-B00D-836A-01DD-847FD5160483}.Release|Any CPU.Build.0 = Release|Any CPU
{D6FC7446-D621-6607-8B56-DBFF09E12B6D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D6FC7446-D621-6607-8B56-DBFF09E12B6D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D6FC7446-D621-6607-8B56-DBFF09E12B6D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D6FC7446-D621-6607-8B56-DBFF09E12B6D}.Release|Any CPU.Build.0 = Release|Any CPU
{D9EC6920-FC64-BE4A-ECC0-BFD93DA3FEC1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D9EC6920-FC64-BE4A-ECC0-BFD93DA3FEC1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D9EC6920-FC64-BE4A-ECC0-BFD93DA3FEC1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D9EC6920-FC64-BE4A-ECC0-BFD93DA3FEC1}.Release|Any CPU.Build.0 = Release|Any CPU
{2D870641-D0E0-20A4-F37D-15DA8673ABCB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2D870641-D0E0-20A4-F37D-15DA8673ABCB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2D870641-D0E0-20A4-F37D-15DA8673ABCB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2D870641-D0E0-20A4-F37D-15DA8673ABCB}.Release|Any CPU.Build.0 = Release|Any CPU
{2CA9B9D5-2DC8-260D-0931-3AA8F065172C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2CA9B9D5-2DC8-260D-0931-3AA8F065172C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2CA9B9D5-2DC8-260D-0931-3AA8F065172C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2CA9B9D5-2DC8-260D-0931-3AA8F065172C}.Release|Any CPU.Build.0 = Release|Any CPU
{3153BFCD-9C29-CFC5-A014-A425BB1CF856}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3153BFCD-9C29-CFC5-A014-A425BB1CF856}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3153BFCD-9C29-CFC5-A014-A425BB1CF856}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3153BFCD-9C29-CFC5-A014-A425BB1CF856}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{35E456EA-B00D-836A-01DD-847FD5160483} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
{D6FC7446-D621-6607-8B56-DBFF09E12B6D} = {C9AD4566-5B66-40D2-B6C0-7CE2C1434FFA}
{D9EC6920-FC64-BE4A-ECC0-BFD93DA3FEC1} = {FD560FA8-19E9-42DF-AFAC-D8D439F16916}
{2D870641-D0E0-20A4-F37D-15DA8673ABCB} = {FD560FA8-19E9-42DF-AFAC-D8D439F16916}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {83A42B0B-1337-4726-94BF-622DD10C68BD}
EndGlobalSection
EndGlobal

View File

@ -0,0 +1,28 @@
using linesider_screen_bankend.Core;
using linesider_screen_bankend.Modules.ModuleName.Views;
using Prism.Ioc;
using Prism.Modularity;
using Prism.Regions;
namespace linesider_screen_bankend.Modules.ModuleName
{
public class ModuleNameModule : IModule
{
private readonly IRegionManager _regionManager;
public ModuleNameModule(IRegionManager regionManager)
{
_regionManager = regionManager;
}
public void OnInitialized(IContainerProvider containerProvider)
{
_regionManager.RequestNavigate(RegionNames.ContentRegion, "ViewA");
}
public void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterForNavigation<ViewA>();
}
}
}

View File

@ -0,0 +1,27 @@
using linesider_screen_bankend.Core.Mvvm;
using linesider_screen_bankend.Services.Interfaces;
using Prism.Regions;
namespace linesider_screen_bankend.Modules.ModuleName.ViewModels
{
public class ViewAViewModel : RegionViewModelBase
{
private string _message;
public string Message
{
get { return _message; }
set { SetProperty(ref _message, value); }
}
public ViewAViewModel(IRegionManager regionManager, IMessageService messageService) :
base(regionManager)
{
Message = messageService.GetMessage();
}
public override void OnNavigatedTo(NavigationContext navigationContext)
{
//do something
}
}
}

View File

@ -0,0 +1,13 @@
<UserControl x:Class="linesider_screen_bankend.Modules.ModuleName.Views.ViewA"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:linesider_screen_bankend.Modules.ModuleName.Views"
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True" >
<Grid>
<TextBlock Text="{Binding Message}"
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="24"/>
</Grid>
</UserControl>

View File

@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace linesider_screen_bankend.Modules.ModuleName.Views
{
/// <summary>
/// Interaction logic for ViewA.xaml
/// </summary>
public partial class ViewA : UserControl
{
public ViewA()
{
InitializeComponent();
}
}
}

View File

@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0-windows</TargetFramework>
<UseWPF>true</UseWPF>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Prism.Wpf" Version="8.1.97" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\linesider_screen_bankend.Core\linesider_screen_bankend.Core.csproj" />
<ProjectReference Include="..\..\Services\linesider_screen_bankend.Services.Interfaces\linesider_screen_bankend.Services.Interfaces.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,7 @@
namespace linesider_screen_bankend.Services.Interfaces
{
public interface IMessageService
{
string GetMessage();
}
}

View File

@ -0,0 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
</PropertyGroup>
</Project>

View File

@ -0,0 +1,12 @@
using linesider_screen_bankend.Services.Interfaces;
namespace linesider_screen_bankend.Services
{
public class MessageService : IMessageService
{
public string GetMessage()
{
return "Hello from the Message Service";
}
}
}

View File

@ -0,0 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\linesider_screen_bankend.Services.Interfaces\linesider_screen_bankend.Services.Interfaces.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,41 @@
using linesider_screen_bankend.Modules.ModuleName.ViewModels;
using linesider_screen_bankend.Services.Interfaces;
using Moq;
using Prism.Regions;
using Xunit;
namespace linesider_screen_bankend.Modules.ModuleName.Tests.ViewModels
{
public class ViewAViewModelFixture
{
Mock<IMessageService> _messageServiceMock;
Mock<IRegionManager> _regionManagerMock;
const string MessageServiceDefaultMessage = "Some Value";
public ViewAViewModelFixture()
{
var messageService = new Mock<IMessageService>();
messageService.Setup(x => x.GetMessage()).Returns(MessageServiceDefaultMessage);
_messageServiceMock = messageService;
_regionManagerMock = new Mock<IRegionManager>();
}
[Fact]
public void MessagePropertyValueUpdated()
{
var vm = new ViewAViewModel(_regionManagerMock.Object, _messageServiceMock.Object);
_messageServiceMock.Verify(x => x.GetMessage(), Times.Once);
Assert.Equal(MessageServiceDefaultMessage, vm.Message);
}
[Fact]
public void MessageINotifyPropertyChangedCalled()
{
var vm = new ViewAViewModel(_regionManagerMock.Object, _messageServiceMock.Object);
Assert.PropertyChanged(vm, nameof(vm.Message), () => vm.Message = "Changed");
}
}
}

View File

@ -0,0 +1,27 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0-windows</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.1.0" />
<PackageReference Include="Moq" Version="4.17.2" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="3.1.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Modules\linesider_screen_bankend.Modules.ModuleName\linesider_screen_bankend.Modules.ModuleName.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,35 @@
using System;
using Prism.Regions;
namespace linesider_screen_bankend.Core.Mvvm
{
public class RegionViewModelBase : ViewModelBase, INavigationAware, IConfirmNavigationRequest
{
protected IRegionManager RegionManager { get; private set; }
public RegionViewModelBase(IRegionManager regionManager)
{
RegionManager = regionManager;
}
public virtual void ConfirmNavigationRequest(NavigationContext navigationContext, Action<bool> continuationCallback)
{
continuationCallback(true);
}
public virtual bool IsNavigationTarget(NavigationContext navigationContext)
{
return true;
}
public virtual void OnNavigatedFrom(NavigationContext navigationContext)
{
}
public virtual void OnNavigatedTo(NavigationContext navigationContext)
{
}
}
}

View File

@ -0,0 +1,18 @@
using Prism.Mvvm;
using Prism.Navigation;
namespace linesider_screen_bankend.Core.Mvvm
{
public abstract class ViewModelBase : BindableBase, IDestructible
{
protected ViewModelBase()
{
}
public virtual void Destroy()
{
}
}
}

View File

@ -0,0 +1,7 @@
namespace linesider_screen_bankend.Core
{
public static class RegionNames
{
public const string ContentRegion = "ContentRegion";
}
}

View File

@ -0,0 +1,119 @@
using System;
using BarTender;
namespace linesider_screen_bankend.Core.Tools
{
public class BartenderPrintHelper : IDisposable
{
private Application _btApp;
private Format _btFormat;
private bool _disposed = false;
/// <summary>
/// 初始化 Bartender 应用程序
/// </summary>
public BartenderPrintHelper()
{
try
{
_btApp = new Application();
}
catch (Exception ex)
{
throw new Exception("无法初始化 Bartender 应用程序。请确保 BarTender 已安装。", ex);
}
}
/// <summary>
/// 打印标签
/// </summary>
/// <param name="templatePath">标签模板路径</param>
/// <param name="subStringValues">标签变量键值对</param>
/// <param name="copies">打印份数</param>
/// <param name="serializedLabels">序列化标签数量</param>
/// <returns>是否打印成功</returns>
public bool PrintLabel(string templatePath, System.Collections.Generic.Dictionary<string, string> subStringValues, int copies = 1, int serializedLabels = 1)
{
if (_disposed)
throw new ObjectDisposedException("BartenderPrintHelper", "对象已被释放,不能执行打印操作。");
try
{
// 打开标签模板
_btFormat = _btApp.Formats.Open(templatePath);
// 设置打印参数
_btFormat.PrintSetup.IdenticalCopiesOfLabel = copies;
_btFormat.PrintSetup.NumberSerializedLabels = serializedLabels;
// 设置标签变量值
if (subStringValues != null)
{
foreach (var item in subStringValues)
{
_btFormat.SetNamedSubStringValue(item.Key, item.Value);
}
}
// 执行打印
_btFormat.PrintOut(false, false);
return true;
}
catch (Exception ex)
{
throw new Exception($"打印标签时出错: {ex.Message}", ex);
}
finally
{
if (_btFormat != null)
{
_btFormat.Close(BtSaveOptions.btDoNotSaveChanges);
System.Runtime.InteropServices.Marshal.ReleaseComObject(_btFormat);
_btFormat = null;
}
}
}
/// <summary>
/// 释放资源
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
// 清理托管资源
}
// 清理非托管资源
if (_btFormat != null)
{
_btFormat.Close(BtSaveOptions.btDoNotSaveChanges);
System.Runtime.InteropServices.Marshal.ReleaseComObject(_btFormat);
_btFormat = null;
}
if (_btApp != null)
{
_btApp.Quit(BtSaveOptions.btDoNotSaveChanges);
System.Runtime.InteropServices.Marshal.ReleaseComObject(_btApp);
_btApp = null;
}
_disposed = true;
}
}
~BartenderPrintHelper()
{
Dispose(false);
}
}
}

View File

@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0-windows</TargetFramework>
<UseWPF>true</UseWPF>
</PropertyGroup>
<ItemGroup>
<COMReference Include="BarTender">
<WrapperTool>tlbimp</WrapperTool>
<VersionMinor>0</VersionMinor>
<VersionMajor>11</VersionMajor>
<Guid>d58562c1-e51b-11cf-8941-00a024a9083f</Guid>
<Lcid>0</Lcid>
<Isolated>false</Isolated>
<EmbedInteropTypes>true</EmbedInteropTypes>
</COMReference>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Prism.Wpf" Version="8.1.97" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,9 @@
<prism:PrismApplication x:Class="linesider_screen_bankend.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:linesider_screen_bankend"
xmlns:prism="http://prismlibrary.com/" >
<Application.Resources>
</Application.Resources>
</prism:PrismApplication>

View File

@ -0,0 +1,93 @@
using System;
using System.Threading;
using System.Windows;
using EmbedIO;
using EmbedIO.WebApi;
using linesider_screen_bankend.Modules.ModuleName;
using linesider_screen_bankend.Services;
using linesider_screen_bankend.Services.Interfaces;
using linesider_screen_bankend.Views;
using Prism.Ioc;
using Prism.Modularity;
using linesider_screen_bankend.Controller;
namespace linesider_screen_bankend
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App
{
protected override Window CreateShell()
{
return Container.Resolve<MainWindow>();
}
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterSingleton<IMessageService, MessageService>();
}
protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog)
{
moduleCatalog.AddModule<ModuleNameModule>();
}
private CancellationTokenSource _cts;
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
// 启动Web服务器
//StartWebServer();
}
//private void StartWebServer()
//{
// _cts = new CancellationTokenSource();
// var url = "http://localhost:9696/";
// // 在新线程中运行服务器避免阻塞UI线程
// var serverThread = new Thread(() =>
// {
// try
// {
// using (var server = CreateWebServer(url))
// {
// server.RunAsync(_cts.Token).Wait();
// }
// }
// catch (Exception ex)
// {
// // 处理异常
// MessageBox.Show($"Web服务器错误: {ex.Message}");
// }
// })
// {
// IsBackground = true
// };
// serverThread.Start();
//}
//private WebServer CreateWebServer(string url)
//{
// var server = new WebServer(o => o
// .WithUrlPrefix(url)
// .WithMode(HttpListenerMode.EmbedIO))
// .WithWebApi("/api", m => m
// .WithController<MyApiController>());
// return server;
//}
//protected override void OnExit(ExitEventArgs e)
//{
// // 应用程序退出时停止服务器
// _cts?.Cancel();
// base.OnExit(e);
//}
}
}

View File

@ -0,0 +1,55 @@
using EmbedIO.Routing;
using EmbedIO.WebApi;
using EmbedIO;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace linesider_screen_bankend.Controller
{
public class MyApiController : WebApiController
{
private readonly Action<string> _logAction;
public MyApiController(Action<string> logAction)
{
_logAction = logAction;
}
[Route(HttpVerbs.Get, "/hello")]
public string GetHello()
{
_logAction?.Invoke($"GET /api/hello from {HttpContext.RemoteEndPoint}");
return "Hello from WPF!";
}
[Route(HttpVerbs.Post, "/data")]
public async Task<string> PostData()
{
try
{
var data = await HttpContext.GetRequestDataAsync<MyDataModel>();
_logAction?.Invoke($"POST /api/data from {HttpContext.RemoteEndPoint}: {data}");
return $"Received: Name={data.Name}, Value={data.Value}";
}
catch (Exception ex)
{
_logAction?.Invoke($"Error in POST /api/data: {ex.Message}");
throw;
}
}
}
public class MyDataModel
{
public string Name { get; set; }
public int Value { get; set; }
public override string ToString()
{
return $"Name: {Name}, Value: {Value}";
}
}
}

View File

@ -0,0 +1,31 @@
using EmbedIO.Routing;
using EmbedIO.WebApi;
using EmbedIO;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace linesider_screen_bankend.Controller
{
public class PrintCommunicationController: WebApiController
{
private readonly Action<string> _logAction;
public PrintCommunicationController(Action<string> logAction)
{
_logAction = logAction;
}
[Route(HttpVerbs.Get, "/hello")]
public string GetHello()
{
_logAction?.Invoke($"GET /api/hello from {HttpContext.RemoteEndPoint}");
return "Hello from WPF!";
}
}
}

View File

@ -0,0 +1,19 @@
using Prism.Mvvm;
namespace linesider_screen_bankend.ViewModels
{
public class MainWindowViewModel : BindableBase
{
private string _title = "Prism Application";
public string Title
{
get { return _title; }
set { SetProperty(ref _title, value); }
}
public MainWindowViewModel()
{
}
}
}

View File

@ -0,0 +1,30 @@
<Window x:Class="linesider_screen_bankend.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True"
xmlns:core="clr-namespace:linesider_screen_bankend.Core;assembly=linesider_screen_bankend.Core"
Title="{Binding Title}" Height="350" Width="525" >
<Grid>
<!--<ContentControl prism:RegionManager.RegionName="{x:Static core:RegionNames.ContentRegion}" />-->
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" Orientation="Horizontal" Margin="10">
<TextBlock Text="EmbedIO Server URL:" VerticalAlignment="Center"/>
<TextBox x:Name="UrlTextBox" Text="http://localhost:9696/" Width="200" Margin="10,0"/>
<Button x:Name="StartButton" Content="Start Server" Click="StartButton_Click" Width="100" Margin="5,0"/>
<Button x:Name="StopButton" Content="Stop Server" Click="StopButton_Click" Width="100" Margin="5,0" IsEnabled="False"/>
</StackPanel>
<GroupBox Grid.Row="1" Header="HTTP Request Log" Margin="10">
<Grid>
<TextBox x:Name="LogTextBox" IsReadOnly="True"
VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto"
FontFamily="Consolas" FontSize="12" TextWrapping="NoWrap"/>
</Grid>
</GroupBox>
</Grid>
</Window>

View File

@ -0,0 +1,138 @@
using EmbedIO;
using EmbedIO.WebApi;
using linesider_screen_bankend.Controller;
using System;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Threading;
namespace linesider_screen_bankend.Views
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private WebServer _server;
private CancellationTokenSource _cts;
private readonly ConcurrentQueue<string> _logQueue = new();
private readonly DispatcherTimer _logTimer = new();
public MainWindow()
{
InitializeComponent();
// 设置日志定时器每100毫秒处理一次日志队列
_logTimer.Interval = TimeSpan.FromMilliseconds(100);
_logTimer.Tick += ProcessLogQueue;
_logTimer.Start();
}
private void StartButton_Click(object sender, RoutedEventArgs e)
{
try
{
_cts = new CancellationTokenSource();
var url = UrlTextBox.Text.Trim();
_server = CreateWebServer(url);
Task.Run(() =>
{
try
{
AddLog($"Starting server at {url}");
_server.RunAsync(_cts.Token).Wait();
}
catch (Exception ex)
{
AddLog($"Server error: {ex.Message}");
}
finally
{
Dispatcher.Invoke(() =>
{
StartButton.IsEnabled = true;
StopButton.IsEnabled = false;
});
AddLog("Server stopped");
}
});
StartButton.IsEnabled = false;
StopButton.IsEnabled = true;
}
catch (Exception ex)
{
AddLog($"Failed to start server: {ex.Message}");
}
}
private void StopButton_Click(object sender, RoutedEventArgs e)
{
try
{
_cts?.Cancel();
StartButton.IsEnabled = true;
StopButton.IsEnabled = false;
}
catch (Exception ex)
{
AddLog($"Error stopping server: {ex.Message}");
}
}
private WebServer CreateWebServer(string url)
{
var server = new WebServer(o => o
.WithUrlPrefix(url)
.WithMode(HttpListenerMode.EmbedIO))
.WithWebApi("/api", m => m
.WithController<MyApiController>(() => new MyApiController(AddLog)))
.WithStaticFolder("/", Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "wwwroot"), true);
// 添加请求日志中间件
server.OnHttpException += async (context, exception) =>
{
AddLog($"HTTP Exception: {exception.Message}");
await Task.CompletedTask;
};
server.StateChanged += (sender, e) =>
AddLog($"Server state changed from {e.OldState} to {e.NewState}");
return server;
}
// 线程安全的日志添加方法
private void AddLog(string message)
{
var timestamp = DateTime.Now.ToString("HH:mm:ss.fff");
_logQueue.Enqueue($"[{timestamp}] {message}");
}
// 在UI线程上处理日志队列
private void ProcessLogQueue(object sender, EventArgs e)
{
while (_logQueue.TryDequeue(out var message))
{
LogTextBox.AppendText(message + Environment.NewLine);
LogTextBox.ScrollToEnd();
}
}
protected override void OnClosed(EventArgs e)
{
_logTimer.Stop();
_cts?.Cancel();
_server?.Dispose();
base.OnClosed(e);
}
}
}

View File

@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net6.0-windows</TargetFramework>
<UseWPF>true</UseWPF>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="EmbedIO" Version="3.5.2" />
<PackageReference Include="Prism.Unity" Version="8.1.97" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\linesider_screen_bankend.Core\linesider_screen_bankend.Core.csproj" />
<ProjectReference Include="..\Modules\linesider_screen_bankend.Modules.ModuleName\linesider_screen_bankend.Modules.ModuleName.csproj" />
<ProjectReference Include="..\Services\linesider_screen_bankend.Services.Interfaces\linesider_screen_bankend.Services.Interfaces.csproj" />
<ProjectReference Include="..\Services\linesider_screen_bankend.Services\linesider_screen_bankend.Services.csproj" />
</ItemGroup>
</Project>