update code for v1.1
63
.gitattributes
vendored
Normal file
|
@ -0,0 +1,63 @@
|
|||
###############################################################################
|
||||
# Set default behavior to automatically normalize line endings.
|
||||
###############################################################################
|
||||
* text=auto
|
||||
|
||||
###############################################################################
|
||||
# Set default behavior for command prompt diff.
|
||||
#
|
||||
# This is need for earlier builds of msysgit that does not have it on by
|
||||
# default for csharp files.
|
||||
# Note: This is only used by command line
|
||||
###############################################################################
|
||||
#*.cs diff=csharp
|
||||
|
||||
###############################################################################
|
||||
# Set the merge driver for project and solution files
|
||||
#
|
||||
# Merging from the command prompt will add diff markers to the files if there
|
||||
# are conflicts (Merging from VS is not affected by the settings below, in VS
|
||||
# the diff markers are never inserted). Diff markers may cause the following
|
||||
# file extensions to fail to load in VS. An alternative would be to treat
|
||||
# these files as binary and thus will always conflict and require user
|
||||
# intervention with every merge. To do so, just uncomment the entries below
|
||||
###############################################################################
|
||||
#*.sln merge=binary
|
||||
#*.csproj merge=binary
|
||||
#*.vbproj merge=binary
|
||||
#*.vcxproj merge=binary
|
||||
#*.vcproj merge=binary
|
||||
#*.dbproj merge=binary
|
||||
#*.fsproj merge=binary
|
||||
#*.lsproj merge=binary
|
||||
#*.wixproj merge=binary
|
||||
#*.modelproj merge=binary
|
||||
#*.sqlproj merge=binary
|
||||
#*.wwaproj merge=binary
|
||||
|
||||
###############################################################################
|
||||
# behavior for image files
|
||||
#
|
||||
# image files are treated as binary by default.
|
||||
###############################################################################
|
||||
#*.jpg binary
|
||||
#*.png binary
|
||||
#*.gif binary
|
||||
|
||||
###############################################################################
|
||||
# diff behavior for common document formats
|
||||
#
|
||||
# Convert binary document formats to text before diffing them. This feature
|
||||
# is only available from the command line. Turn it on by uncommenting the
|
||||
# entries below.
|
||||
###############################################################################
|
||||
#*.doc diff=astextplain
|
||||
#*.DOC diff=astextplain
|
||||
#*.docx diff=astextplain
|
||||
#*.DOCX diff=astextplain
|
||||
#*.dot diff=astextplain
|
||||
#*.DOT diff=astextplain
|
||||
#*.pdf diff=astextplain
|
||||
#*.PDF diff=astextplain
|
||||
#*.rtf diff=astextplain
|
||||
#*.RTF diff=astextplain
|
370
.gitignore
vendored
Normal file
|
@ -0,0 +1,370 @@
|
|||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
##
|
||||
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
|
||||
|
||||
SiemensCereflowSetup/*.exe
|
||||
|
||||
# User-specific files
|
||||
*.rsuser
|
||||
*.suo
|
||||
*.user
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
|
||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||
*.userprefs
|
||||
|
||||
# Mono auto generated files
|
||||
mono_crash.*
|
||||
|
||||
# Build results
|
||||
[Dd]ebug/
|
||||
[Dd]ebugPublic/
|
||||
[Rr]elease/
|
||||
[Rr]eleases/
|
||||
x64/
|
||||
x86/
|
||||
[Ww][Ii][Nn]32/
|
||||
[Aa][Rr][Mm]/
|
||||
[Aa][Rr][Mm]64/
|
||||
bld/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
[Oo]ut/
|
||||
[Ll]og/
|
||||
[Ll]ogs/
|
||||
|
||||
# Visual Studio 2015/2017 cache/options directory
|
||||
.vs/
|
||||
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||
#wwwroot/
|
||||
|
||||
# Visual Studio 2017 auto generated files
|
||||
Generated\ Files/
|
||||
|
||||
# MSTest test Results
|
||||
[Tt]est[Rr]esult*/
|
||||
[Bb]uild[Ll]og.*
|
||||
|
||||
# NUnit
|
||||
*.VisualState.xml
|
||||
TestResult.xml
|
||||
nunit-*.xml
|
||||
|
||||
# Build Results of an ATL Project
|
||||
[Dd]ebugPS/
|
||||
[Rr]eleasePS/
|
||||
dlldata.c
|
||||
|
||||
# Benchmark Results
|
||||
BenchmarkDotNet.Artifacts/
|
||||
|
||||
# .NET Core
|
||||
project.lock.json
|
||||
project.fragment.lock.json
|
||||
artifacts/
|
||||
|
||||
# ASP.NET Scaffolding
|
||||
ScaffoldingReadMe.txt
|
||||
|
||||
# StyleCop
|
||||
StyleCopReport.xml
|
||||
|
||||
# Files built by Visual Studio
|
||||
*_i.c
|
||||
*_p.c
|
||||
*_h.h
|
||||
*.ilk
|
||||
*.meta
|
||||
*.obj
|
||||
*.iobj
|
||||
*.pch
|
||||
*.pdb
|
||||
*.ipdb
|
||||
*.pgc
|
||||
*.pgd
|
||||
*.rsp
|
||||
*.sbr
|
||||
*.tlb
|
||||
*.tli
|
||||
*.tlh
|
||||
*.tmp
|
||||
*.tmp_proj
|
||||
*_wpftmp.csproj
|
||||
*.log
|
||||
*.vspscc
|
||||
*.vssscc
|
||||
.builds
|
||||
*.pidb
|
||||
*.svclog
|
||||
*.scc
|
||||
|
||||
# Chutzpah Test files
|
||||
_Chutzpah*
|
||||
|
||||
# Visual C++ cache files
|
||||
ipch/
|
||||
*.aps
|
||||
*.ncb
|
||||
*.opendb
|
||||
*.opensdf
|
||||
*.sdf
|
||||
*.cachefile
|
||||
*.VC.db
|
||||
*.VC.VC.opendb
|
||||
|
||||
# Visual Studio profiler
|
||||
*.psess
|
||||
*.vsp
|
||||
*.vspx
|
||||
*.sap
|
||||
|
||||
# Visual Studio Trace Files
|
||||
*.e2e
|
||||
|
||||
# TFS 2012 Local Workspace
|
||||
$tf/
|
||||
|
||||
# Guidance Automation Toolkit
|
||||
*.gpState
|
||||
|
||||
# ReSharper is a .NET coding add-in
|
||||
_ReSharper*/
|
||||
*.[Rr]e[Ss]harper
|
||||
*.DotSettings.user
|
||||
|
||||
# TeamCity is a build add-in
|
||||
_TeamCity*
|
||||
|
||||
# DotCover is a Code Coverage Tool
|
||||
*.dotCover
|
||||
|
||||
# AxoCover is a Code Coverage Tool
|
||||
.axoCover/*
|
||||
!.axoCover/settings.json
|
||||
|
||||
# Coverlet is a free, cross platform Code Coverage Tool
|
||||
coverage*.json
|
||||
coverage*.xml
|
||||
coverage*.info
|
||||
|
||||
# Visual Studio code coverage results
|
||||
*.coverage
|
||||
*.coveragexml
|
||||
|
||||
# NCrunch
|
||||
_NCrunch_*
|
||||
.*crunch*.local.xml
|
||||
nCrunchTemp_*
|
||||
|
||||
# MightyMoose
|
||||
*.mm.*
|
||||
AutoTest.Net/
|
||||
|
||||
# Web workbench (sass)
|
||||
.sass-cache/
|
||||
|
||||
# Installshield output folder
|
||||
[Ee]xpress/
|
||||
|
||||
# DocProject is a documentation generator add-in
|
||||
DocProject/buildhelp/
|
||||
DocProject/Help/*.HxT
|
||||
DocProject/Help/*.HxC
|
||||
DocProject/Help/*.hhc
|
||||
DocProject/Help/*.hhk
|
||||
DocProject/Help/*.hhp
|
||||
DocProject/Help/Html2
|
||||
DocProject/Help/html
|
||||
|
||||
# Click-Once directory
|
||||
publish/
|
||||
|
||||
# Publish Web Output
|
||||
*.[Pp]ublish.xml
|
||||
*.azurePubxml
|
||||
# Note: Comment the next line if you want to checkin your web deploy settings,
|
||||
# but database connection strings (with potential passwords) will be unencrypted
|
||||
#*.pubxml
|
||||
#*.publishproj
|
||||
|
||||
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||
# checkin your Azure Web App publish settings, but sensitive information contained
|
||||
# in these scripts will be unencrypted
|
||||
PublishScripts/
|
||||
|
||||
# NuGet Packages
|
||||
*.nupkg
|
||||
# NuGet Symbol Packages
|
||||
*.snupkg
|
||||
# The packages folder can be ignored because of Package Restore
|
||||
**/[Pp]ackages/*
|
||||
# except build/, which is used as an MSBuild target.
|
||||
!**/[Pp]ackages/build/
|
||||
# Uncomment if necessary however generally it will be regenerated when needed
|
||||
#!**/[Pp]ackages/repositories.config
|
||||
# NuGet v3's project.json files produces more ignorable files
|
||||
*.nuget.props
|
||||
*.nuget.targets
|
||||
|
||||
# Microsoft Azure Build Output
|
||||
csx/
|
||||
*.build.csdef
|
||||
|
||||
# Microsoft Azure Emulator
|
||||
ecf/
|
||||
rcf/
|
||||
|
||||
# Windows Store app package directories and files
|
||||
AppPackages/
|
||||
BundleArtifacts/
|
||||
Package.StoreAssociation.xml
|
||||
_pkginfo.txt
|
||||
*.appx
|
||||
*.appxbundle
|
||||
*.appxupload
|
||||
|
||||
# Visual Studio cache files
|
||||
# files ending in .cache can be ignored
|
||||
*.[Cc]ache
|
||||
# but keep track of directories ending in .cache
|
||||
!?*.[Cc]ache/
|
||||
|
||||
# Others
|
||||
ClientBin/
|
||||
~$*
|
||||
*~
|
||||
*.dbmdl
|
||||
*.dbproj.schemaview
|
||||
*.jfm
|
||||
*.pfx
|
||||
*.publishsettings
|
||||
orleans.codegen.cs
|
||||
|
||||
# Including strong name files can present a security risk
|
||||
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
|
||||
#*.snk
|
||||
|
||||
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
||||
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
||||
#bower_components/
|
||||
|
||||
# RIA/Silverlight projects
|
||||
Generated_Code/
|
||||
|
||||
# Backup & report files from converting an old project file
|
||||
# to a newer Visual Studio version. Backup files are not needed,
|
||||
# because we have git ;-)
|
||||
_UpgradeReport_Files/
|
||||
Backup*/
|
||||
UpgradeLog*.XML
|
||||
UpgradeLog*.htm
|
||||
ServiceFabricBackup/
|
||||
*.rptproj.bak
|
||||
|
||||
# SQL Server files
|
||||
*.mdf
|
||||
*.ldf
|
||||
*.ndf
|
||||
|
||||
# Business Intelligence projects
|
||||
*.rdl.data
|
||||
*.bim.layout
|
||||
*.bim_*.settings
|
||||
*.rptproj.rsuser
|
||||
*- [Bb]ackup.rdl
|
||||
*- [Bb]ackup ([0-9]).rdl
|
||||
*- [Bb]ackup ([0-9][0-9]).rdl
|
||||
|
||||
# Microsoft Fakes
|
||||
FakesAssemblies/
|
||||
|
||||
# GhostDoc plugin setting file
|
||||
*.GhostDoc.xml
|
||||
|
||||
# Node.js Tools for Visual Studio
|
||||
.ntvs_analysis.dat
|
||||
node_modules/
|
||||
|
||||
# Visual Studio 6 build log
|
||||
*.plg
|
||||
|
||||
# Visual Studio 6 workspace options file
|
||||
*.opt
|
||||
|
||||
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
|
||||
*.vbw
|
||||
|
||||
# Visual Studio LightSwitch build output
|
||||
**/*.HTMLClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/ModelManifest.xml
|
||||
**/*.Server/GeneratedArtifacts
|
||||
**/*.Server/ModelManifest.xml
|
||||
_Pvt_Extensions
|
||||
|
||||
# Paket dependency manager
|
||||
.paket/paket.exe
|
||||
paket-files/
|
||||
|
||||
# FAKE - F# Make
|
||||
.fake/
|
||||
|
||||
# CodeRush personal settings
|
||||
.cr/personal
|
||||
|
||||
# Python Tools for Visual Studio (PTVS)
|
||||
__pycache__/
|
||||
*.pyc
|
||||
|
||||
# Cake - Uncomment if you are using it
|
||||
# tools/**
|
||||
# !tools/packages.config
|
||||
|
||||
# Tabs Studio
|
||||
*.tss
|
||||
|
||||
# Telerik's JustMock configuration file
|
||||
*.jmconfig
|
||||
|
||||
# BizTalk build output
|
||||
*.btp.cs
|
||||
*.btm.cs
|
||||
*.odx.cs
|
||||
*.xsd.cs
|
||||
|
||||
# OpenCover UI analysis results
|
||||
OpenCover/
|
||||
|
||||
# Azure Stream Analytics local run output
|
||||
ASALocalRun/
|
||||
|
||||
# MSBuild Binary and Structured Log
|
||||
*.binlog
|
||||
|
||||
# NVidia Nsight GPU debugger configuration file
|
||||
*.nvuser
|
||||
|
||||
# MFractors (Xamarin productivity tool) working folder
|
||||
.mfractor/
|
||||
|
||||
# Local History for Visual Studio
|
||||
.localhistory/
|
||||
|
||||
# BeatPulse healthcheck temp database
|
||||
healthchecksdb
|
||||
|
||||
# Backup folder for Package Reference Convert tool in Visual Studio 2017
|
||||
MigrationBackup/
|
||||
|
||||
# Ionide (cross platform F# VS Code tools) working folder
|
||||
.ionide/
|
||||
|
||||
# Fody - auto-generated XML schema
|
||||
FodyWeavers.xsd
|
||||
/SiemensCereflow/Properties/launchSettings.json
|
||||
/SiemensCereflow/cereflow_command_line
|
||||
/*/astroke_cmd
|
||||
/*/iBrain_cmd
|
||||
/MdpC3/Properties/launchSettings.json
|
32
Astroke/App.xaml
Normal file
|
@ -0,0 +1,32 @@
|
|||
<Application x:Class="Astroke.App"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
Startup="OnStartup">
|
||||
<Application.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<materialDesign:BundledTheme BaseTheme="Light"
|
||||
PrimaryColor="Blue"
|
||||
SecondaryColor="Red" />
|
||||
<ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Defaults.xaml" />
|
||||
<ResourceDictionary Source="pack://application:,,,/Livia;component/Resources/Strings/Strings.xaml" />
|
||||
<ResourceDictionary Source="pack://application:,,,/Livia;component/Resources/Strings/Licenses.xaml" />
|
||||
<ResourceDictionary Source="pack://application:,,,/Livia;component/Resources/Themes/LiviaTheme.xaml" />
|
||||
<ResourceDictionary Source="pack://application:,,,/Livia;component/Resources/Themes/LiviaWindow.xaml" />
|
||||
<ResourceDictionary Source="pack://application:,,,/Livia;component/Resources/Themes/LiviaDialogDataTemplate.xaml" />
|
||||
<ResourceDictionary Source="pack://application:,,,/Livia;component/Resources/Themes/LiviaButton.xaml" />
|
||||
<ResourceDictionary Source="pack://application:,,,/Livia;component/Resources/Themes/LiviaCard.xaml" />
|
||||
<ResourceDictionary Source="pack://application:,,,/Livia;component/Resources/Themes/LiviaDataGrid.xaml" />
|
||||
<ResourceDictionary Source="pack://application:,,,/Livia;component/Resources/Themes/LiviaDatePicker.xaml" />
|
||||
<!--<ResourceDictionary Source="pack://application:,,,/Livia;component/Resources/Themes/LiviaScrollViewer.xaml" />-->
|
||||
<ResourceDictionary Source="pack://application:,,,/Livia;component/Resources/Themes/MaterialTitleBarFloatingActionButton.xaml" />
|
||||
<ResourceDictionary Source="pack://application:,,,/Livia;component/Resources/Themes/MainWindowTabButton.xaml" />
|
||||
<ResourceDictionary Source="pack://application:,,,/Livia;component/Resources/Themes/SettingsWindowItem.xaml" />
|
||||
<ResourceDictionary Source="pack://application:,,,/Livia;component/Resources/Themes/ScrollViewBarStyle.xaml" />
|
||||
<ResourceDictionary Source="pack://application:,,,/Livia;component/Resources/Themes/LiviaTabItem.xaml" />
|
||||
<ResourceDictionary Source="/Resources/Strings/Strings.xaml" />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
||||
</Application.Resources>
|
||||
</Application>
|
65
Astroke/App.xaml.cs
Normal file
|
@ -0,0 +1,65 @@
|
|||
using System.Windows;
|
||||
using Astroke.ViewModels;
|
||||
using Astroke.Views.Controls;
|
||||
using JetBrains.Annotations;
|
||||
using Livia.Models;
|
||||
using Livia.Models.Data;
|
||||
using Livia.Properties;
|
||||
using Livia.Utility.DependencyInjection;
|
||||
using Livia.ViewModels;
|
||||
using Livia.Views;
|
||||
using Livia.Views.Utility;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Velopack;
|
||||
|
||||
namespace Astroke;
|
||||
|
||||
/// <summary>
|
||||
/// Interaction logic for App.xaml
|
||||
/// </summary>
|
||||
public partial class App
|
||||
{
|
||||
|
||||
private readonly ILogger _logger;
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly IWarningSystem _warningSystem;
|
||||
[UsedImplicitly] private Mutex? _mutex;
|
||||
|
||||
public App()
|
||||
{
|
||||
ServiceConfigurations.AppName = "astroke";
|
||||
VelopackApp.Build().Run();
|
||||
ServiceProviderFactory.Init(sc => sc.AddSingleton<ILiviaControlViewModel, AStrokeControlViewModel>());
|
||||
|
||||
InitializeComponent();
|
||||
_serviceProvider = ServiceProviderFactory.ServiceProvider;
|
||||
_logger = ActivatorUtilities.GetServiceOrCreateInstance<ILogger<App>>(ServiceProviderFactory.ServiceProvider);
|
||||
_warningSystem = ActivatorUtilities.GetServiceOrCreateInstance<IWarningSystem>(ServiceProviderFactory.ServiceProvider);
|
||||
_logger.LogInformation("App Starting");
|
||||
SettingsLogger.Init();
|
||||
|
||||
AppHelper helper = ActivatorUtilities.GetServiceOrCreateInstance<AppHelper>(ServiceProviderFactory.ServiceProvider);
|
||||
|
||||
Dispatcher.UnhandledException += helper.OnDispatcherUnhandledException;
|
||||
helper.UpdateSettings();
|
||||
helper.LoadStrings();
|
||||
}
|
||||
|
||||
private void OnStartup(object sender, StartupEventArgs eventArgs)
|
||||
{
|
||||
_mutex = new Mutex(true, "Livia", out bool isNewInstance);
|
||||
if (!isNewInstance)
|
||||
{
|
||||
_warningSystem.ShowDialog(WarningWindowKind.Warning, true, "InstanceIsRunningError");
|
||||
Current.Shutdown();
|
||||
}
|
||||
|
||||
MainWindow mainWindow = ActivatorUtilities.CreateInstance<MainWindow>(_serviceProvider);
|
||||
mainWindow.AddMainControl(new AStrokeControl(ActivatorUtilities.GetServiceOrCreateInstance<ILiviaControlViewModel>(ServiceProviderFactory.ServiceProvider)));
|
||||
MainWindow = mainWindow;
|
||||
mainWindow.Show();
|
||||
|
||||
_logger.LogInformation("App Started");
|
||||
}
|
||||
}
|
10
Astroke/AssemblyInfo.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
using System.Windows;
|
||||
|
||||
[assembly: ThemeInfo(
|
||||
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
|
||||
//(used if a resource is not found in the page,
|
||||
// or application resource dictionaries)
|
||||
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
|
||||
//(used if a resource is not found in the page,
|
||||
// app, or any theme specific resource dictionaries)
|
||||
)]
|
39
Astroke/Astroke.csproj
Normal file
|
@ -0,0 +1,39 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>net8.0-windows</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<UseWPF>true</UseWPF>
|
||||
<ApplicationIcon>Resources\Images\astroke.ico</ApplicationIcon>
|
||||
<AssemblyName>aStroke</AssemblyName>
|
||||
<IncludeNativeLibrariesForSelfExtract>true</IncludeNativeLibrariesForSelfExtract>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<None Remove="Resources\Images\astroke.ico" />
|
||||
<None Remove="Resources\Images\placeholder.png" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup>
|
||||
<!-- This overrides the default Program.Main that WPF creates for you, and allows you to add VelopackApp -->
|
||||
<StartupObject>Astroke.Program</StartupObject>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Resources\Images\astroke.ico">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Resources\Images\placeholder.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Livia\Livia.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Update="Views\Controls\AStrokeControl.xaml.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Update="Views\Controls\AStrokeRoiSummaryControl.xaml.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
</Project>
|
31
Astroke/Program.cs
Normal file
|
@ -0,0 +1,31 @@
|
|||
using System.Windows;
|
||||
using JetBrains.Annotations;
|
||||
using Velopack;
|
||||
|
||||
namespace Astroke;
|
||||
|
||||
// from https://github.com/velopack/velopack/blob/master/samples/VeloWpfSample/Program.cs
|
||||
[UsedImplicitly]
|
||||
public class Program
|
||||
{
|
||||
|
||||
[STAThread]
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
try
|
||||
{
|
||||
//TODO::multi language
|
||||
VelopackApp.Build().WithFirstRun(_ => MessageBox.Show("软件安装成功。后续请使用桌面快捷方式访问本软件。")).Run();
|
||||
|
||||
// We can now launch the WPF application as normal.
|
||||
App app = new();
|
||||
app.InitializeComponent();
|
||||
app.Run();
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show($"Unhandled exception: {ex}");
|
||||
}
|
||||
}
|
||||
}
|
BIN
Astroke/Resources/Images/astroke.ico
Normal file
After Width: | Height: | Size: 114 KiB |
BIN
Astroke/Resources/Images/placeholder.png
Normal file
After Width: | Height: | Size: 20 KiB |
24
Astroke/Resources/Strings/Strings.xaml
Normal file
|
@ -0,0 +1,24 @@
|
|||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:system="clr-namespace:System;assembly=System.Runtime">
|
||||
|
||||
<system:String x:Key="LoginControlMessage">欢迎使用aStroke</system:String>
|
||||
<system:String x:Key="WindowTitle">aStroke</system:String>
|
||||
<system:String x:Key="PlaceHolderMessage">请上传MD-pCASL或SD-pCASL、3D T2 FLAIR、DWI (4mm层厚) 序列,所有序列使用同一StudyInstanceUID</system:String>
|
||||
|
||||
<system:String x:Key="RoiTabHeader1">CBF</system:String>
|
||||
<system:String x:Key="RoiTabHeader2">弥散受限</system:String>
|
||||
<system:String x:Key="RoiTabHeader3">ATT</system:String>
|
||||
<system:String x:Key="RoiTabHeader4">aCBV</system:String>
|
||||
<system:String x:Key="RoiTabHeader5">前循环ASPECTS</system:String>
|
||||
<system:String x:Key="RoiTabHeader5Prompt">尾状核、内囊、豆状核、脑岛存在CBF<10或ADC<620扣1分;M1-M6区域CBF<10或ADC<620体积大于等于该区域三分之一扣除1分。</system:String>
|
||||
<system:String x:Key="RoiTabHeader6Prompt">CBF<10或ADC<620中脑和桥脑,扣除2分;大脑后动脉供血区左、大脑后动脉供血区右、小脑左,小脑右、丘脑左、丘脑右区域扣除1分。</system:String>
|
||||
<system:String x:Key="RoiTabHeader6">后循环ASPECTS</system:String>
|
||||
|
||||
<system:String x:Key="ManufacturerInformation"
|
||||
xml:space="preserve">注册人/企业名称: 安影科技(北京)有限公司
|
||||
生产地址: 北京市大兴区宝参南街16号院3号楼(3A)201单元205、206、207室
|
||||
售后服务单位: 安影科技(北京)有限公司
|
||||
联系方式: 010-69465675</system:String>
|
||||
|
||||
</ResourceDictionary>
|
12
Astroke/Resources/Strings/en.xaml
Normal file
|
@ -0,0 +1,12 @@
|
|||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:system="clr-namespace:System;assembly=System.Runtime">
|
||||
|
||||
<system:String x:Key="LoginControlMessage">Welcome to aStroke</system:String>
|
||||
<system:String x:Key="WindowTitle">aStroke</system:String>
|
||||
|
||||
<system:String x:Key="RoiTabHeader0">Ischemic</system:String>
|
||||
<system:String x:Key="RoiTabHeader1">Restricted Diffusion</system:String>
|
||||
<system:String x:Key="RoiTabHeader2">Collateral Circulation</system:String>
|
||||
<system:String x:Key="RoiTabHeader3">ASPECTS</system:String>
|
||||
</ResourceDictionary>
|
207
Astroke/ViewModels/AStrokeControlViewModel.cs
Normal file
|
@ -0,0 +1,207 @@
|
|||
using System.IO;
|
||||
using System.Windows;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using Livia.Models;
|
||||
using Livia.Utility;
|
||||
using Livia.Utility.DependencyInjection;
|
||||
using Livia.ViewModels;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Astroke.ViewModels;
|
||||
|
||||
public class AStrokeControlViewModel : ObservableRecipient, ILiviaControlViewModel, IRecipient<DataLoadedMessage>, IRecipient<PerfusionDataGridChangeParameterMessage>
|
||||
{
|
||||
public ImageRotationViewerControlViewModel ArterialTerritoriesImageRotationViewerControlViewModel { get; }
|
||||
public ImageRotationViewerControlViewModel AspectsImageRotationViewerControlViewModel { get; }
|
||||
|
||||
public PerfusionDataGridGroupControlViewModel PerfusionDataGridGroupViewModel { get; }
|
||||
|
||||
public AStrokeRoiSummaryControlViewModel LeftAStrokeRoiSummaryControlViewModel { get; }
|
||||
public AStrokeRoiSummaryControlViewModel RightAStrokeRoiSummaryControlViewModel { get; }
|
||||
public RoiSummaryControlViewModel LeftRestrictedDiffusionRoiSummaryControlViewModel { get; }
|
||||
public RoiSummaryControlViewModel RightRestrictedDiffusionRoiSummaryControlViewModel { get; }
|
||||
public RoiSummaryControlViewModel LeftAttRoiSummaryControlViewModel { get; }
|
||||
public RoiSummaryControlViewModel RightAttRoiSummaryControlViewModel { get; }
|
||||
public RoiSummaryControlViewModel LeftAcbvRoiSummaryControlViewModel { get; }
|
||||
public RoiSummaryControlViewModel RightAcbvRoiSummaryControlViewModel { get; }
|
||||
public AspectsScoreRoiSummaryControlViewModel LeftAnteriorAspectsScoreRoiSummaryControlViewModel { get; }
|
||||
public AspectsScoreRoiSummaryControlViewModel RightAnteriorAspectsScoreRoiSummaryControlViewModel { get; }
|
||||
public AspectsScoreRoiSummaryControlViewModel PosteriorAspectsScoreRoiSummaryControlViewModel { get; }
|
||||
public MosaicImageGroupControlViewModel MosaicImageGroupViewModel { get; }
|
||||
//TODO::temp fix I do not like this
|
||||
public bool AttTabVisible { get => _attTabVisible; set => SetProperty(ref _attTabVisible, value); }
|
||||
public IDataBlockLoader DataBlockLoader { get; }
|
||||
public int RoiTabSelectedIndex { get => _roiTabSelectedIndex; set => SetProperty(ref _roiTabSelectedIndex, value); }
|
||||
|
||||
private int _roiTabSelectedIndex;
|
||||
private bool _attTabVisible = true;
|
||||
|
||||
public AStrokeControlViewModel(IDataBlockLoader dataBlockLoader)
|
||||
{
|
||||
WeakReferenceMessenger.Default.RegisterAll(this);
|
||||
DataBlockLoader = dataBlockLoader;
|
||||
|
||||
ArterialTerritoriesImageRotationViewerControlViewModel = ActivatorUtilities.CreateInstance<ImageRotationViewerControlViewModel>(ServiceProviderFactory.ServiceProvider);
|
||||
ArterialTerritoriesImageRotationViewerControlViewModel.DisplayName = (string)Application.Current.TryFindResource("ArterialTerritoriesRotationViewerName");
|
||||
ArterialTerritoriesImageRotationViewerControlViewModel.AtlasMaskLoadPath = @"atlas\AnImage_ArterialTerritories\mask";
|
||||
ArterialTerritoriesImageRotationViewerControlViewModel.StructLoadPath = "structure";
|
||||
ArterialTerritoriesImageRotationViewerControlViewModel.StructDataType = ImageRotationViewerDataType.Dicom2D;
|
||||
ArterialTerritoriesImageRotationViewerControlViewModel.AtlasId = "AnImage_ArterialTerritories";
|
||||
ArterialTerritoriesImageRotationViewerControlViewModel.ImageIndexSyncKey = "structure";
|
||||
dataBlockLoader.AddViewModel(ArterialTerritoriesImageRotationViewerControlViewModel);
|
||||
|
||||
AspectsImageRotationViewerControlViewModel = ActivatorUtilities.CreateInstance<ImageRotationViewerControlViewModel>(ServiceProviderFactory.ServiceProvider);
|
||||
AspectsImageRotationViewerControlViewModel.DisplayName = (string)Application.Current.TryFindResource("AspectsRotationViewerName");
|
||||
AspectsImageRotationViewerControlViewModel.AtlasMaskLoadPath = @"atlas\AnImage_ASPECTS\mask";
|
||||
AspectsImageRotationViewerControlViewModel.StructLoadPath = "structure";
|
||||
AspectsImageRotationViewerControlViewModel.StructDataType = ImageRotationViewerDataType.Dicom2D;
|
||||
AspectsImageRotationViewerControlViewModel.AtlasId = "AnImage_ASPECTS";
|
||||
AspectsImageRotationViewerControlViewModel.ImageIndexSyncKey = "structure";
|
||||
dataBlockLoader.AddViewModel(AspectsImageRotationViewerControlViewModel);
|
||||
|
||||
PerfusionDataGridControlViewModel cerebrumCerebellumPerfusionDataGridControlViewModel = ActivatorUtilities.CreateInstance<PerfusionDataGridControlViewModel>(ServiceProviderFactory.ServiceProvider);
|
||||
cerebrumCerebellumPerfusionDataGridControlViewModel.LoadPath = Path.Combine("atlas", "AnImage_CerebrumCerebellum");
|
||||
cerebrumCerebellumPerfusionDataGridControlViewModel.GridTabName = (string)Application.Current.TryFindResource("CerebrumCerebellumPerfusionDataGridName");
|
||||
|
||||
PerfusionDataGridControlViewModel arterialTerritoriesPerfusionDataGridControlViewModel = ActivatorUtilities.CreateInstance<PerfusionDataGridControlViewModel>(ServiceProviderFactory.ServiceProvider);
|
||||
arterialTerritoriesPerfusionDataGridControlViewModel.LoadPath = Path.Combine("atlas", "AnImage_ArterialTerritories");
|
||||
arterialTerritoriesPerfusionDataGridControlViewModel.GridTabName = (string)Application.Current.TryFindResource("ArterialTerritoriesPerfusionDataGridName");
|
||||
|
||||
PerfusionDataGridControlViewModel aspectsPerfusionDataGridControlViewModel = ActivatorUtilities.CreateInstance<PerfusionDataGridControlViewModel>(ServiceProviderFactory.ServiceProvider);
|
||||
aspectsPerfusionDataGridControlViewModel.LoadPath = Path.Combine("atlas", "AnImage_ASPECTS");
|
||||
aspectsPerfusionDataGridControlViewModel.GridTabName = (string)Application.Current.TryFindResource("AspectsPerfusionDataGridName");
|
||||
|
||||
PerfusionDataGridGroupViewModel = ActivatorUtilities.CreateInstance<PerfusionDataGridGroupControlViewModel>(ServiceProviderFactory.ServiceProvider);
|
||||
PerfusionDataGridGroupViewModel.PerfusionDataGridViewModelCollection.Add(cerebrumCerebellumPerfusionDataGridControlViewModel);
|
||||
PerfusionDataGridGroupViewModel.PerfusionDataGridViewModelCollection.Add(arterialTerritoriesPerfusionDataGridControlViewModel);
|
||||
PerfusionDataGridGroupViewModel.PerfusionDataGridViewModelCollection.Add(aspectsPerfusionDataGridControlViewModel);
|
||||
dataBlockLoader.AddViewModel(PerfusionDataGridGroupViewModel);
|
||||
|
||||
LeftAStrokeRoiSummaryControlViewModel = ActivatorUtilities.CreateInstance<AStrokeRoiSummaryControlViewModel>(ServiceProviderFactory.ServiceProvider);
|
||||
LeftAStrokeRoiSummaryControlViewModel.LoadPath = Path.Combine("roi", "cbf_location_l.json");
|
||||
LeftAStrokeRoiSummaryControlViewModel.RoiTabId = "cbf";
|
||||
dataBlockLoader.AddViewModel(LeftAStrokeRoiSummaryControlViewModel);
|
||||
|
||||
RightAStrokeRoiSummaryControlViewModel = ActivatorUtilities.CreateInstance<AStrokeRoiSummaryControlViewModel>(ServiceProviderFactory.ServiceProvider);
|
||||
RightAStrokeRoiSummaryControlViewModel.LoadPath = Path.Combine("roi", "cbf_location_r.json");
|
||||
RightAStrokeRoiSummaryControlViewModel.RoiTabId = "cbf";
|
||||
dataBlockLoader.AddViewModel(RightAStrokeRoiSummaryControlViewModel);
|
||||
|
||||
//This is useless but prevents ReSharper form complaining. This function may be needed in the future.
|
||||
RoiExpanderGroupControlViewModel.DefaultDeselectRoiSet.Add("");
|
||||
|
||||
LeftRestrictedDiffusionRoiSummaryControlViewModel = ActivatorUtilities.CreateInstance<RoiSummaryControlViewModel>(ServiceProviderFactory.ServiceProvider);
|
||||
LeftRestrictedDiffusionRoiSummaryControlViewModel.LoadPath = Path.Combine("roi", "restricted_diffusion_location_l.json");
|
||||
LeftRestrictedDiffusionRoiSummaryControlViewModel.RoiTabId = "restricted_diffusion";
|
||||
dataBlockLoader.AddViewModel(LeftRestrictedDiffusionRoiSummaryControlViewModel);
|
||||
|
||||
RightRestrictedDiffusionRoiSummaryControlViewModel = ActivatorUtilities.CreateInstance<RoiSummaryControlViewModel>(ServiceProviderFactory.ServiceProvider);
|
||||
RightRestrictedDiffusionRoiSummaryControlViewModel.LoadPath = Path.Combine("roi", "restricted_diffusion_location_r.json");
|
||||
RightRestrictedDiffusionRoiSummaryControlViewModel.RoiTabId = "restricted_diffusion";
|
||||
dataBlockLoader.AddViewModel(RightRestrictedDiffusionRoiSummaryControlViewModel);
|
||||
|
||||
LeftAnteriorAspectsScoreRoiSummaryControlViewModel = ActivatorUtilities.CreateInstance<AspectsScoreRoiSummaryControlViewModel>(ServiceProviderFactory.ServiceProvider);
|
||||
LeftAnteriorAspectsScoreRoiSummaryControlViewModel.LoadPath = Path.Combine("roi", "anterior_aspects_l.json");
|
||||
LeftAnteriorAspectsScoreRoiSummaryControlViewModel.RoiTabId = "anterior";
|
||||
dataBlockLoader.AddViewModel(LeftAnteriorAspectsScoreRoiSummaryControlViewModel);
|
||||
|
||||
RightAnteriorAspectsScoreRoiSummaryControlViewModel = ActivatorUtilities.CreateInstance<AspectsScoreRoiSummaryControlViewModel>(ServiceProviderFactory.ServiceProvider);
|
||||
RightAnteriorAspectsScoreRoiSummaryControlViewModel.LoadPath = Path.Combine("roi", "anterior_aspects_r.json");
|
||||
RightAnteriorAspectsScoreRoiSummaryControlViewModel.RoiTabId = "anterior";
|
||||
dataBlockLoader.AddViewModel(RightAnteriorAspectsScoreRoiSummaryControlViewModel);
|
||||
|
||||
PosteriorAspectsScoreRoiSummaryControlViewModel = ActivatorUtilities.CreateInstance<AspectsScoreRoiSummaryControlViewModel>(ServiceProviderFactory.ServiceProvider);
|
||||
PosteriorAspectsScoreRoiSummaryControlViewModel.LoadPath = Path.Combine("roi", "posterior_aspects.json");
|
||||
PosteriorAspectsScoreRoiSummaryControlViewModel.IsTwoSided = true;
|
||||
PosteriorAspectsScoreRoiSummaryControlViewModel.RoiTabId = "posterior";
|
||||
dataBlockLoader.AddViewModel(PosteriorAspectsScoreRoiSummaryControlViewModel);
|
||||
|
||||
LeftAttRoiSummaryControlViewModel = ActivatorUtilities.CreateInstance<RoiSummaryControlViewModel>(ServiceProviderFactory.ServiceProvider);
|
||||
LeftAttRoiSummaryControlViewModel.LoadPath = Path.Combine("roi", "att_location_l.json");
|
||||
LeftAttRoiSummaryControlViewModel.RoiTabId = "att";
|
||||
dataBlockLoader.AddViewModel(LeftAttRoiSummaryControlViewModel);
|
||||
|
||||
RightAttRoiSummaryControlViewModel = ActivatorUtilities.CreateInstance<RoiSummaryControlViewModel>(ServiceProviderFactory.ServiceProvider);
|
||||
RightAttRoiSummaryControlViewModel.LoadPath = Path.Combine("roi", "att_location_r.json");
|
||||
RightAttRoiSummaryControlViewModel.RoiTabId = "att";
|
||||
dataBlockLoader.AddViewModel(RightAttRoiSummaryControlViewModel);
|
||||
|
||||
LeftAcbvRoiSummaryControlViewModel = ActivatorUtilities.CreateInstance<RoiSummaryControlViewModel>(ServiceProviderFactory.ServiceProvider);
|
||||
LeftAcbvRoiSummaryControlViewModel.LoadPath = Path.Combine("roi", "acbv_location_l.json");
|
||||
LeftAcbvRoiSummaryControlViewModel.RoiTabId = "acbv";
|
||||
dataBlockLoader.AddViewModel(LeftAcbvRoiSummaryControlViewModel);
|
||||
|
||||
RightAcbvRoiSummaryControlViewModel = ActivatorUtilities.CreateInstance<RoiSummaryControlViewModel>(ServiceProviderFactory.ServiceProvider);
|
||||
RightAcbvRoiSummaryControlViewModel.LoadPath = Path.Combine("roi", "acbv_location_r.json");
|
||||
RightAcbvRoiSummaryControlViewModel.RoiTabId = "acbv";
|
||||
dataBlockLoader.AddViewModel(RightAcbvRoiSummaryControlViewModel);
|
||||
|
||||
MosaicImageGroupViewModel = ActivatorUtilities.CreateInstance<MosaicImageGroupControlViewModel>(ServiceProviderFactory.ServiceProvider);
|
||||
dataBlockLoader.AddDeferredLoadingModuleViewModel(MosaicImageGroupViewModel);
|
||||
|
||||
dataBlockLoader.Init();
|
||||
|
||||
RoiSummaryControlViewModel.RoiTabIdToTabIndexDictionary = new Dictionary<string, int>
|
||||
{
|
||||
{ "cbf", 1 }, { "restricted_diffusion", 2 }, { "att", 3 }, { "acbv", 4 }, {"anterior", 5}, {"posterior", 6}
|
||||
};
|
||||
}
|
||||
|
||||
public List<ReportModuleExpanderViewModel> GenerateReportModuleGroups()
|
||||
{
|
||||
List<ReportModuleItemViewModel> atlasItems =
|
||||
[
|
||||
new ReportModuleItemViewModel(
|
||||
(string)Application.Current.TryFindResource("ArterialTerritoriesRotationViewerName"),
|
||||
"AnImage_ArterialTerritories"),
|
||||
new ReportModuleItemViewModel((string)Application.Current.TryFindResource("AspectsRotationViewerName"),
|
||||
"AnImage_ASPECTS")
|
||||
];
|
||||
|
||||
//TODO::maybe cache it when loading data is better. No need to check colorMap
|
||||
List<ReportModuleItemViewModel> colorImageItems = (from viewModel in MosaicImageGroupViewModel.MosaicImageCollection where viewModel.UseColorBar select new ReportModuleItemViewModel(viewModel.DisplayName, viewModel.DisplayName)).ToList();
|
||||
|
||||
|
||||
List<ReportModuleExpanderViewModel> result =
|
||||
[
|
||||
new ReportModuleExpanderViewModel((string)Application.Current.TryFindResource("ReportModuleAtlas"),
|
||||
"regional_perfusion", atlasItems),
|
||||
new ReportModuleExpanderViewModel((string)Application.Current.TryFindResource("ReportModuleColorImage"),
|
||||
"param_color_img", colorImageItems),
|
||||
new ReportModuleExpanderViewModel("CBF", "cbf",
|
||||
LeftAStrokeRoiSummaryControlViewModel.GenerateReportModuleList()
|
||||
.Concat(RightAStrokeRoiSummaryControlViewModel.GenerateReportModuleList())),
|
||||
new ReportModuleExpanderViewModel((string)Application.Current.TryFindResource("RoiTabHeader2"),
|
||||
"restricted_diffusion",
|
||||
LeftRestrictedDiffusionRoiSummaryControlViewModel.GenerateReportModuleList()
|
||||
.Concat(RightRestrictedDiffusionRoiSummaryControlViewModel.GenerateReportModuleList())),
|
||||
new ReportModuleExpanderViewModel("ATT", "att",
|
||||
LeftAttRoiSummaryControlViewModel.GenerateReportModuleList()
|
||||
.Concat(RightAttRoiSummaryControlViewModel.GenerateReportModuleList())),
|
||||
new ReportModuleExpanderViewModel("aCBV", "acbv",
|
||||
LeftAcbvRoiSummaryControlViewModel.GenerateReportModuleList()
|
||||
.Concat(RightAcbvRoiSummaryControlViewModel.GenerateReportModuleList())),
|
||||
new ReportModuleExpanderViewModel((string)Application.Current.TryFindResource("RoiTabHeader5"),
|
||||
"anterior_aspects",
|
||||
LeftAnteriorAspectsScoreRoiSummaryControlViewModel.GenerateReportModuleList()
|
||||
.Concat(RightAnteriorAspectsScoreRoiSummaryControlViewModel.GenerateReportModuleList())),
|
||||
new ReportModuleExpanderViewModel((string)Application.Current.TryFindResource("RoiTabHeader6"),
|
||||
"posterior_aspects", PosteriorAspectsScoreRoiSummaryControlViewModel.GenerateReportModuleList())
|
||||
];
|
||||
return result;
|
||||
}
|
||||
|
||||
public void Receive(DataLoadedMessage message)
|
||||
{
|
||||
RoiTabSelectedIndex = 1;
|
||||
WeakReferenceMessenger.Default.Send(new RoiTabChangedMessage(RoiTabSelectedIndex));
|
||||
AttTabVisible = LeftAttRoiSummaryControlViewModel.Visible || RightAttRoiSummaryControlViewModel.Visible;
|
||||
}
|
||||
|
||||
public void Receive(PerfusionDataGridChangeParameterMessage message)
|
||||
{
|
||||
if (message.Value.Item2)
|
||||
RoiTabSelectedIndex = 0;
|
||||
}
|
||||
}
|
31
Astroke/ViewModels/AStrokeRoiSummaryControlViewModel.cs
Normal file
|
@ -0,0 +1,31 @@
|
|||
using Livia.Models;
|
||||
using Livia.ViewModels;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Astroke.ViewModels;
|
||||
|
||||
public class AStrokeRoiSummaryControlViewModel(ILogger logger, IRoiLegendManager roiLegendManager)
|
||||
: RoiSummaryControlViewModel(logger, roiLegendManager)
|
||||
{
|
||||
public float Ratio1 { get => _ratio1; private set => SetProperty(ref _ratio1, value); }
|
||||
public float Ratio2 { get => _ratio2; private set => SetProperty(ref _ratio2, value); }
|
||||
public bool ShowDiagnosis { get => _showDiagnosis; private set => SetProperty(ref _showDiagnosis, value); }
|
||||
|
||||
|
||||
private float _ratio1;
|
||||
private float _ratio2;
|
||||
private bool _showDiagnosis;
|
||||
//private const float ShowDiagnosisThreshold = 1.8f;
|
||||
|
||||
protected override void UpdateDiagnosis()
|
||||
{
|
||||
float iscVolume = SelectedIsCRoiExpanderGroupViewModel?.SelectedVolume ?? 0;
|
||||
float ispVolume = SelectedIsPRoiExpanderGroupViewModel?.SelectedVolume ?? 0;
|
||||
|
||||
Ratio1 = ispVolume / iscVolume;
|
||||
Ratio2 = 100 * ispVolume / (ispVolume + iscVolume);
|
||||
|
||||
//ShowDiagnosis = Ratio1 > ShowDiagnosisThreshold && iscVolume > 0;
|
||||
ShowDiagnosis = false;
|
||||
}
|
||||
}
|
39
Astroke/ViewModels/AspectsScoreRoiSummaryControlViewModel.cs
Normal file
|
@ -0,0 +1,39 @@
|
|||
using Livia.Models;
|
||||
using Livia.ViewModels;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Astroke.ViewModels;
|
||||
|
||||
public class AspectsScoreRoiSummaryControlViewModel(ILogger logger, IRoiLegendManager roiLegendManager)
|
||||
: RoiSummaryControlViewModel(logger, roiLegendManager)
|
||||
{
|
||||
|
||||
public int CbfIscScore { get => _cbfIscScore; private set => SetProperty(ref _cbfIscScore, value); }
|
||||
public int AdcIscScore { get => _adcIscScore; private set => SetProperty(ref _adcIscScore, value); }
|
||||
|
||||
private int _cbfIscScore;
|
||||
private int _adcIscScore;
|
||||
private const int MaxScore = 10;
|
||||
private const float Threshold = 100.0f / 3;
|
||||
|
||||
protected override void UpdateDiagnosis()
|
||||
{
|
||||
CbfIscScore = CalculateScores(GetSelectedRoiExpanderGroupControlViewModel("CBF-IC")?.RoiViewModelCollection.Items);
|
||||
AdcIscScore = CalculateScores(GetSelectedRoiExpanderGroupControlViewModel("ADC-IC")?.RoiViewModelCollection.Items);
|
||||
}
|
||||
|
||||
|
||||
private static int CalculateScores(IEnumerable<RoiExpanderControlViewModel>? roiCollection)
|
||||
{
|
||||
if (roiCollection == null) return MaxScore;
|
||||
|
||||
int score = MaxScore - roiCollection.Where(roiExpanderControlViewModel => roiExpanderControlViewModel.IsChecked)
|
||||
.SelectMany(roiExpanderControlViewModel => roiExpanderControlViewModel.Locations)
|
||||
.GroupBy(roiLocationDataViewModel => roiLocationDataViewModel.Name, roiLocationDataViewModel => roiLocationDataViewModel)
|
||||
.Where(group => !group.Key.StartsWith("M") || group.Select(x => x.RegionPercentage).Sum() > Threshold)
|
||||
.Sum(group => group.First().Weight);
|
||||
|
||||
//Just in case
|
||||
return Math.Clamp(score, 0, MaxScore);
|
||||
}
|
||||
}
|
283
Astroke/Views/Controls/AStrokeControl.xaml
Normal file
|
@ -0,0 +1,283 @@
|
|||
<UserControl x:Class="Astroke.Views.Controls.AStrokeControl"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:viewModels="clr-namespace:Astroke.ViewModels"
|
||||
xmlns:liviaControls="clr-namespace:Livia.Views.Controls;assembly=Livia"
|
||||
xmlns:controls="clr-namespace:Astroke.Views.Controls"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
xmlns:attachedProperties="clr-namespace:Livia.Views.AttachedProperties;assembly=Livia"
|
||||
mc:Ignorable="d"
|
||||
VerticalAlignment="Stretch"
|
||||
HorizontalAlignment="Stretch"
|
||||
d:Background="{StaticResource PrimaryColorDarkBrush}"
|
||||
d:DataContext="{d:DesignInstance Type=viewModels:AStrokeControlViewModel}"
|
||||
d:DesignHeight="960"
|
||||
d:DesignWidth="1920">
|
||||
<Grid>
|
||||
<StackPanel VerticalAlignment="Center"
|
||||
Visibility="{Binding DataBlockLoader.PlaceholderVisible, Converter={StaticResource BooleanToVisibilityConverter}}">
|
||||
<Image VerticalAlignment="Center"
|
||||
RenderOptions.BitmapScalingMode="HighQuality"
|
||||
Width="972"
|
||||
Margin="0, 0, 0, 80"
|
||||
Source="/Resources/Images/placeholder.png" />
|
||||
<TextBlock Style="{StaticResource MaterialDesignHeadline5TextBlock}"
|
||||
HorizontalAlignment="Center"
|
||||
Foreground="{StaticResource PrimaryTextColorBrush}"
|
||||
Text="{StaticResource PlaceHolderMessage}" />
|
||||
</StackPanel>
|
||||
<Grid Visibility="{Binding DataBlockLoader.PlaceholderVisible, Converter={StaticResource InverseBooleanToVisibilityConverter}}"
|
||||
d:Visibility="Visible">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="2.5*" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid Grid.Column="0"
|
||||
Grid.Row="0"
|
||||
Margin="20, 20, 10, 10">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid Grid.Column="0">
|
||||
<liviaControls:ImageRotationViewerControl DataContext="{Binding ArterialTerritoriesImageRotationViewerControlViewModel}" />
|
||||
<Button Style="{StaticResource LiviaStretchIconButton}"
|
||||
Margin="5 5, 5, 45"
|
||||
CommandParameter="{Binding ArterialTerritoriesImageRotationViewerControlViewModel}" />
|
||||
</Grid>
|
||||
<Grid Grid.Column="1"
|
||||
Margin="20, 0, 0, 0">
|
||||
<liviaControls:ImageRotationViewerControl DataContext="{Binding AspectsImageRotationViewerControlViewModel}" />
|
||||
<Button Style="{StaticResource LiviaStretchIconButton}"
|
||||
Margin="5 5, 5, 45"
|
||||
CommandParameter="{Binding AspectsImageRotationViewerControlViewModel}" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Border Grid.Column="0"
|
||||
Grid.Row="1"
|
||||
CornerRadius="8"
|
||||
Margin="20, 10, 10, 20"
|
||||
Background="{StaticResource PrimaryColorBrush}">
|
||||
<liviaControls:MosaicImageGroupControl DataContext="{Binding MosaicImageGroupViewModel}" />
|
||||
</Border>
|
||||
<Border Grid.Column="1"
|
||||
Grid.Row="0"
|
||||
Grid.RowSpan="2"
|
||||
CornerRadius="8"
|
||||
Margin="10, 20, 20, 20"
|
||||
Background="{StaticResource PrimaryColorBrush}">
|
||||
<TabControl Style="{StaticResource MaterialDesignFilledTabControl}"
|
||||
materialDesign:ColorZoneAssist.Mode="Custom"
|
||||
HorizontalContentAlignment="Left"
|
||||
SelectedIndex="{Binding RoiTabSelectedIndex}"
|
||||
SelectionChanged="TabSelectionChanged"
|
||||
materialDesign:ColorZoneAssist.Foreground="{StaticResource PrimaryColorHighlightBrush}">
|
||||
<TabItem Header="{StaticResource PerfusionDataGridTabName}">
|
||||
<liviaControls:PerfusionDataGridGroupControl DataContext="{Binding PerfusionDataGridGroupViewModel}"/>
|
||||
</TabItem>
|
||||
<TabItem>
|
||||
<TabItem.Header>
|
||||
<TextBlock Text="{StaticResource RoiTabHeader1}"/>
|
||||
</TabItem.Header>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Rectangle Grid.Column="1"
|
||||
Width="2"
|
||||
RadiusX="1"
|
||||
RadiusY="1">
|
||||
<Rectangle.Fill>
|
||||
<LinearGradientBrush StartPoint="0, 0"
|
||||
EndPoint="1, 1">
|
||||
<GradientStop Color="#00D9D9D9"
|
||||
Offset="0.1" />
|
||||
<GradientStop Color="#D9D9D9"
|
||||
Offset="0.5" />
|
||||
<GradientStop Color="#00D9D9D9"
|
||||
Offset="0.9" />
|
||||
</LinearGradientBrush>
|
||||
</Rectangle.Fill>
|
||||
</Rectangle>
|
||||
<controls:AStrokeRoiSummaryControl Grid.Column="2"
|
||||
DataContext="{Binding LeftAStrokeRoiSummaryControlViewModel }" />
|
||||
<controls:AStrokeRoiSummaryControl Grid.Column="0"
|
||||
DataContext="{Binding RightAStrokeRoiSummaryControlViewModel }" />
|
||||
</Grid>
|
||||
</TabItem>
|
||||
<TabItem>
|
||||
<TabItem.Header>
|
||||
<TextBlock Text="{StaticResource RoiTabHeader2}" />
|
||||
</TabItem.Header>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Rectangle Grid.Column="1"
|
||||
Width="2"
|
||||
RadiusX="1"
|
||||
RadiusY="1">
|
||||
<Rectangle.Fill>
|
||||
<LinearGradientBrush StartPoint="0, 0"
|
||||
EndPoint="1, 1">
|
||||
<GradientStop Color="#00D9D9D9"
|
||||
Offset="0.1" />
|
||||
<GradientStop Color="#D9D9D9"
|
||||
Offset="0.5" />
|
||||
<GradientStop Color="#00D9D9D9"
|
||||
Offset="0.9" />
|
||||
</LinearGradientBrush>
|
||||
</Rectangle.Fill>
|
||||
</Rectangle>
|
||||
<liviaControls:RoiSummaryControl Grid.Column="2"
|
||||
DataContext="{Binding LeftRestrictedDiffusionRoiSummaryControlViewModel }" />
|
||||
<liviaControls:RoiSummaryControl Grid.Column="0"
|
||||
DataContext="{Binding RightRestrictedDiffusionRoiSummaryControlViewModel}" />
|
||||
</Grid>
|
||||
</TabItem>
|
||||
<TabItem Visibility="{Binding AttTabVisible, Converter={StaticResource BooleanToVisibilityConverter}}">
|
||||
<TabItem.Header>
|
||||
<TextBlock Text="{StaticResource RoiTabHeader3}"/>
|
||||
</TabItem.Header>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Rectangle Grid.Column="1"
|
||||
Width="2"
|
||||
RadiusX="1"
|
||||
RadiusY="1">
|
||||
<Rectangle.Fill>
|
||||
<LinearGradientBrush StartPoint="0, 0"
|
||||
EndPoint="1, 1">
|
||||
<GradientStop Color="#00D9D9D9"
|
||||
Offset="0.1" />
|
||||
<GradientStop Color="#D9D9D9"
|
||||
Offset="0.5" />
|
||||
<GradientStop Color="#00D9D9D9"
|
||||
Offset="0.9" />
|
||||
</LinearGradientBrush>
|
||||
</Rectangle.Fill>
|
||||
</Rectangle>
|
||||
<liviaControls:RoiSummaryControl Grid.Column="2"
|
||||
DataContext="{Binding LeftAttRoiSummaryControlViewModel }" />
|
||||
<liviaControls:RoiSummaryControl Grid.Column="0"
|
||||
DataContext="{Binding RightAttRoiSummaryControlViewModel }" />
|
||||
</Grid>
|
||||
</TabItem>
|
||||
<TabItem Visibility="{Binding AttTabVisible, Converter={StaticResource BooleanToVisibilityConverter}}">
|
||||
<TabItem.Header>
|
||||
<TextBlock Text="{StaticResource RoiTabHeader4}" />
|
||||
</TabItem.Header>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Rectangle Grid.Column="1"
|
||||
Width="2"
|
||||
RadiusX="1"
|
||||
RadiusY="1">
|
||||
<Rectangle.Fill>
|
||||
<LinearGradientBrush StartPoint="0, 0"
|
||||
EndPoint="1, 1">
|
||||
<GradientStop Color="#00D9D9D9"
|
||||
Offset="0.1" />
|
||||
<GradientStop Color="#D9D9D9"
|
||||
Offset="0.5" />
|
||||
<GradientStop Color="#00D9D9D9"
|
||||
Offset="0.9" />
|
||||
</LinearGradientBrush>
|
||||
</Rectangle.Fill>
|
||||
</Rectangle>
|
||||
<liviaControls:RoiSummaryControl Grid.Column="2"
|
||||
DataContext="{Binding LeftAcbvRoiSummaryControlViewModel }" />
|
||||
<liviaControls:RoiSummaryControl Grid.Column="0"
|
||||
DataContext="{Binding RightAcbvRoiSummaryControlViewModel }" />
|
||||
</Grid>
|
||||
</TabItem>
|
||||
<TabItem>
|
||||
<TabItem.Header>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
|
||||
<TextBlock Text="{StaticResource RoiTabHeader5}" />
|
||||
|
||||
<Button Style="{StaticResource LiviaPrimaryTinyIconButton}"
|
||||
Margin="4, 1, 0, 0"
|
||||
Foreground="{StaticResource White1Brush}"
|
||||
attachedProperties:IconKindProperties.IconKind="HelpCircleOutline"
|
||||
VerticalAlignment="Center"
|
||||
ToolTip="{StaticResource RoiTabHeader5Prompt}">
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</TabItem.Header>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Rectangle Grid.Row="0"
|
||||
Grid.Column="1"
|
||||
Width="2"
|
||||
RadiusX="1"
|
||||
RadiusY="1">
|
||||
<Rectangle.Fill>
|
||||
<LinearGradientBrush StartPoint="0, 0"
|
||||
EndPoint="1, 1">
|
||||
<GradientStop Color="#00D9D9D9"
|
||||
Offset="0.1" />
|
||||
<GradientStop Color="#D9D9D9"
|
||||
Offset="0.5" />
|
||||
<GradientStop Color="#00D9D9D9"
|
||||
Offset="0.9" />
|
||||
</LinearGradientBrush>
|
||||
</Rectangle.Fill>
|
||||
</Rectangle>
|
||||
<controls:AspectsScoreRoiSummaryControl Grid.Column="2"
|
||||
DataContext="{Binding LeftAnteriorAspectsScoreRoiSummaryControlViewModel }" />
|
||||
<controls:AspectsScoreRoiSummaryControl Grid.Column="0"
|
||||
DataContext="{Binding RightAnteriorAspectsScoreRoiSummaryControlViewModel }" />
|
||||
</Grid>
|
||||
</TabItem>
|
||||
<TabItem>
|
||||
<TabItem.Header>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Text="{StaticResource RoiTabHeader6}"/>
|
||||
|
||||
<Button Style="{StaticResource LiviaPrimaryTinyIconButton}"
|
||||
Margin="4, 1, 0, 0"
|
||||
Foreground="{StaticResource White1Brush}"
|
||||
attachedProperties:IconKindProperties.IconKind="HelpCircleOutline"
|
||||
VerticalAlignment="Center"
|
||||
ToolTip="{StaticResource RoiTabHeader6Prompt}">
|
||||
</Button>
|
||||
|
||||
</StackPanel>
|
||||
</TabItem.Header>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<controls:AspectsScoreRoiSummaryControl Grid.Column="0"
|
||||
DataContext="{Binding PosteriorAspectsScoreRoiSummaryControlViewModel}" />
|
||||
</Grid>
|
||||
</TabItem>
|
||||
</TabControl>
|
||||
</Border>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</UserControl>
|
29
Astroke/Views/Controls/AStrokeControl.xaml.cs
Normal file
|
@ -0,0 +1,29 @@
|
|||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using System.Windows.Controls;
|
||||
using Livia.Utility;
|
||||
|
||||
namespace Astroke.Views.Controls;
|
||||
|
||||
/// <summary>
|
||||
/// Interaction logic for AStrokeControl.xaml
|
||||
/// </summary>
|
||||
public partial class AStrokeControl
|
||||
{
|
||||
public AStrokeControl(object viewModel)
|
||||
{
|
||||
InitializeComponent();
|
||||
DataContext = viewModel;
|
||||
}
|
||||
|
||||
private void TabSelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (sender is not TabControl tab)
|
||||
return;
|
||||
|
||||
//This is also fired on start up, ignore those calls.
|
||||
if (!tab.IsLoaded)
|
||||
return;
|
||||
|
||||
WeakReferenceMessenger.Default.Send(new RoiTabChangedMessage(tab.SelectedIndex));
|
||||
}
|
||||
}
|
148
Astroke/Views/Controls/AStrokeRoiSummaryControl.xaml
Normal file
|
@ -0,0 +1,148 @@
|
|||
<UserControl x:Class="Astroke.Views.Controls.AStrokeRoiSummaryControl"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:liviaControls="clr-namespace:Livia.Views.Controls;assembly=Livia"
|
||||
xmlns:viewModels="clr-namespace:Astroke.ViewModels"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
mc:Ignorable="d"
|
||||
d:Background="{StaticResource PrimaryColorBrush}"
|
||||
d:DataContext="{d:DesignInstance Type=viewModels:AStrokeRoiSummaryControlViewModel}"
|
||||
d:DesignHeight="450"
|
||||
d:DesignWidth="800">
|
||||
|
||||
<Grid Margin="10"
|
||||
Visibility="{Binding Visible, Converter={StaticResource BooleanToVisibilityConverter}}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<liviaControls:RoiSummaryControl Grid.Row="0"
|
||||
DataContext="{Binding }" />
|
||||
|
||||
<StackPanel Grid.Row="1"
|
||||
Orientation="Vertical">
|
||||
<Rectangle Height="1"
|
||||
Margin="35, 0, 60, 20"
|
||||
Fill="{StaticResource PrimaryTextColorBrush}" />
|
||||
<UniformGrid Rows="1"
|
||||
Margin="45, 0, 70, 10"
|
||||
VerticalAlignment="Bottom">
|
||||
<Grid Margin="0, 0, 20, 0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"
|
||||
MaxWidth="200" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Border Background="{StaticResource PrimaryColorLightBrush}"
|
||||
CornerRadius="8">
|
||||
<WrapPanel Orientation="Vertical"
|
||||
Margin="10"
|
||||
HorizontalAlignment="Center">
|
||||
<TextBlock Style="{StaticResource MaterialDesignBody1TextBlock}"
|
||||
TextAlignment="Center"
|
||||
Text="{Binding Ratio1, StringFormat=N2}"
|
||||
Foreground="{StaticResource HighlightTextColorBrush}" />
|
||||
<StackPanel Orientation="Horizontal"
|
||||
HorizontalAlignment="Center">
|
||||
<Ellipse Fill="#ff69b4"
|
||||
VerticalAlignment="Center"
|
||||
Height="8"
|
||||
Width="8" />
|
||||
<TextBlock Style="{StaticResource MaterialDesignBody1TextBlock}"
|
||||
Text="/"
|
||||
VerticalAlignment="Center"
|
||||
Margin="3, 0, 3, 3"
|
||||
TextAlignment="Center"
|
||||
Foreground="{StaticResource PrimaryTextColorBrush}" />
|
||||
<Ellipse Fill="#0000ff"
|
||||
VerticalAlignment="Center"
|
||||
Height="8"
|
||||
Width="8" />
|
||||
</StackPanel>
|
||||
</WrapPanel>
|
||||
|
||||
</Border>
|
||||
</Grid>
|
||||
<Grid Margin="20, 0, 0, 0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="9999*"
|
||||
MaxWidth="200" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Border Grid.Column="1"
|
||||
Background="{StaticResource PrimaryColorLightBrush}"
|
||||
CornerRadius="8">
|
||||
<WrapPanel Orientation="Vertical"
|
||||
Margin="10"
|
||||
HorizontalAlignment="Center">
|
||||
<TextBlock Style="{StaticResource MaterialDesignBody1TextBlock}"
|
||||
TextAlignment="Center"
|
||||
Text="{Binding Ratio2, StringFormat={}{0:N2}%}"
|
||||
Foreground="{StaticResource HighlightTextColorBrush}" />
|
||||
|
||||
<StackPanel Orientation="Horizontal"
|
||||
HorizontalAlignment="Center">
|
||||
<Ellipse Fill="#ff69b4"
|
||||
VerticalAlignment="Center"
|
||||
Height="8"
|
||||
Width="8" />
|
||||
<TextBlock Style="{StaticResource MaterialDesignBody1TextBlock}"
|
||||
Text="/("
|
||||
VerticalAlignment="Center"
|
||||
Margin="3, 0, 3, 3"
|
||||
TextAlignment="Center"
|
||||
Foreground="{StaticResource PrimaryTextColorBrush}" />
|
||||
<Ellipse Fill="#0000ff"
|
||||
VerticalAlignment="Center"
|
||||
Height="8"
|
||||
Width="8" />
|
||||
<TextBlock Style="{StaticResource MaterialDesignBody1TextBlock}"
|
||||
Text="+"
|
||||
VerticalAlignment="Center"
|
||||
Margin="3, 0, 3, 3"
|
||||
TextAlignment="Center"
|
||||
Foreground="{StaticResource PrimaryTextColorBrush}" />
|
||||
<Ellipse Fill="#ff69b4"
|
||||
VerticalAlignment="Center"
|
||||
Height="8"
|
||||
Width="8" />
|
||||
<TextBlock Style="{StaticResource MaterialDesignBody1TextBlock}"
|
||||
Text=")"
|
||||
VerticalAlignment="Center"
|
||||
Margin="3, 0, 3, 3"
|
||||
TextAlignment="Center"
|
||||
Foreground="{StaticResource PrimaryTextColorBrush}" />
|
||||
</StackPanel>
|
||||
</WrapPanel>
|
||||
|
||||
</Border>
|
||||
</Grid>
|
||||
</UniformGrid>
|
||||
<Border BorderBrush="{StaticResource PrimaryTextColorBrush}"
|
||||
BorderThickness="1"
|
||||
Visibility="{Binding ShowDiagnosis, Converter={StaticResource BooleanToVisibilityConverter}}"
|
||||
Margin="45, 0, 70, 10"
|
||||
CornerRadius="8">
|
||||
<StackPanel Orientation="Horizontal"
|
||||
HorizontalAlignment="Center">
|
||||
<materialDesign:PackIcon Kind="AlertCircleOutline"
|
||||
VerticalAlignment="Center"
|
||||
Width="16"
|
||||
Height="16"
|
||||
Foreground="{StaticResource IscColorBrush}" />
|
||||
<TextBlock Style="{StaticResource MaterialDesignBody1TextBlock}"
|
||||
Margin="5"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{StaticResource IscColorBrush}"
|
||||
Text="{StaticResource AstrokeDiagnosis1}" />
|
||||
<TextBlock Style="{StaticResource MaterialDesignBody1TextBlock}"
|
||||
Margin="5"
|
||||
Foreground="{StaticResource HighlightTextColorBrush}"
|
||||
Text="{StaticResource AstrokeDiagnosis2}" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
</UserControl>
|
12
Astroke/Views/Controls/AStrokeRoiSummaryControl.xaml.cs
Normal file
|
@ -0,0 +1,12 @@
|
|||
namespace Astroke.Views.Controls;
|
||||
|
||||
/// <summary>
|
||||
/// Interaction logic for AStrokeRoiSummaryControl.xaml
|
||||
/// </summary>
|
||||
public partial class AStrokeRoiSummaryControl
|
||||
{
|
||||
public AStrokeRoiSummaryControl()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
101
Astroke/Views/Controls/AspectsScoreRoiSummaryControl.xaml
Normal file
|
@ -0,0 +1,101 @@
|
|||
<UserControl x:Class="Astroke.Views.Controls.AspectsScoreRoiSummaryControl"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:liviaControls="clr-namespace:Livia.Views.Controls;assembly=Livia"
|
||||
xmlns:viewModels="clr-namespace:Astroke.ViewModels"
|
||||
xmlns:converters="clr-namespace:Livia.Views.Converters;assembly=Livia"
|
||||
mc:Ignorable="d"
|
||||
d:Background="{StaticResource PrimaryColorBrush}"
|
||||
d:DataContext="{d:DesignInstance Type=viewModels:AspectsScoreRoiSummaryControlViewModel}"
|
||||
d:DesignHeight="450"
|
||||
d:DesignWidth="800">
|
||||
<UserControl.Resources>
|
||||
<ResourceDictionary>
|
||||
<converters:AspectsScoreToColorConverter x:Key="AspectsScoreToColorConverter" />
|
||||
</ResourceDictionary>
|
||||
</UserControl.Resources>
|
||||
<Grid Margin="10">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<liviaControls:RoiSummaryControl Grid.Row="0"
|
||||
DataContext="{Binding }"
|
||||
Visibility="{Binding Visible, Converter={StaticResource BooleanToVisibilityConverter}}"/>
|
||||
<StackPanel Grid.Row="1"
|
||||
Orientation="Vertical">
|
||||
<Rectangle Height="1"
|
||||
Margin="35, 0, 60, 20"
|
||||
Fill="{StaticResource PrimaryTextColorBrush}" />
|
||||
<UniformGrid Rows="1"
|
||||
Margin="45, 0, 70, 10"
|
||||
VerticalAlignment="Bottom">
|
||||
<Grid Margin="0, 0, 20, 0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"
|
||||
MaxWidth="200" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Border Background="{StaticResource PrimaryColorLightBrush}"
|
||||
CornerRadius="8">
|
||||
<WrapPanel Orientation="Vertical"
|
||||
Margin="10"
|
||||
HorizontalAlignment="Center">
|
||||
<StackPanel Orientation="Horizontal"
|
||||
HorizontalAlignment="Center">
|
||||
<TextBlock Style="{StaticResource MaterialDesignBody1TextBlock}"
|
||||
TextAlignment="Center"
|
||||
Text="{Binding CbfIscScore}"
|
||||
FontWeight="Bold"
|
||||
d:Text="10"
|
||||
Foreground="{Binding CbfIscScore, Converter={StaticResource AspectsScoreToColorConverter}}" />
|
||||
<TextBlock Style="{StaticResource MaterialDesignBody1TextBlock}"
|
||||
TextAlignment="Center"
|
||||
Text=" / 10"
|
||||
Foreground="{StaticResource PrimaryTextColorBrush}" />
|
||||
</StackPanel>
|
||||
<TextBlock Style="{StaticResource MaterialDesignBody1TextBlock}"
|
||||
Text="{StaticResource AspectsCbfIscScore}"
|
||||
TextAlignment="Center"
|
||||
Foreground="{StaticResource PrimaryTextColorBrush}" />
|
||||
</WrapPanel>
|
||||
</Border>
|
||||
</Grid>
|
||||
<Grid Margin="20, 0, 0, 0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="9999*"
|
||||
MaxWidth="200" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Border Grid.Column="1"
|
||||
Background="{StaticResource PrimaryColorLightBrush}"
|
||||
CornerRadius="8">
|
||||
<WrapPanel Orientation="Vertical"
|
||||
Margin="10"
|
||||
HorizontalAlignment="Center">
|
||||
<StackPanel Orientation="Horizontal"
|
||||
HorizontalAlignment="Center">
|
||||
<TextBlock Style="{StaticResource MaterialDesignBody1TextBlock}"
|
||||
TextAlignment="Center"
|
||||
Text="{Binding AdcIscScore}"
|
||||
FontWeight="Bold"
|
||||
d:Text="10"
|
||||
Foreground="{Binding AdcIscScore, Converter={StaticResource AspectsScoreToColorConverter}}" />
|
||||
<TextBlock Style="{StaticResource MaterialDesignBody1TextBlock}"
|
||||
TextAlignment="Center"
|
||||
Text=" / 10"
|
||||
Foreground="{StaticResource PrimaryTextColorBrush}" />
|
||||
</StackPanel>
|
||||
<TextBlock Style="{StaticResource MaterialDesignBody1TextBlock}"
|
||||
Text="{StaticResource AspectsAdcIscScore}"
|
||||
TextAlignment="Center"
|
||||
Foreground="{StaticResource PrimaryTextColorBrush}" />
|
||||
</WrapPanel>
|
||||
</Border>
|
||||
</Grid>
|
||||
</UniformGrid>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</UserControl>
|
12
Astroke/Views/Controls/AspectsScoreRoiSummaryControl.xaml.cs
Normal file
|
@ -0,0 +1,12 @@
|
|||
namespace Astroke.Views.Controls;
|
||||
|
||||
/// <summary>
|
||||
/// Interaction logic for AspectsScoreRoiSummaryControl.xaml
|
||||
/// </summary>
|
||||
public partial class AspectsScoreRoiSummaryControl
|
||||
{
|
||||
public AspectsScoreRoiSummaryControl()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
32
CereFlow/App.xaml
Normal file
|
@ -0,0 +1,32 @@
|
|||
<Application x:Class="CereFlow.App"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
Startup="OnStartup">
|
||||
<Application.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<materialDesign:BundledTheme BaseTheme="Light"
|
||||
PrimaryColor="Blue"
|
||||
SecondaryColor="Red" />
|
||||
<ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Defaults.xaml" />
|
||||
<ResourceDictionary Source="pack://application:,,,/Livia;component/Resources/Strings/Strings.xaml" />
|
||||
<ResourceDictionary Source="pack://application:,,,/Livia;component/Resources/Strings/Licenses.xaml" />
|
||||
<ResourceDictionary Source="pack://application:,,,/Livia;component/Resources/Themes/LiviaTheme.xaml" />
|
||||
<ResourceDictionary Source="pack://application:,,,/Livia;component/Resources/Themes/LiviaWindow.xaml" />
|
||||
<ResourceDictionary Source="pack://application:,,,/Livia;component/Resources/Themes/LiviaDialogDataTemplate.xaml" />
|
||||
<ResourceDictionary Source="pack://application:,,,/Livia;component/Resources/Themes/LiviaButton.xaml" />
|
||||
<ResourceDictionary Source="pack://application:,,,/Livia;component/Resources/Themes/LiviaCard.xaml" />
|
||||
<ResourceDictionary Source="pack://application:,,,/Livia;component/Resources/Themes/LiviaDataGrid.xaml" />
|
||||
<ResourceDictionary Source="pack://application:,,,/Livia;component/Resources/Themes/LiviaDatePicker.xaml" />
|
||||
<!--<ResourceDictionary Source="pack://application:,,,/Livia;component/Resources/Themes/LiviaScrollViewer.xaml" />-->
|
||||
<ResourceDictionary Source="pack://application:,,,/Livia;component/Resources/Themes/MaterialTitleBarFloatingActionButton.xaml" />
|
||||
<ResourceDictionary Source="pack://application:,,,/Livia;component/Resources/Themes/MainWindowTabButton.xaml" />
|
||||
<ResourceDictionary Source="pack://application:,,,/Livia;component/Resources/Themes/SettingsWindowItem.xaml" />
|
||||
<ResourceDictionary Source="pack://application:,,,/Livia;component/Resources/Themes/ScrollViewBarStyle.xaml" />
|
||||
<ResourceDictionary Source="pack://application:,,,/Livia;component/Resources/Themes/LiviaTabItem.xaml" />
|
||||
<ResourceDictionary Source="/Resources/Strings/Strings.xaml" />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
||||
</Application.Resources>
|
||||
</Application>
|
85
CereFlow/App.xaml.cs
Normal file
|
@ -0,0 +1,85 @@
|
|||
using System.IO;
|
||||
using System.Windows;
|
||||
using CereFlow.ViewModels;
|
||||
using CereFlow.Views.Controls;
|
||||
using JetBrains.Annotations;
|
||||
using Livia.Models;
|
||||
using Livia.Models.Data;
|
||||
using Livia.Properties;
|
||||
using Livia.Utility.DependencyInjection;
|
||||
using Livia.ViewModels;
|
||||
using Livia.Views;
|
||||
using Livia.Views.Utility;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Velopack;
|
||||
|
||||
namespace CereFlow;
|
||||
|
||||
/// <summary>
|
||||
/// Interaction logic for App.xaml
|
||||
/// </summary>
|
||||
public partial class App
|
||||
{
|
||||
|
||||
private readonly ILogger _logger;
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly IWarningSystem _warningSystem;
|
||||
private readonly IProcessManager _processManager;
|
||||
[UsedImplicitly] private Mutex? _mutex;
|
||||
|
||||
public App()
|
||||
{
|
||||
ServiceConfigurations.AppName = "cereflow";
|
||||
VelopackApp.Build().Run();
|
||||
ServiceProviderFactory.Init(sc => sc.AddSingleton<ILiviaControlViewModel, CereFlowControlViewModel>());
|
||||
|
||||
InitializeComponent();
|
||||
_serviceProvider = ServiceProviderFactory.ServiceProvider;
|
||||
_logger = ActivatorUtilities.GetServiceOrCreateInstance<ILogger<App>>(ServiceProviderFactory.ServiceProvider);
|
||||
_warningSystem = ActivatorUtilities.GetServiceOrCreateInstance<IWarningSystem>(ServiceProviderFactory.ServiceProvider);
|
||||
_processManager = ActivatorUtilities.GetServiceOrCreateInstance<IProcessManager>(ServiceProviderFactory.ServiceProvider);
|
||||
_logger.LogInformation("App Starting");
|
||||
SettingsLogger.Init();
|
||||
|
||||
AppHelper helper = ActivatorUtilities.GetServiceOrCreateInstance<AppHelper>(ServiceProviderFactory.ServiceProvider);
|
||||
|
||||
Dispatcher.UnhandledException += helper.OnDispatcherUnhandledException;
|
||||
helper.UpdateSettings();
|
||||
helper.LoadStrings();
|
||||
}
|
||||
|
||||
private void OnStartup(object sender, StartupEventArgs eventArgs)
|
||||
{
|
||||
_mutex = new Mutex(true, "Livia", out bool isNewInstance);
|
||||
if (!isNewInstance)
|
||||
{
|
||||
_warningSystem.ShowDialog(WarningWindowKind.Warning, true, "InstanceIsRunningError");
|
||||
Current.Shutdown();
|
||||
}
|
||||
|
||||
if (File.Exists(Path.Join(Directory.GetCurrentDirectory(), "astroke_cmd", "astroke_back-sqlite", "astroke_back-sqlite.exe")))
|
||||
{
|
||||
bool success = _processManager.Start([
|
||||
new ServerFileLocation("astroke_back-sqlite.exe", Path.Join(Directory.GetCurrentDirectory(), "astroke_cmd", "astroke_back-sqlite")),
|
||||
new ServerFileLocation("data-storage-sqlite.exe", Path.Join(Directory.GetCurrentDirectory(), "astroke_cmd", "data-storage-sqlite"))
|
||||
]);
|
||||
if (!success)
|
||||
{
|
||||
_warningSystem.ShowDialog(WarningWindowKind.Error, true, "SystemCrashing");
|
||||
_logger.LogError("Cannot start process");
|
||||
Current.Shutdown();
|
||||
return;
|
||||
}
|
||||
Settings.Default.ServerAddress = "127.0.0.1";
|
||||
Settings.Default.ServerPort = 8080;
|
||||
}
|
||||
|
||||
MainWindow mainWindow = ActivatorUtilities.CreateInstance<MainWindow>(_serviceProvider);
|
||||
mainWindow.AddMainControl(new CereFlowControl(ActivatorUtilities.GetServiceOrCreateInstance<ILiviaControlViewModel>(ServiceProviderFactory.ServiceProvider)));
|
||||
MainWindow = mainWindow;
|
||||
mainWindow.Show();
|
||||
|
||||
_logger.LogInformation("App Started");
|
||||
}
|
||||
}
|
10
CereFlow/AssemblyInfo.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
using System.Windows;
|
||||
|
||||
[assembly: ThemeInfo(
|
||||
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
|
||||
//(used if a resource is not found in the page,
|
||||
// or application resource dictionaries)
|
||||
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
|
||||
//(used if a resource is not found in the page,
|
||||
// app, or any theme specific resource dictionaries)
|
||||
)]
|
38
CereFlow/CereFlow.csproj
Normal file
|
@ -0,0 +1,38 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>net8.0-windows</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<UseWPF>true</UseWPF>
|
||||
<ApplicationIcon>Resources\Images\cereflow.ico</ApplicationIcon>
|
||||
<AssemblyName>Cereflow</AssemblyName>
|
||||
<IncludeNativeLibrariesForSelfExtract>true</IncludeNativeLibrariesForSelfExtract>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="astroke_cmd\**">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="Resources\Images\cereflow.ico" />
|
||||
<None Remove="Resources\Images\placeholder.png" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup>
|
||||
<!-- This overrides the default Program.Main that WPF creates for you, and allows you to add VelopackApp -->
|
||||
<StartupObject>CereFlow.Program</StartupObject>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Resources\Images\cereflow.ico">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Resources\Images\placeholder.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Livia\Livia.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
31
CereFlow/Program.cs
Normal file
|
@ -0,0 +1,31 @@
|
|||
using JetBrains.Annotations;
|
||||
using System.Windows;
|
||||
using Velopack;
|
||||
|
||||
namespace CereFlow;
|
||||
|
||||
// from https://github.com/velopack/velopack/blob/master/samples/VeloWpfSample/Program.cs
|
||||
[UsedImplicitly]
|
||||
public class Program
|
||||
{
|
||||
|
||||
[STAThread]
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
try
|
||||
{
|
||||
//TODO::multi language
|
||||
VelopackApp.Build().WithFirstRun(_ => MessageBox.Show("软件安装成功。后续请使用桌面快捷方式访问本软件。")).Run();
|
||||
|
||||
// We can now launch the WPF application as normal.
|
||||
App app = new();
|
||||
app.InitializeComponent();
|
||||
app.Run();
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show($"Unhandled exception: {ex}");
|
||||
}
|
||||
}
|
||||
}
|
BIN
CereFlow/Resources/Images/cereflow.ico
Normal file
After Width: | Height: | Size: 115 KiB |
BIN
CereFlow/Resources/Images/placeholder.png
Normal file
After Width: | Height: | Size: 99 KiB |
23
CereFlow/Resources/Strings/Strings.xaml
Normal file
|
@ -0,0 +1,23 @@
|
|||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:system="clr-namespace:System;assembly=System.Runtime">
|
||||
|
||||
<system:String x:Key="LoginControlMessage">欢迎使用CereFlow</system:String>
|
||||
<system:String x:Key="WindowTitle">CereFlow</system:String>
|
||||
<system:String x:Key="PlaceHolderMessage">请上传MD-pCASL或SD-pCASL、3D T2 FLAIR序列,所有序列使用同一StudyInstanceUID</system:String>
|
||||
|
||||
<system:String x:Key="RoiTabHeader1">CBF</system:String>
|
||||
<system:String x:Key="RoiTabHeader2">ATT</system:String>
|
||||
<system:String x:Key="RoiTabHeader3">aCBV</system:String>
|
||||
<system:String x:Key="SoftwareVersionInfo"
|
||||
xml:space="preserve">软件名称:医学图像处理软件
|
||||
规格型号:CereFlow
|
||||
发布版本:V1
|
||||
完整版本:V1.0.0.0</system:String>
|
||||
|
||||
<system:String x:Key="ManufacturerInformation"
|
||||
xml:space="preserve">注册人/企业名称: 安影科技(北京)有限公司
|
||||
生产地址: 北京市大兴区宝参南街16号院3号楼(3A)201单元205、206、207室
|
||||
售后服务单位: 安影科技(北京)有限公司
|
||||
联系方式: 010-69465675</system:String>
|
||||
</ResourceDictionary>
|
163
CereFlow/ViewModels/CereFlowControlViewModel.cs
Normal file
|
@ -0,0 +1,163 @@
|
|||
using System.IO;
|
||||
using System.Windows;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using Livia.Models;
|
||||
using Livia.Utility;
|
||||
using Livia.Utility.DependencyInjection;
|
||||
using Livia.ViewModels;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace CereFlow.ViewModels;
|
||||
|
||||
public class CereFlowControlViewModel : ObservableRecipient, ILiviaControlViewModel, IRecipient<DataLoadedMessage>, IRecipient<PerfusionDataGridChangeParameterMessage>
|
||||
{
|
||||
public ImageRotationViewerControlViewModel BrainLobesImageRotationViewerControlViewModel { get; }
|
||||
public ImageRotationViewerControlViewModel LimbicSystemImageRotationViewerControlViewModel { get; }
|
||||
public PerfusionDataGridGroupControlViewModel PerfusionDataGridGroupViewModel { get; }
|
||||
|
||||
public RoiSummaryControlViewModel LeftAnteriorRoiSummaryControlViewModel { get; }
|
||||
public RoiSummaryControlViewModel RightAnteriorRoiSummaryControlViewModel { get; }
|
||||
public RoiSummaryControlViewModel LeftAttRoiSummaryControlViewModel { get; }
|
||||
public RoiSummaryControlViewModel RightAttRoiSummaryControlViewModel { get; }
|
||||
public RoiSummaryControlViewModel LeftAcbvRoiSummaryControlViewModel { get; }
|
||||
public RoiSummaryControlViewModel RightAcbvRoiSummaryControlViewModel { get; }
|
||||
|
||||
public MosaicImageGroupControlViewModel MosaicImageGroupViewModel { get; }
|
||||
//TODO::temp fix I do not like this
|
||||
public bool AttTabVisible { get => _attTabVisible; set => SetProperty(ref _attTabVisible, value); }
|
||||
public IDataBlockLoader DataBlockLoader { get; }
|
||||
public int RoiTabSelectedIndex { get => _roiTabSelectedIndex; set => SetProperty(ref _roiTabSelectedIndex, value); }
|
||||
|
||||
private int _roiTabSelectedIndex;
|
||||
private bool _attTabVisible = true;
|
||||
|
||||
public CereFlowControlViewModel(IDataBlockLoader dataBlockLoader)
|
||||
{
|
||||
WeakReferenceMessenger.Default.RegisterAll(this);
|
||||
DataBlockLoader = dataBlockLoader;
|
||||
|
||||
BrainLobesImageRotationViewerControlViewModel = ActivatorUtilities.CreateInstance<ImageRotationViewerControlViewModel>(ServiceProviderFactory.ServiceProvider);
|
||||
BrainLobesImageRotationViewerControlViewModel.DisplayName = (string)Application.Current.TryFindResource("BrainLobesRotationViewerName");
|
||||
BrainLobesImageRotationViewerControlViewModel.AtlasMaskLoadPath = @"atlas\AnImage_BrainLobes\mask";
|
||||
BrainLobesImageRotationViewerControlViewModel.StructLoadPath = "structure";
|
||||
BrainLobesImageRotationViewerControlViewModel.StructDataType = ImageRotationViewerDataType.Dicom2D;
|
||||
BrainLobesImageRotationViewerControlViewModel.AtlasId = "AnImage_BrainLobes";
|
||||
BrainLobesImageRotationViewerControlViewModel.ImageIndexSyncKey = "structure";
|
||||
dataBlockLoader.AddViewModel(BrainLobesImageRotationViewerControlViewModel);
|
||||
|
||||
LimbicSystemImageRotationViewerControlViewModel = ActivatorUtilities.CreateInstance<ImageRotationViewerControlViewModel>(ServiceProviderFactory.ServiceProvider);
|
||||
LimbicSystemImageRotationViewerControlViewModel.DisplayName = (string)Application.Current.TryFindResource("LimbicSystemRotationViewerName");
|
||||
LimbicSystemImageRotationViewerControlViewModel.AtlasMaskLoadPath = @"atlas\LimbicSystem\mask";
|
||||
LimbicSystemImageRotationViewerControlViewModel.StructLoadPath = "structure";
|
||||
LimbicSystemImageRotationViewerControlViewModel.StructDataType = ImageRotationViewerDataType.Dicom2D;
|
||||
LimbicSystemImageRotationViewerControlViewModel.AtlasId = "LimbicSystem";
|
||||
LimbicSystemImageRotationViewerControlViewModel.ImageIndexSyncKey = "structure";
|
||||
dataBlockLoader.AddViewModel(LimbicSystemImageRotationViewerControlViewModel);
|
||||
|
||||
PerfusionDataGridControlViewModel cerebrumCerebellumPerfusionDataGridControlViewModel = ActivatorUtilities.CreateInstance<PerfusionDataGridControlViewModel>(ServiceProviderFactory.ServiceProvider);
|
||||
cerebrumCerebellumPerfusionDataGridControlViewModel.LoadPath = Path.Combine("atlas", "AnImage_CerebrumCerebellum");
|
||||
cerebrumCerebellumPerfusionDataGridControlViewModel.GridTabName = (string)Application.Current.TryFindResource("CerebrumCerebellumPerfusionDataGridName");
|
||||
|
||||
PerfusionDataGridControlViewModel brainLobesPerfusionDataGridControlViewModel = ActivatorUtilities.CreateInstance<PerfusionDataGridControlViewModel>(ServiceProviderFactory.ServiceProvider);
|
||||
brainLobesPerfusionDataGridControlViewModel.LoadPath = Path.Combine("atlas", "AnImage_BrainLobes");
|
||||
brainLobesPerfusionDataGridControlViewModel.GridTabName = (string)Application.Current.TryFindResource("BrainLobesPerfusionDataGridName");
|
||||
|
||||
PerfusionDataGridControlViewModel limbicSystemPerfusionDataGridControlViewModel = ActivatorUtilities.CreateInstance<PerfusionDataGridControlViewModel>(ServiceProviderFactory.ServiceProvider);
|
||||
limbicSystemPerfusionDataGridControlViewModel.LoadPath = Path.Combine("atlas", "LimbicSystem");
|
||||
limbicSystemPerfusionDataGridControlViewModel.GridTabName = (string)Application.Current.TryFindResource("LimbicSystemPerfusionDataGridName");
|
||||
|
||||
PerfusionDataGridGroupViewModel = ActivatorUtilities.CreateInstance<PerfusionDataGridGroupControlViewModel>(ServiceProviderFactory.ServiceProvider);
|
||||
PerfusionDataGridGroupViewModel.PerfusionDataGridViewModelCollection.Add(cerebrumCerebellumPerfusionDataGridControlViewModel);
|
||||
PerfusionDataGridGroupViewModel.PerfusionDataGridViewModelCollection.Add(brainLobesPerfusionDataGridControlViewModel);
|
||||
PerfusionDataGridGroupViewModel.PerfusionDataGridViewModelCollection.Add(limbicSystemPerfusionDataGridControlViewModel);
|
||||
dataBlockLoader.AddViewModel(PerfusionDataGridGroupViewModel);
|
||||
|
||||
LeftAnteriorRoiSummaryControlViewModel = ActivatorUtilities.CreateInstance<RoiSummaryControlViewModel>(ServiceProviderFactory.ServiceProvider);
|
||||
LeftAnteriorRoiSummaryControlViewModel.LoadPath = Path.Combine("roi", "cbf_location_l.json");
|
||||
LeftAnteriorRoiSummaryControlViewModel.RoiTabId = "cbf";
|
||||
dataBlockLoader.AddViewModel(LeftAnteriorRoiSummaryControlViewModel);
|
||||
|
||||
RightAnteriorRoiSummaryControlViewModel = ActivatorUtilities.CreateInstance<RoiSummaryControlViewModel>(ServiceProviderFactory.ServiceProvider);
|
||||
RightAnteriorRoiSummaryControlViewModel.LoadPath = Path.Combine("roi", "cbf_location_r.json");
|
||||
RightAnteriorRoiSummaryControlViewModel.RoiTabId = "cbf";
|
||||
dataBlockLoader.AddViewModel(RightAnteriorRoiSummaryControlViewModel);
|
||||
|
||||
LeftAttRoiSummaryControlViewModel = ActivatorUtilities.CreateInstance<RoiSummaryControlViewModel>(ServiceProviderFactory.ServiceProvider);
|
||||
LeftAttRoiSummaryControlViewModel.LoadPath = Path.Combine("roi", "att_location_l.json");
|
||||
LeftAttRoiSummaryControlViewModel.RoiTabId = "att";
|
||||
dataBlockLoader.AddViewModel(LeftAttRoiSummaryControlViewModel);
|
||||
|
||||
RightAttRoiSummaryControlViewModel = ActivatorUtilities.CreateInstance<RoiSummaryControlViewModel>(ServiceProviderFactory.ServiceProvider);
|
||||
RightAttRoiSummaryControlViewModel.LoadPath = Path.Combine("roi", "att_location_r.json");
|
||||
RightAttRoiSummaryControlViewModel.RoiTabId = "att";
|
||||
dataBlockLoader.AddViewModel(RightAttRoiSummaryControlViewModel);
|
||||
|
||||
LeftAcbvRoiSummaryControlViewModel = ActivatorUtilities.CreateInstance<RoiSummaryControlViewModel>(ServiceProviderFactory.ServiceProvider);
|
||||
LeftAcbvRoiSummaryControlViewModel.LoadPath = Path.Combine("roi", "acbv_location_l.json");
|
||||
LeftAcbvRoiSummaryControlViewModel.RoiTabId = "acbv";
|
||||
dataBlockLoader.AddViewModel(LeftAcbvRoiSummaryControlViewModel);
|
||||
|
||||
RightAcbvRoiSummaryControlViewModel = ActivatorUtilities.CreateInstance<RoiSummaryControlViewModel>(ServiceProviderFactory.ServiceProvider);
|
||||
RightAcbvRoiSummaryControlViewModel.LoadPath = Path.Combine("roi", "acbv_location_r.json");
|
||||
RightAcbvRoiSummaryControlViewModel.RoiTabId = "acbv";
|
||||
dataBlockLoader.AddViewModel(RightAcbvRoiSummaryControlViewModel);
|
||||
|
||||
MosaicImageGroupViewModel = ActivatorUtilities.CreateInstance<MosaicImageGroupControlViewModel>(ServiceProviderFactory.ServiceProvider);
|
||||
dataBlockLoader.AddDeferredLoadingModuleViewModel(MosaicImageGroupViewModel);
|
||||
|
||||
dataBlockLoader.Init();
|
||||
|
||||
RoiSummaryControlViewModel.RoiTabIdToTabIndexDictionary = new Dictionary<string, int>
|
||||
{
|
||||
{ "cbf", 1 }, { "att", 2 }, { "acbv", 3 }
|
||||
};
|
||||
}
|
||||
|
||||
public List<ReportModuleExpanderViewModel> GenerateReportModuleGroups()
|
||||
{
|
||||
List<ReportModuleItemViewModel> atlasItems =
|
||||
[
|
||||
new ReportModuleItemViewModel((string)Application.Current.TryFindResource("CerebrumCerebellumPerfusionDataGridName"),
|
||||
"AnImage_CerebrumCerebellum"),
|
||||
new ReportModuleItemViewModel((string)Application.Current.TryFindResource("BrainLobesRotationViewerName"),
|
||||
"AnImage_BrainLobes"),
|
||||
new ReportModuleItemViewModel((string)Application.Current.TryFindResource("LimbicSystemRotationViewerName"),
|
||||
"LimbicSystem")
|
||||
];
|
||||
|
||||
//TODO::maybe cache it when loading data is better. No need to check colorMap
|
||||
List<ReportModuleItemViewModel> colorImageItems = (from viewModel in MosaicImageGroupViewModel.MosaicImageCollection where viewModel.UseColorBar select new ReportModuleItemViewModel(viewModel.DisplayName, viewModel.DisplayName)).ToList();
|
||||
|
||||
List<ReportModuleExpanderViewModel> result =
|
||||
[
|
||||
new ReportModuleExpanderViewModel((string)Application.Current.TryFindResource("ReportModuleAtlas"),
|
||||
"regional_perfusion", atlasItems),
|
||||
new ReportModuleExpanderViewModel((string)Application.Current.TryFindResource("ReportModuleColorImage"),
|
||||
"param_color_img", colorImageItems),
|
||||
new ReportModuleExpanderViewModel("CBF", "cbf",
|
||||
LeftAnteriorRoiSummaryControlViewModel.GenerateReportModuleList()
|
||||
.Concat(RightAnteriorRoiSummaryControlViewModel.GenerateReportModuleList())),
|
||||
new ReportModuleExpanderViewModel("ATT", "att",
|
||||
LeftAttRoiSummaryControlViewModel.GenerateReportModuleList()
|
||||
.Concat(RightAttRoiSummaryControlViewModel.GenerateReportModuleList())),
|
||||
new ReportModuleExpanderViewModel("aCBV", "acbv",
|
||||
LeftAcbvRoiSummaryControlViewModel.GenerateReportModuleList()
|
||||
.Concat(RightAcbvRoiSummaryControlViewModel.GenerateReportModuleList()))
|
||||
];
|
||||
return result;
|
||||
}
|
||||
|
||||
public void Receive(DataLoadedMessage message)
|
||||
{
|
||||
RoiTabSelectedIndex = 1;
|
||||
WeakReferenceMessenger.Default.Send(new RoiTabChangedMessage(RoiTabSelectedIndex));
|
||||
AttTabVisible = LeftAttRoiSummaryControlViewModel.Visible || RightAttRoiSummaryControlViewModel.Visible;
|
||||
}
|
||||
|
||||
public void Receive(PerfusionDataGridChangeParameterMessage message)
|
||||
{
|
||||
if (message.Value.Item2)
|
||||
RoiTabSelectedIndex = 0;
|
||||
}
|
||||
}
|
182
CereFlow/Views/Controls/CereFlowControl.xaml
Normal file
|
@ -0,0 +1,182 @@
|
|||
<UserControl x:Class="CereFlow.Views.Controls.CereFlowControl"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:viewModels="clr-namespace:CereFlow.ViewModels"
|
||||
xmlns:liviaControls="clr-namespace:Livia.Views.Controls;assembly=Livia"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
mc:Ignorable="d"
|
||||
VerticalAlignment="Stretch"
|
||||
HorizontalAlignment="Stretch"
|
||||
d:Background="{StaticResource PrimaryColorDarkBrush}"
|
||||
d:DataContext="{d:DesignInstance Type=viewModels:CereFlowControlViewModel}"
|
||||
d:DesignHeight="960"
|
||||
d:DesignWidth="1920">
|
||||
<Grid>
|
||||
<StackPanel VerticalAlignment="Center"
|
||||
Visibility="{Binding DataBlockLoader.PlaceholderVisible, Converter={StaticResource BooleanToVisibilityConverter}}">
|
||||
<Image VerticalAlignment="Center"
|
||||
RenderOptions.BitmapScalingMode="HighQuality"
|
||||
Width="600"
|
||||
Visibility="{Binding DataBlockLoader.PlaceholderVisible, Converter={StaticResource BooleanToVisibilityConverter}}"
|
||||
Margin="0, 0, 0, 80"
|
||||
Source="/Resources/Images/placeholder.png" />
|
||||
<TextBlock Style="{StaticResource MaterialDesignHeadline5TextBlock}"
|
||||
HorizontalAlignment="Center"
|
||||
Foreground="{StaticResource PrimaryTextColorBrush}"
|
||||
Text="{StaticResource PlaceHolderMessage}" />
|
||||
</StackPanel>
|
||||
<Grid Visibility="{Binding DataBlockLoader.PlaceholderVisible, Converter={StaticResource InverseBooleanToVisibilityConverter}}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="2.5*" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid Grid.Column="0"
|
||||
Grid.Row="0"
|
||||
Margin="20, 20, 10, 10">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid Grid.Column="0">
|
||||
<liviaControls:ImageRotationViewerControl DataContext="{Binding BrainLobesImageRotationViewerControlViewModel}" />
|
||||
<Button Style="{StaticResource LiviaStretchIconButton}"
|
||||
Margin="5 5, 5, 45"
|
||||
CommandParameter="{Binding BrainLobesImageRotationViewerControlViewModel}" />
|
||||
</Grid>
|
||||
<Grid Grid.Column="1"
|
||||
Margin="20, 0, 0, 0">
|
||||
<liviaControls:ImageRotationViewerControl DataContext="{Binding LimbicSystemImageRotationViewerControlViewModel}" />
|
||||
<Button Style="{StaticResource LiviaStretchIconButton}"
|
||||
Margin="5 5, 5, 45"
|
||||
CommandParameter="{Binding LimbicSystemImageRotationViewerControlViewModel}" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Border Grid.Column="0"
|
||||
Grid.Row="1"
|
||||
CornerRadius="8"
|
||||
Margin="20, 10, 10, 20"
|
||||
Background="{StaticResource PrimaryColorBrush}">
|
||||
<liviaControls:MosaicImageGroupControl DataContext="{Binding MosaicImageGroupViewModel}" />
|
||||
</Border>
|
||||
|
||||
<Border Grid.Column="1"
|
||||
Grid.Row="0"
|
||||
Grid.RowSpan="2"
|
||||
CornerRadius="8"
|
||||
Margin="10, 20, 20, 20"
|
||||
Background="{StaticResource PrimaryColorBrush}">
|
||||
<TabControl
|
||||
HorizontalContentAlignment="Left"
|
||||
SelectedIndex="{Binding RoiTabSelectedIndex}"
|
||||
SelectionChanged="TabSelectionChanged"
|
||||
materialDesign:ColorZoneAssist.Foreground="{StaticResource PrimaryColorHighlightBrush}">
|
||||
<TabItem Header="{StaticResource PerfusionDataGridTabName}">
|
||||
<liviaControls:PerfusionDataGridGroupControl DataContext="{Binding PerfusionDataGridGroupViewModel}" />
|
||||
</TabItem>
|
||||
<TabItem>
|
||||
<TabItem.Header>
|
||||
<TextBlock Text="{StaticResource RoiTabHeader1}"/>
|
||||
</TabItem.Header>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Rectangle Grid.Column="1"
|
||||
Width="2"
|
||||
RadiusX="1"
|
||||
RadiusY="1">
|
||||
<Rectangle.Fill>
|
||||
<LinearGradientBrush StartPoint="0, 0"
|
||||
EndPoint="1, 1">
|
||||
<GradientStop Color="#00D9D9D9"
|
||||
Offset="0.1" />
|
||||
<GradientStop Color="#D9D9D9"
|
||||
Offset="0.5" />
|
||||
<GradientStop Color="#00D9D9D9"
|
||||
Offset="0.9" />
|
||||
</LinearGradientBrush>
|
||||
</Rectangle.Fill>
|
||||
</Rectangle>
|
||||
<liviaControls:RoiSummaryControl Grid.Column="2"
|
||||
DataContext="{Binding LeftAnteriorRoiSummaryControlViewModel }" />
|
||||
<liviaControls:RoiSummaryControl Grid.Column="0"
|
||||
DataContext="{Binding RightAnteriorRoiSummaryControlViewModel }" />
|
||||
</Grid>
|
||||
</TabItem>
|
||||
<TabItem Visibility="{Binding AttTabVisible, Converter={StaticResource BooleanToVisibilityConverter}}">
|
||||
<TabItem.Header>
|
||||
<TextBlock Text="{StaticResource RoiTabHeader2}" />
|
||||
</TabItem.Header>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Rectangle Grid.Column="1"
|
||||
Width="2"
|
||||
RadiusX="1"
|
||||
RadiusY="1">
|
||||
<Rectangle.Fill>
|
||||
<LinearGradientBrush StartPoint="0, 0"
|
||||
EndPoint="1, 1">
|
||||
<GradientStop Color="#00D9D9D9"
|
||||
Offset="0.1" />
|
||||
<GradientStop Color="#D9D9D9"
|
||||
Offset="0.5" />
|
||||
<GradientStop Color="#00D9D9D9"
|
||||
Offset="0.9" />
|
||||
</LinearGradientBrush>
|
||||
</Rectangle.Fill>
|
||||
</Rectangle>
|
||||
<liviaControls:RoiSummaryControl Grid.Column="2"
|
||||
DataContext="{Binding LeftAttRoiSummaryControlViewModel }" />
|
||||
<liviaControls:RoiSummaryControl Grid.Column="0"
|
||||
DataContext="{Binding RightAttRoiSummaryControlViewModel }" />
|
||||
</Grid>
|
||||
</TabItem>
|
||||
<TabItem Visibility="{Binding AttTabVisible, Converter={StaticResource BooleanToVisibilityConverter}}">
|
||||
<TabItem.Header>
|
||||
<TextBlock Text="{StaticResource RoiTabHeader3}" />
|
||||
</TabItem.Header>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Rectangle Grid.Column="1"
|
||||
Width="2"
|
||||
RadiusX="1"
|
||||
RadiusY="1">
|
||||
<Rectangle.Fill>
|
||||
<LinearGradientBrush StartPoint="0, 0"
|
||||
EndPoint="1, 1">
|
||||
<GradientStop Color="#00D9D9D9"
|
||||
Offset="0.1" />
|
||||
<GradientStop Color="#D9D9D9"
|
||||
Offset="0.5" />
|
||||
<GradientStop Color="#00D9D9D9"
|
||||
Offset="0.9" />
|
||||
</LinearGradientBrush>
|
||||
</Rectangle.Fill>
|
||||
</Rectangle>
|
||||
<liviaControls:RoiSummaryControl Grid.Column="2"
|
||||
DataContext="{Binding LeftAcbvRoiSummaryControlViewModel }" />
|
||||
<liviaControls:RoiSummaryControl Grid.Column="0"
|
||||
DataContext="{Binding RightAcbvRoiSummaryControlViewModel }" />
|
||||
</Grid>
|
||||
</TabItem>
|
||||
</TabControl>
|
||||
</Border>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</UserControl>
|
29
CereFlow/Views/Controls/CereFlowControl.xaml.cs
Normal file
|
@ -0,0 +1,29 @@
|
|||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using Livia.Utility;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace CereFlow.Views.Controls;
|
||||
|
||||
/// <summary>
|
||||
/// Interaction logic for CereFlowControl.xaml
|
||||
/// </summary>
|
||||
public partial class CereFlowControl
|
||||
{
|
||||
public CereFlowControl(object viewModel)
|
||||
{
|
||||
InitializeComponent();
|
||||
DataContext = viewModel;
|
||||
}
|
||||
|
||||
private void TabSelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (sender is not TabControl tab)
|
||||
return;
|
||||
|
||||
//This is also fired on start up, ignore those calls.
|
||||
if (!tab.IsLoaded)
|
||||
return;
|
||||
|
||||
WeakReferenceMessenger.Default.Send(new RoiTabChangedMessage(tab.SelectedIndex));
|
||||
}
|
||||
}
|
723
CereflowSetup/CereflowSetup.vdproj
Normal file
|
@ -0,0 +1,723 @@
|
|||
"DeployProject"
|
||||
{
|
||||
"VSVersion" = "3:800"
|
||||
"ProjectType" = "8:{978C614F-708E-4E1A-B201-565925725DBA}"
|
||||
"IsWebType" = "8:FALSE"
|
||||
"ProjectName" = "8:CereflowSetup"
|
||||
"LanguageId" = "3:1033"
|
||||
"CodePage" = "3:1252"
|
||||
"UILanguageId" = "3:1033"
|
||||
"SccProjectName" = "8:"
|
||||
"SccLocalPath" = "8:"
|
||||
"SccAuxPath" = "8:"
|
||||
"SccProvider" = "8:"
|
||||
"Hierarchy"
|
||||
{
|
||||
"Entry"
|
||||
{
|
||||
"MsmKey" = "8:_2D6D81C1F5374DEBA422B6A331B84BD0"
|
||||
"OwnerKey" = "8:_UNDEFINED"
|
||||
"MsmSig" = "8:_UNDEFINED"
|
||||
}
|
||||
"Entry"
|
||||
{
|
||||
"MsmKey" = "8:_B27012BB8F70435188B5B529268559FF"
|
||||
"OwnerKey" = "8:_UNDEFINED"
|
||||
"MsmSig" = "8:_UNDEFINED"
|
||||
}
|
||||
}
|
||||
"Configurations"
|
||||
{
|
||||
"Debug"
|
||||
{
|
||||
"DisplayName" = "8:Debug"
|
||||
"IsDebugOnly" = "11:TRUE"
|
||||
"IsReleaseOnly" = "11:FALSE"
|
||||
"OutputFilename" = "8:Debug\\CereflowSetup.msi"
|
||||
"PackageFilesAs" = "3:2"
|
||||
"PackageFileSize" = "3:-2147483648"
|
||||
"CabType" = "3:1"
|
||||
"Compression" = "3:2"
|
||||
"SignOutput" = "11:FALSE"
|
||||
"CertificateFile" = "8:"
|
||||
"PrivateKeyFile" = "8:"
|
||||
"TimeStampServer" = "8:"
|
||||
"InstallerBootstrapper" = "3:2"
|
||||
}
|
||||
"Release"
|
||||
{
|
||||
"DisplayName" = "8:Release"
|
||||
"IsDebugOnly" = "11:FALSE"
|
||||
"IsReleaseOnly" = "11:TRUE"
|
||||
"OutputFilename" = "8:Release\\CereflowSetup.msi"
|
||||
"PackageFilesAs" = "3:2"
|
||||
"PackageFileSize" = "3:-2147483648"
|
||||
"CabType" = "3:1"
|
||||
"Compression" = "3:2"
|
||||
"SignOutput" = "11:FALSE"
|
||||
"CertificateFile" = "8:"
|
||||
"PrivateKeyFile" = "8:"
|
||||
"TimeStampServer" = "8:"
|
||||
"InstallerBootstrapper" = "3:2"
|
||||
}
|
||||
}
|
||||
"Deployable"
|
||||
{
|
||||
"CustomAction"
|
||||
{
|
||||
}
|
||||
"DefaultFeature"
|
||||
{
|
||||
"Name" = "8:DefaultFeature"
|
||||
"Title" = "8:"
|
||||
"Description" = "8:"
|
||||
}
|
||||
"ExternalPersistence"
|
||||
{
|
||||
"LaunchCondition"
|
||||
{
|
||||
}
|
||||
}
|
||||
"File"
|
||||
{
|
||||
"{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_B27012BB8F70435188B5B529268559FF"
|
||||
{
|
||||
"SourcePath" = "8:..\\CereFlow\\Resources\\Images\\cereflow.ico"
|
||||
"TargetName" = "8:cereflow.ico"
|
||||
"Tag" = "8:"
|
||||
"Folder" = "8:_7ED91EF9E9DC4F258879A7AF6A8DF7A0"
|
||||
"Condition" = "8:"
|
||||
"Transitive" = "11:FALSE"
|
||||
"Vital" = "11:TRUE"
|
||||
"ReadOnly" = "11:FALSE"
|
||||
"Hidden" = "11:FALSE"
|
||||
"System" = "11:FALSE"
|
||||
"Permanent" = "11:FALSE"
|
||||
"SharedLegacy" = "11:FALSE"
|
||||
"PackageAs" = "3:1"
|
||||
"Register" = "3:1"
|
||||
"Exclude" = "11:FALSE"
|
||||
"IsDependency" = "11:FALSE"
|
||||
"IsolateTo" = "8:"
|
||||
}
|
||||
}
|
||||
"FileType"
|
||||
{
|
||||
}
|
||||
"Folder"
|
||||
{
|
||||
"{3C67513D-01DD-4637-8A68-80971EB9504F}:_7ED91EF9E9DC4F258879A7AF6A8DF7A0"
|
||||
{
|
||||
"DefaultLocation" = "8:[ProgramFilesFolder][Manufacturer]\\[ProductName]"
|
||||
"Name" = "8:#1925"
|
||||
"AlwaysCreate" = "11:FALSE"
|
||||
"Condition" = "8:"
|
||||
"Transitive" = "11:FALSE"
|
||||
"Property" = "8:TARGETDIR"
|
||||
"Folders"
|
||||
{
|
||||
}
|
||||
}
|
||||
"{1525181F-901A-416C-8A58-119130FE478E}:_873BE5ABA8A84564A2B4F803AAF94D2F"
|
||||
{
|
||||
"Name" = "8:#1919"
|
||||
"AlwaysCreate" = "11:FALSE"
|
||||
"Condition" = "8:"
|
||||
"Transitive" = "11:FALSE"
|
||||
"Property" = "8:ProgramMenuFolder"
|
||||
"Folders"
|
||||
{
|
||||
}
|
||||
}
|
||||
"{1525181F-901A-416C-8A58-119130FE478E}:_C85D7F9E1FE1467BB11324EE89B84513"
|
||||
{
|
||||
"Name" = "8:#1916"
|
||||
"AlwaysCreate" = "11:FALSE"
|
||||
"Condition" = "8:"
|
||||
"Transitive" = "11:FALSE"
|
||||
"Property" = "8:DesktopFolder"
|
||||
"Folders"
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
"LaunchCondition"
|
||||
{
|
||||
}
|
||||
"Locator"
|
||||
{
|
||||
}
|
||||
"MsiBootstrapper"
|
||||
{
|
||||
"LangId" = "3:1033"
|
||||
"RequiresElevation" = "11:FALSE"
|
||||
}
|
||||
"Product"
|
||||
{
|
||||
"Name" = "8:Microsoft Visual Studio"
|
||||
"ProductName" = "8:CereflowSetup"
|
||||
"ProductCode" = "8:{5F54381A-6788-48C2-8AFE-A42CACBE84A6}"
|
||||
"PackageCode" = "8:{14E5A1C9-5020-4359-88BC-F31111D9C7C9}"
|
||||
"UpgradeCode" = "8:{E6C117DE-A634-4C09-B44A-F1CE9D2FF464}"
|
||||
"AspNetVersion" = "8:"
|
||||
"RestartWWWService" = "11:FALSE"
|
||||
"RemovePreviousVersions" = "11:FALSE"
|
||||
"DetectNewerInstalledVersion" = "11:TRUE"
|
||||
"InstallAllUsers" = "11:FALSE"
|
||||
"ProductVersion" = "8:1.0.0"
|
||||
"Manufacturer" = "8:Default Company Name"
|
||||
"ARPHELPTELEPHONE" = "8:"
|
||||
"ARPHELPLINK" = "8:"
|
||||
"Title" = "8:CereflowSetup"
|
||||
"Subject" = "8:"
|
||||
"ARPCONTACT" = "8:Default Company Name"
|
||||
"Keywords" = "8:"
|
||||
"ARPCOMMENTS" = "8:"
|
||||
"ARPURLINFOABOUT" = "8:"
|
||||
"ARPPRODUCTICON" = "8:"
|
||||
"ARPIconIndex" = "3:0"
|
||||
"SearchPath" = "8:"
|
||||
"UseSystemSearchPath" = "11:TRUE"
|
||||
"TargetPlatform" = "3:0"
|
||||
"PreBuildEvent" = "8:"
|
||||
"PostBuildEvent" = "8:"
|
||||
"RunPostBuildEvent" = "3:0"
|
||||
}
|
||||
"Registry"
|
||||
{
|
||||
"HKLM"
|
||||
{
|
||||
"Keys"
|
||||
{
|
||||
"{60EA8692-D2D5-43EB-80DC-7906BF13D6EF}:_124202A0D6FA4ADC85A00AC456C54A93"
|
||||
{
|
||||
"Name" = "8:Software"
|
||||
"Condition" = "8:"
|
||||
"AlwaysCreate" = "11:FALSE"
|
||||
"DeleteAtUninstall" = "11:FALSE"
|
||||
"Transitive" = "11:FALSE"
|
||||
"Keys"
|
||||
{
|
||||
"{60EA8692-D2D5-43EB-80DC-7906BF13D6EF}:_AFACDCDCF1924C949A18DCEADE2CE0A3"
|
||||
{
|
||||
"Name" = "8:[Manufacturer]"
|
||||
"Condition" = "8:"
|
||||
"AlwaysCreate" = "11:FALSE"
|
||||
"DeleteAtUninstall" = "11:FALSE"
|
||||
"Transitive" = "11:FALSE"
|
||||
"Keys"
|
||||
{
|
||||
}
|
||||
"Values"
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
"Values"
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"HKCU"
|
||||
{
|
||||
"Keys"
|
||||
{
|
||||
"{60EA8692-D2D5-43EB-80DC-7906BF13D6EF}:_EEF576547A4342AFABA9EB240771C951"
|
||||
{
|
||||
"Name" = "8:Software"
|
||||
"Condition" = "8:"
|
||||
"AlwaysCreate" = "11:FALSE"
|
||||
"DeleteAtUninstall" = "11:FALSE"
|
||||
"Transitive" = "11:FALSE"
|
||||
"Keys"
|
||||
{
|
||||
"{60EA8692-D2D5-43EB-80DC-7906BF13D6EF}:_F243AD100825425C90404C0E0E08F392"
|
||||
{
|
||||
"Name" = "8:[Manufacturer]"
|
||||
"Condition" = "8:"
|
||||
"AlwaysCreate" = "11:FALSE"
|
||||
"DeleteAtUninstall" = "11:FALSE"
|
||||
"Transitive" = "11:FALSE"
|
||||
"Keys"
|
||||
{
|
||||
}
|
||||
"Values"
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
"Values"
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"HKCR"
|
||||
{
|
||||
"Keys"
|
||||
{
|
||||
}
|
||||
}
|
||||
"HKU"
|
||||
{
|
||||
"Keys"
|
||||
{
|
||||
}
|
||||
}
|
||||
"HKPU"
|
||||
{
|
||||
"Keys"
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
"Sequences"
|
||||
{
|
||||
}
|
||||
"Shortcut"
|
||||
{
|
||||
"{970C0BB2-C7D0-45D7-ABFA-7EC378858BC0}:_A12D9ADBE6904A71BB8BF392E02573A9"
|
||||
{
|
||||
"Name" = "8:Cereflow"
|
||||
"Arguments" = "8:"
|
||||
"Description" = "8:"
|
||||
"ShowCmd" = "3:1"
|
||||
"IconIndex" = "3:0"
|
||||
"Transitive" = "11:FALSE"
|
||||
"Target" = "8:_2D6D81C1F5374DEBA422B6A331B84BD0"
|
||||
"Folder" = "8:_C85D7F9E1FE1467BB11324EE89B84513"
|
||||
"WorkingFolder" = "8:_7ED91EF9E9DC4F258879A7AF6A8DF7A0"
|
||||
"Icon" = "8:_B27012BB8F70435188B5B529268559FF"
|
||||
"Feature" = "8:"
|
||||
}
|
||||
}
|
||||
"UserInterface"
|
||||
{
|
||||
"{2479F3F5-0309-486D-8047-8187E2CE5BA0}:_160DFB22EF9F437D8B91E5715878B1E2"
|
||||
{
|
||||
"UseDynamicProperties" = "11:FALSE"
|
||||
"IsDependency" = "11:FALSE"
|
||||
"SourcePath" = "8:<VsdDialogDir>\\VsdUserInterface.wim"
|
||||
}
|
||||
"{DF760B10-853B-4699-99F2-AFF7185B4A62}:_4FCB9E7469D2449CB4E0F17B8F75A785"
|
||||
{
|
||||
"Name" = "8:#1901"
|
||||
"Sequence" = "3:2"
|
||||
"Attributes" = "3:2"
|
||||
"Dialogs"
|
||||
{
|
||||
"{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_8E89B6156E464446958E8B125D456493"
|
||||
{
|
||||
"Sequence" = "3:100"
|
||||
"DisplayName" = "8:Progress"
|
||||
"UseDynamicProperties" = "11:TRUE"
|
||||
"IsDependency" = "11:FALSE"
|
||||
"SourcePath" = "8:<VsdDialogDir>\\VsdAdminProgressDlg.wid"
|
||||
"Properties"
|
||||
{
|
||||
"BannerBitmap"
|
||||
{
|
||||
"Name" = "8:BannerBitmap"
|
||||
"DisplayName" = "8:#1001"
|
||||
"Description" = "8:#1101"
|
||||
"Type" = "3:8"
|
||||
"ContextData" = "8:Bitmap"
|
||||
"Attributes" = "3:4"
|
||||
"Setting" = "3:1"
|
||||
"UsePlugInResources" = "11:TRUE"
|
||||
}
|
||||
"ShowProgress"
|
||||
{
|
||||
"Name" = "8:ShowProgress"
|
||||
"DisplayName" = "8:#1009"
|
||||
"Description" = "8:#1109"
|
||||
"Type" = "3:5"
|
||||
"ContextData" = "8:1;True=1;False=0"
|
||||
"Attributes" = "3:0"
|
||||
"Setting" = "3:0"
|
||||
"Value" = "3:1"
|
||||
"DefaultValue" = "3:1"
|
||||
"UsePlugInResources" = "11:TRUE"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"{DF760B10-853B-4699-99F2-AFF7185B4A62}:_5E51515C4ADB467687202FB541DC2564"
|
||||
{
|
||||
"Name" = "8:#1902"
|
||||
"Sequence" = "3:1"
|
||||
"Attributes" = "3:3"
|
||||
"Dialogs"
|
||||
{
|
||||
"{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_0C8FA1CCB31045E3A597BF8EB203CC3B"
|
||||
{
|
||||
"Sequence" = "3:100"
|
||||
"DisplayName" = "8:Finished"
|
||||
"UseDynamicProperties" = "11:TRUE"
|
||||
"IsDependency" = "11:FALSE"
|
||||
"SourcePath" = "8:<VsdDialogDir>\\VsdFinishedDlg.wid"
|
||||
"Properties"
|
||||
{
|
||||
"BannerBitmap"
|
||||
{
|
||||
"Name" = "8:BannerBitmap"
|
||||
"DisplayName" = "8:#1001"
|
||||
"Description" = "8:#1101"
|
||||
"Type" = "3:8"
|
||||
"ContextData" = "8:Bitmap"
|
||||
"Attributes" = "3:4"
|
||||
"Setting" = "3:1"
|
||||
"UsePlugInResources" = "11:TRUE"
|
||||
}
|
||||
"UpdateText"
|
||||
{
|
||||
"Name" = "8:UpdateText"
|
||||
"DisplayName" = "8:#1058"
|
||||
"Description" = "8:#1158"
|
||||
"Type" = "3:15"
|
||||
"ContextData" = "8:"
|
||||
"Attributes" = "3:0"
|
||||
"Setting" = "3:1"
|
||||
"Value" = "8:#1258"
|
||||
"DefaultValue" = "8:#1258"
|
||||
"UsePlugInResources" = "11:TRUE"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"{DF760B10-853B-4699-99F2-AFF7185B4A62}:_783157A827D74CCCA3E472EB3C894797"
|
||||
{
|
||||
"Name" = "8:#1900"
|
||||
"Sequence" = "3:1"
|
||||
"Attributes" = "3:1"
|
||||
"Dialogs"
|
||||
{
|
||||
"{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_ABAAE1BE487E467EBD3A90A3FD5E4032"
|
||||
{
|
||||
"Sequence" = "3:300"
|
||||
"DisplayName" = "8:Confirm Installation"
|
||||
"UseDynamicProperties" = "11:TRUE"
|
||||
"IsDependency" = "11:FALSE"
|
||||
"SourcePath" = "8:<VsdDialogDir>\\VsdConfirmDlg.wid"
|
||||
"Properties"
|
||||
{
|
||||
"BannerBitmap"
|
||||
{
|
||||
"Name" = "8:BannerBitmap"
|
||||
"DisplayName" = "8:#1001"
|
||||
"Description" = "8:#1101"
|
||||
"Type" = "3:8"
|
||||
"ContextData" = "8:Bitmap"
|
||||
"Attributes" = "3:4"
|
||||
"Setting" = "3:1"
|
||||
"UsePlugInResources" = "11:TRUE"
|
||||
}
|
||||
}
|
||||
}
|
||||
"{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_FEB446DD33F741119A74C0CC9FDBA931"
|
||||
{
|
||||
"Sequence" = "3:100"
|
||||
"DisplayName" = "8:Welcome"
|
||||
"UseDynamicProperties" = "11:TRUE"
|
||||
"IsDependency" = "11:FALSE"
|
||||
"SourcePath" = "8:<VsdDialogDir>\\VsdWelcomeDlg.wid"
|
||||
"Properties"
|
||||
{
|
||||
"BannerBitmap"
|
||||
{
|
||||
"Name" = "8:BannerBitmap"
|
||||
"DisplayName" = "8:#1001"
|
||||
"Description" = "8:#1101"
|
||||
"Type" = "3:8"
|
||||
"ContextData" = "8:Bitmap"
|
||||
"Attributes" = "3:4"
|
||||
"Setting" = "3:1"
|
||||
"UsePlugInResources" = "11:TRUE"
|
||||
}
|
||||
"CopyrightWarning"
|
||||
{
|
||||
"Name" = "8:CopyrightWarning"
|
||||
"DisplayName" = "8:#1002"
|
||||
"Description" = "8:#1102"
|
||||
"Type" = "3:3"
|
||||
"ContextData" = "8:"
|
||||
"Attributes" = "3:0"
|
||||
"Setting" = "3:1"
|
||||
"Value" = "8:#1202"
|
||||
"DefaultValue" = "8:#1202"
|
||||
"UsePlugInResources" = "11:TRUE"
|
||||
}
|
||||
"Welcome"
|
||||
{
|
||||
"Name" = "8:Welcome"
|
||||
"DisplayName" = "8:#1003"
|
||||
"Description" = "8:#1103"
|
||||
"Type" = "3:3"
|
||||
"ContextData" = "8:"
|
||||
"Attributes" = "3:0"
|
||||
"Setting" = "3:1"
|
||||
"Value" = "8:#1203"
|
||||
"DefaultValue" = "8:#1203"
|
||||
"UsePlugInResources" = "11:TRUE"
|
||||
}
|
||||
}
|
||||
}
|
||||
"{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_FEDFAB6CEE4244F9B3B81ED69551FA8F"
|
||||
{
|
||||
"Sequence" = "3:200"
|
||||
"DisplayName" = "8:Installation Folder"
|
||||
"UseDynamicProperties" = "11:TRUE"
|
||||
"IsDependency" = "11:FALSE"
|
||||
"SourcePath" = "8:<VsdDialogDir>\\VsdFolderDlg.wid"
|
||||
"Properties"
|
||||
{
|
||||
"BannerBitmap"
|
||||
{
|
||||
"Name" = "8:BannerBitmap"
|
||||
"DisplayName" = "8:#1001"
|
||||
"Description" = "8:#1101"
|
||||
"Type" = "3:8"
|
||||
"ContextData" = "8:Bitmap"
|
||||
"Attributes" = "3:4"
|
||||
"Setting" = "3:1"
|
||||
"UsePlugInResources" = "11:TRUE"
|
||||
}
|
||||
"InstallAllUsersVisible"
|
||||
{
|
||||
"Name" = "8:InstallAllUsersVisible"
|
||||
"DisplayName" = "8:#1059"
|
||||
"Description" = "8:#1159"
|
||||
"Type" = "3:5"
|
||||
"ContextData" = "8:1;True=1;False=0"
|
||||
"Attributes" = "3:0"
|
||||
"Setting" = "3:0"
|
||||
"Value" = "3:1"
|
||||
"DefaultValue" = "3:1"
|
||||
"UsePlugInResources" = "11:TRUE"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"{2479F3F5-0309-486D-8047-8187E2CE5BA0}:_9187271DD8514C54AF4A6B16AA1687C5"
|
||||
{
|
||||
"UseDynamicProperties" = "11:FALSE"
|
||||
"IsDependency" = "11:FALSE"
|
||||
"SourcePath" = "8:<VsdDialogDir>\\VsdBasicDialogs.wim"
|
||||
}
|
||||
"{DF760B10-853B-4699-99F2-AFF7185B4A62}:_DB4BDF9A2E774BFFAB00D9ECCB393FFB"
|
||||
{
|
||||
"Name" = "8:#1900"
|
||||
"Sequence" = "3:2"
|
||||
"Attributes" = "3:1"
|
||||
"Dialogs"
|
||||
{
|
||||
"{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_21981E7F23A041F4B9938C14783AD0DE"
|
||||
{
|
||||
"Sequence" = "3:200"
|
||||
"DisplayName" = "8:Installation Folder"
|
||||
"UseDynamicProperties" = "11:TRUE"
|
||||
"IsDependency" = "11:FALSE"
|
||||
"SourcePath" = "8:<VsdDialogDir>\\VsdAdminFolderDlg.wid"
|
||||
"Properties"
|
||||
{
|
||||
"BannerBitmap"
|
||||
{
|
||||
"Name" = "8:BannerBitmap"
|
||||
"DisplayName" = "8:#1001"
|
||||
"Description" = "8:#1101"
|
||||
"Type" = "3:8"
|
||||
"ContextData" = "8:Bitmap"
|
||||
"Attributes" = "3:4"
|
||||
"Setting" = "3:1"
|
||||
"UsePlugInResources" = "11:TRUE"
|
||||
}
|
||||
}
|
||||
}
|
||||
"{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_7599D2571DFF4E44A332EB8B554FE7C6"
|
||||
{
|
||||
"Sequence" = "3:300"
|
||||
"DisplayName" = "8:Confirm Installation"
|
||||
"UseDynamicProperties" = "11:TRUE"
|
||||
"IsDependency" = "11:FALSE"
|
||||
"SourcePath" = "8:<VsdDialogDir>\\VsdAdminConfirmDlg.wid"
|
||||
"Properties"
|
||||
{
|
||||
"BannerBitmap"
|
||||
{
|
||||
"Name" = "8:BannerBitmap"
|
||||
"DisplayName" = "8:#1001"
|
||||
"Description" = "8:#1101"
|
||||
"Type" = "3:8"
|
||||
"ContextData" = "8:Bitmap"
|
||||
"Attributes" = "3:4"
|
||||
"Setting" = "3:1"
|
||||
"UsePlugInResources" = "11:TRUE"
|
||||
}
|
||||
}
|
||||
}
|
||||
"{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_8D654475334F468EA66E1958B0726EC0"
|
||||
{
|
||||
"Sequence" = "3:100"
|
||||
"DisplayName" = "8:Welcome"
|
||||
"UseDynamicProperties" = "11:TRUE"
|
||||
"IsDependency" = "11:FALSE"
|
||||
"SourcePath" = "8:<VsdDialogDir>\\VsdAdminWelcomeDlg.wid"
|
||||
"Properties"
|
||||
{
|
||||
"BannerBitmap"
|
||||
{
|
||||
"Name" = "8:BannerBitmap"
|
||||
"DisplayName" = "8:#1001"
|
||||
"Description" = "8:#1101"
|
||||
"Type" = "3:8"
|
||||
"ContextData" = "8:Bitmap"
|
||||
"Attributes" = "3:4"
|
||||
"Setting" = "3:1"
|
||||
"UsePlugInResources" = "11:TRUE"
|
||||
}
|
||||
"CopyrightWarning"
|
||||
{
|
||||
"Name" = "8:CopyrightWarning"
|
||||
"DisplayName" = "8:#1002"
|
||||
"Description" = "8:#1102"
|
||||
"Type" = "3:3"
|
||||
"ContextData" = "8:"
|
||||
"Attributes" = "3:0"
|
||||
"Setting" = "3:1"
|
||||
"Value" = "8:#1202"
|
||||
"DefaultValue" = "8:#1202"
|
||||
"UsePlugInResources" = "11:TRUE"
|
||||
}
|
||||
"Welcome"
|
||||
{
|
||||
"Name" = "8:Welcome"
|
||||
"DisplayName" = "8:#1003"
|
||||
"Description" = "8:#1103"
|
||||
"Type" = "3:3"
|
||||
"ContextData" = "8:"
|
||||
"Attributes" = "3:0"
|
||||
"Setting" = "3:1"
|
||||
"Value" = "8:#1203"
|
||||
"DefaultValue" = "8:#1203"
|
||||
"UsePlugInResources" = "11:TRUE"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"{DF760B10-853B-4699-99F2-AFF7185B4A62}:_E581374CDE55425F96F5DEAD4C6B6AA2"
|
||||
{
|
||||
"Name" = "8:#1901"
|
||||
"Sequence" = "3:1"
|
||||
"Attributes" = "3:2"
|
||||
"Dialogs"
|
||||
{
|
||||
"{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_BC10DEBD96674C05A6A25CD31C644289"
|
||||
{
|
||||
"Sequence" = "3:100"
|
||||
"DisplayName" = "8:Progress"
|
||||
"UseDynamicProperties" = "11:TRUE"
|
||||
"IsDependency" = "11:FALSE"
|
||||
"SourcePath" = "8:<VsdDialogDir>\\VsdProgressDlg.wid"
|
||||
"Properties"
|
||||
{
|
||||
"BannerBitmap"
|
||||
{
|
||||
"Name" = "8:BannerBitmap"
|
||||
"DisplayName" = "8:#1001"
|
||||
"Description" = "8:#1101"
|
||||
"Type" = "3:8"
|
||||
"ContextData" = "8:Bitmap"
|
||||
"Attributes" = "3:4"
|
||||
"Setting" = "3:1"
|
||||
"UsePlugInResources" = "11:TRUE"
|
||||
}
|
||||
"ShowProgress"
|
||||
{
|
||||
"Name" = "8:ShowProgress"
|
||||
"DisplayName" = "8:#1009"
|
||||
"Description" = "8:#1109"
|
||||
"Type" = "3:5"
|
||||
"ContextData" = "8:1;True=1;False=0"
|
||||
"Attributes" = "3:0"
|
||||
"Setting" = "3:0"
|
||||
"Value" = "3:1"
|
||||
"DefaultValue" = "3:1"
|
||||
"UsePlugInResources" = "11:TRUE"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"{DF760B10-853B-4699-99F2-AFF7185B4A62}:_E6027BC617E4402AAFE0FDE9CEBF2375"
|
||||
{
|
||||
"Name" = "8:#1902"
|
||||
"Sequence" = "3:2"
|
||||
"Attributes" = "3:3"
|
||||
"Dialogs"
|
||||
{
|
||||
"{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_070560CA07E44842BAE230A06BBDF2DD"
|
||||
{
|
||||
"Sequence" = "3:100"
|
||||
"DisplayName" = "8:Finished"
|
||||
"UseDynamicProperties" = "11:TRUE"
|
||||
"IsDependency" = "11:FALSE"
|
||||
"SourcePath" = "8:<VsdDialogDir>\\VsdAdminFinishedDlg.wid"
|
||||
"Properties"
|
||||
{
|
||||
"BannerBitmap"
|
||||
{
|
||||
"Name" = "8:BannerBitmap"
|
||||
"DisplayName" = "8:#1001"
|
||||
"Description" = "8:#1101"
|
||||
"Type" = "3:8"
|
||||
"ContextData" = "8:Bitmap"
|
||||
"Attributes" = "3:4"
|
||||
"Setting" = "3:1"
|
||||
"UsePlugInResources" = "11:TRUE"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"MergeModule"
|
||||
{
|
||||
}
|
||||
"ProjectOutput"
|
||||
{
|
||||
"{5259A561-127C-4D43-A0A1-72F10C7B3BF8}:_2D6D81C1F5374DEBA422B6A331B84BD0"
|
||||
{
|
||||
"SourcePath" = "8:..\\CereFlow\\obj\\Release\\net8.0-windows\\apphost.exe"
|
||||
"TargetName" = "8:"
|
||||
"Tag" = "8:"
|
||||
"Folder" = "8:_7ED91EF9E9DC4F258879A7AF6A8DF7A0"
|
||||
"Condition" = "8:"
|
||||
"Transitive" = "11:FALSE"
|
||||
"Vital" = "11:TRUE"
|
||||
"ReadOnly" = "11:FALSE"
|
||||
"Hidden" = "11:FALSE"
|
||||
"System" = "11:FALSE"
|
||||
"Permanent" = "11:FALSE"
|
||||
"SharedLegacy" = "11:FALSE"
|
||||
"PackageAs" = "3:1"
|
||||
"Register" = "3:1"
|
||||
"Exclude" = "11:FALSE"
|
||||
"IsDependency" = "11:FALSE"
|
||||
"IsolateTo" = "8:"
|
||||
"ProjectOutputGroupRegister" = "3:1"
|
||||
"OutputConfiguration" = "8:Release|Any CPU"
|
||||
"OutputGroupCanonicalName" = "8:PublishItems"
|
||||
"OutputProjectGuid" = "8:{224E4779-75F0-4EE9-A570-17A9A312D424}"
|
||||
"ShowKeyOutput" = "11:TRUE"
|
||||
"ExcludeFilters"
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
23
DicomViewer/App.xaml
Normal file
|
@ -0,0 +1,23 @@
|
|||
<Application x:Class="DicomViewer.App"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
StartupUri="/Views/MainWindow.xaml">
|
||||
<Application.Resources>
|
||||
<ResourceDictionary>
|
||||
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
|
||||
<ResourceDictionary Source="Resources/Strings/Strings.xaml" />
|
||||
<materialDesign:BundledTheme BaseTheme="Light"
|
||||
PrimaryColor="Blue"
|
||||
SecondaryColor="Red" />
|
||||
<ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Defaults.xaml" />
|
||||
<ResourceDictionary Source="Resources/Themes/Theme.xaml" />
|
||||
<ResourceDictionary Source="pack://application:,,,/Livia;component/Resources/Themes/ScrollViewBarStyle.xaml" />
|
||||
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
||||
|
||||
</Application.Resources>
|
||||
</Application>
|
6
DicomViewer/App.xaml.cs
Normal file
|
@ -0,0 +1,6 @@
|
|||
namespace DicomViewer;
|
||||
|
||||
/// <summary>
|
||||
/// Interaction logic for App.xaml
|
||||
/// </summary>
|
||||
public partial class App;
|
10
DicomViewer/AssemblyInfo.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
using System.Windows;
|
||||
|
||||
[assembly: ThemeInfo(
|
||||
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
|
||||
//(used if a resource is not found in the page,
|
||||
// or application resource dictionaries)
|
||||
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
|
||||
//(used if a resource is not found in the page,
|
||||
// app, or any theme specific resource dictionaries)
|
||||
)]
|
17
DicomViewer/DicomViewer.csproj
Normal file
|
@ -0,0 +1,17 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>net8.0-windows</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<UseWPF>true</UseWPF>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="fo-dicom.Codecs" Version="5.11.0" />
|
||||
<PackageReference Include="OxyPlot.Core" Version="2.1.2" />
|
||||
<PackageReference Include="OxyPlot.Wpf" Version="2.1.2" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Livia\Livia.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
30
DicomViewer/Models/DicomDataTuple.cs
Normal file
|
@ -0,0 +1,30 @@
|
|||
using System.Windows.Media.Imaging;
|
||||
using FellowOakDicom;
|
||||
using FellowOakDicom.Imaging;
|
||||
using FellowOakDicom.Imaging.Codec;
|
||||
using FellowOakDicom.Imaging.Render;
|
||||
using Livia.Utility;
|
||||
|
||||
namespace DicomViewer.Models;
|
||||
|
||||
internal class DicomDataTuple
|
||||
{
|
||||
public BitmapImage Image { get; }
|
||||
|
||||
private readonly IPixelData _pixelData;
|
||||
private readonly DicomDataset _dataset;
|
||||
|
||||
public DicomDataTuple(string path, int frame = 0)
|
||||
{
|
||||
_dataset = DicomFile.Open(path).Clone(DicomTransferSyntax.ImplicitVRLittleEndian).Dataset;
|
||||
Image = new DicomImage(_dataset).RenderImage(frame).AsClonedBitmap().ToBitmapImage();
|
||||
|
||||
_pixelData = PixelDataFactory.Create(DicomPixelData.Create(_dataset), frame);
|
||||
}
|
||||
|
||||
public double GetPixel(int x, int y)
|
||||
{
|
||||
return _pixelData.GetPixel(x, y) * _dataset.GetSingleValueOrDefault(DicomTag.RescaleSlope, 1.0) + _dataset.GetSingleValueOrDefault(DicomTag.RescaleIntercept, 0);
|
||||
}
|
||||
|
||||
}
|
4
DicomViewer/Resources/Strings/Strings.xaml
Normal file
|
@ -0,0 +1,4 @@
|
|||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
|
||||
</ResourceDictionary>
|
66
DicomViewer/Resources/Themes/Theme.xaml
Normal file
|
@ -0,0 +1,66 @@
|
|||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
|
||||
<SolidColorBrush x:Key="PrimaryColorBrush">#1E2A3C</SolidColorBrush>
|
||||
<SolidColorBrush x:Key="PrimaryColorLightBrush">#233D63</SolidColorBrush>
|
||||
<SolidColorBrush x:Key="PrimaryColorDarkBrush">#121325</SolidColorBrush>
|
||||
<SolidColorBrush x:Key="PrimaryColorHighlightBrush">#0066D3</SolidColorBrush>
|
||||
<SolidColorBrush x:Key="PrimaryColorHoverBrush">#0043A8</SolidColorBrush>
|
||||
<SolidColorBrush x:Key="PrimaryColorAlternativeBrush">#0043A8</SolidColorBrush>
|
||||
|
||||
<SolidColorBrush x:Key="PrimaryColorHighlightOpaqueBrush">#4C0066D3</SolidColorBrush>
|
||||
|
||||
<SolidColorBrush x:Key="PrimaryTextColorBrush">#96B1DA</SolidColorBrush>
|
||||
|
||||
<SolidColorBrush x:Key="PrimaryGridTextButtonColorBrush">#1C84F3</SolidColorBrush>
|
||||
|
||||
<SolidColorBrush x:Key="HighlightTextColorBrush">#37D300</SolidColorBrush>
|
||||
|
||||
<SolidColorBrush x:Key="ServerJobListRowBackground">#233249</SolidColorBrush>
|
||||
<SolidColorBrush x:Key="SuccessTextColorBrush">#37D300</SolidColorBrush>
|
||||
<SolidColorBrush x:Key="FailedTextColorBrush">#C53B00</SolidColorBrush>
|
||||
<SolidColorBrush x:Key="ProcessingTextColorBrush">#0066D3</SolidColorBrush>
|
||||
|
||||
<SolidColorBrush x:Key="DataGridHeaderBackgroundBrush">#233D63</SolidColorBrush>
|
||||
<SolidColorBrush x:Key="DataGridRowBackgroundBrush">#3C6096</SolidColorBrush>
|
||||
<SolidColorBrush x:Key="DataGridAlternatingRowBackgroundBrush">#264A81</SolidColorBrush>
|
||||
<SolidColorBrush x:Key="DataGridWarningBrush">#D3CB00</SolidColorBrush>
|
||||
<SolidColorBrush x:Key="DataGridCriticalBrush">#FF5E4D</SolidColorBrush>
|
||||
|
||||
<Color x:Key="Isc1Color">#C53B00</Color>
|
||||
<Color x:Key="Isc2Color">#912C00</Color>
|
||||
<Color x:Key="Isp1Color">#95DE64</Color>
|
||||
<Color x:Key="Isp2Color">#389E0D</Color>
|
||||
<Color x:Key="OpColor">#A11DFF</Color>
|
||||
<Color x:Key="CsfColor">#0066D3</Color>
|
||||
<SolidColorBrush x:Key="IscColorBrush">#C53B00</SolidColorBrush>
|
||||
|
||||
<SolidColorBrush x:Key="SecondaryColorBrush">#9430A2</SolidColorBrush>
|
||||
<SolidColorBrush x:Key="SecondaryAlternativeColorBrush">#B856C6</SolidColorBrush>
|
||||
|
||||
<SolidColorBrush x:Key="LoginColorBrush">#FFA53C</SolidColorBrush>
|
||||
<SolidColorBrush x:Key="LoginAlternativeColorBrush">#FFC582</SolidColorBrush>
|
||||
|
||||
<SolidColorBrush x:Key="BackgroundColorBrush">#121325</SolidColorBrush>
|
||||
|
||||
<SolidColorBrush x:Key="White1Brush">#FFFFFF</SolidColorBrush>
|
||||
|
||||
<Color x:Key="White2">#FAFAFA</Color>
|
||||
<SolidColorBrush x:Key="White2Brush">#FAFAFA</SolidColorBrush>
|
||||
|
||||
<Color x:Key="Gray1">#EBEBEB</Color>
|
||||
<Color x:Key="Gray1Transparent">#00EBEBEB</Color>
|
||||
|
||||
<SolidColorBrush x:Key="Gray2Brush">#F5F5F5</SolidColorBrush>
|
||||
<SolidColorBrush x:Key="Gray3Brush">#BBBBBB</SolidColorBrush>
|
||||
<SolidColorBrush x:Key="Gray4Brush">#666666</SolidColorBrush>
|
||||
|
||||
<Color x:Key="Black1">#333333</Color>
|
||||
<SolidColorBrush x:Key="Black1Brush">#333333</SolidColorBrush>
|
||||
|
||||
<SolidColorBrush x:Key="AlertColorBrush">#A8071A</SolidColorBrush>
|
||||
<SolidColorBrush x:Key="AlertAlternativeColorBrush">#F03F3F</SolidColorBrush>
|
||||
|
||||
<SolidColorBrush x:Key="ProgressBarBackgroundBrush">#8F7199C7</SolidColorBrush>
|
||||
|
||||
</ResourceDictionary>
|
190
DicomViewer/ViewModels/MainWindowViewModel.cs
Normal file
|
@ -0,0 +1,190 @@
|
|||
using System.Collections.ObjectModel;
|
||||
using System.IO;
|
||||
using System.Text.RegularExpressions;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using DicomViewer.Models;
|
||||
using FellowOakDicom.Imaging;
|
||||
using OxyPlot;
|
||||
using OxyPlot.Series;
|
||||
|
||||
namespace DicomViewer.ViewModels;
|
||||
|
||||
public readonly record struct PixelInfo
|
||||
{
|
||||
public string Name { get; init; }
|
||||
public string Value { get; init; }
|
||||
}
|
||||
|
||||
|
||||
internal class MainWindowViewModel : ObservableObject
|
||||
{
|
||||
public DicomDataTuple? CurrentImage { get => _currentImage; private set => SetProperty(ref _currentImage, value); }
|
||||
public int CurrentIndex
|
||||
{
|
||||
get => _currentIndex;
|
||||
private set
|
||||
{
|
||||
SetProperty(ref _currentIndex, value);
|
||||
UpdateCurrentImage();
|
||||
}
|
||||
}
|
||||
public int MaxIndex { get => _maxIndex; private set => SetProperty(ref _maxIndex, value); }
|
||||
public string CurrentImageName
|
||||
{
|
||||
get => _currentImageName;
|
||||
set
|
||||
{
|
||||
SetProperty(ref _currentImageName, value);
|
||||
UpdateCurrentImage();
|
||||
}
|
||||
}
|
||||
public PlotModel CbfPlotModelModel { get => _cbfPlotModelModel; set => SetProperty(ref _cbfPlotModelModel, value); }
|
||||
public ObservableCollection<string> ImageList { get; } = [];
|
||||
public ObservableCollection<PixelInfo> PixelInfoList { get; } = [];
|
||||
public string CursorPosition { get => _cursorPosition; set => SetProperty(ref _cursorPosition, value); }
|
||||
|
||||
private readonly Dictionary<string, List<DicomDataTuple>> _imageDictionary = new();
|
||||
private int _currentIndex;
|
||||
private int _maxIndex;
|
||||
private int _pldCount;
|
||||
private string _currentImageName = string.Empty;
|
||||
private string _cursorPosition = string.Empty;
|
||||
private PlotModel _cbfPlotModelModel = new();
|
||||
private DicomDataTuple? _currentImage;
|
||||
|
||||
|
||||
public void LoadData(string path)
|
||||
{
|
||||
CurrentImageName = "corr-cbf";
|
||||
_imageDictionary.Clear();
|
||||
ImageList.Clear();
|
||||
_pldCount = 0;
|
||||
|
||||
try
|
||||
{
|
||||
//read from each sub folder
|
||||
foreach (string directory in Directory.GetDirectories(Path.Combine(path, "asl")))
|
||||
{
|
||||
string key = Path.GetFileName(directory);
|
||||
ImageList.Add(key);
|
||||
List<DicomDataTuple> imageList = LoadFromDir(directory);
|
||||
_imageDictionary.Add(key, imageList);
|
||||
|
||||
if (Regex.IsMatch(key, @"cbf\d+"))
|
||||
_pldCount++;
|
||||
}
|
||||
|
||||
//read java temp
|
||||
string javaTempDir = Path.Combine(path, "java_temp", "reconstructed");
|
||||
if (Directory.Exists(javaTempDir))
|
||||
{
|
||||
foreach (string file in Directory.GetFiles(javaTempDir, "*.dcm"))
|
||||
{
|
||||
string key = "j_" + Path.GetFileNameWithoutExtension(file);
|
||||
ImageList.Add(key);
|
||||
List<DicomDataTuple> imageList = LoadFromFile(file);
|
||||
_imageDictionary.Add(key, imageList);
|
||||
}
|
||||
}
|
||||
|
||||
MaxIndex = _imageDictionary.First().Value.Count;
|
||||
CurrentIndex = MaxIndex / 2;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine(e);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private static List<DicomDataTuple> LoadFromDir(string path)
|
||||
{
|
||||
string[] files = Directory.GetFiles(path);
|
||||
Array.Sort(files);
|
||||
List<DicomDataTuple> dataList = files.Select(file => new DicomDataTuple(file)).ToList();
|
||||
return dataList;
|
||||
}
|
||||
|
||||
private static List<DicomDataTuple> LoadFromFile(string path)
|
||||
{
|
||||
DicomImage image = new(path);
|
||||
List<DicomDataTuple> dataList = Enumerable.Range(0, image.NumberOfFrames).Select(index => new DicomDataTuple(path, index)).ToList();
|
||||
return dataList;
|
||||
}
|
||||
|
||||
public void UpdateCbfPlot(int x, int y)
|
||||
{
|
||||
try
|
||||
{
|
||||
//draw plot
|
||||
PlotModel model = new() { Title = "CBF Plot" };
|
||||
LineSeries cbfLine = new() { Title = "CBF", LineJoin = LineJoin.Bevel, MarkerType = MarkerType.Circle };
|
||||
for (int i = 1; i <= _pldCount; i++)
|
||||
{
|
||||
cbfLine.Points.Add(new DataPoint(i + 1, _imageDictionary[$"cbf{i}"][CurrentIndex - 1].GetPixel(x, y)));
|
||||
}
|
||||
model.Series.Add(cbfLine);
|
||||
|
||||
//Siemens 5
|
||||
if (_imageDictionary.ContainsKey("j_PLD1"))
|
||||
{
|
||||
LineSeries pldLine = new() { Title = "PLD", LineJoin = LineJoin.Bevel, MarkerType = MarkerType.Square };
|
||||
for (int i = 1; i <= _pldCount; i++)
|
||||
{
|
||||
pldLine.Points.Add(new DataPoint(i + 1, _imageDictionary[$"j_PLD{i}"][CurrentIndex - 1].GetPixel(x, y)));
|
||||
}
|
||||
model.Series.Add(pldLine);
|
||||
}
|
||||
|
||||
CbfPlotModelModel = model;
|
||||
|
||||
//add all info
|
||||
PixelInfoList.Clear();
|
||||
foreach (KeyValuePair<string, List<DicomDataTuple>> pair in _imageDictionary)
|
||||
{
|
||||
string value = string.Empty;
|
||||
try
|
||||
{
|
||||
value = pair.Value[CurrentIndex - 1].GetPixel(x, y).ToString("0.##");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine(e);
|
||||
}
|
||||
PixelInfoList.Add(new PixelInfo { Name = pair.Key, Value = value });
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateCurrentImage()
|
||||
{
|
||||
try
|
||||
{
|
||||
CurrentImage = _imageDictionary[CurrentImageName][CurrentIndex - 1];
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void NextImage()
|
||||
{
|
||||
if (CurrentIndex >= MaxIndex)
|
||||
return;
|
||||
|
||||
CurrentIndex++;
|
||||
}
|
||||
|
||||
public void PrevImage()
|
||||
{
|
||||
if (CurrentIndex <= 1)
|
||||
return;
|
||||
|
||||
CurrentIndex--;
|
||||
}
|
||||
}
|
134
DicomViewer/Views/MainWindow.xaml
Normal file
|
@ -0,0 +1,134 @@
|
|||
<Window x:Class="DicomViewer.Views.MainWindow"
|
||||
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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
d:DataContext="{d:DesignInstance Type=viewModels:MainWindowViewModel}"
|
||||
xmlns:viewModels="clr-namespace:DicomViewer.ViewModels"
|
||||
xmlns:oxy="http://oxyplot.org/wpf"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
mc:Ignorable="d"
|
||||
Title="Dicom Viewer"
|
||||
Height="450"
|
||||
Width="800">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<ToolBarTray Grid.Row="0">
|
||||
<ToolBar Style="{StaticResource MaterialDesignToolBar}"
|
||||
ClipToBounds="False">
|
||||
<Button Click="OpenButtonOnClick"
|
||||
Content="{materialDesign:PackIcon Kind=FolderOpen}" />
|
||||
</ToolBar>
|
||||
</ToolBarTray>
|
||||
<Grid Grid.Row="1"
|
||||
Background="{StaticResource BackgroundColorBrush}"
|
||||
PreviewMouseWheel="OnPreviewMouseWheel">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<ScrollViewer Grid.Column="0"
|
||||
VerticalScrollBarVisibility="Auto">
|
||||
<ItemsControl HorizontalAlignment="Left"
|
||||
ItemsSource="{Binding ImageList}"
|
||||
Margin="10">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Style="{StaticResource MaterialDesignBody1TextBlock}"
|
||||
Foreground="{StaticResource White1Brush}"
|
||||
Text="{Binding }" />
|
||||
<RadioButton GroupName="MainWindowTabs"
|
||||
BorderBrush="{StaticResource White1Brush}"
|
||||
Checked="ToggleButtonOnChecked"
|
||||
Margin="10" />
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</ScrollViewer>
|
||||
<Grid Grid.Column="2">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<oxy:PlotView Grid.Row="0"
|
||||
Model="{Binding CbfPlotModelModel}"
|
||||
VerticalAlignment="Top"
|
||||
Height="450"
|
||||
Width="450" />
|
||||
|
||||
<ItemsControl Grid.Row="1"
|
||||
Margin="0, 10"
|
||||
VerticalAlignment="Bottom"
|
||||
HorizontalAlignment="Left"
|
||||
ItemsSource="{Binding PixelInfoList}">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<UniformGrid Columns="3" />
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<UniformGrid Width="250"
|
||||
Columns="2">
|
||||
<TextBlock Style="{StaticResource MaterialDesignBody1TextBlock}"
|
||||
Foreground="{StaticResource White1Brush}"
|
||||
Margin="10, 0"
|
||||
Text="{Binding Name}" />
|
||||
<TextBlock Style="{StaticResource MaterialDesignBody1TextBlock}"
|
||||
Foreground="{StaticResource White1Brush}"
|
||||
Margin="10, 0"
|
||||
Text="{Binding Value}" />
|
||||
</UniformGrid>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</Grid>
|
||||
<Grid Grid.Column="1">
|
||||
<Image VerticalAlignment="Stretch"
|
||||
x:Name="Image"
|
||||
MouseUp="OnImageMouseUp"
|
||||
MouseMove="OnImageMouseMove"
|
||||
RenderOptions.BitmapScalingMode="HighQuality"
|
||||
Opacity="0.5"
|
||||
Margin="8, 0"
|
||||
Source="{Binding CurrentImage.Image, FallbackValue={x:Null}}" />
|
||||
|
||||
<TextBlock Foreground="{StaticResource White1Brush}"
|
||||
VerticalAlignment="Bottom"
|
||||
HorizontalAlignment="Right"
|
||||
Text="{Binding CursorPosition}"
|
||||
Style="{StaticResource MaterialDesignBody1TextBlock}"
|
||||
d:Text="1, 9" />
|
||||
|
||||
<StackPanel Orientation="Horizontal"
|
||||
Margin="10, 0"
|
||||
VerticalAlignment="Bottom">
|
||||
<TextBlock Foreground="{StaticResource White1Brush}"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Center"
|
||||
Text="{Binding CurrentIndex}"
|
||||
Style="{StaticResource MaterialDesignBody1TextBlock}"
|
||||
d:Text="1" />
|
||||
<TextBlock Foreground="{StaticResource White1Brush}"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Center"
|
||||
Text="/"
|
||||
Style="{StaticResource MaterialDesignBody1TextBlock}" />
|
||||
<TextBlock Foreground="{StaticResource White1Brush}"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Center"
|
||||
Text="{Binding MaxIndex}"
|
||||
Style="{StaticResource MaterialDesignBody1TextBlock}"
|
||||
d:Text="20" />
|
||||
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Window>
|
79
DicomViewer/Views/MainWindow.xaml.cs
Normal file
|
@ -0,0 +1,79 @@
|
|||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
using DicomViewer.ViewModels;
|
||||
using FellowOakDicom;
|
||||
using FellowOakDicom.Imaging;
|
||||
using Ookii.Dialogs.Wpf;
|
||||
|
||||
namespace DicomViewer.Views;
|
||||
|
||||
/// <summary>
|
||||
/// Interaction logic for MainWindow.xaml
|
||||
/// </summary>
|
||||
public partial class MainWindow
|
||||
{
|
||||
private readonly MainWindowViewModel _viewModel = new();
|
||||
|
||||
public MainWindow()
|
||||
{
|
||||
//TODO::move this somewhere
|
||||
new DicomSetupBuilder()
|
||||
.RegisterServices(s => s.AddFellowOakDicom().AddImageManager<WinFormsImageManager>())
|
||||
.Build();
|
||||
|
||||
InitializeComponent();
|
||||
DataContext = _viewModel;
|
||||
}
|
||||
|
||||
private void OnPreviewMouseWheel(object sender, MouseWheelEventArgs e)
|
||||
{
|
||||
if (e.Delta < 0)
|
||||
{
|
||||
((MainWindowViewModel)DataContext).PrevImage();
|
||||
}
|
||||
else
|
||||
{
|
||||
((MainWindowViewModel)DataContext).NextImage();
|
||||
}
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
private void OnImageMouseUp(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
(int x, int y) = GetPixelIndex(e.GetPosition(Image));
|
||||
_viewModel.UpdateCbfPlot(x, y);
|
||||
}
|
||||
|
||||
private void OnImageMouseMove(object sender, MouseEventArgs e)
|
||||
{
|
||||
(int x, int y) = GetPixelIndex(e.GetPosition(Image));
|
||||
|
||||
_viewModel.CursorPosition = $"{x}, {y}";
|
||||
}
|
||||
|
||||
private (int x, int y) GetPixelIndex(Point pos)
|
||||
{
|
||||
if (_viewModel.CurrentImage == null)
|
||||
return (0, 0);
|
||||
|
||||
int x = (int)Math.Floor(pos.X * _viewModel.CurrentImage.Image.PixelWidth / Image.ActualWidth);
|
||||
int y = (int)Math.Floor(pos.Y * _viewModel.CurrentImage.Image.PixelHeight / Image.ActualHeight);
|
||||
return (x, y);
|
||||
}
|
||||
|
||||
private void OpenButtonOnClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
VistaFolderBrowserDialog dialog = new();
|
||||
|
||||
if (!(dialog.ShowDialog() ?? false))
|
||||
return;
|
||||
|
||||
_viewModel.LoadData(dialog.SelectedPath);
|
||||
}
|
||||
|
||||
private void ToggleButtonOnChecked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
_viewModel.CurrentImageName = (string)((RadioButton)sender).DataContext;
|
||||
}
|
||||
}
|
37
Livia.Tests/Livia.Tests.csproj
Normal file
|
@ -0,0 +1,37 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0-windows</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
<IsTestProject>true</IsTestProject>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="coverlet.collector" Version="6.0.2">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
|
||||
<PackageReference Include="Moq" Version="4.20.70" />
|
||||
<PackageReference Include="xunit" Version="2.8.0" />
|
||||
<PackageReference Include="Xunit.StaFact" Version="1.1.11" />
|
||||
<PackageReference Include="Xunit.DependencyInjection" Version="9.2.1" />
|
||||
<PackageReference Include="Xunit.DependencyInjection.Logging" Version="9.0.0" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Livia\Livia.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Using Include="Xunit" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
60
Livia.Tests/Models/AesOperationTests.cs
Normal file
|
@ -0,0 +1,60 @@
|
|||
using Livia.Models;
|
||||
|
||||
namespace Livia.Tests.Models;
|
||||
|
||||
public class AesOperationTests(IAesOperation aesOperation)
|
||||
{
|
||||
[Theory]
|
||||
[InlineData("", "")]
|
||||
[InlineData("", " ")]
|
||||
[InlineData(" ", " ")]
|
||||
[InlineData("123465", " ")]
|
||||
public void EncryptString_InvalidKey_EmptyString(string key, string text)
|
||||
{
|
||||
string result = aesOperation.EncryptString(key, text);
|
||||
Assert.Equal(string.Empty, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EncryptString_0key_EncryptedString()
|
||||
{
|
||||
string result = aesOperation.EncryptString("00000000000000000000000000000000", "");
|
||||
Assert.Equal("AxjghOFmbmmYkcePiqmJYA==", result);
|
||||
|
||||
result = aesOperation.EncryptString("00000000000000000000000000000000", "abc");
|
||||
Assert.Equal("5mTKIdERZle61IWzOzuB9g==", result);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("", "")]
|
||||
[InlineData("", " ")]
|
||||
[InlineData(" ", " ")]
|
||||
public void DecryptString_InvalidKey_EmptyString(string key, string text)
|
||||
{
|
||||
string result = aesOperation.DecryptString(key, text);
|
||||
Assert.Equal(string.Empty, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DecryptString_0key_DecryptedString()
|
||||
{
|
||||
string result = aesOperation.DecryptString("00000000000000000000000000000000", "AxjghOFmbmmYkcePiqmJYA==");
|
||||
Assert.Equal("", result);
|
||||
|
||||
result = aesOperation.DecryptString("00000000000000000000000000000000", "5mTKIdERZle61IWzOzuB9g==");
|
||||
Assert.Equal("abc", result);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("")]
|
||||
[InlineData("123")]
|
||||
[InlineData("Five score years ago, a great American, in whose symbolic shadow we stand today, signed the Emancipation Proclamation. " +
|
||||
"This momentous decree came as a great beacon light of hope to millions of Negro slaves who had been seared in the flames of withering injustice. " +
|
||||
"It came as a joyous daybreak to end the long night of their captivity.")]
|
||||
public void EncryptDecryptString_0key_TextSame(string text)
|
||||
{
|
||||
string encrypted = aesOperation.EncryptString("00000000000000000000000000000000", text);
|
||||
string result = aesOperation.DecryptString("00000000000000000000000000000000", encrypted);
|
||||
Assert.Equal(text, result);
|
||||
}
|
||||
}
|
137
Livia.Tests/Models/DataBlockLoaderTests.cs
Normal file
|
@ -0,0 +1,137 @@
|
|||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using Livia.Models;
|
||||
using Livia.Models.Data;
|
||||
using Livia.Utility;
|
||||
using Livia.Utility.DependencyInjection;
|
||||
using Livia.Views.Utility;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Moq;
|
||||
|
||||
namespace Livia.Tests.Models;
|
||||
|
||||
public class DataBlockLoaderTests
|
||||
{
|
||||
private readonly DataBlockLoader _dataBlockLoader;
|
||||
|
||||
private readonly Mock<IWarningSystem> _warningSystemMock = new();
|
||||
private readonly Mock<IPatientInfoManager> _patientInfoManagerMock = new();
|
||||
private readonly Mock<IServerHandler> _serverHandlerMock = new();
|
||||
private readonly Mock<IRoiLegendManager> _roiLegendManagerMock = new();
|
||||
|
||||
public static IEnumerable<object[]> SharedDataTestInput => [["TestKey1", "TestValue1"], ["TestKey2", float.Pi], ["TestKey3", new LogoutMessage()]];
|
||||
|
||||
public DataBlockLoaderTests(ILogger logger)
|
||||
{
|
||||
_dataBlockLoader = new DataBlockLoader(_warningSystemMock.Object, _patientInfoManagerMock.Object, logger, _serverHandlerMock.Object, _roiLegendManagerMock.Object);
|
||||
_serverHandlerMock.SetupAllProperties();
|
||||
_warningSystemMock.Setup(o => o.ShowDialog(It.IsAny<WarningWindowKind>(), It.IsAny<bool>(), It.IsAny<string>())).Returns(true);
|
||||
|
||||
//TODO:: temp measure
|
||||
ServiceProviderFactory.Init(_ => { });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Constructor_ReceivesLogoutMessage_ShowPlaceHolder()
|
||||
{
|
||||
//arrange
|
||||
_dataBlockLoader.PlaceholderVisible = false;
|
||||
|
||||
//act
|
||||
WeakReferenceMessenger.Default.Send(new LogoutMessage());
|
||||
|
||||
//assert
|
||||
Assert.True(_dataBlockLoader.PlaceholderVisible);
|
||||
}
|
||||
|
||||
[Theory, MemberData(nameof(SharedDataTestInput))]
|
||||
public void SaveSharedData_SaveAndGetData_ReturnsData(string key, object data)
|
||||
{
|
||||
//act
|
||||
_dataBlockLoader.SaveSharedData(key, data);
|
||||
object getResult = _dataBlockLoader.GetSharedData<object>(key);
|
||||
|
||||
//assert
|
||||
Assert.Equal(data, getResult);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SaveSharedData_SaveDuplicateData_OverrideData()
|
||||
{
|
||||
//act
|
||||
const string key = "DuplicateKey";
|
||||
object correctValue = "override";
|
||||
_dataBlockLoader.SaveSharedData(key, 0);
|
||||
_dataBlockLoader.SaveSharedData(key, correctValue);
|
||||
object getResult = _dataBlockLoader.GetSharedData<object>(key);
|
||||
|
||||
//assert
|
||||
Assert.Equal(correctValue, getResult);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetSharedData_KeyDoesNotExists_ThrowsException()
|
||||
{
|
||||
//assert
|
||||
Assert.Throws<KeyNotFoundException>(GetData);
|
||||
return;
|
||||
|
||||
void GetData() => _dataBlockLoader.GetSharedData<object>("KeyThatDoesNotExists");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetSharedData_TypeMismatch_ThrowsException()
|
||||
{
|
||||
//act
|
||||
const string key = "TypeMismatchKey";
|
||||
_dataBlockLoader.SaveSharedData(key, float.E);
|
||||
|
||||
//assert
|
||||
Assert.Throws<KeyNotFoundException>(GetData);
|
||||
return;
|
||||
|
||||
void GetData() => _dataBlockLoader.GetSharedData<double>(key);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SharedDataContainsKey_ContainsKey_ReturnsTrue()
|
||||
{
|
||||
//arrange
|
||||
const string key = "KeyThatExists";
|
||||
_dataBlockLoader.SaveSharedData(key, "Some Value");
|
||||
|
||||
//act
|
||||
bool result = _dataBlockLoader.SharedDataContainsKey(key);
|
||||
|
||||
//assert
|
||||
Assert.True(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SharedDataContainsKey_DoesNotContainsKey_ReturnsFalse()
|
||||
{
|
||||
//act
|
||||
const string key = "KeyThatDoesNotExists";
|
||||
bool result = _dataBlockLoader.SharedDataContainsKey(key);
|
||||
|
||||
//assert
|
||||
Assert.False(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task LoadData_EmptyData_ShowsError()
|
||||
{
|
||||
//arrange
|
||||
DataBlock dataBlock = new(string.Empty);
|
||||
|
||||
//act
|
||||
await _dataBlockLoader.LoadData(dataBlock);
|
||||
|
||||
//assert
|
||||
_warningSystemMock.Verify(o => o.ShowDialog(It.IsAny<WarningWindowKind>(), It.IsAny<bool>(),
|
||||
It.Is<string>(s => s.Equals("InvalidDataError"))), Times.Once);
|
||||
Assert.False(_serverHandlerMock.Object.Processing);
|
||||
Assert.Null(_dataBlockLoader.CurrentDisplayingDataBlock);
|
||||
Assert.True(_dataBlockLoader.PlaceholderVisible);
|
||||
}
|
||||
}
|
||||
|
119
Livia.Tests/Models/SerialKeyManagerTests.cs
Normal file
|
@ -0,0 +1,119 @@
|
|||
using System.Text.Json;
|
||||
using Livia.Models;
|
||||
using Livia.Properties;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Moq;
|
||||
|
||||
namespace Livia.Tests.Models;
|
||||
|
||||
public class SerialKeyManagerTests
|
||||
{
|
||||
private readonly ISerialKeyManager _serialKeyManager;
|
||||
private readonly Mock<IAesOperation> _aesOperationMock = new();
|
||||
|
||||
public SerialKeyManagerTests(ILogger logger)
|
||||
{
|
||||
_serialKeyManager = new SerialKeyManager(logger, _aesOperationMock.Object);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Constructor_ValidParameter_HardwareIdNotEmpty()
|
||||
{
|
||||
// assert
|
||||
Assert.NotEqual(string.Empty, _serialKeyManager.HardwareId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Validate_EmptyKey_ReturnsFalse()
|
||||
{
|
||||
// arrange
|
||||
_aesOperationMock.Setup(x => x.DecryptString(It.IsAny<string>(), It.IsAny<string>())).Returns(string.Empty);
|
||||
|
||||
// act
|
||||
bool result = _serialKeyManager.Validate();
|
||||
|
||||
// assert
|
||||
_aesOperationMock.Verify(x => x.DecryptString(It.IsAny<string>(), It.IsAny<string>()), Times.Once());
|
||||
Assert.False(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Validate_WrongHardwareId_ReturnsFalse()
|
||||
{
|
||||
// arrange
|
||||
AuthenticationKeyLicense license = new()
|
||||
{
|
||||
HardwareId = "Not my ID",
|
||||
ExpireDateTime = DateTime.MaxValue
|
||||
};
|
||||
string licenseJson = JsonSerializer.Serialize(license);
|
||||
_aesOperationMock.Setup(x => x.DecryptString(It.IsAny<string>(), It.IsAny<string>())).Returns(licenseJson);
|
||||
|
||||
// act
|
||||
bool result = _serialKeyManager.Validate();
|
||||
|
||||
// assert
|
||||
_aesOperationMock.Verify(x => x.DecryptString(It.IsAny<string>(), It.IsAny<string>()), Times.Once());
|
||||
Assert.False(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Validate_ExpiredLicense_ReturnsFalse()
|
||||
{
|
||||
// arrange
|
||||
AuthenticationKeyLicense license = new()
|
||||
{
|
||||
HardwareId = _serialKeyManager.HardwareId,
|
||||
ExpireDateTime = DateTime.MinValue
|
||||
};
|
||||
string licenseJson = JsonSerializer.Serialize(license);
|
||||
_aesOperationMock.Setup(x => x.DecryptString(It.IsAny<string>(), It.IsAny<string>())).Returns(licenseJson);
|
||||
|
||||
// act
|
||||
bool result = _serialKeyManager.Validate();
|
||||
|
||||
// assert
|
||||
_aesOperationMock.Verify(x => x.DecryptString(It.IsAny<string>(), It.IsAny<string>()), Times.Once());
|
||||
Assert.False(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Validate_ValidLicense_ReturnsTrue()
|
||||
{
|
||||
// arrange
|
||||
Settings.Default.Reset();
|
||||
AuthenticationKeyLicense license = new()
|
||||
{
|
||||
HardwareId = _serialKeyManager.HardwareId,
|
||||
ExpireDateTime = DateTime.MaxValue
|
||||
};
|
||||
string licenseJson = JsonSerializer.Serialize(license);
|
||||
_aesOperationMock.Setup(x => x.DecryptString(It.IsAny<string>(), It.IsAny<string>())).Returns(licenseJson);
|
||||
|
||||
// act
|
||||
bool result = _serialKeyManager.Validate();
|
||||
|
||||
// assert
|
||||
Assert.True(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EncryptLicense_ValidLicense_ReturnsEncryptedString()
|
||||
{
|
||||
// arrange
|
||||
AuthenticationKeyLicense license = new()
|
||||
{
|
||||
HardwareId = _serialKeyManager.HardwareId,
|
||||
ExpireDateTime = DateTime.Now
|
||||
};
|
||||
_aesOperationMock.Setup(x => x.EncryptString(It.IsAny<string>(), It.IsAny<string>())).Returns<string, string>((_, text) => text);
|
||||
|
||||
// act
|
||||
string licenseJson = _serialKeyManager.EncryptLicense(license);
|
||||
|
||||
// assert
|
||||
_aesOperationMock.Verify(x => x.EncryptString(It.IsAny<string>(), It.IsAny<string>()), Times.Once());
|
||||
Assert.Equal(JsonSerializer.Serialize(license), licenseJson);
|
||||
}
|
||||
}
|
||||
|
21
Livia.Tests/Startup.cs
Normal file
|
@ -0,0 +1,21 @@
|
|||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Xunit.DependencyInjection.Logging;
|
||||
using Livia.Models;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using ILogger = Microsoft.Extensions.Logging.ILogger;
|
||||
|
||||
namespace Livia.Tests;
|
||||
|
||||
internal class Startup
|
||||
{
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
services.AddTransient<IAesOperation, AesOperation>();
|
||||
|
||||
services.AddLogging(lb => lb.AddXunitOutput());
|
||||
services.AddSingleton<ILogger>(svc => svc.GetRequiredService<ILogger<object>>());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
127
Livia.sln
Normal file
|
@ -0,0 +1,127 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.4.33103.184
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Livia", "Livia\Livia.csproj", "{3750D234-A239-4910-9B19-9A6DF466001E}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DicomViewer", "DicomViewer\DicomViewer.csproj", "{29AD0B66-354D-4310-B8B6-015E0ADE902C}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CereFlow", "CereFlow\CereFlow.csproj", "{224E4779-75F0-4EE9-A570-17A9A312D424}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Astroke", "Astroke\Astroke.csproj", "{834C4298-073E-4593-BE0D-93004F30BCDA}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SiemensCereflow", "SiemensCereflow\SiemensCereflow.csproj", "{92C0171E-563E-4FB1-A361-E0787B67B674}"
|
||||
EndProject
|
||||
Project("{54435603-DBB4-11D2-8724-00A0C9A8B90C}") = "SiemensCereflowSetup", "SiemensCereflowSetup\SiemensCereflowSetup.vdproj", "{7BBA876A-A168-4BB3-AC58-7BA50A91764D}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MdpC2", "MdpC2\MdpC2.csproj", "{7684575F-9438-4D1E-AD9A-C5EEBC15501A}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MdpC3", "MdpC3\MdpC3.csproj", "{C7F52038-5E82-4EFE-8CB1-72EF326A7136}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "iBrain", "iBrain\iBrain.csproj", "{259D197A-20EC-45DC-8FC6-502A44F6534A}"
|
||||
EndProject
|
||||
Project("{54435603-DBB4-11D2-8724-00A0C9A8B90C}") = "MdpC2Setup", "MdpC2Setup\MdpC2Setup.vdproj", "{CFBAC228-98C7-442F-AE03-C3BE406A8CAD}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LiviaAdmin", "LiviaAdmin\LiviaAdmin.csproj", "{85F0DBC3-190C-48A1-89EC-18F5D79FAAB2}"
|
||||
EndProject
|
||||
Project("{54435603-DBB4-11D2-8724-00A0C9A8B90C}") = "MdpC3Setup", "MdpC3Setup\MdpC3Setup.vdproj", "{6E11B06B-47C1-42B6-B9A6-39AF3521165D}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Livia.Tests", "Livia.Tests\Livia.Tests.csproj", "{F2AE835D-84C6-45F6-8C14-CB9B0A32B577}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MdpC2.Tests", "MdpC2.Tests\MdpC2.Tests.csproj", "{68AF80FD-B9ED-4347-B641-7FFCB75C1CAB}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MdpC6", "MdpC6\MdpC6.csproj", "{5A95BEEF-7E0A-401E-9DE4-58DBB235E761}"
|
||||
EndProject
|
||||
Project("{54435603-DBB4-11D2-8724-00A0C9A8B90C}") = "MdpC6Setup", "MdpC6Setup\MdpC6Setup.vdproj", "{E0CC8636-899B-4248-8BA5-3265BB87067E}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tp1", "Tp1\Tp1.csproj", "{635E883B-B096-4910-A37E-87D9C4FD06CE}"
|
||||
EndProject
|
||||
Project("{54435603-DBB4-11D2-8724-00A0C9A8B90C}") = "Tp1Setup", "Tp1Setup\Tp1Setup.vdproj", "{E41C96CD-B9D8-4FFF-B0A5-AE59C6ED04F6}"
|
||||
EndProject
|
||||
Project("{54435603-DBB4-11D2-8724-00A0C9A8B90C}") = "CereflowSetup", "CereflowSetup\CereflowSetup.vdproj", "{BE715911-6929-4C88-9A20-0C1BCE79D0BA}"
|
||||
EndProject
|
||||
Project("{54435603-DBB4-11D2-8724-00A0C9A8B90C}") = "iBrainSetup", "iBrainSetup\iBrainSetup.vdproj", "{B0BA5D41-99DC-4B28-A0C0-0ED9A018DC92}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{3750D234-A239-4910-9B19-9A6DF466001E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{3750D234-A239-4910-9B19-9A6DF466001E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{3750D234-A239-4910-9B19-9A6DF466001E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{3750D234-A239-4910-9B19-9A6DF466001E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{29AD0B66-354D-4310-B8B6-015E0ADE902C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{29AD0B66-354D-4310-B8B6-015E0ADE902C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{29AD0B66-354D-4310-B8B6-015E0ADE902C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{29AD0B66-354D-4310-B8B6-015E0ADE902C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{224E4779-75F0-4EE9-A570-17A9A312D424}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{224E4779-75F0-4EE9-A570-17A9A312D424}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{224E4779-75F0-4EE9-A570-17A9A312D424}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{224E4779-75F0-4EE9-A570-17A9A312D424}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{834C4298-073E-4593-BE0D-93004F30BCDA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{834C4298-073E-4593-BE0D-93004F30BCDA}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{834C4298-073E-4593-BE0D-93004F30BCDA}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{834C4298-073E-4593-BE0D-93004F30BCDA}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{92C0171E-563E-4FB1-A361-E0787B67B674}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{92C0171E-563E-4FB1-A361-E0787B67B674}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{92C0171E-563E-4FB1-A361-E0787B67B674}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{92C0171E-563E-4FB1-A361-E0787B67B674}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{7BBA876A-A168-4BB3-AC58-7BA50A91764D}.Debug|Any CPU.ActiveCfg = Debug
|
||||
{7BBA876A-A168-4BB3-AC58-7BA50A91764D}.Release|Any CPU.ActiveCfg = Release
|
||||
{7684575F-9438-4D1E-AD9A-C5EEBC15501A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{7684575F-9438-4D1E-AD9A-C5EEBC15501A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{7684575F-9438-4D1E-AD9A-C5EEBC15501A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{7684575F-9438-4D1E-AD9A-C5EEBC15501A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{C7F52038-5E82-4EFE-8CB1-72EF326A7136}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{C7F52038-5E82-4EFE-8CB1-72EF326A7136}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C7F52038-5E82-4EFE-8CB1-72EF326A7136}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C7F52038-5E82-4EFE-8CB1-72EF326A7136}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{259D197A-20EC-45DC-8FC6-502A44F6534A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{259D197A-20EC-45DC-8FC6-502A44F6534A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{259D197A-20EC-45DC-8FC6-502A44F6534A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{259D197A-20EC-45DC-8FC6-502A44F6534A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{CFBAC228-98C7-442F-AE03-C3BE406A8CAD}.Debug|Any CPU.ActiveCfg = Debug
|
||||
{CFBAC228-98C7-442F-AE03-C3BE406A8CAD}.Debug|Any CPU.Build.0 = Debug
|
||||
{CFBAC228-98C7-442F-AE03-C3BE406A8CAD}.Release|Any CPU.ActiveCfg = Release
|
||||
{CFBAC228-98C7-442F-AE03-C3BE406A8CAD}.Release|Any CPU.Build.0 = Release
|
||||
{85F0DBC3-190C-48A1-89EC-18F5D79FAAB2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{85F0DBC3-190C-48A1-89EC-18F5D79FAAB2}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{85F0DBC3-190C-48A1-89EC-18F5D79FAAB2}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{85F0DBC3-190C-48A1-89EC-18F5D79FAAB2}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{6E11B06B-47C1-42B6-B9A6-39AF3521165D}.Debug|Any CPU.ActiveCfg = Debug
|
||||
{6E11B06B-47C1-42B6-B9A6-39AF3521165D}.Release|Any CPU.ActiveCfg = Release
|
||||
{F2AE835D-84C6-45F6-8C14-CB9B0A32B577}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{F2AE835D-84C6-45F6-8C14-CB9B0A32B577}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{F2AE835D-84C6-45F6-8C14-CB9B0A32B577}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{F2AE835D-84C6-45F6-8C14-CB9B0A32B577}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{68AF80FD-B9ED-4347-B641-7FFCB75C1CAB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{68AF80FD-B9ED-4347-B641-7FFCB75C1CAB}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{68AF80FD-B9ED-4347-B641-7FFCB75C1CAB}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{68AF80FD-B9ED-4347-B641-7FFCB75C1CAB}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{5A95BEEF-7E0A-401E-9DE4-58DBB235E761}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{5A95BEEF-7E0A-401E-9DE4-58DBB235E761}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{5A95BEEF-7E0A-401E-9DE4-58DBB235E761}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{5A95BEEF-7E0A-401E-9DE4-58DBB235E761}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{E0CC8636-899B-4248-8BA5-3265BB87067E}.Debug|Any CPU.ActiveCfg = Debug
|
||||
{E0CC8636-899B-4248-8BA5-3265BB87067E}.Release|Any CPU.ActiveCfg = Release
|
||||
{635E883B-B096-4910-A37E-87D9C4FD06CE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{635E883B-B096-4910-A37E-87D9C4FD06CE}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{635E883B-B096-4910-A37E-87D9C4FD06CE}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{635E883B-B096-4910-A37E-87D9C4FD06CE}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{E41C96CD-B9D8-4FFF-B0A5-AE59C6ED04F6}.Debug|Any CPU.ActiveCfg = Debug
|
||||
{E41C96CD-B9D8-4FFF-B0A5-AE59C6ED04F6}.Release|Any CPU.ActiveCfg = Release
|
||||
{BE715911-6929-4C88-9A20-0C1BCE79D0BA}.Debug|Any CPU.ActiveCfg = Debug
|
||||
{BE715911-6929-4C88-9A20-0C1BCE79D0BA}.Release|Any CPU.ActiveCfg = Release
|
||||
{B0BA5D41-99DC-4B28-A0C0-0ED9A018DC92}.Debug|Any CPU.ActiveCfg = Debug
|
||||
{B0BA5D41-99DC-4B28-A0C0-0ED9A018DC92}.Release|Any CPU.ActiveCfg = Release
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {263C3ADC-BDF8-429B-B27F-B2639AD2365B}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
24
Livia.sln.DotSettings
Normal file
|
@ -0,0 +1,24 @@
|
|||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7684575F_002D9438_002D4D1E_002DAD9A_002DC5EEBC15501A_002Fd_003Aastroke_005Fcmd/@EntryIndexedValue">ExplicitlyExcluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=92C0171E_002D563E_002D4FB1_002DA361_002DE0787B67B674_002Fd_003Acereflow_005Fcommand_005Fline/@EntryIndexedValue">ExplicitlyExcluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=C7F52038_002D5E82_002D4EFE_002D8CB1_002D72EF326A7136_002Fd_003Aastroke_005Fcmd/@EntryIndexedValue">ExplicitlyExcluded</s:String>
|
||||
<s:Boolean x:Key="/Default/GrammarAndSpelling/GrammarChecking/Exceptions/=Id/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Acbv/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=animage/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=astroke/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Borderless/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Bupt/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Cere/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=cereflow/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Clowd/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=dataset/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Debounce/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=decryptor/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=DICOM/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Limbic/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=PACS/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=SCVF/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Snackbar/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Templated/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Transitioner/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Xuanwu/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
111
Livia/App.config
Normal file
|
@ -0,0 +1,111 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<configuration>
|
||||
<configSections>
|
||||
<sectionGroup name="userSettings" type="System.Configuration.UserSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
|
||||
<section name="Livia.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" allowExeDefinition="MachineToLocalUser" requirePermission="false" />
|
||||
<section name="Livia.Properties.ViewSettings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" allowExeDefinition="MachineToLocalUser" requirePermission="false" />
|
||||
</sectionGroup>
|
||||
</configSections>
|
||||
<userSettings>
|
||||
<Livia.Properties.Settings>
|
||||
<setting name="UpgradeRequired" serializeAs="String">
|
||||
<value>True</value>
|
||||
</setting>
|
||||
<setting name="ApplicationQuitNormally" serializeAs="String">
|
||||
<value>True</value>
|
||||
</setting>
|
||||
<setting name="DefaultUserName" serializeAs="String">
|
||||
<value />
|
||||
</setting>
|
||||
<setting name="DeveloperMode" serializeAs="String">
|
||||
<value>False</value>
|
||||
</setting>
|
||||
<setting name="ServerAddress" serializeAs="String">
|
||||
<value>astroke.cereflow.cn</value>
|
||||
</setting>
|
||||
<setting name="ServerPort" serializeAs="String">
|
||||
<value>443</value>
|
||||
</setting>
|
||||
<setting name="OpenReportOnFinish" serializeAs="String">
|
||||
<value>True</value>
|
||||
</setting>
|
||||
<setting name="ShowArchiveDownloadButton" serializeAs="String">
|
||||
<value>True</value>
|
||||
</setting>
|
||||
<setting name="AutoRefreshServerJobList" serializeAs="String">
|
||||
<value>False</value>
|
||||
</setting>
|
||||
<setting name="LastReadNoticeId" serializeAs="String">
|
||||
<value>-1</value>
|
||||
</setting>
|
||||
<setting name="ShowSelectReportModuleDialog" serializeAs="String">
|
||||
<value>True</value>
|
||||
</setting>
|
||||
<setting name="ShowPushToPacsButton" serializeAs="String">
|
||||
<value>False</value>
|
||||
</setting>
|
||||
<setting name="ShowDicomNodeTab" serializeAs="String">
|
||||
<value>False</value>
|
||||
</setting>
|
||||
<setting name="ShowReportModuleAalCheckbox" serializeAs="String">
|
||||
<value>False</value>
|
||||
</setting>
|
||||
<setting name="LogoutAfter" serializeAs="String">
|
||||
<value>10</value>
|
||||
</setting>
|
||||
<setting name="Language" serializeAs="String">
|
||||
<value>zh-CN</value>
|
||||
</setting>
|
||||
<setting name="CultureInfo" serializeAs="String">
|
||||
<value />
|
||||
</setting>
|
||||
<setting name="AuthenticationKey" serializeAs="String">
|
||||
<value />
|
||||
</setting>
|
||||
<setting name="UpdateChannel" serializeAs="String">
|
||||
<value>stable</value>
|
||||
</setting>
|
||||
<setting name="ShowUpdateButton" serializeAs="String">
|
||||
<value>True</value>
|
||||
</setting>
|
||||
<setting name="ShowSortedDownloadButton" serializeAs="String">
|
||||
<value>True</value>
|
||||
</setting>
|
||||
</Livia.Properties.Settings>
|
||||
<Livia.Properties.ViewSettings>
|
||||
<setting name="MainWindowWidth" serializeAs="String">
|
||||
<value>1024</value>
|
||||
</setting>
|
||||
<setting name="MainWindowHeight" serializeAs="String">
|
||||
<value>768</value>
|
||||
</setting>
|
||||
<setting name="MainWindowTop" serializeAs="String">
|
||||
<value>0</value>
|
||||
</setting>
|
||||
<setting name="MainWindowLeft" serializeAs="String">
|
||||
<value>0</value>
|
||||
</setting>
|
||||
<setting name="MainWindowIsMaximized" serializeAs="String">
|
||||
<value>False</value>
|
||||
</setting>
|
||||
<setting name="MainWindowRestoreWidth" serializeAs="String">
|
||||
<value>1024</value>
|
||||
</setting>
|
||||
<setting name="MainWindowRestoreHeight" serializeAs="String">
|
||||
<value>768</value>
|
||||
</setting>
|
||||
<setting name="MainWindowRestoreTop" serializeAs="String">
|
||||
<value>0</value>
|
||||
</setting>
|
||||
<setting name="MainWindowRestoreLeft" serializeAs="String">
|
||||
<value>0</value>
|
||||
</setting>
|
||||
<setting name="FontFamily" serializeAs="String">
|
||||
<value>Source Han Sans SC VF</value>
|
||||
</setting>
|
||||
<setting name="FontWeight" serializeAs="String">
|
||||
<value>400</value>
|
||||
</setting>
|
||||
</Livia.Properties.ViewSettings>
|
||||
</userSettings>
|
||||
</configuration>
|
10
Livia/AssemblyInfo.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
using System.Windows;
|
||||
|
||||
[assembly: ThemeInfo(
|
||||
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
|
||||
//(used if a resource is not found in the page,
|
||||
// or application resource dictionaries)
|
||||
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
|
||||
//(used if a resource is not found in the page,
|
||||
// app, or any theme specific resource dictionaries)
|
||||
)]
|
88
Livia/Livia.csproj
Normal file
|
@ -0,0 +1,88 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>Library</OutputType>
|
||||
<TargetFramework>net8.0-windows</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<UseWPF>true</UseWPF>
|
||||
<AssemblyName>Livia</AssemblyName>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<Authors>安影科技(北京)有限公司</Authors>
|
||||
<Company>$(Authors)</Company>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<None Remove="Resources\Images\an-image.png" />
|
||||
<None Remove="Resources\Images\black-white.png" />
|
||||
<None Remove="Resources\Images\login_background.png" />
|
||||
<None Remove="Resources\Images\orange-green.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Resource Include="Resources\Fonts\SourceHanSans-VF.otf.ttc">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Resource>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Resource Include="Resources\Images\an-image.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Resource>
|
||||
<Resource Include="Resources\Images\black-white.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Resource>
|
||||
<Resource Include="Resources\Images\login_background.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Resource>
|
||||
<Resource Include="Resources\Images\orange-green.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Resource>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.2" />
|
||||
<PackageReference Include="fo-dicom" Version="5.1.2" />
|
||||
<PackageReference Include="fo-dicom.Imaging.Desktop" Version="5.1.2" />
|
||||
<PackageReference Include="JetBrains.Annotations" Version="2023.3.0" />
|
||||
<PackageReference Include="MaterialDesignThemes" Version="4.9.0" />
|
||||
<PackageReference Include="Ookii.Dialogs.Wpf" Version="5.0.1" />
|
||||
<PackageReference Include="Serilog" Version="3.1.1" />
|
||||
<PackageReference Include="Serilog.Extensions.Logging" Version="8.0.0" />
|
||||
<PackageReference Include="Serilog.Settings.Configuration" Version="8.0.0" />
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
|
||||
<PackageReference Include="Serilog.Sinks.Trace" Version="3.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="8.0.0" />
|
||||
<PackageReference Include="System.Drawing.Common" Version="8.0.3" />
|
||||
<PackageReference Include="System.Management" Version="8.0.0" />
|
||||
<PackageReference Include="Velopack" Version="0.0.359" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Update="Properties\Settings.Designer.cs">
|
||||
<DesignTimeSharedInput>True</DesignTimeSharedInput>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Settings.settings</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Update="Properties\ViewSettings.Designer.cs">
|
||||
<DesignTimeSharedInput>True</DesignTimeSharedInput>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>ViewSettings.settings</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Update="Properties\Settings.settings">
|
||||
<Generator>PublicSettingsSingleFileGenerator</Generator>
|
||||
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
|
||||
</None>
|
||||
<None Update="Properties\ViewSettings.settings">
|
||||
<Generator>PublicSettingsSingleFileGenerator</Generator>
|
||||
<LastGenOutput>ViewSettings.Designer.cs</LastGenOutput>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
|
||||
<_Parameter1>$(AssemblyName).Tests</_Parameter1>
|
||||
</AssemblyAttribute>
|
||||
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
|
||||
<_Parameter1>MdpC2.Tests</_Parameter1>
|
||||
</AssemblyAttribute>
|
||||
</ItemGroup>
|
||||
</Project>
|
73
Livia/Models/AesOperation.cs
Normal file
|
@ -0,0 +1,73 @@
|
|||
using System.IO;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Livia.Models;
|
||||
//from https://www.c-sharpcorner.com/article/encryption-and-decryption-using-a-symmetric-key-in-c-sharp/
|
||||
|
||||
public interface IAesOperation
|
||||
{
|
||||
string EncryptString(string key, string plainText);
|
||||
string DecryptString(string key, string cipherText);
|
||||
}
|
||||
|
||||
internal class AesOperation(ILogger logger) : IAesOperation
|
||||
{
|
||||
public string EncryptString(string key, string plainText)
|
||||
{
|
||||
try
|
||||
{
|
||||
byte[] iv = new byte[16];
|
||||
byte[] array;
|
||||
|
||||
using (Aes aes = Aes.Create())
|
||||
{
|
||||
aes.Key = Encoding.UTF8.GetBytes(key);
|
||||
aes.IV = iv;
|
||||
|
||||
ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV);
|
||||
|
||||
using MemoryStream memoryStream = new();
|
||||
using CryptoStream cryptoStream = new(memoryStream, encryptor, CryptoStreamMode.Write);
|
||||
using (StreamWriter streamWriter = new(cryptoStream))
|
||||
{
|
||||
streamWriter.Write(plainText);
|
||||
}
|
||||
|
||||
array = memoryStream.ToArray();
|
||||
}
|
||||
|
||||
return Convert.ToBase64String(array);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.LogError(e, "Encrypt failed");
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
public string DecryptString(string key, string cipherText)
|
||||
{
|
||||
try
|
||||
{
|
||||
byte[] iv = new byte[16];
|
||||
byte[] buffer = Convert.FromBase64String(cipherText);
|
||||
|
||||
using Aes aes = Aes.Create();
|
||||
aes.Key = Encoding.UTF8.GetBytes(key);
|
||||
aes.IV = iv;
|
||||
ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
|
||||
|
||||
using MemoryStream memoryStream = new(buffer);
|
||||
using CryptoStream cryptoStream = new(memoryStream, decryptor, CryptoStreamMode.Read);
|
||||
using StreamReader streamReader = new(cryptoStream);
|
||||
return streamReader.ReadToEnd();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.LogError(e, "Decrypt failed");
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
120
Livia/Models/AppHelper.cs
Normal file
|
@ -0,0 +1,120 @@
|
|||
using System.Configuration;
|
||||
using System.IO;
|
||||
using System.Windows;
|
||||
using System.Windows.Threading;
|
||||
using Livia.Properties;
|
||||
using Livia.Views.Utility;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Livia.Models;
|
||||
|
||||
public class AppHelper(ILogger logger, IWarningSystem warningSystem)
|
||||
{
|
||||
public void UpdateSettings()
|
||||
{
|
||||
try
|
||||
{
|
||||
string? configPathStr = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.PerUserRoamingAndLocal).FilePath;
|
||||
if (configPathStr == null)
|
||||
return;
|
||||
|
||||
string[] pathSplit = configPathStr.Split('\\');
|
||||
string currentDirectory = string.Join("\\", pathSplit.Take(pathSplit.Length - 2));
|
||||
string subFolderNamePrefix = string.Join('_', pathSplit[^3].Split('_').Take(2));
|
||||
|
||||
logger.LogInformation("Settings directory is {path}", currentDirectory);
|
||||
if (Directory.Exists(currentDirectory))
|
||||
{
|
||||
logger.LogInformation("Settings directory already exists, move on");
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.LogInformation("Settings directory does not exist. Finding another one");
|
||||
string searchDirectory = string.Join("\\", pathSplit.Take(pathSplit.Length - 3));
|
||||
if (Directory.Exists(searchDirectory))
|
||||
{
|
||||
string[] possibleDirectories = Directory.GetDirectories(searchDirectory, "*", SearchOption.TopDirectoryOnly);
|
||||
|
||||
foreach (string possibleDirectory in possibleDirectories)
|
||||
{
|
||||
string directoryName = Path.GetRelativePath(searchDirectory, possibleDirectory);
|
||||
if (!directoryName.StartsWith(subFolderNamePrefix))
|
||||
continue;
|
||||
logger.LogInformation("Found {directory}", directoryName);
|
||||
Directory.Move(possibleDirectory, currentDirectory);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.LogError(e, "Error updating settings");
|
||||
throw;
|
||||
}
|
||||
|
||||
if (!Settings.Default.UpgradeRequired)
|
||||
return;
|
||||
|
||||
logger.LogInformation("Upgrading Settings");
|
||||
|
||||
Settings.Default.Upgrade();
|
||||
ViewSettings.Default.Upgrade();
|
||||
|
||||
Settings.Default.UpgradeRequired = false;
|
||||
|
||||
Settings.Default.Save();
|
||||
ViewSettings.Default.Save();
|
||||
}
|
||||
|
||||
public void LoadStrings()
|
||||
{
|
||||
//load language files
|
||||
string language = Settings.Default.Language;
|
||||
if (language == "zh-CN")
|
||||
{
|
||||
logger.LogInformation("Using zh language, no additional strings needs loading");
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
language = language.Split('-')[0];
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.LogInformation(e, "Cannot parse language {language}", language);
|
||||
}
|
||||
|
||||
logger.LogInformation($"Loading strings for {language}");
|
||||
|
||||
//livia dict
|
||||
LoadXamlFile($"/livia;component/Resources/Strings/{language}.xaml");
|
||||
|
||||
//dict of each project
|
||||
LoadXamlFile($"../Resources/Strings/{language}.xaml");
|
||||
}
|
||||
}
|
||||
|
||||
private void LoadXamlFile(string path)
|
||||
{
|
||||
try
|
||||
{
|
||||
//livia dict
|
||||
Application.Current.Resources.MergedDictionaries.Add(new ResourceDictionary
|
||||
{
|
||||
Source = new Uri(path, UriKind.RelativeOrAbsolute)
|
||||
});
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.LogError(e, "Error load language files for {path}", path);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnDispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
|
||||
{
|
||||
logger.LogError(e.Exception, "System crashing");
|
||||
warningSystem.ShowDialog(WarningWindowKind.Error, true, "SystemCrashing");
|
||||
}
|
||||
}
|
90
Livia/Models/Data/AdditionalInfoJson.cs
Normal file
|
@ -0,0 +1,90 @@
|
|||
using System.Text.Json.Serialization;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace Livia.Models.Data;
|
||||
|
||||
public readonly record struct AdditionalInfoJson
|
||||
{
|
||||
[JsonPropertyName("Atlas"), UsedImplicitly]
|
||||
public Dictionary<string, List<AtlasInfo>> AtlasInfoListDict { get; init; }
|
||||
|
||||
[JsonPropertyName("ShowMosaic"), UsedImplicitly]
|
||||
public List<MosaicImageInfo> MosaicImageInfoList { get; init; }
|
||||
|
||||
[JsonPropertyName("ScanInfo")]
|
||||
public ScanInfo ScanInfo { get; init; }
|
||||
|
||||
[JsonPropertyName("ShowAxial"), UsedImplicitly]
|
||||
public List<AxialOverviewImageInfo> AxialOverviewImageInfoList { get; init; }
|
||||
|
||||
[JsonPropertyName("AxialOverlay"), UsedImplicitly]
|
||||
public List<RoiOverlayInfo> AxialRoiOverlayInfoList { get; init; }
|
||||
|
||||
[JsonPropertyName("MosaicOverlay"), UsedImplicitly]
|
||||
public List<RoiOverlayInfo> MosaicRoiOverlayInfoList { get; init; }
|
||||
}
|
||||
|
||||
public readonly record struct ScanInfo
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public string PatientName { get; init; }
|
||||
|
||||
[UsedImplicitly, JsonPropertyName("PatientID")]
|
||||
public string? PatientId { get; init; }
|
||||
|
||||
[UsedImplicitly]
|
||||
public string PatientSex { get; init; }
|
||||
|
||||
[UsedImplicitly]
|
||||
public string PatientAge { get; init; }
|
||||
|
||||
[UsedImplicitly]
|
||||
public string StudyDate { get; init; }
|
||||
}
|
||||
|
||||
public readonly record struct MosaicImageInfo
|
||||
{
|
||||
[JsonPropertyName("ImgID"), UsedImplicitly]
|
||||
public string ImgId { get; init; }
|
||||
[UsedImplicitly]
|
||||
public List<float>? Range { get; init; }
|
||||
|
||||
[UsedImplicitly]
|
||||
public string Unit { get; init; }
|
||||
|
||||
[UsedImplicitly]
|
||||
public bool IsGray { get; init; }
|
||||
|
||||
[UsedImplicitly]
|
||||
public string FilePath { get; init; }
|
||||
}
|
||||
|
||||
public readonly record struct AtlasInfo
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public string AtlasName { get; init; }
|
||||
|
||||
[UsedImplicitly]
|
||||
public List<float> Range { get; init; }
|
||||
|
||||
[UsedImplicitly]
|
||||
public string Unit { get; init; }
|
||||
}
|
||||
|
||||
public readonly record struct AxialOverviewImageInfo
|
||||
{
|
||||
[JsonPropertyName("ImgID"), UsedImplicitly]
|
||||
public string ImgId { get; init; }
|
||||
|
||||
[UsedImplicitly]
|
||||
public string FilePath { get; init; }
|
||||
}
|
||||
|
||||
public readonly record struct RoiOverlayInfo
|
||||
{
|
||||
[JsonPropertyName("Region"), UsedImplicitly]
|
||||
public string RoiTabId { get; init; }
|
||||
|
||||
[JsonPropertyName("ImgID"), UsedImplicitly]
|
||||
public string ImgId { get; init; }
|
||||
}
|
36
Livia/Models/Data/DataBlock.cs
Normal file
|
@ -0,0 +1,36 @@
|
|||
using System.IO;
|
||||
using Livia.Utility;
|
||||
using Livia.Utility.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Livia.Models.Data;
|
||||
|
||||
public class DataBlock(string sourceDir)
|
||||
{
|
||||
public string ZipPath { get; private set; } = string.Empty;
|
||||
public string ResultPath { get; set; } = string.Empty;
|
||||
public string Key { get; set; } = string.Empty;
|
||||
public string SourceDir { get; } = sourceDir;
|
||||
public Dictionary<string, string>? ArgsDict { get; init; }
|
||||
|
||||
private readonly ILogger _logger = ServiceProviderFactory.ServiceProvider.GetRequiredService<ILogger>();
|
||||
|
||||
public async Task Pack(CancellationToken token)
|
||||
{
|
||||
// in case it is not there
|
||||
try
|
||||
{
|
||||
// ReSharper disable once StringLiteralTypo
|
||||
string zipName = DateTime.Now.ToString("yyyyMMddHHmmssffff") + ".zip";
|
||||
string zipPath = Path.Combine(ServiceConfigurations.TempFolder, zipName);
|
||||
await Task.Run(() => LiviaUtility.CreateFromDirectory(SourceDir, zipPath, token), token).ConfigureAwait(false);
|
||||
ZipPath = zipPath;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError(e, "Error packing {path}", SourceDir);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
644
Livia/Models/Data/LiviaHttpClient.cs
Normal file
|
@ -0,0 +1,644 @@
|
|||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Text;
|
||||
using System.Text.Encodings.Web;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Text.Unicode;
|
||||
using JetBrains.Annotations;
|
||||
using Livia.Utility;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Livia.Models.Data;
|
||||
|
||||
public readonly record struct LoginResponseJson
|
||||
{
|
||||
|
||||
[JsonPropertyName("access_token")]
|
||||
public string AccessToken { get; init; }
|
||||
|
||||
[JsonPropertyName("token_type")]
|
||||
public string TokenType { get; init; }
|
||||
}
|
||||
|
||||
public readonly record struct LoginErrorResponseJson
|
||||
{
|
||||
|
||||
[JsonPropertyName("errno")]
|
||||
public int ErrorNumber { get; init; }
|
||||
|
||||
[JsonPropertyName("count")]
|
||||
public int? Count { get; init; }
|
||||
|
||||
[JsonPropertyName("seconds")]
|
||||
public int? Seconds { get; init; }
|
||||
}
|
||||
|
||||
|
||||
public readonly record struct ProcessDataResponseJson
|
||||
{
|
||||
|
||||
[JsonPropertyName("code")]
|
||||
public int Code { get; init; }
|
||||
|
||||
[JsonPropertyName("msg")]
|
||||
public string Msg { get; init; }
|
||||
|
||||
[JsonPropertyName("key")]
|
||||
public string Key { get; init; }
|
||||
|
||||
[JsonPropertyName("callback")]
|
||||
public string CallBack { get; init; }
|
||||
|
||||
[JsonPropertyName("errno")]
|
||||
public int ErrorNumber { get; init; }
|
||||
}
|
||||
|
||||
public readonly record struct ServerResponseJson
|
||||
{
|
||||
|
||||
[JsonPropertyName("code")]
|
||||
public int Code { get; init; }
|
||||
|
||||
[JsonPropertyName("msg")]
|
||||
public string Msg { get; init; }
|
||||
|
||||
[JsonPropertyName("errno")]
|
||||
public int ErrorNumber { get; init; }
|
||||
}
|
||||
|
||||
public readonly record struct SimpleKeyJson
|
||||
{
|
||||
[JsonPropertyName("key")]
|
||||
[UsedImplicitly]
|
||||
public string Key { get; init; }
|
||||
}
|
||||
|
||||
public readonly record struct ReportKeyIdJson
|
||||
{
|
||||
[JsonPropertyName("key")]
|
||||
[UsedImplicitly]
|
||||
public string Key { get; init; }
|
||||
|
||||
[JsonPropertyName("report_id")]
|
||||
[UsedImplicitly]
|
||||
public int ReportId { get; init; }
|
||||
}
|
||||
|
||||
public readonly record struct ReportIdJson
|
||||
{
|
||||
[JsonPropertyName("report_id")]
|
||||
[UsedImplicitly]
|
||||
public int ReportId { get; init; }
|
||||
|
||||
[JsonPropertyName("errno")]
|
||||
public int? ErrorNumber { get; init; }
|
||||
}
|
||||
|
||||
public readonly record struct EditPasswordJson
|
||||
{
|
||||
[JsonPropertyName("old_password")]
|
||||
[UsedImplicitly]
|
||||
public string OldPassword { get; init; }
|
||||
|
||||
[JsonPropertyName("new_password")]
|
||||
[UsedImplicitly]
|
||||
public string NewPassword { get; init; }
|
||||
}
|
||||
|
||||
public readonly record struct SelectSeriesJson
|
||||
{
|
||||
[JsonPropertyName("key")]
|
||||
[UsedImplicitly]
|
||||
public string Key { get; init; }
|
||||
|
||||
[JsonPropertyName("choose_series")]
|
||||
[UsedImplicitly]
|
||||
public List<string> SelectedSeries { get; init; }
|
||||
}
|
||||
|
||||
public readonly record struct DongleInfoResponseJson
|
||||
{
|
||||
[JsonPropertyName("code")]
|
||||
public int Code { get; init; }
|
||||
|
||||
[JsonPropertyName("quota")]
|
||||
public uint Quota { get; init; }
|
||||
|
||||
[JsonPropertyName("msg")]
|
||||
public string? Msg { get; init; }
|
||||
}
|
||||
|
||||
public readonly record struct ServerJobListJsonResponse
|
||||
{
|
||||
[JsonPropertyName("code")]
|
||||
public int Code { get; init; }
|
||||
|
||||
[JsonPropertyName("data")]
|
||||
[UsedImplicitly]
|
||||
public List<ServerJobData> DataList { get; init; }
|
||||
|
||||
[JsonPropertyName("count")]
|
||||
[UsedImplicitly]
|
||||
public int Count { get; init; }
|
||||
}
|
||||
|
||||
public readonly record struct ServerNoticeJsonResponse
|
||||
{
|
||||
[JsonPropertyName("code")]
|
||||
public int Code { get; init; }
|
||||
|
||||
[JsonPropertyName("notice")]
|
||||
[UsedImplicitly]
|
||||
public ServerNotice? Notice { get; init; }
|
||||
}
|
||||
|
||||
public readonly record struct ServerNotice
|
||||
{
|
||||
[JsonPropertyName("notice_type")]
|
||||
[UsedImplicitly]
|
||||
public int NoticeType { get; init; }
|
||||
|
||||
[JsonPropertyName("server_status")]
|
||||
[UsedImplicitly]
|
||||
public int ServerStatus { get; init; }
|
||||
|
||||
[JsonPropertyName("create_at")]
|
||||
[UsedImplicitly]
|
||||
public DateTime CreateAt { get; init; }
|
||||
|
||||
[JsonPropertyName("id")]
|
||||
[UsedImplicitly]
|
||||
public int Id { get; init; }
|
||||
|
||||
[JsonPropertyName("is_show")]
|
||||
[UsedImplicitly]
|
||||
public bool ShowMessage { get; init; }
|
||||
|
||||
[JsonPropertyName("content")]
|
||||
[UsedImplicitly]
|
||||
public string Content { get; init; }
|
||||
}
|
||||
|
||||
public readonly record struct DicomNodeInfoJsonResponse
|
||||
{
|
||||
[JsonPropertyName("code")]
|
||||
public int Code { get; init; }
|
||||
|
||||
[JsonPropertyName("dicom"), UsedImplicitly]
|
||||
public DicomNodeInfo Dicom { get; init; }
|
||||
}
|
||||
|
||||
public readonly record struct DicomNodeInfo
|
||||
{
|
||||
[JsonPropertyName("host"), UsedImplicitly]
|
||||
public string Host { get; init; }
|
||||
|
||||
[JsonPropertyName("port"), UsedImplicitly]
|
||||
public int Port { get; init; }
|
||||
|
||||
[JsonPropertyName("aet"), UsedImplicitly]
|
||||
public string Aet { get; init; }
|
||||
}
|
||||
|
||||
public class ReportItem
|
||||
{
|
||||
[JsonPropertyName("id"), UsedImplicitly]
|
||||
public int Id { get; init; }
|
||||
|
||||
[JsonPropertyName("title"), UsedImplicitly]
|
||||
public string? Title { get; init; }
|
||||
}
|
||||
|
||||
public interface ILiviaHttpClient
|
||||
{
|
||||
Task Login(string username, string password);
|
||||
Task<ServerResponseJson> EditPassword(string oldPassword, string newPassword);
|
||||
Task<ServerResponseJson> Cancel(string key);
|
||||
Task<ServerResponseJson> DeleteReport(string key, int id);
|
||||
Task<DongleInfoResponseJson> UpdateDongleInfo();
|
||||
Task<ServerJobListJsonResponse> CheckServerJobList(int limit, int offset);
|
||||
Task<List<ReportItem>> GetReportList(string key);
|
||||
Task<ServerJobListJsonResponse> SearchData(SearchQueryJson json);
|
||||
Task DownloadReport(string key, int id, string savePath, CancellationToken token);
|
||||
Task<ProcessDataResponseJson> ProcessData(string zipPath, CancellationToken token);
|
||||
Task CheckResult(string key, string savePath, CancellationToken token);
|
||||
Task DownloadArchive(string key, string savePath, CancellationToken token);
|
||||
Task DownloadSorted(string key, string savePath, CancellationToken token);
|
||||
Task<ServerResponseJson> DeleteData(string? id, CancellationToken token);
|
||||
Task<ServerResponseJson> DeleteHistory(string key, CancellationToken token);
|
||||
Task<ServerNoticeJsonResponse> GetNotice();
|
||||
Task<SeriesGroupListJson> GetSeriesGroupList(string key);
|
||||
Task<ServerResponseJson> SelectSeries(string key, List<string> series);
|
||||
Task<ServerResponseJson> Start(string key);
|
||||
Task<ReportIdJson> SelectReportModule(SelectReportModuleJson json);
|
||||
Task<ServerResponseJson> PushToPacs(string key, int id);
|
||||
Task<DicomNodeInfoJsonResponse> GetDicomNodeInfo();
|
||||
}
|
||||
|
||||
public class LiviaHttpClientFactory(IServiceProvider serviceProvider)
|
||||
{
|
||||
public ILiviaHttpClient Create()
|
||||
{
|
||||
return serviceProvider.GetRequiredService<ILiviaHttpClient>();
|
||||
}
|
||||
}
|
||||
|
||||
internal class LiviaHttpClient(HttpClient httpClient, ILogger logger) : ILiviaHttpClient
|
||||
{
|
||||
private readonly HttpClient _httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
|
||||
private static string _token = string.Empty;
|
||||
|
||||
private static readonly JsonSerializerOptions EncodeAll = new()
|
||||
{
|
||||
Encoder = JavaScriptEncoder.Create(UnicodeRanges.All)
|
||||
};
|
||||
|
||||
private static readonly JsonSerializerOptions IgnoreNull = new()
|
||||
{
|
||||
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
|
||||
};
|
||||
|
||||
private void UpdateToken()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(_token))
|
||||
{
|
||||
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _token);
|
||||
}
|
||||
}
|
||||
|
||||
public static void ClearToken()
|
||||
{
|
||||
_token = string.Empty;
|
||||
}
|
||||
|
||||
public async Task Login(string username, string password)
|
||||
{
|
||||
KeyValuePair<string, string>[] data =
|
||||
[
|
||||
new KeyValuePair<string, string>("username", username),
|
||||
new KeyValuePair<string, string>("password", password)
|
||||
];
|
||||
|
||||
logger.LogInformation("Sending post request");
|
||||
HttpResponseMessage result = await _httpClient.PostAsync(ServiceConfigurations.PostLoginPath, new FormUrlEncodedContent(data)).ConfigureAwait(false);
|
||||
await using Stream contentStream = await result.Content.ReadAsStreamAsync();
|
||||
if (result.StatusCode == HttpStatusCode.OK)
|
||||
{
|
||||
LoginResponseJson responseJson = await JsonSerializer.DeserializeAsync<LoginResponseJson>(contentStream);
|
||||
_token = responseJson.AccessToken;
|
||||
}
|
||||
else
|
||||
{
|
||||
LoginErrorResponseJson responseJson = await JsonSerializer.DeserializeAsync<LoginErrorResponseJson>(contentStream);
|
||||
throw new LoginErrorException(responseJson);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<ServerResponseJson> EditPassword(string oldPassword, string newPassword)
|
||||
{
|
||||
UpdateToken();
|
||||
HttpRequestMessage request = new(HttpMethod.Put, ServiceConfigurations.PutEditPasswordPath);
|
||||
EditPasswordJson processJson = new()
|
||||
{
|
||||
OldPassword = oldPassword,
|
||||
NewPassword = newPassword
|
||||
};
|
||||
|
||||
string jsonString = JsonSerializer.Serialize(processJson);
|
||||
using StringContent stringContent = new(jsonString, Encoding.UTF8, "application/json");
|
||||
logger.LogInformation("Sending edit password request");
|
||||
//_logger.LogInformation("JSON is {json}", jsonString);
|
||||
request.Content = stringContent;
|
||||
using HttpResponseMessage result = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false);
|
||||
result.EnsureSuccessStatusCode();
|
||||
await using Stream contentStream = await result.Content.ReadAsStreamAsync();
|
||||
return await JsonSerializer.DeserializeAsync<ServerResponseJson>(contentStream);
|
||||
}
|
||||
|
||||
public async Task<ServerResponseJson> Cancel(string key)
|
||||
{
|
||||
UpdateToken();
|
||||
HttpRequestMessage request = new(HttpMethod.Post, ServiceConfigurations.PostCancelPath);
|
||||
SimpleKeyJson processJson = new()
|
||||
{
|
||||
Key = key
|
||||
};
|
||||
|
||||
string jsonString = JsonSerializer.Serialize(processJson);
|
||||
using StringContent stringContent = new(jsonString, Encoding.UTF8, "application/json");
|
||||
logger.LogInformation("Sending cancel request");
|
||||
logger.LogInformation("JSON is {json}", jsonString);
|
||||
request.Content = stringContent;
|
||||
using HttpResponseMessage result = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false);
|
||||
result.EnsureSuccessStatusCode();
|
||||
await using Stream contentStream = await result.Content.ReadAsStreamAsync();
|
||||
return await JsonSerializer.DeserializeAsync<ServerResponseJson>(contentStream);
|
||||
}
|
||||
|
||||
public async Task<ServerResponseJson> DeleteReport(string key, int id)
|
||||
{
|
||||
UpdateToken();
|
||||
HttpRequestMessage request = new(HttpMethod.Delete, ServiceConfigurations.DeleteReportPath);
|
||||
ReportKeyIdJson processKeyIdJson = new()
|
||||
{
|
||||
Key = key,
|
||||
ReportId = id
|
||||
};
|
||||
|
||||
string jsonString = JsonSerializer.Serialize(processKeyIdJson);
|
||||
using StringContent stringContent = new(jsonString, Encoding.UTF8, "application/json");
|
||||
logger.LogInformation("Sending DeleteReport request");
|
||||
logger.LogInformation("JSON is {json}", jsonString);
|
||||
request.Content = stringContent;
|
||||
using HttpResponseMessage result = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false);
|
||||
result.EnsureSuccessStatusCode();
|
||||
await using Stream contentStream = await result.Content.ReadAsStreamAsync();
|
||||
return await JsonSerializer.DeserializeAsync<ServerResponseJson>(contentStream);
|
||||
}
|
||||
|
||||
public async Task<DongleInfoResponseJson> UpdateDongleInfo()
|
||||
{
|
||||
UpdateToken();
|
||||
HttpRequestMessage request = new(HttpMethod.Get, ServiceConfigurations.GetDongleInfoPath);
|
||||
logger.LogInformation("Sending UpdateDongleInfo request");
|
||||
using HttpResponseMessage result = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false);
|
||||
result.EnsureSuccessStatusCode();
|
||||
await using Stream contentStream = await result.Content.ReadAsStreamAsync();
|
||||
return await JsonSerializer.DeserializeAsync<DongleInfoResponseJson>(contentStream);
|
||||
}
|
||||
|
||||
public async Task<ServerJobListJsonResponse> CheckServerJobList(int limit, int offset)
|
||||
{
|
||||
UpdateToken();
|
||||
string queryUri = $"{ServiceConfigurations.GetServerJobListPath}?limit={limit}&offset={offset}";
|
||||
HttpRequestMessage request = new(HttpMethod.Get, queryUri);
|
||||
logger.LogInformation("Sending CheckServerJobList request");
|
||||
using HttpResponseMessage result = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false);
|
||||
result.EnsureSuccessStatusCode();
|
||||
//_logger.LogInformation(result.Content.ReadAsStringAsync().GetAwaiter().GetResult());
|
||||
//return new ServerJobListJsonResponse();
|
||||
await using Stream contentStream = await result.Content.ReadAsStreamAsync();
|
||||
return await JsonSerializer.DeserializeAsync<ServerJobListJsonResponse>(contentStream);
|
||||
}
|
||||
|
||||
public async Task<List<ReportItem>> GetReportList(string key)
|
||||
{
|
||||
UpdateToken();
|
||||
string getUrl = string.Format(ServiceConfigurations.GetReportLIstPath, key);
|
||||
HttpRequestMessage request = new(HttpMethod.Get, getUrl);
|
||||
logger.LogInformation("Sending GetReportList request");
|
||||
HttpResponseMessage result = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false);
|
||||
result.EnsureSuccessStatusCode();
|
||||
//_logger.LogInformation(result.Content.ReadAsStringAsync().GetAwaiter().GetResult());
|
||||
//return new List<ReportItem>();
|
||||
await using Stream contentStream = await result.Content.ReadAsStreamAsync();
|
||||
return await JsonSerializer.DeserializeAsync<List<ReportItem>>(contentStream) ?? [];
|
||||
}
|
||||
|
||||
public async Task<ServerJobListJsonResponse> SearchData(SearchQueryJson json)
|
||||
{
|
||||
UpdateToken();
|
||||
HttpRequestMessage request = new(HttpMethod.Post, ServiceConfigurations.PostSearchPath);
|
||||
string jsonString = JsonSerializer.Serialize(json, IgnoreNull);
|
||||
using StringContent stringContent = new(jsonString, Encoding.UTF8, "application/json");
|
||||
logger.LogInformation("Sending SearchData request");
|
||||
logger.LogInformation("JSON is {json}", jsonString);
|
||||
request.Content = stringContent;
|
||||
using HttpResponseMessage result = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false);
|
||||
result.EnsureSuccessStatusCode();
|
||||
//_logger.LogInformation(result.Content.ReadAsStringAsync().GetAwaiter().GetResult());
|
||||
//return new ServerJobListJsonResponse();
|
||||
await using Stream contentStream = await result.Content.ReadAsStreamAsync();
|
||||
return await JsonSerializer.DeserializeAsync<ServerJobListJsonResponse>(contentStream);
|
||||
}
|
||||
|
||||
public async Task DownloadReport(string key, int id, string savePath, CancellationToken token)
|
||||
{
|
||||
UpdateToken();
|
||||
string extension = Path.GetExtension(savePath)[1..];
|
||||
string getUrl = string.Format(ServiceConfigurations.GetReportPath, key, id, extension);
|
||||
HttpRequestMessage request = new(HttpMethod.Get, getUrl);
|
||||
logger.LogInformation("Sending DownloadReport request");
|
||||
HttpResponseMessage result = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, token).ConfigureAwait(false);
|
||||
result.EnsureSuccessStatusCode();
|
||||
await using Stream contentStream = await result.Content.ReadAsStreamAsync(token);
|
||||
if (result.Content.Headers.ContentType?.MediaType == "application/json")
|
||||
{
|
||||
ServerResponseJson serverResponseJson = await JsonSerializer.DeserializeAsync<ServerResponseJson>(contentStream, cancellationToken: token);
|
||||
throw new ServerErrorException(serverResponseJson);
|
||||
}
|
||||
await using FileStream outputFileStream = new(savePath, FileMode.Create);
|
||||
await contentStream.CopyToAsync(outputFileStream, token);
|
||||
}
|
||||
|
||||
public async Task<ProcessDataResponseJson> ProcessData(string zipPath, CancellationToken token)
|
||||
{
|
||||
UpdateToken();
|
||||
logger.LogInformation("Sending post request, fileName is {fileName}", zipPath);
|
||||
string queryUri = $"{ServiceConfigurations.PostProcessDataPath}?language={LiviaUtility.GetLanguageOnlyString()}";
|
||||
|
||||
await using FileStream fileStream = new(zipPath, FileMode.Open);
|
||||
MultipartFormDataContent form = new()
|
||||
{
|
||||
{ new StreamContent(fileStream), "file", "data.zip" }
|
||||
};
|
||||
|
||||
HttpRequestMessage request = new(HttpMethod.Post, queryUri);
|
||||
logger.LogInformation("Sending ProcessData request");
|
||||
request.Content = form;
|
||||
using HttpResponseMessage result = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, token).ConfigureAwait(false);
|
||||
result.EnsureSuccessStatusCode();
|
||||
await using Stream contentStream = await result.Content.ReadAsStreamAsync(token);
|
||||
return await JsonSerializer.DeserializeAsync<ProcessDataResponseJson>(contentStream, cancellationToken: token);
|
||||
}
|
||||
|
||||
public async Task CheckResult(string key, string savePath, CancellationToken token)
|
||||
{
|
||||
UpdateToken();
|
||||
string getUrl = string.Format(ServiceConfigurations.GetCheckResultPath, key);
|
||||
HttpRequestMessage request = new(HttpMethod.Get, getUrl);
|
||||
logger.LogInformation("Sending CheckResult request");
|
||||
using HttpResponseMessage result = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, token).ConfigureAwait(false);
|
||||
result.EnsureSuccessStatusCode();
|
||||
await using Stream contentStream = await result.Content.ReadAsStreamAsync(token);
|
||||
|
||||
if (result.Content.Headers.ContentType?.MediaType == "application/json")
|
||||
{
|
||||
ServerResponseJson serverResponseJson = await JsonSerializer.DeserializeAsync<ServerResponseJson>(contentStream, cancellationToken: token);
|
||||
throw new ResultNotReadyException(serverResponseJson);
|
||||
}
|
||||
|
||||
await using FileStream outputFileStream = new(savePath, FileMode.Create);
|
||||
await contentStream.CopyToAsync(outputFileStream, token);
|
||||
}
|
||||
|
||||
public async Task DownloadArchive(string key, string savePath, CancellationToken token)
|
||||
{
|
||||
UpdateToken();
|
||||
string getUrl = string.Format(ServiceConfigurations.GetDownloadArchivePath, key);
|
||||
HttpRequestMessage request = new(HttpMethod.Get, getUrl);
|
||||
logger.LogInformation("Sending DownloadArchive request");
|
||||
HttpResponseMessage result = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, token).ConfigureAwait(false);
|
||||
result.EnsureSuccessStatusCode();
|
||||
await using Stream contentStream = await result.Content.ReadAsStreamAsync(token);
|
||||
await using FileStream outputFileStream = new(savePath, FileMode.Create);
|
||||
await contentStream.CopyToAsync(outputFileStream, token);
|
||||
}
|
||||
|
||||
public async Task DownloadSorted(string key, string savePath, CancellationToken token)
|
||||
{
|
||||
UpdateToken();
|
||||
string getUrl = string.Format(ServiceConfigurations.GetDownloadSortedPath, key);
|
||||
HttpRequestMessage request = new(HttpMethod.Get, getUrl);
|
||||
logger.LogInformation("Sending DownloadSorted request");
|
||||
HttpResponseMessage result = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, token).ConfigureAwait(false);
|
||||
result.EnsureSuccessStatusCode();
|
||||
await using Stream contentStream = await result.Content.ReadAsStreamAsync(token);
|
||||
await using FileStream outputFileStream = new(savePath, FileMode.Create);
|
||||
await contentStream.CopyToAsync(outputFileStream, token);
|
||||
}
|
||||
|
||||
public async Task<ServerResponseJson> DeleteData(string? id, CancellationToken token)
|
||||
{
|
||||
UpdateToken();
|
||||
string queryUri = $"{ServiceConfigurations.DeleteDataPath}?id={id}";
|
||||
HttpRequestMessage request = new(HttpMethod.Delete, queryUri);
|
||||
logger.LogInformation("Sending DeleteData request");
|
||||
HttpResponseMessage result = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, token).ConfigureAwait(false);
|
||||
result.EnsureSuccessStatusCode();
|
||||
await using Stream contentStream = await result.Content.ReadAsStreamAsync(token);
|
||||
return await JsonSerializer.DeserializeAsync<ServerResponseJson>(contentStream, cancellationToken: token);
|
||||
}
|
||||
|
||||
public async Task<ServerResponseJson> DeleteHistory(string key, CancellationToken token)
|
||||
{
|
||||
UpdateToken();
|
||||
string queryUri = $"{ServiceConfigurations.DeleteHistoryPath}?key={key}";
|
||||
HttpRequestMessage request = new(HttpMethod.Delete, queryUri);
|
||||
logger.LogInformation("Sending DeleteHistory request");
|
||||
HttpResponseMessage result = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, token).ConfigureAwait(false);
|
||||
result.EnsureSuccessStatusCode();
|
||||
await using Stream contentStream = await result.Content.ReadAsStreamAsync(token);
|
||||
return await JsonSerializer.DeserializeAsync<ServerResponseJson>(contentStream, cancellationToken: token);
|
||||
}
|
||||
|
||||
public async Task<ServerNoticeJsonResponse> GetNotice()
|
||||
{
|
||||
UpdateToken();
|
||||
HttpRequestMessage request = new(HttpMethod.Get, ServiceConfigurations.GetNoticePath);
|
||||
logger.LogInformation("Sending GetNotice request");
|
||||
using HttpResponseMessage result = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false);
|
||||
result.EnsureSuccessStatusCode();
|
||||
await using Stream contentStream = await result.Content.ReadAsStreamAsync();
|
||||
return await JsonSerializer.DeserializeAsync<ServerNoticeJsonResponse>(contentStream);
|
||||
}
|
||||
|
||||
public async Task<SeriesGroupListJson> GetSeriesGroupList(string key)
|
||||
{
|
||||
UpdateToken();
|
||||
string getUrl = string.Format(ServiceConfigurations.GetSeriesGroupListPath, key);
|
||||
HttpRequestMessage request = new(HttpMethod.Get, getUrl);
|
||||
logger.LogInformation("Sending GetSeriesGroupList request");
|
||||
using HttpResponseMessage result = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false);
|
||||
result.EnsureSuccessStatusCode();
|
||||
await using Stream contentStream = await result.Content.ReadAsStreamAsync();
|
||||
return await JsonSerializer.DeserializeAsync<SeriesGroupListJson>(contentStream);
|
||||
}
|
||||
|
||||
public async Task<ServerResponseJson> SelectSeries(string key, List<string> series)
|
||||
{
|
||||
UpdateToken();
|
||||
HttpRequestMessage request = new(HttpMethod.Post, ServiceConfigurations.PostSelectSeriesPath);
|
||||
SelectSeriesJson processJson = new()
|
||||
{
|
||||
Key = key,
|
||||
SelectedSeries = series
|
||||
};
|
||||
|
||||
string jsonString = JsonSerializer.Serialize(processJson);
|
||||
using StringContent stringContent = new(jsonString, Encoding.UTF8, "application/json");
|
||||
logger.LogInformation("Sending SelectSeries request");
|
||||
logger.LogInformation("JSON is {json}", jsonString);
|
||||
request.Content = stringContent;
|
||||
using HttpResponseMessage result = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false);
|
||||
result.EnsureSuccessStatusCode();
|
||||
await using Stream contentStream = await result.Content.ReadAsStreamAsync();
|
||||
return await JsonSerializer.DeserializeAsync<ServerResponseJson>(contentStream);
|
||||
}
|
||||
|
||||
public async Task<ServerResponseJson> Start(string key)
|
||||
{
|
||||
UpdateToken();
|
||||
HttpRequestMessage request = new(HttpMethod.Post, ServiceConfigurations.PostStartPath);
|
||||
SimpleKeyJson processJson = new()
|
||||
{
|
||||
Key = key
|
||||
};
|
||||
|
||||
string jsonString = JsonSerializer.Serialize(processJson);
|
||||
using StringContent stringContent = new(jsonString, Encoding.UTF8, "application/json");
|
||||
logger.LogInformation("Sending Start request");
|
||||
logger.LogInformation("JSON is {json}", jsonString);
|
||||
request.Content = stringContent;
|
||||
using HttpResponseMessage result = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false);
|
||||
result.EnsureSuccessStatusCode();
|
||||
await using Stream contentStream = await result.Content.ReadAsStreamAsync();
|
||||
return await JsonSerializer.DeserializeAsync<ServerResponseJson>(contentStream);
|
||||
}
|
||||
|
||||
public async Task<ReportIdJson> SelectReportModule(SelectReportModuleJson json)
|
||||
{
|
||||
UpdateToken();
|
||||
HttpRequestMessage request = new(HttpMethod.Post, ServiceConfigurations.PostSelectReportModulePath);
|
||||
string jsonString = JsonSerializer.Serialize(json, EncodeAll);
|
||||
using StringContent stringContent = new(jsonString, Encoding.UTF8, "application/json");
|
||||
logger.LogInformation("Sending SelectReportModule request");
|
||||
logger.LogInformation("JSON is {json}", jsonString);
|
||||
request.Content = stringContent;
|
||||
using HttpResponseMessage result = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false);
|
||||
result.EnsureSuccessStatusCode();
|
||||
//_logger.LogInformation(result.Content.ReadAsStringAsync().GetAwaiter().GetResult());
|
||||
//return new ReportIdJson();
|
||||
await using Stream contentStream = await result.Content.ReadAsStreamAsync();
|
||||
ReportIdJson resultJson = await JsonSerializer.DeserializeAsync<ReportIdJson>(contentStream);
|
||||
if (resultJson.ErrorNumber != null)
|
||||
throw new SelectReportModuleFailedException(resultJson.ErrorNumber.Value);
|
||||
return resultJson;
|
||||
}
|
||||
|
||||
public async Task<ServerResponseJson> PushToPacs(string key, int id)
|
||||
{
|
||||
UpdateToken();
|
||||
HttpRequestMessage request = new(HttpMethod.Post, ServiceConfigurations.PostPushReportPath);
|
||||
ReportKeyIdJson processKeyIdJson = new()
|
||||
{
|
||||
Key = key,
|
||||
ReportId = id
|
||||
};
|
||||
|
||||
string jsonString = JsonSerializer.Serialize(processKeyIdJson);
|
||||
using StringContent stringContent = new(jsonString, Encoding.UTF8, "application/json");
|
||||
logger.LogInformation("Sending PushToPacs request");
|
||||
logger.LogInformation("JSON is {json}", jsonString);
|
||||
request.Content = stringContent;
|
||||
using HttpResponseMessage result = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false);
|
||||
result.EnsureSuccessStatusCode();
|
||||
await using Stream contentStream = await result.Content.ReadAsStreamAsync();
|
||||
return await JsonSerializer.DeserializeAsync<ServerResponseJson>(contentStream);
|
||||
}
|
||||
|
||||
public async Task<DicomNodeInfoJsonResponse> GetDicomNodeInfo()
|
||||
{
|
||||
UpdateToken();
|
||||
HttpRequestMessage request = new(HttpMethod.Get, ServiceConfigurations.GetDicomNodeInfo);
|
||||
logger.LogInformation("Sending GetDicomNodeInfo request");
|
||||
using HttpResponseMessage result = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false);
|
||||
result.EnsureSuccessStatusCode();
|
||||
await using Stream contentStream = await result.Content.ReadAsStreamAsync();
|
||||
return await JsonSerializer.DeserializeAsync<DicomNodeInfoJsonResponse>(contentStream);
|
||||
}
|
||||
}
|
258
Livia/Models/Data/LocalCommandLineServerHandler.cs
Normal file
|
@ -0,0 +1,258 @@
|
|||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using Livia.Utility;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Livia.Models.Data;
|
||||
|
||||
public class LocalCommandLineServerHandler(ILogger logger) : IServerHandler
|
||||
{
|
||||
public event PropertyChangedEventHandler? PropertyChanged;
|
||||
public event PropertyChangingEventHandler? PropertyChanging;
|
||||
public bool Processing { get; set; }
|
||||
|
||||
public bool IsLoggedIn => true;
|
||||
public string Quota => "0";
|
||||
|
||||
private const int FilePrintCount = 10;
|
||||
|
||||
public async Task<(bool success, string messageIndex)> ProcessData(DataBlock dataBlock, CancellationToken token)
|
||||
{
|
||||
string outputPath = Path.Combine(ServiceConfigurations.TempFolder, dataBlock.Key);
|
||||
string prevWorkingDirectory = Environment.CurrentDirectory;
|
||||
|
||||
if (dataBlock.ArgsDict == null)
|
||||
{
|
||||
return (false, "ServerErrorUnknown");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
string inputDir = dataBlock.ArgsDict["input_dir"];
|
||||
logger.LogInformation("Input dir: {path}", inputDir);
|
||||
DirectoryInfo dirInfo = new(inputDir);
|
||||
if (!dirInfo.Exists)
|
||||
{
|
||||
return (false, "InvalidInputDirectoryError");
|
||||
}
|
||||
|
||||
logger.LogInformation("Printing first {num} files", FilePrintCount);
|
||||
LogDirInfo(dirInfo, FilePrintCount);
|
||||
|
||||
string exePath = Path.Join(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), "cereflow_command_line");
|
||||
logger.LogInformation("Attempting to find exe in {path}", exePath);
|
||||
Environment.CurrentDirectory = exePath;
|
||||
string args = $"--input_dir \"{inputDir}\" --tmp_dir \"{outputPath}\" --output_dicom_dir \"{dataBlock.ArgsDict["output_dir"]}\" --language \"{LiviaUtility.GetLanguageOnlyString()}\"";
|
||||
logger.LogInformation("Args is :{args}", args);
|
||||
|
||||
Process process = new();
|
||||
process.StartInfo.FileName = "cmd";
|
||||
process.StartInfo.Arguments = args;
|
||||
process.StartInfo.UseShellExecute = false;
|
||||
process.StartInfo.CreateNoWindow = true;
|
||||
process.StartInfo.RedirectStandardOutput = true;
|
||||
process.StartInfo.RedirectStandardError = true;
|
||||
process.StartInfo.RedirectStandardInput = true;
|
||||
process.StartInfo.StandardOutputEncoding = Encoding.UTF8;
|
||||
process.StartInfo.StandardErrorEncoding = Encoding.UTF8;
|
||||
process.StartInfo.StandardInputEncoding = Encoding.UTF8;
|
||||
process.OutputDataReceived += OutputDataReceived;
|
||||
process.ErrorDataReceived += ErrorDataReceived;
|
||||
process.Start();
|
||||
|
||||
await using StreamWriter streamWriter = process.StandardInput;
|
||||
//set everything to utf-8
|
||||
// ReSharper disable once StringLiteralTypo
|
||||
await streamWriter.WriteLineAsync(@"C:\Windows\System32\chcp.com 65001");
|
||||
// ReSharper disable once StringLiteralTypo
|
||||
await streamWriter.WriteLineAsync("set PYTHONIOENCODING=utf-8");
|
||||
// ReSharper disable once StringLiteralTypo
|
||||
await streamWriter.WriteLineAsync("set PYTHONUTF8=1");
|
||||
await streamWriter.WriteLineAsync($"command_line.exe {args}");
|
||||
await streamWriter.WriteLineAsync("exit");
|
||||
|
||||
process.BeginOutputReadLine();
|
||||
process.BeginErrorReadLine();
|
||||
|
||||
await process.WaitForExitAsync(token);
|
||||
logger.LogInformation("Process exited with code {code}", process.ExitCode);
|
||||
|
||||
if (process.ExitCode != 0)
|
||||
{
|
||||
return (false, $"ServerError{process.ExitCode}");
|
||||
}
|
||||
process.Close();
|
||||
|
||||
dataBlock.ResultPath = Path.Join(outputPath, "result");
|
||||
File.Create(Path.Join(outputPath, "complete"));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.LogError(e, "Error in ProcessData:");
|
||||
return (false, "ServerErrorUnknown");
|
||||
}
|
||||
finally
|
||||
{
|
||||
Environment.CurrentDirectory = prevWorkingDirectory;
|
||||
}
|
||||
|
||||
return (true, "");
|
||||
}
|
||||
|
||||
private void LogDirInfo(DirectoryInfo dirInfo, int count)
|
||||
{
|
||||
foreach (FileInfo fileInfo in dirInfo.GetFiles())
|
||||
{
|
||||
logger.LogInformation(fileInfo.FullName);
|
||||
count--;
|
||||
if (count <= 0)
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (DirectoryInfo directoryInfo in dirInfo.GetDirectories())
|
||||
{
|
||||
LogDirInfo(directoryInfo, count);
|
||||
if (count <= 0)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void OutputDataReceived(object sendingProcess, DataReceivedEventArgs outLine)
|
||||
{
|
||||
logger.LogInformation(outLine.Data);
|
||||
}
|
||||
|
||||
private void ErrorDataReceived(object sendingProcess, DataReceivedEventArgs outLine)
|
||||
{
|
||||
logger.LogError(outLine.Data);
|
||||
}
|
||||
|
||||
public Task<(bool success, string messageIndex)> DownloadData(DataBlock dataBlock)
|
||||
{
|
||||
return Task.FromResult((false, "ServerErrorUnknown"));
|
||||
}
|
||||
|
||||
public Task<(bool success, string messageIndex)> DownloadReport(string key, int id, string savePath, CancellationToken token)
|
||||
{
|
||||
return Task.FromResult((false, "ServerErrorUnknown"));
|
||||
}
|
||||
|
||||
public Task<(bool success, string messageIndex)> EditPassword(string oldPassword, string newPassword)
|
||||
{
|
||||
return Task.FromResult((false, "ServerErrorUnknown"));
|
||||
}
|
||||
|
||||
public Task<(List<ServerJobData> list, int count)> CheckServerJobList(int limit, int offset)
|
||||
{
|
||||
return Task.FromResult((new List<ServerJobData>(), 0));
|
||||
}
|
||||
|
||||
public Task<(List<ServerJobData> list, int count)> SearchData(SearchQueryJson json)
|
||||
{
|
||||
return Task.FromResult((new List<ServerJobData>(), 0));
|
||||
}
|
||||
|
||||
public Task<List<ReportItem>> GetReportList(string key)
|
||||
{
|
||||
return Task.FromResult(new List<ReportItem>());
|
||||
}
|
||||
|
||||
public Task<string> Login(string username, string password)
|
||||
{
|
||||
return Task.FromResult("ServerErrorUnknown");
|
||||
}
|
||||
|
||||
public Task Cancel(string key)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task DeleteReport(string key, int id)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task<(bool success, string messageIndex)> DownloadArchive(string? key, string savePath, CancellationToken token)
|
||||
{
|
||||
return Task.FromResult((false, "ServerErrorUnknown"));
|
||||
}
|
||||
|
||||
public Task<(bool success, string messageIndex)> DownloadSorted(string? key, string savePath, CancellationToken none)
|
||||
{
|
||||
return Task.FromResult((false, "ServerErrorUnknown"));
|
||||
}
|
||||
|
||||
public Task<(bool success, string messageIndex)> DeleteData(int? dataKey, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.FromResult((false, "ServerErrorUnknown"));
|
||||
}
|
||||
|
||||
public Task<(bool success, string messageIndex)> DeleteHistory(string? key, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.FromResult((false, "ServerErrorUnknown"));
|
||||
}
|
||||
|
||||
public Task UpdateDongleInfo()
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task<ServerNotice?> GetNotice()
|
||||
{
|
||||
return Task.FromResult<ServerNotice?>(null);
|
||||
}
|
||||
|
||||
public Task<List<SeriesGroup>> GetSeriesGroupList(string key)
|
||||
{
|
||||
return Task.FromResult(new List<SeriesGroup>());
|
||||
}
|
||||
|
||||
public Task<(bool success, string messageIndex)> SelectSeries(string key, List<string> series)
|
||||
{
|
||||
return Task.FromResult((false, "ServerErrorUnknown"));
|
||||
}
|
||||
|
||||
public Task<(bool success, string messageIndex)> SelectReportModule(string fileName, SelectReportModuleJson json, CancellationToken token)
|
||||
{
|
||||
return Task.FromResult((false, "ServerErrorUnknown"));
|
||||
}
|
||||
|
||||
public Task<(bool success, string messageIndex)> PushToPacs(string key, int id)
|
||||
{
|
||||
return Task.FromResult((false, "ServerErrorUnknown"));
|
||||
}
|
||||
|
||||
public Task<DicomNodeInfo?> GetDicomNodeInfo()
|
||||
{
|
||||
return Task.FromResult<DicomNodeInfo?>(null);
|
||||
}
|
||||
|
||||
public static (string msgIndex, string arg, Dictionary<string, string>? argsDictionary) ParseArgs(IEnumerable<string> requiredArgList)
|
||||
{
|
||||
Dictionary<string, string>? argsDictionary;
|
||||
try
|
||||
{
|
||||
argsDictionary = LiviaUtility.ParseArgs(Environment.GetCommandLineArgs());
|
||||
foreach (string arg in requiredArgList)
|
||||
{
|
||||
if (argsDictionary.ContainsKey(arg))
|
||||
continue;
|
||||
|
||||
//set argsDictionary to null is important here because otherwise commandline will run.
|
||||
return ("ArgumentNotFoundError", arg, null);
|
||||
}
|
||||
}
|
||||
catch (ArgumentException e)
|
||||
{
|
||||
return ("InvalidArgumentError", e.Message, null);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return ("ParsingArgumentError", e.Message, null);
|
||||
}
|
||||
|
||||
return ("", "", argsDictionary);
|
||||
}
|
||||
}
|
16
Livia/Models/Data/MdpCServerHandler.cs
Normal file
|
@ -0,0 +1,16 @@
|
|||
using System.IO;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Livia.Models.Data;
|
||||
|
||||
public class MdpCServerHandler(ILogger logger, LiviaHttpClientFactory liviaHttpClientFactory)
|
||||
: ServerHandler(logger, liviaHttpClientFactory)
|
||||
{
|
||||
private static readonly string[] CheckList = ["AnImage_ArterialTerritories.nii.gz", "AnImage_BrainLobes_AAL3.nii.gz", "AnImage_CerebrumCerebellum.nii.gz", "LimbicSystem.nii.gz"
|
||||
];
|
||||
|
||||
public override Task<(bool success, string messageIndex)> ProcessData(DataBlock dataBlock, CancellationToken token)
|
||||
{
|
||||
return CheckList.Any(file => !File.Exists(Path.Combine(dataBlock.SourceDir, file))) ? Task.FromResult((false, "RoiAtlasNotFoundError")) : base.ProcessData(dataBlock, token);
|
||||
}
|
||||
}
|
22
Livia/Models/Data/MosaicImageData.cs
Normal file
|
@ -0,0 +1,22 @@
|
|||
using System.Windows.Media.Imaging;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
|
||||
namespace Livia.Models.Data;
|
||||
|
||||
public class MosaicImageData(BitmapImage image, string displayName, MosaicImageInfo? colorBarInfo)
|
||||
: ObservableObject
|
||||
{
|
||||
public BitmapImage Image { get; } = image;
|
||||
|
||||
public bool IsChecked { get => _isChecked; set => SetProperty(ref _isChecked, value); }
|
||||
public bool ShowJumpToDataGridButton { get; set; }
|
||||
public bool ShowMosaicRoiOverlayTip { get; set; }
|
||||
public string DisplayName { get => _displayName; set => SetProperty(ref _displayName, value); }
|
||||
public bool UseColorBar => MosaicImageInfo.Range != null;
|
||||
public MosaicImageInfo MosaicImageInfo { get; } = colorBarInfo ?? new MosaicImageInfo { Range = new List<float> { 0, 0 }, Unit = "" };
|
||||
|
||||
private bool _isChecked;
|
||||
private string _displayName = displayName;
|
||||
|
||||
//TODO::click effect for radio buttons(use material button)
|
||||
}
|
18
Livia/Models/Data/PerfusionDataJson.cs
Normal file
|
@ -0,0 +1,18 @@
|
|||
using JetBrains.Annotations;
|
||||
using Livia.Utility;
|
||||
|
||||
namespace Livia.Models.Data;
|
||||
|
||||
public readonly record struct PerfusionDataJson
|
||||
{
|
||||
[UsedImplicitly] public List<PerfusionData> Regions { get; init; }
|
||||
}
|
||||
|
||||
public readonly record struct PerfusionData
|
||||
{
|
||||
[UsedImplicitly] public string Name { get; init; }
|
||||
[UsedImplicitly] public List<double> Values { get; init; }
|
||||
[UsedImplicitly] public List<int> Colors { get; init; }
|
||||
[UsedImplicitly] public string EnName { get; init; }
|
||||
public string DisplayName => LiviaUtility.ParseServerString(Name);
|
||||
}
|
42
Livia/Models/Data/RoiSummaryDataJson.cs
Normal file
|
@ -0,0 +1,42 @@
|
|||
using System.Text.Json.Serialization;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace Livia.Models.Data;
|
||||
|
||||
public readonly record struct RoiSummaryDataJson
|
||||
{
|
||||
[UsedImplicitly] public bool? Visible { get; init; }
|
||||
[UsedImplicitly] public List<RoiGroup> Groups { get; init; }
|
||||
}
|
||||
|
||||
public readonly record struct RoiGroup
|
||||
{
|
||||
[UsedImplicitly] public string Name { get; init; }
|
||||
[UsedImplicitly] public string FullName { get; init; }
|
||||
[JsonPropertyName("ID"), UsedImplicitly] public string Id { get; init; }
|
||||
[UsedImplicitly] public string Color { get; init; }
|
||||
[UsedImplicitly] public List<RoiData> Regions { get; init; }
|
||||
[UsedImplicitly] public int? Score { get; init; }
|
||||
[JsonPropertyName("Z-Index"), UsedImplicitly] public int? ZIndex { get; init; }
|
||||
}
|
||||
|
||||
public readonly record struct RoiData
|
||||
{
|
||||
[UsedImplicitly] public string Name { get; init; }
|
||||
[UsedImplicitly] public string FileName { get; init; }
|
||||
[UsedImplicitly] public float Volume { get; init; }
|
||||
[UsedImplicitly] public string? Mask { get; init; }
|
||||
[UsedImplicitly] public string? MosaicMask { get; init; }
|
||||
[UsedImplicitly] public List<LocationData> Locations { get; init; }
|
||||
[UsedImplicitly] public List<RoiGroup>? SubGroups { get; init; }
|
||||
}
|
||||
|
||||
public readonly record struct LocationData
|
||||
{
|
||||
[UsedImplicitly] public string Name { get; init; }
|
||||
[UsedImplicitly] public float Percentage { get; init; }
|
||||
[UsedImplicitly] public float Volume { get; init; }
|
||||
[UsedImplicitly] public float? RegionPercentage { get; init; }
|
||||
[UsedImplicitly] public int? Weight { get; init; }
|
||||
[JsonPropertyName("SecondLocations"), UsedImplicitly] public List<LocationData>? SubLocations { get; init; }
|
||||
}
|
37
Livia/Models/Data/SearchQueryJson.cs
Normal file
|
@ -0,0 +1,37 @@
|
|||
using System.Text.Json.Serialization;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace Livia.Models.Data;
|
||||
|
||||
public readonly record struct SearchQueryCondition
|
||||
{
|
||||
[JsonPropertyName("last_update_time"), UsedImplicitly]
|
||||
public List<string>? ImportTime { get; init; }
|
||||
|
||||
[JsonPropertyName("study_date"), UsedImplicitly]
|
||||
public List<string>? StudyDate { get; init; }
|
||||
|
||||
[JsonPropertyName("patient_id"), UsedImplicitly]
|
||||
public string? PatientId { get; init; }
|
||||
|
||||
[JsonPropertyName("patient_name"), UsedImplicitly]
|
||||
public string? PatientName { get; init; }
|
||||
|
||||
[JsonPropertyName("patient_age"), UsedImplicitly]
|
||||
public List<int>? PatientAge { get; init; }
|
||||
|
||||
[JsonPropertyName("patient_sex"), UsedImplicitly]
|
||||
public string? PatientSex { get; init; }
|
||||
}
|
||||
|
||||
public readonly record struct SearchQueryJson
|
||||
{
|
||||
[JsonPropertyName("conditions"), UsedImplicitly]
|
||||
public SearchQueryCondition Conditions { get; init; }
|
||||
|
||||
[JsonPropertyName("limit"), UsedImplicitly]
|
||||
public int Limit { get; init; }
|
||||
|
||||
[JsonPropertyName("offset"), UsedImplicitly]
|
||||
public int Offset { get; init; }
|
||||
}
|
69
Livia/Models/Data/SelectReportModuleJson.cs
Normal file
|
@ -0,0 +1,69 @@
|
|||
using System.Text.Json.Serialization;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace Livia.Models.Data;
|
||||
|
||||
public readonly record struct ReportModulesRoiData
|
||||
{
|
||||
[JsonPropertyName("name"), UsedImplicitly]
|
||||
public string Name { get; init; }
|
||||
|
||||
[JsonPropertyName("unfold"), UsedImplicitly]
|
||||
public List<string> Unfold { get; init; }
|
||||
}
|
||||
|
||||
public readonly record struct ReportModules
|
||||
{
|
||||
[JsonPropertyName("brain_roi"), UsedImplicitly]
|
||||
public bool BrainRoi { get; init; }
|
||||
|
||||
[JsonPropertyName("AAL"), UsedImplicitly]
|
||||
public bool ReportModuleAal { get; init; }
|
||||
|
||||
[JsonPropertyName("show_roi_location"), UsedImplicitly]
|
||||
public bool ShowRoiLocation { get; init; }
|
||||
|
||||
[JsonPropertyName("regional_perfusion"), UsedImplicitly]
|
||||
public List<object> RegionalPerfusion { get; init; }
|
||||
|
||||
[JsonPropertyName("param_color_img"), UsedImplicitly]
|
||||
public List<object> ParamColorImg { get; init; }
|
||||
|
||||
[JsonPropertyName("cbf"), UsedImplicitly]
|
||||
public List<object> Cbf { get; init; }
|
||||
|
||||
[JsonPropertyName("att"), UsedImplicitly]
|
||||
public List<object> Att { get; init; }
|
||||
|
||||
[JsonPropertyName("acbv"), UsedImplicitly]
|
||||
public List<object> Acbv { get; init; }
|
||||
|
||||
[JsonPropertyName("anterior_aspects"), UsedImplicitly]
|
||||
public List<object> AnteriorAspects { get; init; }
|
||||
|
||||
[JsonPropertyName("posterior_aspects"), UsedImplicitly]
|
||||
public List<object> PosteriorAspects { get; init; }
|
||||
|
||||
[JsonPropertyName("restricted_diffusion"), UsedImplicitly]
|
||||
public List<object> RestrictedDiffusion { get; init; }
|
||||
}
|
||||
|
||||
public readonly record struct SelectReportModuleJson
|
||||
{
|
||||
[JsonPropertyName("key"), UsedImplicitly]
|
||||
public string Key { get; init; }
|
||||
[JsonPropertyName("language"), UsedImplicitly]
|
||||
public string Language { get; init; }
|
||||
|
||||
[JsonPropertyName("anonymous"), UsedImplicitly]
|
||||
public bool Anonymous { get; init; }
|
||||
|
||||
[JsonPropertyName("override"), UsedImplicitly]
|
||||
public bool Override { get; init; }
|
||||
|
||||
[JsonPropertyName("title"), UsedImplicitly]
|
||||
public string Title { get; init; }
|
||||
|
||||
[JsonPropertyName("display_modules"), UsedImplicitly]
|
||||
public ReportModules DisplayModules { get; init; }
|
||||
}
|
32
Livia/Models/Data/SeriesGroupListJson.cs
Normal file
|
@ -0,0 +1,32 @@
|
|||
using System.Text.Json.Serialization;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace Livia.Models.Data;
|
||||
|
||||
public readonly record struct SeriesGroupListJson
|
||||
{
|
||||
[JsonPropertyName("code"), UsedImplicitly]
|
||||
public int Code { get; init; }
|
||||
|
||||
[JsonPropertyName("series_group"), UsedImplicitly]
|
||||
public List<SeriesGroup> SeriesGroupList { get; init; }
|
||||
}
|
||||
|
||||
|
||||
public readonly record struct SeriesGroup
|
||||
{
|
||||
[JsonPropertyName("name"), UsedImplicitly]
|
||||
public string Name { get; init; }
|
||||
|
||||
[JsonPropertyName("series"), UsedImplicitly]
|
||||
public List<Series> Series { get; init; }
|
||||
}
|
||||
|
||||
public readonly record struct Series
|
||||
{
|
||||
[JsonPropertyName("series_description"), UsedImplicitly]
|
||||
public string SeriesDescription { get; init; }
|
||||
|
||||
[JsonPropertyName("uid"), UsedImplicitly]
|
||||
public string Uid { get; init; }
|
||||
}
|
855
Livia/Models/Data/ServerHandler.cs
Normal file
|
@ -0,0 +1,855 @@
|
|||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Net.Sockets;
|
||||
using System.Windows;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using Livia.Utility;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Livia.Models.Data;
|
||||
|
||||
public interface IServerHandler : INotifyPropertyChanged, INotifyPropertyChanging
|
||||
{
|
||||
bool Processing { get; set; }
|
||||
bool IsLoggedIn { get; }
|
||||
string Quota { get; }
|
||||
Task<(bool success, string messageIndex)> ProcessData(DataBlock dataBlock, CancellationToken token);
|
||||
Task<(bool success, string messageIndex)> DownloadData(DataBlock dataBlock);
|
||||
Task<(bool success, string messageIndex)> DownloadReport(string key, int id, string savePath, CancellationToken token);
|
||||
Task<(bool success, string messageIndex)> EditPassword(string oldPassword, string newPassword);
|
||||
Task<(List<ServerJobData> list, int count)> CheckServerJobList(int limit, int offset);
|
||||
Task<(List<ServerJobData> list, int count)> SearchData(SearchQueryJson json);
|
||||
Task<List<ReportItem>> GetReportList(string key);
|
||||
Task<string> Login(string username, string password);
|
||||
Task Cancel(string key);
|
||||
Task DeleteReport(string key, int id);
|
||||
Task<(bool success, string messageIndex)> DownloadArchive(string? key, string savePath, CancellationToken token);
|
||||
Task<(bool success, string messageIndex)> DownloadSorted(string? key, string savePath, CancellationToken none);
|
||||
Task<(bool success, string messageIndex)> DeleteData(int? id, CancellationToken cancellationToken);
|
||||
Task<(bool success, string messageIndex)> DeleteHistory(string? key, CancellationToken cancellationToken);
|
||||
Task UpdateDongleInfo();
|
||||
Task<ServerNotice?> GetNotice();
|
||||
Task<List<SeriesGroup>> GetSeriesGroupList(string key);
|
||||
Task<(bool success, string messageIndex)> SelectSeries(string key, List<string> series);
|
||||
Task<(bool success, string messageIndex)> SelectReportModule(string fileName, SelectReportModuleJson json, CancellationToken token);
|
||||
Task<(bool success, string messageIndex)> PushToPacs(string key, int id);
|
||||
Task<DicomNodeInfo?> GetDicomNodeInfo();
|
||||
}
|
||||
|
||||
public class ServerHandler : ObservableRecipient, IServerHandler, IRecipient<LogoutMessage>, IRecipient<IdleTimerTickMessage>
|
||||
{
|
||||
public bool Processing { get => _processing; set => SetProperty(ref _processing, value); }
|
||||
public bool IsLoggedIn { get => _isLoggedIn; private set => SetProperty(ref _isLoggedIn, value); }
|
||||
public string Quota { get => _quota; private set => SetProperty(ref _quota, value); }
|
||||
|
||||
private bool _processing;
|
||||
private bool _isLoggedIn;
|
||||
private string _quota = string.Empty;
|
||||
private readonly ILogger _logger;
|
||||
private bool _updatingPatientList;
|
||||
private readonly LiviaHttpClientFactory _liviaHttpClientFactory;
|
||||
|
||||
private const int CheckResultRetryCount = 10;
|
||||
private const int CheckResultRetryTime = 5000;
|
||||
|
||||
public ServerHandler(ILogger logger, LiviaHttpClientFactory liviaHttpClientFactory)
|
||||
{
|
||||
_logger = logger;
|
||||
_liviaHttpClientFactory = liviaHttpClientFactory;
|
||||
WeakReferenceMessenger.Default.RegisterAll(this);
|
||||
}
|
||||
|
||||
public async Task Cancel(string key)
|
||||
{
|
||||
if (string.IsNullOrEmpty(key))
|
||||
{
|
||||
_logger.LogWarning("Key is invalid, cannot cancel job");
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
ServerResponseJson serverResponseJson = await _liviaHttpClientFactory.Create().Cancel(key).ConfigureAwait(false);
|
||||
_logger.LogInformation($"Cancel request response is {serverResponseJson}");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError(e, "Error canceling data");
|
||||
}
|
||||
}
|
||||
|
||||
public async Task DeleteReport(string key, int id)
|
||||
{
|
||||
try
|
||||
{
|
||||
ServerResponseJson serverResponseJson = await _liviaHttpClientFactory.Create().DeleteReport(key, id).ConfigureAwait(false);
|
||||
_logger.LogInformation($"DeleteReport response is {serverResponseJson}");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError(e, "Error DeleteReport {id}", id);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<(bool success, string messageIndex)> DownloadData(DataBlock dataBlock)
|
||||
{
|
||||
Processing = true;
|
||||
string messageIndex = string.Empty;
|
||||
|
||||
try
|
||||
{
|
||||
if (!string.IsNullOrEmpty(dataBlock.ResultPath))
|
||||
{
|
||||
// it is finished
|
||||
return (true, messageIndex);
|
||||
}
|
||||
|
||||
string saveName = $"{dataBlock.Key}.zip";
|
||||
string savePath = Path.Combine(ServiceConfigurations.TempFolder, saveName);
|
||||
await _liviaHttpClientFactory.Create().CheckResult(dataBlock.Key, savePath, CancellationToken.None).ConfigureAwait(false);
|
||||
|
||||
string path = Path.Combine(ServiceConfigurations.TempFolder, dataBlock.Key);
|
||||
|
||||
//remove if somehow it is there
|
||||
if (Directory.Exists(path))
|
||||
{
|
||||
Directory.Delete(path, true);
|
||||
}
|
||||
ZipFile.ExtractToDirectory(savePath, path);
|
||||
dataBlock.ResultPath = path;
|
||||
|
||||
return (true, messageIndex);
|
||||
}
|
||||
catch (HttpRequestException e)
|
||||
{
|
||||
if (e.StatusCode == HttpStatusCode.Unauthorized)
|
||||
{
|
||||
messageIndex = "ServerUnauthorizedError";
|
||||
WeakReferenceMessenger.Default.Send(new LogoutMessage());
|
||||
}
|
||||
else
|
||||
{
|
||||
messageIndex = "ProcessingError";
|
||||
}
|
||||
_logger.LogError(e, "Error processing data");
|
||||
return (false, messageIndex);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError(e, "Error processing data");
|
||||
messageIndex = "ProcessingError";
|
||||
return (false, messageIndex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Processing = false;
|
||||
}
|
||||
}
|
||||
|
||||
//TODO::create a return object instead maybe so that fileName can also be returned
|
||||
public virtual async Task<(bool success, string messageIndex)> ProcessData(DataBlock dataBlock, CancellationToken token)
|
||||
{
|
||||
Processing = true;
|
||||
string messageIndex = string.Empty;
|
||||
|
||||
try
|
||||
{
|
||||
await dataBlock.Pack(token).ConfigureAwait(false);
|
||||
ProcessDataResponseJson serverResponseJson = await _liviaHttpClientFactory.Create().ProcessData(dataBlock.ZipPath, token).ConfigureAwait(false);
|
||||
|
||||
if (serverResponseJson.Code != 0)
|
||||
{
|
||||
return (false, GetErrorMessage(serverResponseJson.ErrorNumber));
|
||||
}
|
||||
|
||||
//check for response
|
||||
dataBlock.Key = serverResponseJson.Key;
|
||||
int retry = 0;
|
||||
while (retry < CheckResultRetryCount)
|
||||
{
|
||||
try
|
||||
{
|
||||
string saveName = $"{dataBlock.Key}.zip";
|
||||
string savePath = Path.Combine(ServiceConfigurations.TempFolder, saveName);
|
||||
await _liviaHttpClientFactory.Create().CheckResult(dataBlock.Key, savePath, token).ConfigureAwait(false);
|
||||
|
||||
await UpdateDongleInfo();
|
||||
dataBlock.ResultPath = Path.Combine(ServiceConfigurations.TempFolder, dataBlock.Key);
|
||||
ZipFile.ExtractToDirectory(savePath, dataBlock.ResultPath);
|
||||
|
||||
return (true, messageIndex);
|
||||
}
|
||||
catch (ResultNotReadyException e)
|
||||
{
|
||||
if (e.ServerResponseJson.Code != 0)
|
||||
{
|
||||
return (false, GetErrorMessage(e.ServerResponseJson.ErrorNumber));
|
||||
}
|
||||
|
||||
await Task.Delay(CheckResultRetryTime, token);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
retry++;
|
||||
_logger.LogWarning(e, "Error checking result, try again in 5 seconds");
|
||||
await Task.Delay(CheckResultRetryTime, token);
|
||||
}
|
||||
}
|
||||
|
||||
throw new TimeoutException();
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
await Cancel(dataBlock.Key);
|
||||
return (false, "ProcessingError");
|
||||
}
|
||||
catch (HttpRequestException e)
|
||||
{
|
||||
if (e.StatusCode == HttpStatusCode.Unauthorized)
|
||||
{
|
||||
messageIndex = "ServerUnauthorizedError";
|
||||
WeakReferenceMessenger.Default.Send(new LogoutMessage());
|
||||
}
|
||||
else
|
||||
{
|
||||
messageIndex = "ProcessingError";
|
||||
}
|
||||
_logger.LogError(e, "Error processing data");
|
||||
return (false, messageIndex);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError(e, "Error processing data");
|
||||
messageIndex = "ProcessingError";
|
||||
return (false, messageIndex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Processing = false;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<(bool success, string messageIndex)> DownloadReport(string key, int id, string savePath, CancellationToken token)
|
||||
{
|
||||
Processing = true;
|
||||
string messageIndex;
|
||||
try
|
||||
{
|
||||
await _liviaHttpClientFactory.Create().DownloadReport(key, id, savePath, token).ConfigureAwait(false);
|
||||
return (true, "DownloadReportSuccess");
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
return (false, "ExportFailed");
|
||||
}
|
||||
catch (HttpRequestException e)
|
||||
{
|
||||
if (e.StatusCode == HttpStatusCode.Unauthorized)
|
||||
{
|
||||
messageIndex = "ServerUnauthorizedError";
|
||||
WeakReferenceMessenger.Default.Send(new LogoutMessage());
|
||||
}
|
||||
else
|
||||
{
|
||||
messageIndex = "ExportFailed";
|
||||
}
|
||||
_logger.LogError(e, "DownloadReport");
|
||||
return (false, messageIndex);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError(e, "DownloadReport");
|
||||
messageIndex = "ExportFailed";
|
||||
return (false, messageIndex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Processing = false;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<(bool success, string messageIndex)> DownloadArchive(string? key, string savePath, CancellationToken token)
|
||||
{
|
||||
if (string.IsNullOrEmpty(key))
|
||||
return (false, "ProcessingError");
|
||||
|
||||
Processing = true;
|
||||
string messageIndex = string.Empty;
|
||||
try
|
||||
{
|
||||
await _liviaHttpClientFactory.Create().DownloadArchive(key, savePath, token).ConfigureAwait(false);
|
||||
return (true, messageIndex);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
await Cancel(key);
|
||||
try
|
||||
{
|
||||
File.Delete(savePath);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError(e, "Cannot delete {path}", savePath);
|
||||
}
|
||||
return (false, "ProcessingError");
|
||||
}
|
||||
catch (HttpRequestException e)
|
||||
{
|
||||
if (e.StatusCode == HttpStatusCode.Unauthorized)
|
||||
{
|
||||
messageIndex = "ServerUnauthorizedError";
|
||||
WeakReferenceMessenger.Default.Send(new LogoutMessage());
|
||||
}
|
||||
else
|
||||
{
|
||||
messageIndex = "ProcessingError";
|
||||
}
|
||||
_logger.LogError(e, "Error DownloadArchive");
|
||||
return (false, messageIndex);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError(e, "Error DownloadArchive");
|
||||
messageIndex = "ProcessingError";
|
||||
return (false, messageIndex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Processing = false;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<(bool success, string messageIndex)> DownloadSorted(string? key, string savePath, CancellationToken token)
|
||||
{
|
||||
if (string.IsNullOrEmpty(key))
|
||||
return (false, "ProcessingError");
|
||||
|
||||
Processing = true;
|
||||
string messageIndex = string.Empty;
|
||||
try
|
||||
{
|
||||
await _liviaHttpClientFactory.Create().DownloadSorted(key, savePath, token).ConfigureAwait(false);
|
||||
return (true, messageIndex);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
await Cancel(key);
|
||||
try
|
||||
{
|
||||
File.Delete(savePath);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError(e, "Cannot delete {path}", savePath);
|
||||
}
|
||||
return (false, "ProcessingError");
|
||||
}
|
||||
catch (HttpRequestException e)
|
||||
{
|
||||
if (e.StatusCode == HttpStatusCode.Unauthorized)
|
||||
{
|
||||
messageIndex = "ServerUnauthorizedError";
|
||||
WeakReferenceMessenger.Default.Send(new LogoutMessage());
|
||||
}
|
||||
else
|
||||
{
|
||||
messageIndex = "ProcessingError";
|
||||
}
|
||||
_logger.LogError(e, "Error DownloadSorted");
|
||||
return (false, messageIndex);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError(e, "Error DownloadSorted");
|
||||
messageIndex = "ProcessingError";
|
||||
return (false, messageIndex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Processing = false;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<(bool success, string messageIndex)> DeleteData(int? id, CancellationToken token)
|
||||
{
|
||||
if (id == null)
|
||||
return (false, "ProcessingError");
|
||||
|
||||
Processing = true;
|
||||
string messageIndex;
|
||||
try
|
||||
{
|
||||
ServerResponseJson serverResponseJson = await _liviaHttpClientFactory.Create().DeleteData(id.ToString(), token).ConfigureAwait(false);
|
||||
return serverResponseJson.Code != 0 ? (false, GetErrorMessage(serverResponseJson.ErrorNumber)) : (true, "DeleteSuccess");
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
return (false, "ProcessingError");
|
||||
}
|
||||
catch (HttpRequestException e)
|
||||
{
|
||||
if (e.StatusCode == HttpStatusCode.Unauthorized)
|
||||
{
|
||||
messageIndex = "ServerUnauthorizedError";
|
||||
WeakReferenceMessenger.Default.Send(new LogoutMessage());
|
||||
}
|
||||
else
|
||||
{
|
||||
messageIndex = "ProcessingError";
|
||||
}
|
||||
_logger.LogError(e, "Error DeleteData");
|
||||
return (false, messageIndex);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError(e, "Error DeleteData");
|
||||
messageIndex = "ProcessingError";
|
||||
return (false, messageIndex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Processing = false;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<(bool success, string messageIndex)> DeleteHistory(string? key, CancellationToken token)
|
||||
{
|
||||
if (string.IsNullOrEmpty(key))
|
||||
return (false, "ProcessingError");
|
||||
|
||||
Processing = true;
|
||||
string messageIndex;
|
||||
try
|
||||
{
|
||||
ServerResponseJson serverResponseJson = await _liviaHttpClientFactory.Create().DeleteHistory(key, token).ConfigureAwait(false);
|
||||
return serverResponseJson.Code != 0 ? (false, GetErrorMessage(serverResponseJson.ErrorNumber)) : (true, "DeleteSuccess");
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
return (false, "ProcessingError");
|
||||
}
|
||||
catch (HttpRequestException e)
|
||||
{
|
||||
if (e.StatusCode == HttpStatusCode.Unauthorized)
|
||||
{
|
||||
messageIndex = "ServerUnauthorizedError";
|
||||
WeakReferenceMessenger.Default.Send(new LogoutMessage());
|
||||
}
|
||||
else
|
||||
{
|
||||
messageIndex = "ProcessingError";
|
||||
}
|
||||
_logger.LogError(e, "Error DeleteData");
|
||||
return (false, messageIndex);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError(e, "Error DeleteData");
|
||||
messageIndex = "ProcessingError";
|
||||
return (false, messageIndex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Processing = false;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<(bool success, string messageIndex)> EditPassword(string oldPassword, string newPassword)
|
||||
{
|
||||
Processing = true;
|
||||
string messageIndex;
|
||||
try
|
||||
{
|
||||
ServerResponseJson serverResponseJson = await _liviaHttpClientFactory.Create().EditPassword(oldPassword, newPassword).ConfigureAwait(false);
|
||||
return serverResponseJson.Code != 0 ? (false, GetErrorMessage(serverResponseJson.ErrorNumber)) : (true, "EditPasswordSuccess");
|
||||
}
|
||||
catch (HttpRequestException e)
|
||||
{
|
||||
if (e.StatusCode == HttpStatusCode.Unauthorized)
|
||||
{
|
||||
messageIndex = "ServerUnauthorizedError";
|
||||
WeakReferenceMessenger.Default.Send(new LogoutMessage());
|
||||
}
|
||||
else
|
||||
{
|
||||
messageIndex = "ProcessingError";
|
||||
}
|
||||
_logger.LogError(e, "Error processing data");
|
||||
return (false, messageIndex);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError(e, "Error processing data");
|
||||
messageIndex = "ProcessingError";
|
||||
return (false, messageIndex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Processing = false;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<(List<ServerJobData> list, int count)> CheckServerJobList(int limit, int offset)
|
||||
{
|
||||
//TODO::should return fail instead, so it is now used
|
||||
List<ServerJobData> resultList = [];
|
||||
int count = 0;
|
||||
if (_updatingPatientList)
|
||||
return (resultList, count);
|
||||
|
||||
if (!IsLoggedIn)
|
||||
return (resultList, count);
|
||||
|
||||
_updatingPatientList = true;
|
||||
|
||||
try
|
||||
{
|
||||
ServerJobListJsonResponse serverResponseJson = await _liviaHttpClientFactory.Create().CheckServerJobList(limit, offset).ConfigureAwait(false);
|
||||
|
||||
if (serverResponseJson.Code != 0)
|
||||
throw new Exception(GetErrorMessage(serverResponseJson.Code));
|
||||
resultList.AddRange(serverResponseJson.DataList);
|
||||
count = serverResponseJson.Count;
|
||||
}
|
||||
//TODO::we may get 401 here
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError(e, "Error updating patient list");
|
||||
}
|
||||
finally
|
||||
{
|
||||
_updatingPatientList = false;
|
||||
}
|
||||
return (resultList, count);
|
||||
}
|
||||
|
||||
public async Task<(List<ServerJobData> list, int count)> SearchData(SearchQueryJson json)
|
||||
{
|
||||
//TODO::should return fail instead, so it is now used
|
||||
List<ServerJobData> resultList = [];
|
||||
int count = 0;
|
||||
if (_updatingPatientList)
|
||||
return (resultList, count);
|
||||
|
||||
if (!IsLoggedIn)
|
||||
return (resultList, count);
|
||||
|
||||
_updatingPatientList = true;
|
||||
|
||||
try
|
||||
{
|
||||
ServerJobListJsonResponse serverResponseJson = await _liviaHttpClientFactory.Create().SearchData(json).ConfigureAwait(false);
|
||||
|
||||
if (serverResponseJson.Code != 0)
|
||||
throw new Exception(GetErrorMessage(serverResponseJson.Code));
|
||||
resultList.AddRange(serverResponseJson.DataList);
|
||||
count = serverResponseJson.Count;
|
||||
}
|
||||
//TODO::we may get 401 here
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError(e, "Error SearchData");
|
||||
}
|
||||
finally
|
||||
{
|
||||
_updatingPatientList = false;
|
||||
}
|
||||
return (resultList, count);
|
||||
}
|
||||
|
||||
public async Task<List<ReportItem>> GetReportList(string key)
|
||||
{
|
||||
try
|
||||
{
|
||||
return await _liviaHttpClientFactory.Create().GetReportList(key).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError(e, "Error GetReportList");
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
private string GetErrorMessage(int code)
|
||||
{
|
||||
_logger.LogError($"Server error, errno = {code}");
|
||||
string index = $"ServerError{code}";
|
||||
return Application.Current.TryFindResource(index) != null ? index : "ServerErrorUnknown";
|
||||
}
|
||||
|
||||
public async Task UpdateDongleInfo()
|
||||
{
|
||||
if (!IsLoggedIn)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
DongleInfoResponseJson serverResponseJson = await _liviaHttpClientFactory.Create().UpdateDongleInfo().ConfigureAwait(false);
|
||||
|
||||
if (serverResponseJson.Code != 0)
|
||||
throw new Exception(serverResponseJson.Msg);
|
||||
|
||||
if (serverResponseJson.Quota == uint.MaxValue)
|
||||
{
|
||||
if (Application.Current.TryFindResource("UnlimitedQuota") is string str)
|
||||
{
|
||||
Quota = str;
|
||||
}
|
||||
else
|
||||
{
|
||||
Quota = "";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Quota = serverResponseJson.Quota.ToString();
|
||||
}
|
||||
}
|
||||
//TODO::we may get 401 here
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError(e, "Error GetDongleInfo");
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<ServerNotice?> GetNotice()
|
||||
{
|
||||
try
|
||||
{
|
||||
ServerNoticeJsonResponse serverResponseJson = await _liviaHttpClientFactory.Create().GetNotice().ConfigureAwait(false);
|
||||
|
||||
//no notice
|
||||
if (serverResponseJson.Code == -1)
|
||||
return null;
|
||||
|
||||
if (serverResponseJson.Code != 0)
|
||||
throw new Exception(GetErrorMessage(serverResponseJson.Code));
|
||||
return serverResponseJson.Notice;
|
||||
}
|
||||
//TODO::we may get 401 here
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError(e, "Error updating ServerNotice");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public async Task<List<SeriesGroup>> GetSeriesGroupList(string key)
|
||||
{
|
||||
try
|
||||
{
|
||||
SeriesGroupListJson serverResponseJson = await _liviaHttpClientFactory.Create().GetSeriesGroupList(key).ConfigureAwait(false);
|
||||
|
||||
//no notice
|
||||
if (serverResponseJson.Code == -1)
|
||||
return [];
|
||||
|
||||
if (serverResponseJson.Code != 0)
|
||||
throw new Exception(GetErrorMessage(serverResponseJson.Code));
|
||||
return serverResponseJson.SeriesGroupList;
|
||||
}
|
||||
//TODO::we may get 401 here
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError(e, "Error GetSeriesGroupList");
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
public async Task<(bool success, string messageIndex)> SelectSeries(string key, List<string> series)
|
||||
{
|
||||
string messageIndex;
|
||||
try
|
||||
{
|
||||
ServerResponseJson serverResponseJson = await _liviaHttpClientFactory.Create().SelectSeries(key, series).ConfigureAwait(false);
|
||||
if (serverResponseJson.Code != 0)
|
||||
return (false, GetErrorMessage(serverResponseJson.ErrorNumber));
|
||||
|
||||
serverResponseJson = await _liviaHttpClientFactory.Create().Start(key).ConfigureAwait(false);
|
||||
return serverResponseJson.Code != 0 ? (false, GetErrorMessage(serverResponseJson.ErrorNumber)) : (true, "SelectSeriesSuccess");
|
||||
}
|
||||
catch (HttpRequestException e)
|
||||
{
|
||||
if (e.StatusCode == HttpStatusCode.Unauthorized)
|
||||
{
|
||||
messageIndex = "ServerUnauthorizedError";
|
||||
WeakReferenceMessenger.Default.Send(new LogoutMessage());
|
||||
}
|
||||
else
|
||||
{
|
||||
messageIndex = "ProcessingError";
|
||||
}
|
||||
_logger.LogError(e, "Error SelectSeries");
|
||||
return (false, messageIndex);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError(e, "Error SelectSeries");
|
||||
messageIndex = "ProcessingError";
|
||||
return (false, messageIndex);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual async Task<(bool success, string messageIndex)> SelectReportModule(string fileName, SelectReportModuleJson json, CancellationToken token)
|
||||
{
|
||||
Processing = true;
|
||||
string messageIndex;
|
||||
try
|
||||
{
|
||||
ReportIdJson reportIdJson = await _liviaHttpClientFactory.Create().SelectReportModule(json).ConfigureAwait(false);
|
||||
await _liviaHttpClientFactory.Create().DownloadReport(json.Key, reportIdJson.ReportId, fileName, token).ConfigureAwait(false);
|
||||
return (true, "DownloadReportSuccess");
|
||||
}
|
||||
catch (SelectReportModuleFailedException e)
|
||||
{
|
||||
return (false, GetErrorMessage(e.ErrorNumber));
|
||||
}
|
||||
catch (HttpRequestException e)
|
||||
{
|
||||
if (e.StatusCode == HttpStatusCode.Unauthorized)
|
||||
{
|
||||
messageIndex = "ServerUnauthorizedError";
|
||||
WeakReferenceMessenger.Default.Send(new LogoutMessage());
|
||||
}
|
||||
else
|
||||
{
|
||||
messageIndex = "ExportFailed";
|
||||
}
|
||||
_logger.LogError(e, "Error ReportModule");
|
||||
return (false, messageIndex);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError(e, "Error ReportModule");
|
||||
messageIndex = "ExportFailed";
|
||||
return (false, messageIndex);
|
||||
}
|
||||
finally { Processing = false; }
|
||||
}
|
||||
|
||||
public async Task<(bool success, string messageIndex)> PushToPacs(string key, int id)
|
||||
{
|
||||
string messageIndex;
|
||||
try
|
||||
{
|
||||
ServerResponseJson serverResponseJson = await _liviaHttpClientFactory.Create().PushToPacs(key, id).ConfigureAwait(false);
|
||||
return serverResponseJson.Code != 0 ? (false, GetErrorMessage(serverResponseJson.ErrorNumber)) : (true, "PushToPacsSuccess");
|
||||
}
|
||||
catch (HttpRequestException e)
|
||||
{
|
||||
if (e.StatusCode == HttpStatusCode.Unauthorized)
|
||||
{
|
||||
messageIndex = "ServerUnauthorizedError";
|
||||
WeakReferenceMessenger.Default.Send(new LogoutMessage());
|
||||
}
|
||||
else
|
||||
{
|
||||
messageIndex = "ProcessingError";
|
||||
}
|
||||
_logger.LogError(e, "Error PushToPacs");
|
||||
return (false, messageIndex);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError(e, "Error PushToPacs");
|
||||
messageIndex = "ProcessingError";
|
||||
return (false, messageIndex);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<DicomNodeInfo?> GetDicomNodeInfo()
|
||||
{
|
||||
try
|
||||
{
|
||||
DicomNodeInfoJsonResponse serverResponseJson = await _liviaHttpClientFactory.Create().GetDicomNodeInfo().ConfigureAwait(false);
|
||||
|
||||
//no notice
|
||||
if (serverResponseJson.Code == -1)
|
||||
return null;
|
||||
|
||||
if (serverResponseJson.Code != 0)
|
||||
throw new Exception(GetErrorMessage(serverResponseJson.Code));
|
||||
return serverResponseJson.Dicom;
|
||||
}
|
||||
//TODO::we may get 401 here
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError(e, "Error updating ServerNotice");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public async Task<string> Login(string username, string password)
|
||||
{
|
||||
//TODO::maybe only return index instead
|
||||
if (Processing)
|
||||
{
|
||||
return (string)Application.Current.TryFindResource("LoginError");
|
||||
}
|
||||
Processing = true;
|
||||
string message = string.Empty;
|
||||
try
|
||||
{
|
||||
_logger.LogInformation("Sending post request");
|
||||
|
||||
await _liviaHttpClientFactory.Create().Login(username, password).ConfigureAwait(false);
|
||||
|
||||
IsLoggedIn = true;
|
||||
await UpdateDongleInfo();
|
||||
}
|
||||
catch (LoginErrorException e)
|
||||
{
|
||||
message = (string)(Application.Current.TryFindResource($"ServerError{e.ServerResponseJson.ErrorNumber}") ?? Application.Current.TryFindResource("LoginError"));
|
||||
//Account is locked
|
||||
if (e.ServerResponseJson.ErrorNumber == 10021)
|
||||
{
|
||||
message = string.Format(message, e.ServerResponseJson.Count, e.ServerResponseJson.Seconds);
|
||||
}
|
||||
}
|
||||
catch (HttpRequestException e)
|
||||
{
|
||||
_logger.LogError(e, "HttpRequestException while logging in");
|
||||
if (e.StatusCode == HttpStatusCode.Unauthorized)
|
||||
{
|
||||
_logger.LogError(e, "Incorrect password");
|
||||
message = (string)Application.Current.TryFindResource("IncorrectPasswordError");
|
||||
}
|
||||
else if (e.InnerException is SocketException)
|
||||
{
|
||||
_logger.LogError(e, "SocketException");
|
||||
message = (string)Application.Current.TryFindResource("SocketExceptionError");
|
||||
}
|
||||
else
|
||||
{
|
||||
message = (string)Application.Current.TryFindResource("LoginError");
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError(e, "Error logging in");
|
||||
message = (string)Application.Current.TryFindResource("LoginError");
|
||||
}
|
||||
finally
|
||||
{
|
||||
Processing = false;
|
||||
}
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
public void Receive(LogoutMessage message)
|
||||
{
|
||||
IsLoggedIn = false;
|
||||
LiviaHttpClient.ClearToken();
|
||||
}
|
||||
|
||||
public void Receive(IdleTimerTickMessage message)
|
||||
{
|
||||
if (Processing)
|
||||
message.IdleTimer.ResetTimer();
|
||||
}
|
||||
}
|
102
Livia/Models/Data/ServerJobData.cs
Normal file
|
@ -0,0 +1,102 @@
|
|||
using System.Globalization;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Windows;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace Livia.Models.Data;
|
||||
|
||||
public record DataHistory
|
||||
{
|
||||
[JsonPropertyName("key"), UsedImplicitly]
|
||||
public string? Key { get; init; }
|
||||
|
||||
[JsonPropertyName("import_time"), UsedImplicitly]
|
||||
public string? ImportTimeString { get; init; }
|
||||
|
||||
public DateTime? ImportTime
|
||||
{
|
||||
get
|
||||
{
|
||||
if (ImportTimeString == null)
|
||||
return null;
|
||||
|
||||
if (DateTime.TryParseExact(ImportTimeString, "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime dateTime))
|
||||
return dateTime;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
[JsonPropertyName("errno"), UsedImplicitly]
|
||||
public int? ErrNo { get; init; }
|
||||
|
||||
[JsonPropertyName("status"), UsedImplicitly]
|
||||
public int Status { get; init; }
|
||||
|
||||
public string StatusString => (string)Application.Current.TryFindResource($"ServerJobStatus{Status}");
|
||||
public string Tooltip => (string)(Application.Current.TryFindResource($"ServerError{ErrNo}") ?? StatusString);
|
||||
}
|
||||
|
||||
[UsedImplicitly]
|
||||
public class ServerJobData : ObservableObject
|
||||
{
|
||||
public string? Key => HistoryList?.FirstOrDefault(item => item.Status == 4)?.Key ?? FirstHistory?.Key ?? null;
|
||||
public DateTime? ImportTime => FirstHistory?.ImportTime ?? null;
|
||||
public int Status => _canceled ? 5 : FirstHistory?.Status ?? -1;
|
||||
|
||||
[JsonPropertyName("id"), UsedImplicitly]
|
||||
public int? Id { get; init; }
|
||||
|
||||
[JsonPropertyName("patient_name"), UsedImplicitly]
|
||||
public string? PatientName { get; init; }
|
||||
|
||||
[JsonPropertyName("patient_id"), UsedImplicitly]
|
||||
public string? PatientId { get; init; }
|
||||
|
||||
[JsonPropertyName("patient_age"), UsedImplicitly]
|
||||
public int? PatientAge { get; init; }
|
||||
|
||||
[JsonPropertyName("patient_sex"), UsedImplicitly]
|
||||
public string? PatientSex { get; init; }
|
||||
|
||||
[JsonPropertyName("study_date"), UsedImplicitly]
|
||||
public string? StudyDateString { get; init; }
|
||||
public DateTime? StudyDate
|
||||
{
|
||||
get
|
||||
{
|
||||
if (StudyDateString == null)
|
||||
return null;
|
||||
|
||||
if (DateTime.TryParseExact(StudyDateString, "yyyy-MM-dd", CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime dateTime))
|
||||
return dateTime;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
[JsonPropertyName("sequence"), UsedImplicitly]
|
||||
public List<SeriesGroup>? Sequence { get; init; }
|
||||
|
||||
[JsonPropertyName("history"), UsedImplicitly]
|
||||
public List<DataHistory>? HistoryList { get; init; }
|
||||
|
||||
public bool JobSuccess => HistoryList?.Any(item => item.Status == 4) ?? false;
|
||||
public bool ContainsMultipleHistory => (HistoryList?.Count ?? 0) > 1;
|
||||
public bool CancelButtonVisible => Status < 4 && !_canceled;
|
||||
public bool DeleteButtonVisible => Status >= 4;
|
||||
public bool SelectSeriesButtonVisible => Status == 6;
|
||||
public string Tooltip => FirstHistory?.Tooltip ?? "";
|
||||
public string StatusString => FirstHistory?.StatusString ?? "";
|
||||
|
||||
private DataHistory? FirstHistory => HistoryList?.Count > 0 ? HistoryList[0] : null;
|
||||
private bool _canceled;
|
||||
|
||||
public void SetJobCanceled()
|
||||
{
|
||||
_canceled = true;
|
||||
OnPropertyChanged(nameof(Status));
|
||||
OnPropertyChanged(nameof(CancelButtonVisible));
|
||||
OnPropertyChanged(nameof(Tooltip));
|
||||
OnPropertyChanged(nameof(StatusString));
|
||||
}
|
||||
}
|
34
Livia/Models/Data/ServiceConfigurations.cs
Normal file
|
@ -0,0 +1,34 @@
|
|||
using System.IO;
|
||||
|
||||
namespace Livia.Models.Data;
|
||||
|
||||
public static class ServiceConfigurations
|
||||
{
|
||||
public static string TempFolder = Path.Combine(Path.GetTempPath(), @"An-Image\livia\");
|
||||
|
||||
public static string AppName = "";
|
||||
public static string UpdateServerAddress => $"http://update.cereflow.cn/{AppName}";
|
||||
public static string PostProcessDataPath => $"/{AppName}/handle/packet";
|
||||
public static string PostCancelPath => $"/{AppName}/cancel";
|
||||
public static string DeleteReportPath => $"/{AppName}/report";
|
||||
public static string GetReportPath => $"/{AppName}/{{0}}/report/{{1}}/{{2}}";
|
||||
public static string GetServerJobListPath => $"/{AppName}/work/list";
|
||||
public static string GetCheckResultPath => $"/{AppName}/{{0}}/result/packet";
|
||||
public static string GetReportLIstPath => $"/{AppName}/{{0}}/report/list";
|
||||
public static string GetDownloadSortedPath => $"/{AppName}/{{0}}/result/sorted";
|
||||
public static string DeleteDataPath => $"/{AppName}/patient";
|
||||
public static string DeleteHistoryPath => $"/{AppName}/history";
|
||||
public static string GetSeriesGroupListPath => $"/{AppName}/{{0}}/handle/series_view";
|
||||
public static string PostSelectSeriesPath => $"/{AppName}/handle/choose";
|
||||
public static string PostSearchPath => $"/{AppName}/find";
|
||||
public static string PostStartPath => $"/{AppName}/handle/start";
|
||||
public static string PostPushReportPath => $"/{AppName}/report/store";
|
||||
public static string PostSelectReportModulePath => $"/{AppName}/report/filter";
|
||||
|
||||
public const string PostLoginPath = "/token";
|
||||
public const string PutEditPasswordPath = "/user/password";
|
||||
public const string GetDownloadArchivePath = "/study/{0}/archive";
|
||||
public const string GetDongleInfoPath = "/dongle";
|
||||
public const string GetNoticePath = "/notice";
|
||||
public const string GetDicomNodeInfo = "/dicom/node";
|
||||
}
|
177
Livia/Models/DataBlockLoader.cs
Normal file
|
@ -0,0 +1,177 @@
|
|||
using Livia.Models.Data;
|
||||
using Livia.ViewModels;
|
||||
using Livia.Views.Utility;
|
||||
using System.Collections.Concurrent;
|
||||
using System.IO;
|
||||
using System.Text.Json;
|
||||
using System.Windows;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using Livia.Utility;
|
||||
|
||||
namespace Livia.Models;
|
||||
|
||||
public interface IDataBlockLoader
|
||||
{
|
||||
DataBlock? CurrentDisplayingDataBlock { get; }
|
||||
bool PlaceholderVisible { get; }
|
||||
T GetSharedData<T>(string key);
|
||||
void AddViewModel(ILiviaModuleViewModel module);
|
||||
void AddDeferredLoadingModuleViewModel(ILiviaModuleViewModel module);
|
||||
Task LoadData(DataBlock dataBlock);
|
||||
Task LoadServerData(ServerJobData data);
|
||||
void Init();
|
||||
void SaveSharedData(string key, object data);
|
||||
bool SharedDataContainsKey(string key);
|
||||
}
|
||||
|
||||
internal class DataBlockLoader : ObservableObject, IDataBlockLoader, IRecipient<LogoutMessage>
|
||||
{
|
||||
public bool PlaceholderVisible { get => _placeholderVisible; protected internal set => SetProperty(ref _placeholderVisible, value); }
|
||||
public DataBlock? CurrentDisplayingDataBlock { get; private set; }
|
||||
|
||||
private readonly IWarningSystem _warningSystem;
|
||||
private readonly ILogger _logger;
|
||||
private readonly IPatientInfoManager _patientInfoManager;
|
||||
private readonly IServerHandler _serverHandler;
|
||||
private readonly IRoiLegendManager _roiLegendManager;
|
||||
|
||||
private readonly ConcurrentDictionary<string, object> _sharedData = new();
|
||||
private readonly List<ILiviaModuleViewModel> _moduleViewModels = [];
|
||||
private readonly List<ILiviaModuleViewModel> _deferredLoadingModuleViewModels = [];
|
||||
private readonly Dictionary<string, DataBlock> _dataBlocks = new();
|
||||
private bool _placeholderVisible = true;
|
||||
|
||||
public DataBlockLoader(IWarningSystem warningSystem, IPatientInfoManager patientInfoManager, ILogger logger, IServerHandler serverHandler, IRoiLegendManager roiLegendManager)
|
||||
{
|
||||
_warningSystem = warningSystem;
|
||||
_patientInfoManager = patientInfoManager;
|
||||
_logger = logger;
|
||||
_serverHandler = serverHandler;
|
||||
_roiLegendManager = roiLegendManager;
|
||||
WeakReferenceMessenger.Default.RegisterAll(this);
|
||||
}
|
||||
|
||||
public void SaveSharedData(string key, object data)
|
||||
{
|
||||
_sharedData[key] = data;
|
||||
}
|
||||
|
||||
public T GetSharedData<T>(string key)
|
||||
{
|
||||
return _sharedData[key] is T t ? t : throw new KeyNotFoundException();
|
||||
}
|
||||
|
||||
public bool SharedDataContainsKey(string key)
|
||||
{
|
||||
return _sharedData.ContainsKey(key);
|
||||
}
|
||||
|
||||
public void AddViewModel(ILiviaModuleViewModel module)
|
||||
{
|
||||
_moduleViewModels.Add(module);
|
||||
}
|
||||
|
||||
public void AddDeferredLoadingModuleViewModel(ILiviaModuleViewModel module)
|
||||
{
|
||||
_deferredLoadingModuleViewModels.Add(module);
|
||||
}
|
||||
|
||||
public async Task LoadData(DataBlock dataBlock)
|
||||
{
|
||||
_serverHandler.Processing = true;
|
||||
DateTime startTime = DateTime.Now;
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrEmpty(dataBlock.Key))
|
||||
throw new NullReferenceException();
|
||||
|
||||
_dataBlocks[dataBlock.Key] = dataBlock;
|
||||
CurrentDisplayingDataBlock = dataBlock;
|
||||
string path = dataBlock.ResultPath;
|
||||
|
||||
_sharedData.Clear();
|
||||
_roiLegendManager.Clear();
|
||||
|
||||
AdditionalInfoJson additionalInfo = JsonSerializer.Deserialize<AdditionalInfoJson>(await File.ReadAllTextAsync(Path.Combine(path, "additional_info.json")));
|
||||
_patientInfoManager.Init(additionalInfo);
|
||||
SaveSharedData("additionalInfo", additionalInfo);
|
||||
|
||||
string defaultMaskToLoad = additionalInfo.AtlasInfoListDict.Keys.First(k => k.Contains("cbf", StringComparison.CurrentCultureIgnoreCase));
|
||||
ImageRotationViewerControlViewModel.LoadAtlasName = defaultMaskToLoad;
|
||||
PerfusionDataGridControlViewModel.LoadAtlasName = defaultMaskToLoad;
|
||||
|
||||
Dictionary<string, ConcurrentBag<RoiExpanderControlViewModel>> roiCollectionDictionary = new();
|
||||
SaveSharedData("roiCollection", roiCollectionDictionary);
|
||||
|
||||
await Task.WhenAll(_moduleViewModels.Select(module => module.LoadData(dataBlock)));
|
||||
await Task.WhenAll(_deferredLoadingModuleViewModels.Select(module => module.LoadData(dataBlock)));
|
||||
CurrentDisplayingDataBlock = dataBlock;
|
||||
WeakReferenceMessenger.Default.Send(new DataLoadedMessage());
|
||||
|
||||
PlaceholderVisible = false;
|
||||
_warningSystem.ShowDialog(WarningWindowKind.Info, false, "DataImportComplete");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
PlaceholderVisible = true;
|
||||
CurrentDisplayingDataBlock = null;
|
||||
_logger.LogError(e, "Error loading data from {path}", dataBlock.Key);
|
||||
_warningSystem.ShowDialog(WarningWindowKind.Warning, true, "InvalidDataError");
|
||||
}
|
||||
finally
|
||||
{
|
||||
_serverHandler.Processing = false;
|
||||
DateTime endTime = DateTime.Now;
|
||||
TimeSpan timeUsed = endTime - startTime;
|
||||
_logger.LogInformation("Load time = {time}", timeUsed);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task LoadServerData(ServerJobData data)
|
||||
{
|
||||
if (string.IsNullOrEmpty(data.Key))
|
||||
{
|
||||
_logger.LogError("Cannot load data with empty key");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_dataBlocks.TryGetValue(data.Key, out DataBlock? value))
|
||||
{
|
||||
value = new DataBlock(string.Empty)
|
||||
{
|
||||
Key = data.Key
|
||||
};
|
||||
_dataBlocks[data.Key] = value;
|
||||
}
|
||||
DataBlock dataBlock = value;
|
||||
|
||||
(bool success, string messageIndex) = await _serverHandler.DownloadData(dataBlock);
|
||||
if (!success)
|
||||
{
|
||||
_warningSystem.ShowDialog(WarningWindowKind.Warning, true, messageIndex);
|
||||
return;
|
||||
}
|
||||
|
||||
await Application.Current.Dispatcher.Invoke(() => LoadData(dataBlock));
|
||||
}
|
||||
|
||||
public void Init()
|
||||
{
|
||||
foreach (ILiviaModuleViewModel liviaModuleViewModel in _moduleViewModels)
|
||||
{
|
||||
liviaModuleViewModel.Init();
|
||||
}
|
||||
|
||||
foreach (ILiviaModuleViewModel liviaModuleViewModel in _deferredLoadingModuleViewModels)
|
||||
{
|
||||
liviaModuleViewModel.Init();
|
||||
}
|
||||
}
|
||||
|
||||
public void Receive(LogoutMessage message)
|
||||
{
|
||||
PlaceholderVisible = true;
|
||||
}
|
||||
}
|
84
Livia/Models/IdleTimer.cs
Normal file
|
@ -0,0 +1,84 @@
|
|||
using System.Windows.Threading;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using Livia.Models.Data;
|
||||
using Livia.Properties;
|
||||
using Livia.Utility;
|
||||
using Livia.Views.Utility;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Livia.Models;
|
||||
|
||||
public interface IIdleTimer
|
||||
{
|
||||
void ResetTimer();
|
||||
}
|
||||
|
||||
public class IdleTimerTickMessage(IIdleTimer idleTimer)
|
||||
{
|
||||
public readonly IIdleTimer IdleTimer = idleTimer;
|
||||
}
|
||||
|
||||
public class IdleTimer : IIdleTimer
|
||||
{
|
||||
private DateTime _lastActiveTime = DateTime.Now;
|
||||
private readonly TimeSpan _lockAfter = TimeSpan.FromMinutes(Settings.Default.LogoutAfter);
|
||||
private readonly ILogger _logger;
|
||||
private readonly IServerHandler _serverHandler;
|
||||
private readonly IWarningSystem _warningSystem;
|
||||
|
||||
private static readonly TimeSpan TickInterval = new(0, 0, 5);
|
||||
private static readonly TimeSpan SnackBarWarningTime = new(0, 1, 0);
|
||||
|
||||
public IdleTimer(ILogger logger, IServerHandler serverHandler, IWarningSystem warningSystem)
|
||||
{
|
||||
_logger = logger;
|
||||
_serverHandler = serverHandler;
|
||||
_warningSystem = warningSystem;
|
||||
|
||||
if (Settings.Default.LogoutAfter <= 0)
|
||||
return;
|
||||
|
||||
DispatcherTimer dispatcherTimer = new();
|
||||
dispatcherTimer.Tick += Tick;
|
||||
dispatcherTimer.Interval = TickInterval;
|
||||
dispatcherTimer.Start();
|
||||
}
|
||||
|
||||
private void Tick(object? sender, EventArgs? e)
|
||||
{
|
||||
//sent tick message, any busy module will reset timer when receives this message
|
||||
WeakReferenceMessenger.Default.Send(new IdleTimerTickMessage(this));
|
||||
|
||||
//no access, do not lock again
|
||||
if (!_serverHandler.IsLoggedIn)
|
||||
{
|
||||
ResetTimer();
|
||||
return;
|
||||
}
|
||||
|
||||
TimeSpan idleTime = DateTime.Now - _lastActiveTime;
|
||||
|
||||
TimeSpan timeToLock = _lockAfter - idleTime;
|
||||
|
||||
if (timeToLock <= TimeSpan.Zero)
|
||||
{
|
||||
_logger.LogInformation("Idle time up");
|
||||
_warningSystem.AddSnackbarMessage("IdleTimeout");
|
||||
ResetTimer();
|
||||
WeakReferenceMessenger.Default.Send(new LogoutMessage());
|
||||
|
||||
}
|
||||
else if (timeToLock <= SnackBarWarningTime)
|
||||
{
|
||||
_warningSystem.AddSnackbarMessage(TickInterval - TimeSpan.FromSeconds(0.7), "IdleTimeoutWarning", (int)Math.Ceiling(timeToLock.TotalSeconds));
|
||||
}
|
||||
|
||||
//waiting
|
||||
}
|
||||
|
||||
public void ResetTimer()
|
||||
{
|
||||
//_logger.LogInformation("Timer reset");
|
||||
_lastActiveTime = DateTime.Now;
|
||||
}
|
||||
}
|
263
Livia/Models/ImageSeries.cs
Normal file
|
@ -0,0 +1,263 @@
|
|||
using System.Windows.Media;
|
||||
using FellowOakDicom.Imaging;
|
||||
using System.Windows.Media.Imaging;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using FellowOakDicom;
|
||||
using Livia.Utility;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Livia.Utility.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Livia.Models;
|
||||
|
||||
public interface IImageSeries : IDisposable
|
||||
{
|
||||
ImageSource? CurrentBitmapImage { get; }
|
||||
double WindowCenterModifier { get; set; }
|
||||
double WindowWidthModifier { get; set; }
|
||||
bool WindowCenterModifiable { get; }
|
||||
bool IsMask { set; }
|
||||
bool ShowBoundary { get; set; }
|
||||
//An index of the series where you can find the mask, used to locate ROI
|
||||
int? MaskJumpToIndex { get; }
|
||||
int Count { get; }
|
||||
public void SetImageIndex(int n);
|
||||
Task LoadData(string loadPath);
|
||||
}
|
||||
|
||||
internal class PlainImageSeries : ObservableObject, IImageSeries
|
||||
{
|
||||
public ImageSource? CurrentBitmapImage { get => _currentBitmapImage; private set => SetProperty(ref _currentBitmapImage, value); }
|
||||
public double WindowCenterModifier { get; set; }
|
||||
public double WindowWidthModifier { get; set; }
|
||||
public bool WindowCenterModifiable => false;
|
||||
public bool IsMask { set => throw new NotImplementedException(); }
|
||||
public bool ShowBoundary
|
||||
{
|
||||
get => throw new NotImplementedException();
|
||||
set => throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public int? MaskJumpToIndex => -1;
|
||||
|
||||
public int Count => _bitmapImages.Count;
|
||||
|
||||
private readonly List<BitmapImage> _bitmapImages = [];
|
||||
private ImageSource? _currentBitmapImage;
|
||||
|
||||
private BitmapImage? GetImage(int n)
|
||||
{
|
||||
if (n < 0 || n >= Count)
|
||||
return null;
|
||||
|
||||
return _bitmapImages[n];
|
||||
}
|
||||
|
||||
public void SetImageIndex(int n)
|
||||
{
|
||||
CurrentBitmapImage = GetImage(n);
|
||||
}
|
||||
|
||||
public async Task LoadData(string loadPath)
|
||||
{
|
||||
_bitmapImages.Clear();
|
||||
foreach (string file in LiviaUtility.ReadFilesFromDir(loadPath))
|
||||
{
|
||||
_bitmapImages.Add(await LiviaUtility.LoadBitmapAsync(file));
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
CurrentBitmapImage = null;
|
||||
_bitmapImages.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
internal class Dicom2DImageSeries : ObservableObject, IImageSeries
|
||||
{
|
||||
public ImageSource? CurrentBitmapImage { get => _currentBitmapImage; private set => SetProperty(ref _currentBitmapImage, value); }
|
||||
public double WindowCenterModifier { get; set; } = 1;
|
||||
public double WindowWidthModifier { get; set; } = 1;
|
||||
public bool WindowCenterModifiable => Count > 0 && _defaultWindowCenter > 0 && _defaultWindowWidth > 0;
|
||||
public bool IsMask { set => throw new NotImplementedException(); }
|
||||
public bool ShowBoundary
|
||||
{
|
||||
get => throw new NotImplementedException();
|
||||
set => throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public int? MaskJumpToIndex => -1;
|
||||
public int Count => _dicomImages.Count;
|
||||
|
||||
private readonly List<DicomImage> _dicomImages = [];
|
||||
private double _defaultWindowCenter;
|
||||
private double _defaultWindowWidth;
|
||||
|
||||
private readonly SemaphoreSlim _lock = new(1, 1);
|
||||
private string _loadedPath = string.Empty;
|
||||
private readonly ILogger _logger = ActivatorUtilities.GetServiceOrCreateInstance<ILogger>(ServiceProviderFactory.ServiceProvider);
|
||||
private ImageSource? _currentBitmapImage;
|
||||
|
||||
private BitmapImage? GetImage(int n)
|
||||
{
|
||||
if (n < 0 || n >= Count)
|
||||
return null;
|
||||
|
||||
DicomImage dicomImage = _dicomImages[n];
|
||||
dicomImage.WindowCenter = _defaultWindowCenter * WindowCenterModifier;
|
||||
dicomImage.WindowWidth = Math.Max(_defaultWindowWidth * WindowWidthModifier, 1);
|
||||
|
||||
BitmapImage bitmapImage = dicomImage.RenderImage().AsClonedBitmap().ToBitmapImage();
|
||||
bitmapImage.Freeze();
|
||||
|
||||
return bitmapImage;
|
||||
}
|
||||
|
||||
public void SetImageIndex(int n)
|
||||
{
|
||||
CurrentBitmapImage = GetImage(n);
|
||||
}
|
||||
|
||||
public async Task LoadData(string loadPath)
|
||||
{
|
||||
await _lock.WaitAsync();
|
||||
try
|
||||
{
|
||||
if (loadPath == _loadedPath)
|
||||
{
|
||||
_logger.LogInformation("{path} is already loaded, move on", loadPath);
|
||||
return;
|
||||
}
|
||||
|
||||
_dicomImages.Clear();
|
||||
double min = double.MaxValue;
|
||||
double max = double.MinValue;
|
||||
foreach (string file in LiviaUtility.ReadFilesFromDir(loadPath))
|
||||
{
|
||||
DicomFile dicomFile = await DicomFile.OpenAsync(file);
|
||||
_dicomImages.Add(new DicomImage(dicomFile.Dataset));
|
||||
int windowCenter = dicomFile.Dataset.GetSingleValueOrDefault(DicomTag.WindowCenter, 0);
|
||||
double windowWidthHalf = dicomFile.Dataset.GetSingleValueOrDefault(DicomTag.WindowWidth, 0) / 2.0;
|
||||
min = Math.Min(min, windowCenter - windowWidthHalf);
|
||||
max = Math.Max(max, windowCenter + windowWidthHalf);
|
||||
}
|
||||
|
||||
_defaultWindowCenter = (max + min) / 2;
|
||||
_defaultWindowWidth = max - min;
|
||||
|
||||
_loadedPath = loadPath;
|
||||
}
|
||||
finally
|
||||
{
|
||||
_lock.Release();
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
CurrentBitmapImage = null;
|
||||
_dicomImages.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
internal class Dicom3DImageSeries : ObservableObject, IImageSeries
|
||||
{
|
||||
public ImageSource? CurrentBitmapImage { get => _currentBitmapImage; private set => SetProperty(ref _currentBitmapImage, value); }
|
||||
public double WindowCenterModifier { get; set; } = 1;
|
||||
public double WindowWidthModifier { get; set; } = 1;
|
||||
public bool WindowCenterModifiable => Count > 0 && _defaultWindowCenter > 0 && _defaultWindowWidth > 0;
|
||||
public bool IsMask { get; set; }
|
||||
|
||||
public bool ShowBoundary
|
||||
{
|
||||
get => _showBoundary;
|
||||
set
|
||||
{
|
||||
_showBoundary = value;
|
||||
//reload stuff
|
||||
SetImageIndex(_currentImageIndex);
|
||||
}
|
||||
}
|
||||
|
||||
public int? MaskJumpToIndex { get => _maskJumpToIndex; private set => SetProperty(ref _maskJumpToIndex, value); }
|
||||
|
||||
public int Count => _dicomImage?.NumberOfFrames ?? 0;
|
||||
|
||||
private DicomImage? _dicomImage;
|
||||
private ImageSource? _currentBitmapImage;
|
||||
private int? _maskJumpToIndex;
|
||||
private bool _showBoundary = true;
|
||||
private int _currentImageIndex;
|
||||
private int _defaultWindowCenter;
|
||||
private int _defaultWindowWidth;
|
||||
|
||||
public void SetImageIndex(int n)
|
||||
{
|
||||
if (_dicomImage == null || n < 0 || n >= Count)
|
||||
return;
|
||||
|
||||
_currentImageIndex = n;
|
||||
if (_defaultWindowCenter != 0 && _defaultWindowWidth != 0)
|
||||
{
|
||||
_dicomImage.WindowCenter = _defaultWindowCenter * WindowCenterModifier;
|
||||
_dicomImage.WindowWidth = Math.Max(_defaultWindowWidth * WindowWidthModifier, 1);
|
||||
}
|
||||
BitmapImage image = _dicomImage.RenderImage(n).AsClonedBitmap().ToBitmapImage();
|
||||
|
||||
image.Freeze();
|
||||
if (!IsMask)
|
||||
{
|
||||
CurrentBitmapImage = image;
|
||||
return;
|
||||
}
|
||||
|
||||
BitmapSource transparencyImage = LiviaUtility.CreateTransparency(image, ShowBoundary);
|
||||
CurrentBitmapImage = transparencyImage;
|
||||
}
|
||||
|
||||
public async Task LoadData(string loadPath)
|
||||
{
|
||||
DicomFile dcmFile = await DicomFile.OpenAsync(loadPath);
|
||||
_dicomImage = new DicomImage(dcmFile.Dataset);
|
||||
_defaultWindowCenter = dcmFile.Dataset.GetSingleValueOrDefault<int?>(DicomTag.WindowCenter, null) ?? 0;
|
||||
_defaultWindowWidth = dcmFile.Dataset.GetSingleValueOrDefault<int?>(DicomTag.WindowWidth, null) ?? 0;
|
||||
|
||||
if (!IsMask)
|
||||
return;
|
||||
|
||||
//just run it. Do not care how long it takes, as long as it is not blocking ui thread.
|
||||
_ = Task.Run(UpdateMaskJumpToIndex);
|
||||
}
|
||||
|
||||
private void UpdateMaskJumpToIndex()
|
||||
{
|
||||
if (_dicomImage == null)
|
||||
return;
|
||||
|
||||
//find am index with max pixel count. When locate this roi, jump here.
|
||||
int[] result = new int[Count];
|
||||
Parallel.For(0, Count, i =>
|
||||
{
|
||||
BitmapImage image = _dicomImage.RenderImage(i).AsClonedBitmap().ToBitmapImage();
|
||||
image.Freeze();
|
||||
int bytesPerPixel = (image.Format.BitsPerPixel + 7) / 8;
|
||||
int stride = bytesPerPixel * image.PixelWidth;
|
||||
byte[] buffer = new byte[stride * image.PixelHeight];
|
||||
int alphaCount = buffer.Length / 4;
|
||||
image.CopyPixels(buffer, stride, 0);
|
||||
|
||||
int count = buffer.Count(b => b > 0) - alphaCount;
|
||||
result[i] = count;
|
||||
});
|
||||
int maxCount = result.Max();
|
||||
|
||||
MaskJumpToIndex = maxCount == 0 ? null : Array.IndexOf(result, maxCount);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
CurrentBitmapImage = null;
|
||||
_dicomImage = null;
|
||||
}
|
||||
}
|
41
Livia/Models/PatientInfoManager.cs
Normal file
|
@ -0,0 +1,41 @@
|
|||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Livia.Models.Data;
|
||||
|
||||
namespace Livia.Models;
|
||||
|
||||
public interface IPatientInfoManager
|
||||
{
|
||||
bool AnonymousMode { get; set; }
|
||||
string PatientName { get; }
|
||||
string PatientId { get; }
|
||||
string PatientSex { get; }
|
||||
string PatientAge { get; }
|
||||
DateTime StudyDate { get; }
|
||||
void Init(AdditionalInfoJson additionalInfo);
|
||||
}
|
||||
|
||||
internal class PatientInfoManager : ObservableObject, IPatientInfoManager
|
||||
{
|
||||
public bool AnonymousMode { get => _anonymousMode; set => SetProperty(ref _anonymousMode, value); }
|
||||
public string PatientName { get => _patientName; private set => SetProperty(ref _patientName, value); }
|
||||
public string PatientId { get => _patientId; private set => SetProperty(ref _patientId, value); }
|
||||
public string PatientSex { get => _patientSex; private set => SetProperty(ref _patientSex, value); }
|
||||
public string PatientAge { get => _patientAge; private set => SetProperty(ref _patientAge, value); }
|
||||
public DateTime StudyDate { get => _studyDate; private set => SetProperty(ref _studyDate, value); }
|
||||
|
||||
private bool _anonymousMode;
|
||||
private string _patientName = string.Empty;
|
||||
private string _patientId = string.Empty;
|
||||
private string _patientSex = string.Empty;
|
||||
private string _patientAge = string.Empty;
|
||||
private DateTime _studyDate = DateTime.MinValue;
|
||||
|
||||
public void Init(AdditionalInfoJson additionalInfo)
|
||||
{
|
||||
PatientName = additionalInfo.ScanInfo.PatientName;
|
||||
PatientId = additionalInfo.ScanInfo.PatientId ?? string.Empty;
|
||||
PatientAge = additionalInfo.ScanInfo.PatientAge;
|
||||
PatientSex = additionalInfo.ScanInfo.PatientSex;
|
||||
StudyDate = DateTime.ParseExact(additionalInfo.ScanInfo.StudyDate, "yyyyMMdd", System.Globalization.CultureInfo.InvariantCulture);
|
||||
}
|
||||
}
|
170
Livia/Models/ProcessManager.cs
Normal file
|
@ -0,0 +1,170 @@
|
|||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using Livia.Properties;
|
||||
using Livia.Utility;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Livia.Models;
|
||||
|
||||
public interface IProcessManager
|
||||
{
|
||||
bool Start(ServerFileLocation[] serverFileLocations);
|
||||
}
|
||||
|
||||
public readonly struct ServerFileLocation
|
||||
{
|
||||
public ServerFileLocation(string fileName, string workingDirectory)
|
||||
{
|
||||
FileName = fileName;
|
||||
FileNameNoExtension = Path.GetFileNameWithoutExtension(fileName);
|
||||
WorkingDirectory = workingDirectory;
|
||||
FullPath = Path.Combine(WorkingDirectory, FileName);
|
||||
}
|
||||
|
||||
public readonly string FileName;
|
||||
public readonly string FileNameNoExtension;
|
||||
public readonly string WorkingDirectory;
|
||||
public readonly string FullPath;
|
||||
}
|
||||
|
||||
|
||||
public class ProcessManager : ObservableRecipient, IProcessManager, IRecipient<ShutdownMessage>
|
||||
{
|
||||
|
||||
private readonly ILogger _logger;
|
||||
private ServerFileLocation[] _serverFileLocations = Array.Empty<ServerFileLocation>();
|
||||
private Process[] _servers = Array.Empty<Process>();
|
||||
private const int MaxRestartCount = 10;
|
||||
private int _restartCount;
|
||||
|
||||
public ProcessManager(ILogger logger)
|
||||
{
|
||||
_logger = logger;
|
||||
WeakReferenceMessenger.Default.RegisterAll(this);
|
||||
}
|
||||
|
||||
private Process StartProcess(ServerFileLocation fileLocation)
|
||||
{
|
||||
_logger.LogInformation("Starting service");
|
||||
Process process = new();
|
||||
process.StartInfo.FileName = fileLocation.FileName;
|
||||
process.StartInfo.WorkingDirectory = fileLocation.WorkingDirectory;
|
||||
process.StartInfo.UseShellExecute = true;
|
||||
process.StartInfo.CreateNoWindow = true;
|
||||
process.EnableRaisingEvents = true;
|
||||
process.StartInfo.WindowStyle = Settings.Default.DeveloperMode ? ProcessWindowStyle.Normal : ProcessWindowStyle.Hidden;
|
||||
process.Exited += ProcessOnExited;
|
||||
if (!process.Start())
|
||||
{
|
||||
throw new Exception("Failed to start process");
|
||||
}
|
||||
return process;
|
||||
}
|
||||
|
||||
private void ProcessOnExited(object? sender, EventArgs e)
|
||||
{
|
||||
if (_restartCount >= MaxRestartCount)
|
||||
{
|
||||
_logger.LogCritical("ProcessManager MaxRestartCount reached");
|
||||
return;
|
||||
}
|
||||
_logger.LogCritical("Server process has exited! Attempting to restart it");
|
||||
|
||||
for (int i = 0; i < _servers.Length; i++)
|
||||
{
|
||||
if (sender != _servers[i])
|
||||
continue;
|
||||
|
||||
_restartCount++;
|
||||
_servers[i] = StartProcess(_serverFileLocations[i]);
|
||||
return;
|
||||
}
|
||||
|
||||
_logger.LogCritical("Cannot determine which process has exited!");
|
||||
}
|
||||
|
||||
private void CleanUp()
|
||||
{
|
||||
foreach (Process process in _servers)
|
||||
{
|
||||
//disable this so that we are not going to attempt to restart it
|
||||
process.EnableRaisingEvents = false;
|
||||
}
|
||||
|
||||
KillExisting();
|
||||
|
||||
//remove everything from temp
|
||||
string[] folders = Directory.GetDirectories(Path.GetTempPath());
|
||||
foreach (string folderPath in folders)
|
||||
{
|
||||
DirectoryInfo info = new(folderPath);
|
||||
string folderName = info.Name;
|
||||
if (!folderName.StartsWith("_MEI"))
|
||||
continue;
|
||||
|
||||
//pretty new, wait and see if it will be auto deleted
|
||||
TimeSpan folderAge = DateTime.Now - info.LastWriteTime;
|
||||
if (folderAge < TimeSpan.FromDays(1))
|
||||
continue;
|
||||
|
||||
try
|
||||
{
|
||||
_logger.LogInformation("{folder} is {age} old, deleting it", folderPath, folderAge);
|
||||
Directory.Delete(folderPath, true);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogInformation(e, "Error Deleting {folder}", folderPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void KillExisting()
|
||||
{
|
||||
//find all and kill them
|
||||
foreach (ServerFileLocation fileLocation in _serverFileLocations)
|
||||
{
|
||||
try
|
||||
{
|
||||
Process[] processes = Process.GetProcessesByName(fileLocation.FileNameNoExtension);
|
||||
Process? lastCreatedProcess = processes.Where(process => process.MainModule?.FileName == fileLocation.FullPath).MaxBy(process => process.StartTime);
|
||||
|
||||
lastCreatedProcess?.Kill();
|
||||
lastCreatedProcess?.WaitForExit();
|
||||
lastCreatedProcess?.Dispose();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError(e, "Error killing process{process}", fileLocation.FileNameNoExtension);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool Start(ServerFileLocation[] serverFileLocations)
|
||||
{
|
||||
KillExisting();
|
||||
_serverFileLocations = serverFileLocations;
|
||||
_servers = new Process[_serverFileLocations.Length];
|
||||
try
|
||||
{
|
||||
for (int i = 0; i < _serverFileLocations.Length; i++)
|
||||
{
|
||||
_servers[i] = StartProcess(_serverFileLocations[i]);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogCritical(ex, "Cannot start server");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Receive(ShutdownMessage message)
|
||||
{
|
||||
CleanUp();
|
||||
}
|
||||
}
|
71
Livia/Models/RoiLegendManager.cs
Normal file
|
@ -0,0 +1,71 @@
|
|||
using System.Collections.ObjectModel;
|
||||
using System.Windows.Media;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using Livia.Utility;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Livia.Models;
|
||||
|
||||
public class LegendItem
|
||||
{
|
||||
public string ItemFullName { get; init; } = string.Empty;
|
||||
public string ItemName { get; init; } = string.Empty;
|
||||
public Color ItemColor { get; init; }
|
||||
}
|
||||
|
||||
public interface IRoiLegendManager
|
||||
{
|
||||
void Clear();
|
||||
void AddItem(int tabKey, string id, string name, string fullName, Color color);
|
||||
}
|
||||
|
||||
public class RoiLegendManager : ObservableRecipient, IRoiLegendManager, IRecipient<RoiTabChangedMessage>
|
||||
{
|
||||
public ObservableCollection<LegendItem> LegendItems { get; } = [];
|
||||
private Dictionary<int, List<LegendItem>> LegendItemsDict { get; } = new();
|
||||
|
||||
private readonly HashSet<string> _currentItems = [];
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public RoiLegendManager(ILogger logger)
|
||||
{
|
||||
_logger = logger;
|
||||
WeakReferenceMessenger.Default.RegisterAll(this);
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
LegendItems.Clear();
|
||||
LegendItemsDict.Clear();
|
||||
_currentItems.Clear();
|
||||
}
|
||||
|
||||
public void AddItem(int tabKey, string id, string name, string fullName, Color color)
|
||||
{
|
||||
string hashKey = $"{tabKey}###{id}";
|
||||
if (!_currentItems.Add(hashKey))
|
||||
return;
|
||||
|
||||
LegendItem item = new()
|
||||
{
|
||||
ItemName = LiviaUtility.ParseServerString(name),
|
||||
ItemFullName = LiviaUtility.ParseServerString(fullName),
|
||||
ItemColor = color
|
||||
};
|
||||
LegendItemsDict.AddToCollectionWithKey(tabKey, item);
|
||||
}
|
||||
|
||||
public void Receive(RoiTabChangedMessage message)
|
||||
{
|
||||
if (LegendItemsDict.TryGetValue(message.Value, out List<LegendItem>? list))
|
||||
{
|
||||
LegendItems.Clear();
|
||||
LegendItems.AddRange(list);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogWarning("Unknown Roi tab index for legends: {id}", message.Value);
|
||||
}
|
||||
}
|
||||
}
|
78
Livia/Models/SerialKeyManager.cs
Normal file
|
@ -0,0 +1,78 @@
|
|||
using System.Management;
|
||||
using System.Text.Json;
|
||||
using Livia.Properties;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Livia.Models;
|
||||
|
||||
public interface ISerialKeyManager
|
||||
{
|
||||
bool Validate();
|
||||
string HardwareId { get; }
|
||||
string EncryptLicense(AuthenticationKeyLicense license);
|
||||
}
|
||||
|
||||
public readonly record struct AuthenticationKeyLicense
|
||||
{
|
||||
public string HardwareId { get; init; }
|
||||
public DateTime ExpireDateTime { get; init; }
|
||||
}
|
||||
|
||||
internal class SerialKeyManager : ISerialKeyManager
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
private readonly IAesOperation _aesOperation;
|
||||
public string HardwareId { get; } = string.Empty;
|
||||
|
||||
private const string AesKey = "L2CzateeJgdwxVA6ipQ8aj4sxvTWTzd4";
|
||||
|
||||
public SerialKeyManager(ILogger logger, IAesOperation aesOperation)
|
||||
{
|
||||
_logger = logger;
|
||||
_aesOperation = aesOperation;
|
||||
|
||||
using ManagementObjectSearcher searcher = new("select UUID from Win32_ComputerSystemProduct");
|
||||
|
||||
ManagementObjectCollection information = searcher.Get();
|
||||
foreach (ManagementBaseObject? obj in information)
|
||||
{
|
||||
foreach (PropertyData data in obj.Properties)
|
||||
{
|
||||
_logger.LogInformation("{name} = {value}", data.Name, data.Value);
|
||||
HardwareId = (string)data.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool Validate()
|
||||
{
|
||||
string authenticationKey = Settings.Default.AuthenticationKey;
|
||||
_logger.LogInformation("AuthenticationKey = {value}", Settings.Default.AuthenticationKey);
|
||||
string jsonString = _aesOperation.DecryptString(AesKey, authenticationKey);
|
||||
|
||||
try
|
||||
{
|
||||
AuthenticationKeyLicense license = JsonSerializer.Deserialize<AuthenticationKeyLicense>(jsonString);
|
||||
if (!string.Equals(HardwareId, license.HardwareId))
|
||||
throw new Exception("Hardware ID mismatch");
|
||||
|
||||
if (license.ExpireDateTime < DateTime.Now)
|
||||
throw new Exception("Auth key expired");
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError(e, "Cannot decrypt auth key");
|
||||
}
|
||||
|
||||
//reset to empty if incorrect
|
||||
Settings.Default.AuthenticationKey = string.Empty;
|
||||
return false;
|
||||
}
|
||||
|
||||
public string EncryptLicense(AuthenticationKeyLicense license)
|
||||
{
|
||||
return _aesOperation.EncryptString(AesKey, JsonSerializer.Serialize(license));
|
||||
}
|
||||
}
|
24
Livia/Models/ServerExceptions.cs
Normal file
|
@ -0,0 +1,24 @@
|
|||
using Livia.Models.Data;
|
||||
|
||||
namespace Livia.Models;
|
||||
|
||||
public class ResultNotReadyException(ServerResponseJson response) : Exception("Result not ready")
|
||||
{
|
||||
public ServerResponseJson ServerResponseJson { get; } = response;
|
||||
}
|
||||
|
||||
public class ServerErrorException(ServerResponseJson response) : Exception($"Server error {response.ErrorNumber}")
|
||||
{
|
||||
public ServerResponseJson ServerResponseJson { get; } = response;
|
||||
}
|
||||
|
||||
|
||||
public class SelectReportModuleFailedException(int errorNumber) : Exception("Report name already exists")
|
||||
{
|
||||
public int ErrorNumber { get; } = errorNumber;
|
||||
}
|
||||
|
||||
public class LoginErrorException(LoginErrorResponseJson response) : Exception("Login error")
|
||||
{
|
||||
public LoginErrorResponseJson ServerResponseJson { get; } = response;
|
||||
}
|
136
Livia/Models/VersionUpdateManager.cs
Normal file
|
@ -0,0 +1,136 @@
|
|||
using System.ComponentModel;
|
||||
using System.Windows.Threading;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Livia.Models.Data;
|
||||
using Livia.Properties;
|
||||
using Livia.Views.Utility;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Velopack;
|
||||
|
||||
namespace Livia.Models;
|
||||
|
||||
public interface IVersionUpdateManager : INotifyPropertyChanged, INotifyPropertyChanging
|
||||
{
|
||||
bool UpdateAvailable { get; }
|
||||
bool Updating { get; }
|
||||
string CurrentVersion { get; }
|
||||
public Task CheckForUpdate(bool showResult);
|
||||
public Task UpdateApp();
|
||||
}
|
||||
|
||||
internal class VersionUpdateManager : ObservableObject, IVersionUpdateManager
|
||||
{
|
||||
public bool UpdateAvailable { get => _updateAvailable; private set => SetProperty(ref _updateAvailable, value); }
|
||||
public bool Updating { get => _updating; private set => SetProperty(ref _updating, value); }
|
||||
public string CurrentVersion { get => _currentVersion; private set => SetProperty(ref _currentVersion, value); }
|
||||
|
||||
private readonly ILogger _logger;
|
||||
private readonly IWarningSystem _warningSystem;
|
||||
|
||||
private bool _updateAvailable;
|
||||
private bool _updating;
|
||||
private string _currentVersion = "Not Installed Version";
|
||||
private readonly UpdateManager _updateManager;
|
||||
|
||||
public VersionUpdateManager(ILogger logger, IWarningSystem warningSystem)
|
||||
{
|
||||
_logger = logger;
|
||||
_warningSystem = warningSystem;
|
||||
UpdateOptions updateOptions = new()
|
||||
{
|
||||
AllowVersionDowngrade = true,
|
||||
ExplicitChannel = $"win-{Settings.Default.UpdateChannel}"
|
||||
};
|
||||
_updateManager = new UpdateManager(ServiceConfigurations.UpdateServerAddress, updateOptions);
|
||||
|
||||
DispatcherTimer dispatcherTimer = new();
|
||||
dispatcherTimer.Tick += Tick;
|
||||
dispatcherTimer.Interval = new TimeSpan(0, 30, 0);
|
||||
dispatcherTimer.Start();
|
||||
|
||||
//tick once on start
|
||||
Tick(null, null);
|
||||
}
|
||||
|
||||
private void Tick(object? sender, EventArgs? e)
|
||||
{
|
||||
Task.Run(() => CheckForUpdate(false));
|
||||
}
|
||||
|
||||
|
||||
public async Task CheckForUpdate(bool showResult)
|
||||
{
|
||||
if (!_updateManager.IsInstalled)
|
||||
{
|
||||
_logger.LogInformation("Not installed version");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Settings.Default.ShowUpdateButton)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CurrentVersion = _updateManager.CurrentVersion?.ToFullString() ?? CurrentVersion;
|
||||
|
||||
_logger.LogInformation("Checking for update");
|
||||
//wait for 30s
|
||||
TimeSpan waitTime = TimeSpan.FromSeconds(30);
|
||||
try
|
||||
{
|
||||
UpdateInfo? updateInfo = await _updateManager.CheckForUpdatesAsync().WaitAsync(waitTime);
|
||||
|
||||
if (updateInfo == null)
|
||||
{
|
||||
if (showResult)
|
||||
_warningSystem.ShowDialog(WarningWindowKind.Info, false, "NoNewVersionInfoMessage");
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogInformation("New Version Found: {version}", updateInfo.TargetFullRelease);
|
||||
|
||||
//NewVersion = updateInfo.FutureReleaseEntry.Version.ToString();
|
||||
UpdateAvailable = true;
|
||||
}
|
||||
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError(e, "Error Checking for update");
|
||||
if (showResult)
|
||||
_warningSystem.ShowDialog(WarningWindowKind.Info, false, "CannotGetUpdateInfoMessage");
|
||||
}
|
||||
}
|
||||
|
||||
public async Task UpdateApp()
|
||||
{
|
||||
Updating = true;
|
||||
_logger.LogInformation("Updating app");
|
||||
|
||||
//wait for 10 min
|
||||
TimeSpan waitTime = TimeSpan.FromMinutes(10);
|
||||
try
|
||||
{
|
||||
UpdateInfo? updateInfo = await _updateManager.CheckForUpdatesAsync().WaitAsync(waitTime);
|
||||
if (updateInfo == null)
|
||||
{
|
||||
_logger.LogError("updateInfo is null");
|
||||
return;
|
||||
}
|
||||
// install new version and restart app
|
||||
await _updateManager.DownloadUpdatesAsync(updateInfo);
|
||||
Settings.Default.ApplicationQuitNormally = true;
|
||||
Settings.Default.Save();
|
||||
_updateManager.ApplyUpdatesAndRestart(updateInfo);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError(e, "Error Updating app");
|
||||
_warningSystem.ShowDialog(WarningWindowKind.Info, false, "CannotGetUpdateInfoMessage");
|
||||
}
|
||||
finally
|
||||
{
|
||||
Updating = false;
|
||||
}
|
||||
}
|
||||
}
|
278
Livia/Properties/Settings.Designer.cs
generated
Normal file
|
@ -0,0 +1,278 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.42000
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace Livia.Properties {
|
||||
|
||||
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.11.0.0")]
|
||||
public sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
|
||||
|
||||
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
|
||||
|
||||
public static Settings Default {
|
||||
get {
|
||||
return defaultInstance;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("True")]
|
||||
public bool UpgradeRequired {
|
||||
get {
|
||||
return ((bool)(this["UpgradeRequired"]));
|
||||
}
|
||||
set {
|
||||
this["UpgradeRequired"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("True")]
|
||||
public bool ApplicationQuitNormally {
|
||||
get {
|
||||
return ((bool)(this["ApplicationQuitNormally"]));
|
||||
}
|
||||
set {
|
||||
this["ApplicationQuitNormally"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("")]
|
||||
public string DefaultUserName {
|
||||
get {
|
||||
return ((string)(this["DefaultUserName"]));
|
||||
}
|
||||
set {
|
||||
this["DefaultUserName"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("False")]
|
||||
public bool DeveloperMode {
|
||||
get {
|
||||
return ((bool)(this["DeveloperMode"]));
|
||||
}
|
||||
set {
|
||||
this["DeveloperMode"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("astroke.cereflow.cn")]
|
||||
public string ServerAddress {
|
||||
get {
|
||||
return ((string)(this["ServerAddress"]));
|
||||
}
|
||||
set {
|
||||
this["ServerAddress"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("443")]
|
||||
public int ServerPort {
|
||||
get {
|
||||
return ((int)(this["ServerPort"]));
|
||||
}
|
||||
set {
|
||||
this["ServerPort"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("True")]
|
||||
public bool OpenReportOnFinish {
|
||||
get {
|
||||
return ((bool)(this["OpenReportOnFinish"]));
|
||||
}
|
||||
set {
|
||||
this["OpenReportOnFinish"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("True")]
|
||||
public bool ShowArchiveDownloadButton {
|
||||
get {
|
||||
return ((bool)(this["ShowArchiveDownloadButton"]));
|
||||
}
|
||||
set {
|
||||
this["ShowArchiveDownloadButton"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("False")]
|
||||
public bool AutoRefreshServerJobList {
|
||||
get {
|
||||
return ((bool)(this["AutoRefreshServerJobList"]));
|
||||
}
|
||||
set {
|
||||
this["AutoRefreshServerJobList"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("-1")]
|
||||
public int LastReadNoticeId {
|
||||
get {
|
||||
return ((int)(this["LastReadNoticeId"]));
|
||||
}
|
||||
set {
|
||||
this["LastReadNoticeId"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("True")]
|
||||
public bool ShowSelectReportModuleDialog {
|
||||
get {
|
||||
return ((bool)(this["ShowSelectReportModuleDialog"]));
|
||||
}
|
||||
set {
|
||||
this["ShowSelectReportModuleDialog"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("False")]
|
||||
public bool ShowPushToPacsButton {
|
||||
get {
|
||||
return ((bool)(this["ShowPushToPacsButton"]));
|
||||
}
|
||||
set {
|
||||
this["ShowPushToPacsButton"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("False")]
|
||||
public bool ShowDicomNodeTab {
|
||||
get {
|
||||
return ((bool)(this["ShowDicomNodeTab"]));
|
||||
}
|
||||
set {
|
||||
this["ShowDicomNodeTab"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("False")]
|
||||
public bool ShowReportModuleAalCheckbox {
|
||||
get {
|
||||
return ((bool)(this["ShowReportModuleAalCheckbox"]));
|
||||
}
|
||||
set {
|
||||
this["ShowReportModuleAalCheckbox"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("10")]
|
||||
public float LogoutAfter {
|
||||
get {
|
||||
return ((float)(this["LogoutAfter"]));
|
||||
}
|
||||
set {
|
||||
this["LogoutAfter"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("zh-CN")]
|
||||
public string Language {
|
||||
get {
|
||||
return ((string)(this["Language"]));
|
||||
}
|
||||
set {
|
||||
this["Language"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("")]
|
||||
public string CultureInfo {
|
||||
get {
|
||||
return ((string)(this["CultureInfo"]));
|
||||
}
|
||||
set {
|
||||
this["CultureInfo"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("")]
|
||||
public string AuthenticationKey {
|
||||
get {
|
||||
return ((string)(this["AuthenticationKey"]));
|
||||
}
|
||||
set {
|
||||
this["AuthenticationKey"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("stable")]
|
||||
public string UpdateChannel {
|
||||
get {
|
||||
return ((string)(this["UpdateChannel"]));
|
||||
}
|
||||
set {
|
||||
this["UpdateChannel"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("True")]
|
||||
public bool ShowUpdateButton {
|
||||
get {
|
||||
return ((bool)(this["ShowUpdateButton"]));
|
||||
}
|
||||
set {
|
||||
this["ShowUpdateButton"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("True")]
|
||||
public bool ShowSortedDownloadButton {
|
||||
get {
|
||||
return ((bool)(this["ShowSortedDownloadButton"]));
|
||||
}
|
||||
set {
|
||||
this["ShowSortedDownloadButton"] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
69
Livia/Properties/Settings.settings
Normal file
|
@ -0,0 +1,69 @@
|
|||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)" GeneratedClassNamespace="Livia.Properties" GeneratedClassName="Settings">
|
||||
<Profiles />
|
||||
<Settings>
|
||||
<Setting Name="UpgradeRequired" Type="System.Boolean" Scope="User">
|
||||
<Value Profile="(Default)">True</Value>
|
||||
</Setting>
|
||||
<Setting Name="ApplicationQuitNormally" Type="System.Boolean" Scope="User">
|
||||
<Value Profile="(Default)">True</Value>
|
||||
</Setting>
|
||||
<Setting Name="DefaultUserName" Type="System.String" Scope="User">
|
||||
<Value Profile="(Default)" />
|
||||
</Setting>
|
||||
<Setting Name="DeveloperMode" Type="System.Boolean" Scope="User">
|
||||
<Value Profile="(Default)">False</Value>
|
||||
</Setting>
|
||||
<Setting Name="ServerAddress" Type="System.String" Scope="User">
|
||||
<Value Profile="(Default)">astroke.cereflow.cn</Value>
|
||||
</Setting>
|
||||
<Setting Name="ServerPort" Type="System.Int32" Scope="User">
|
||||
<Value Profile="(Default)">443</Value>
|
||||
</Setting>
|
||||
<Setting Name="OpenReportOnFinish" Type="System.Boolean" Scope="User">
|
||||
<Value Profile="(Default)">True</Value>
|
||||
</Setting>
|
||||
<Setting Name="ShowArchiveDownloadButton" Type="System.Boolean" Scope="User">
|
||||
<Value Profile="(Default)">True</Value>
|
||||
</Setting>
|
||||
<Setting Name="AutoRefreshServerJobList" Type="System.Boolean" Scope="User">
|
||||
<Value Profile="(Default)">False</Value>
|
||||
</Setting>
|
||||
<Setting Name="LastReadNoticeId" Type="System.Int32" Scope="User">
|
||||
<Value Profile="(Default)">-1</Value>
|
||||
</Setting>
|
||||
<Setting Name="ShowSelectReportModuleDialog" Type="System.Boolean" Scope="User">
|
||||
<Value Profile="(Default)">True</Value>
|
||||
</Setting>
|
||||
<Setting Name="ShowPushToPacsButton" Type="System.Boolean" Scope="User">
|
||||
<Value Profile="(Default)">False</Value>
|
||||
</Setting>
|
||||
<Setting Name="ShowDicomNodeTab" Type="System.Boolean" Scope="User">
|
||||
<Value Profile="(Default)">False</Value>
|
||||
</Setting>
|
||||
<Setting Name="ShowReportModuleAalCheckbox" Type="System.Boolean" Scope="User">
|
||||
<Value Profile="(Default)">False</Value>
|
||||
</Setting>
|
||||
<Setting Name="LogoutAfter" Type="System.Single" Scope="User">
|
||||
<Value Profile="(Default)">10</Value>
|
||||
</Setting>
|
||||
<Setting Name="Language" Type="System.String" Scope="User">
|
||||
<Value Profile="(Default)">zh-CN</Value>
|
||||
</Setting>
|
||||
<Setting Name="CultureInfo" Type="System.String" Scope="User">
|
||||
<Value Profile="(Default)" />
|
||||
</Setting>
|
||||
<Setting Name="AuthenticationKey" Type="System.String" Scope="User">
|
||||
<Value Profile="(Default)" />
|
||||
</Setting>
|
||||
<Setting Name="UpdateChannel" Type="System.String" Scope="User">
|
||||
<Value Profile="(Default)">stable</Value>
|
||||
</Setting>
|
||||
<Setting Name="ShowUpdateButton" Type="System.Boolean" Scope="User">
|
||||
<Value Profile="(Default)">True</Value>
|
||||
</Setting>
|
||||
<Setting Name="ShowSortedDownloadButton" Type="System.Boolean" Scope="User">
|
||||
<Value Profile="(Default)">True</Value>
|
||||
</Setting>
|
||||
</Settings>
|
||||
</SettingsFile>
|
45
Livia/Properties/SettingsLogger.cs
Normal file
|
@ -0,0 +1,45 @@
|
|||
using System.ComponentModel;
|
||||
using System.Configuration;
|
||||
using Livia.Utility.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Livia.Properties;
|
||||
|
||||
// This class allows you to handle specific events on the settings class:
|
||||
// The SettingChanging event is raised before a setting's value is changed.
|
||||
// The PropertyChanged event is raised after a setting's value is changed.
|
||||
// The SettingsLoaded event is raised after the setting values are loaded.
|
||||
// The SettingsSaving event is raised before the setting values are saved.
|
||||
public static class SettingsLogger
|
||||
{
|
||||
private static ILogger? _logger;
|
||||
|
||||
public static void Init()
|
||||
{
|
||||
_logger = ActivatorUtilities.GetServiceOrCreateInstance<ILogger>(ServiceProviderFactory.ServiceProvider);
|
||||
// To add event handlers for saving and changing settings, uncomment the lines below:
|
||||
Settings.Default.SettingChanging += SettingChangingEventHandler;
|
||||
Settings.Default.SettingsSaving += SettingsSavingEventHandler;
|
||||
|
||||
/*
|
||||
ViewSettings.Default.SettingChanging += SettingChangingEventHandler;
|
||||
ViewSettings.Default.SettingsSaving += SettingsSavingEventHandler;
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
private static void SettingChangingEventHandler(object sender, SettingChangingEventArgs e)
|
||||
{
|
||||
// Add code to handle the SettingChangingEvent event here.
|
||||
_logger?.LogInformation("Changing setting for {SettingName} from {sender} to {NewValue}", e.SettingName, sender, e.NewValue);
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static void SettingsSavingEventHandler(object sender, CancelEventArgs e)
|
||||
{
|
||||
_logger?.LogInformation("Saving settings for {sender}", sender);
|
||||
}
|
||||
|
||||
}
|
158
Livia/Properties/ViewSettings.Designer.cs
generated
Normal file
|
@ -0,0 +1,158 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.42000
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace Livia.Properties {
|
||||
|
||||
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.9.0.0")]
|
||||
public sealed partial class ViewSettings : global::System.Configuration.ApplicationSettingsBase {
|
||||
|
||||
private static ViewSettings defaultInstance = ((ViewSettings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new ViewSettings())));
|
||||
|
||||
public static ViewSettings Default {
|
||||
get {
|
||||
return defaultInstance;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("1024")]
|
||||
public double MainWindowWidth {
|
||||
get {
|
||||
return ((double)(this["MainWindowWidth"]));
|
||||
}
|
||||
set {
|
||||
this["MainWindowWidth"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("768")]
|
||||
public double MainWindowHeight {
|
||||
get {
|
||||
return ((double)(this["MainWindowHeight"]));
|
||||
}
|
||||
set {
|
||||
this["MainWindowHeight"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("0")]
|
||||
public double MainWindowTop {
|
||||
get {
|
||||
return ((double)(this["MainWindowTop"]));
|
||||
}
|
||||
set {
|
||||
this["MainWindowTop"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("0")]
|
||||
public double MainWindowLeft {
|
||||
get {
|
||||
return ((double)(this["MainWindowLeft"]));
|
||||
}
|
||||
set {
|
||||
this["MainWindowLeft"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("False")]
|
||||
public bool MainWindowIsMaximized {
|
||||
get {
|
||||
return ((bool)(this["MainWindowIsMaximized"]));
|
||||
}
|
||||
set {
|
||||
this["MainWindowIsMaximized"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("1024")]
|
||||
public double MainWindowRestoreWidth {
|
||||
get {
|
||||
return ((double)(this["MainWindowRestoreWidth"]));
|
||||
}
|
||||
set {
|
||||
this["MainWindowRestoreWidth"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("768")]
|
||||
public double MainWindowRestoreHeight {
|
||||
get {
|
||||
return ((double)(this["MainWindowRestoreHeight"]));
|
||||
}
|
||||
set {
|
||||
this["MainWindowRestoreHeight"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("0")]
|
||||
public double MainWindowRestoreTop {
|
||||
get {
|
||||
return ((double)(this["MainWindowRestoreTop"]));
|
||||
}
|
||||
set {
|
||||
this["MainWindowRestoreTop"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("0")]
|
||||
public double MainWindowRestoreLeft {
|
||||
get {
|
||||
return ((double)(this["MainWindowRestoreLeft"]));
|
||||
}
|
||||
set {
|
||||
this["MainWindowRestoreLeft"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("Source Han Sans SC VF")]
|
||||
public string FontFamily {
|
||||
get {
|
||||
return ((string)(this["FontFamily"]));
|
||||
}
|
||||
set {
|
||||
this["FontFamily"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("400")]
|
||||
public int FontWeight {
|
||||
get {
|
||||
return ((int)(this["FontWeight"]));
|
||||
}
|
||||
set {
|
||||
this["FontWeight"] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
39
Livia/Properties/ViewSettings.settings
Normal file
|
@ -0,0 +1,39 @@
|
|||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)" GeneratedClassNamespace="Livia.Properties" GeneratedClassName="ViewSettings">
|
||||
<Profiles />
|
||||
<Settings>
|
||||
<Setting Name="MainWindowWidth" Type="System.Double" Scope="User">
|
||||
<Value Profile="(Default)">1024</Value>
|
||||
</Setting>
|
||||
<Setting Name="MainWindowHeight" Type="System.Double" Scope="User">
|
||||
<Value Profile="(Default)">768</Value>
|
||||
</Setting>
|
||||
<Setting Name="MainWindowTop" Type="System.Double" Scope="User">
|
||||
<Value Profile="(Default)">0</Value>
|
||||
</Setting>
|
||||
<Setting Name="MainWindowLeft" Type="System.Double" Scope="User">
|
||||
<Value Profile="(Default)">0</Value>
|
||||
</Setting>
|
||||
<Setting Name="MainWindowIsMaximized" Type="System.Boolean" Scope="User">
|
||||
<Value Profile="(Default)">False</Value>
|
||||
</Setting>
|
||||
<Setting Name="MainWindowRestoreWidth" Type="System.Double" Scope="User">
|
||||
<Value Profile="(Default)">1024</Value>
|
||||
</Setting>
|
||||
<Setting Name="MainWindowRestoreHeight" Type="System.Double" Scope="User">
|
||||
<Value Profile="(Default)">768</Value>
|
||||
</Setting>
|
||||
<Setting Name="MainWindowRestoreTop" Type="System.Double" Scope="User">
|
||||
<Value Profile="(Default)">0</Value>
|
||||
</Setting>
|
||||
<Setting Name="MainWindowRestoreLeft" Type="System.Double" Scope="User">
|
||||
<Value Profile="(Default)">0</Value>
|
||||
</Setting>
|
||||
<Setting Name="FontFamily" Type="System.String" Scope="User">
|
||||
<Value Profile="(Default)">Source Han Sans SC VF</Value>
|
||||
</Setting>
|
||||
<Setting Name="FontWeight" Type="System.Int32" Scope="User">
|
||||
<Value Profile="(Default)">400</Value>
|
||||
</Setting>
|
||||
</Settings>
|
||||
</SettingsFile>
|
BIN
Livia/Resources/Fonts/SourceHanSans-VF.otf.ttc
Normal file
BIN
Livia/Resources/Images/an-image.png
Normal file
After Width: | Height: | Size: 245 KiB |
BIN
Livia/Resources/Images/black-white.png
Normal file
After Width: | Height: | Size: 86 KiB |
BIN
Livia/Resources/Images/login_background.png
Normal file
After Width: | Height: | Size: 5.4 MiB |
BIN
Livia/Resources/Images/orange-green.png
Normal file
After Width: | Height: | Size: 121 KiB |
438
Livia/Resources/Strings/Licenses.xaml
Normal file
|
@ -0,0 +1,438 @@
|
|||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:system="clr-namespace:System;assembly=mscorlib">
|
||||
<system:String x:Key="SoftwareVersionInfo"
|
||||
xml:space="preserve">软件名称:医学图像处理软件
|
||||
规格型号:aStroke
|
||||
发布版本:V1
|
||||
完整版本:V1.0.0.0</system:String>
|
||||
|
||||
<system:String x:Key="ManufacturerInformation"
|
||||
xml:space="preserve">注册人/企业名称: 安炬科技(北京)有限公司
|
||||
生产地址: 北京市大兴区宝参南街16号院3号楼(3A)201单元211室
|
||||
售后服务单位: 安炬科技(北京)有限公司
|
||||
联系方式: 010-69465675</system:String>
|
||||
|
||||
<system:String x:Key="AnImageLicense"
|
||||
xml:space="preserve">请认真阅读和理解本《软件安装许可协议》(以下简称《协议》)中规定的所有权利和限制。除非您接受本《协议》条款,否则您无权安装或使用本"软件"及其相关服务。
|
||||
您一旦安装、复制、访问或以其它方式使用本软件产品,将视为对本《协议》的接受,即表示您同意接受本《协议》各项条款的约束。如果您不同意本《协议》中的条款,请不要安装、复制或使用本软件。
|
||||
一、如果您通过合法渠道从安影科技(北京)有限公司取得本软件的任何拷贝,则您拥有如下权利:
|
||||
1、使用权
|
||||
您有权在计算机上安装本软件,并在业务范围内使用本软件,再现本软件具有的全部功能。
|
||||
2、备份
|
||||
出于存档的目的,您有权复制一份本软件作为备份件保存, 您只可以用它来重新安装该软件。
|
||||
3、技术支持
|
||||
您有权获得安影科技(北京)有限公司的全面的技术支持。对于本软件在安装使用过程中产生的任何疑问、问题,您均有权向安影科技提出并要求安影科技给予解答或解决。
|
||||
4、版本升级
|
||||
根据实际需要,您有权获得本软件的各个升级版本的使用权。但如果安影科技(北京)有限公司另有要求,您须支付适当的版本升级费用。
|
||||
二、权利限制
|
||||
1、禁止反向工程、反向编译和反向汇编
|
||||
您对本软件的使用权仅限于再现本软件本身具有的功能,您无权擅自修改本软件,也无权对本软件进行反向工程、反编译或反汇编,也无权在本软件的基础上进行二次开发,衍生新的软件。如果出于适用性或其它原因需要修改本软件,应书面向安影科技(北京)有限公司提出,由安影科技(北京)有限公司进行修改。
|
||||
2、组件分割
|
||||
本软件产品是作为一个单一产品而被授予许可使用, 用户不得将各个部分分开用于任何目的。
|
||||
3、使用
|
||||
对本软件的使用权范围仅限于您单位本身。未经安影科技(北京)有限公司书面允许,您无权以转让、许可、出租、租赁、出借、赠与或其它任何方式向任何第三方提供本软件或其复制件或其中任何部分,无论这种提供是否出于商业目的。
|
||||
4、声明
|
||||
对于本软件,安影科技(北京)有限公司保证不含有任何恶意破坏您的计算机资源(包括文档、程序和其他数据)的功能设置。对于您在使用此软件过程中而产生的利润损失、可用性消失、商业中断,或任何形式的间接、特别、意外或必然的破坏,或任何其他方的索赔,安影科技(北京)有限公司及其代理、销售人概不负责,即使安影科技(北京)有限公司已事先被告知此类事有可能发生。
|
||||
5、知识产权和保密
|
||||
本”软件”的一切知识产权,以及与"软件"相关的所有信息内容,包括但不限于:文字表述及其组合、图标、图饰、图像、图表、色彩、界面设计、版面框架、有关数据、附加程序、印刷材料或电子文档等均为本公司所有,受著作权法和国际著作权条约以及其他知识产权法律法规的保护。对于涉及安影科技(北京)有限公司技术秘密的内容,您负有保密的义务。
|
||||
6、保留权利
|
||||
本协议未明示授权的其他权利仍归本公司所有, 用户使用其他权利时必须获得本公司的书面同意。
|
||||
三、其他
|
||||
1、您有义务采取有效措施,约束您的职员或员工遵守上述条款二(权利限制)等的有关约定。
|
||||
2、本软件许可协议的未尽事宜,由双方其它协议或另行协商约定。本软件许可协议适用中华人民共和国有关法律。以上资料只用于建立我们的客户服务系统,以使我们能够更好的为您提供技术支持和售后服务。
|
||||
3、本协议的一切解释权与修改权归安影科技(北京)有限公司所有。</system:String>
|
||||
<system:String x:Key="ClowdSquirrelLicense"
|
||||
xml:space="preserve">Copyright (c) 2012 GitHub, Inc.
|
||||
|
||||
All rights reserved.
|
||||
|
||||
MIT License (MIT)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.</system:String>
|
||||
|
||||
<system:String x:Key="DotNetLicense"
|
||||
xml:space="preserve">The MIT License (MIT)
|
||||
|
||||
Copyright (c) .NET Foundation and Contributors
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.</system:String>
|
||||
|
||||
<system:String x:Key="WindowsCommunityToolkitLicense"
|
||||
xml:space="preserve">Copyright © .NET Foundation and Contributors
|
||||
|
||||
All rights reserved.
|
||||
|
||||
MIT License (MIT)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.</system:String>
|
||||
|
||||
<system:String x:Key="MaterialDesignInXamlToolkitLicense"
|
||||
xml:space="preserve">The MIT License (MIT)
|
||||
|
||||
Copyright (c) James Willock, Mulholland Software and Contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.</system:String>
|
||||
|
||||
<system:String x:Key="ApacheV2License"
|
||||
xml:space="preserve">Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.</system:String>
|
||||
|
||||
<system:String x:Key="BSD 3-Clause"
|
||||
xml:space="preserve">BSD 3-Clause License
|
||||
|
||||
Copyright (c) C. Augusto Proiete 2018-2021
|
||||
Copyright (c) Sven Groot 2009-2018
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.</system:String>
|
||||
|
||||
<system:String x:Key="fo-dicomLicense"
|
||||
xml:space="preserve">Fellow Oak DICOM
|
||||
|
||||
Copyright (c) 2012-2021 fo-dicom contributors
|
||||
|
||||
This software is licensed under the Microsoft Public License (MS-PL)
|
||||
|
||||
Microsoft Public License (MS-PL)
|
||||
|
||||
This license governs use of the accompanying software. If you use the software, you accept this license. If you do not accept the license, do not use the software.
|
||||
|
||||
1. Definitions
|
||||
The terms "reproduce," "reproduction," "derivative works," and "distribution" have the same meaning here as under U.S. copyright law.
|
||||
A "contribution" is the original software, or any additions or changes to the software.
|
||||
A "contributor" is any person that distributes its contribution under this license.
|
||||
"Licensed patents" are a contributor's patent claims that read directly on its contribution.
|
||||
|
||||
2. Grant of Rights
|
||||
(A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create.
|
||||
(B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software.
|
||||
|
||||
3. Conditions and Limitations
|
||||
(A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks.
|
||||
(B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, your patent license from such contributor to the software ends automatically.
|
||||
(C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution notices that are present in the software.
|
||||
(D) If you distribute any portion of the software in source code form, you may do so only under this license by including a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object code form, you may only do so under a license that complies with this license.
|
||||
(E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular purpose and non-infringement.
|
||||
|
||||
|
||||
|
||||
---- libijg (from DCMTK 3.5.4 COPYRIGHT) ----
|
||||
|
||||
Unless otherwise specified, the DCMTK software package has the following copyright:
|
||||
|
||||
/*
|
||||
* Copyright (C) 1994-2004, OFFIS
|
||||
*
|
||||
* This software and supporting documentation were developed by
|
||||
*
|
||||
* Kuratorium OFFIS e.V.
|
||||
* Healthcare Information and Communication Systems
|
||||
* Escherweg 2
|
||||
* D-26121 Oldenburg, Germany
|
||||
*
|
||||
* THIS SOFTWARE IS MADE AVAILABLE, AS IS, AND OFFIS MAKES NO WARRANTY
|
||||
* REGARDING THE SOFTWARE, ITS PERFORMANCE, ITS MERCHANTABILITY OR
|
||||
* FITNESS FOR ANY PARTICULAR USE, FREEDOM FROM ANY COMPUTER DISEASES OR
|
||||
* ITS CONFORMITY TO ANY SPECIFICATION. THE ENTIRE RISK AS TO QUALITY AND
|
||||
* PERFORMANCE OF THE SOFTWARE IS WITH THE USER.
|
||||
*
|
||||
* Copyright of the software and supporting documentation is, unless
|
||||
* otherwise stated, owned by OFFIS, and free access is hereby granted as
|
||||
* a license to use this software, copy this software and prepare
|
||||
* derivative works based upon this software. However, any distribution
|
||||
* of this software source code or supporting documentation or derivative
|
||||
* works (source code and supporting documentation) must include the
|
||||
* three paragraphs of this copyright notice.
|
||||
*
|
||||
*/
|
||||
|
||||
The dcmjpeg sub-package includes an adapted version of the Independent JPEG
|
||||
Group Toolkit Version 6b, which is contained in dcmjpeg/libijg8,
|
||||
dcmjpeg/libijg12 and dcmjpeg/libijg16. This toolkit is covered by the
|
||||
following copyright. The original README file for the Independent JPEG
|
||||
Group Toolkit is located in dcmjpeg/docs/ijg_readme.txt.
|
||||
|
||||
/*
|
||||
* The authors make NO WARRANTY or representation, either express or implied,
|
||||
* with respect to this software, its quality, accuracy, merchantability, or
|
||||
* fitness for a particular purpose. This software is provided "AS IS", and you,
|
||||
* its user, assume the entire risk as to its quality and accuracy.
|
||||
*
|
||||
* This software is copyright (C) 1991-1998, Thomas G. Lane.
|
||||
* All Rights Reserved except as specified below.
|
||||
*
|
||||
* Permission is hereby granted to use, copy, modify, and distribute this
|
||||
* software (or portions thereof) for any purpose, without fee, subject to these
|
||||
* conditions:
|
||||
* (1) If any part of the source code for this software is distributed, then this
|
||||
* README file must be included, with this copyright and no-warranty notice
|
||||
* unaltered; and any additions, deletions, or changes to the original files
|
||||
* must be clearly indicated in accompanying documentation.
|
||||
* (2) If only executable code is distributed, then the accompanying
|
||||
* documentation must state that "this software is based in part on the work of
|
||||
* the Independent JPEG Group".
|
||||
* (3) Permission for use of this software is granted only if the user accepts
|
||||
* full responsibility for any undesirable consequences; the authors accept
|
||||
* NO LIABILITY for damages of any kind.
|
||||
*
|
||||
* These conditions apply to any software derived from or based on the IJG code,
|
||||
* not just to the unmodified library. If you use our work, you ought to
|
||||
* acknowledge us.
|
||||
*
|
||||
* Permission is NOT granted for the use of any IJG author's name or company name
|
||||
* in advertising or publicity relating to this software or products derived from
|
||||
* it. This software may be referred to only as "the Independent JPEG Group's
|
||||
* software".
|
||||
*
|
||||
* We specifically permit and encourage the use of this software as the basis of
|
||||
* commercial products, provided that all warranty or liability claims are
|
||||
* assumed by the product vendor.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
---- OpenJPEG JPEG 2000 codec (from license.txt) ----
|
||||
|
||||
/*
|
||||
* Copyright (c) 2002-2007, Communications and Remote Sensing Laboratory, Universite catholique de Louvain (UCL), Belgium
|
||||
* Copyright (c) 2002-2007, Professor Benoit Macq
|
||||
* Copyright (c) 2001-2003, David Janssens
|
||||
* Copyright (c) 2002-2003, Yannick Verschueren
|
||||
* Copyright (c) 2003-2007, Francois-Olivier Devaux and Antonin Descampe
|
||||
* Copyright (c) 2005, Herve Drolon, FreeImage Team
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS'
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
---- CharLS JPEG-LS codec (from License.txt) ----
|
||||
|
||||
Copyright (c) 2007-2009, Jan de Vaan
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of my employer, nor the names of its contributors may be
|
||||
used to endorse or promote products derived from this software without
|
||||
specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
|
||||
---- Unity.IO.Compression (from LICENSE.TXT and PATENTS.TXT) ----
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) Microsoft Corporation
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
Microsoft Patent Promise for .NET Libraries and Runtime Components
|
||||
|
||||
Microsoft Corporation and its affiliates ("Microsoft") promise not to assert
|
||||
any .NET Patents against you for making, using, selling, offering for sale,
|
||||
importing, or distributing Covered Code, as part of either a .NET Runtime or
|
||||
as part of any application designed to run on a .NET Runtime.
|
||||
|
||||
If you file, maintain, or voluntarily participate in any claim in a lawsuit
|
||||
alleging direct or contributory patent infringement by any Covered Code, or
|
||||
inducement of patent infringement by any Covered Code, then your rights under
|
||||
this promise will automatically terminate.
|
||||
|
||||
This promise is not an assurance that (i) any .NET Patents are valid or
|
||||
enforceable, or (ii) Covered Code does not infringe patents or other
|
||||
intellectual property rights of any third party. No rights except those
|
||||
expressly stated in this promise are granted, waived, or received by
|
||||
Microsoft, whether by implication, exhaustion, estoppel, or otherwise.
|
||||
This is a personal promise directly from Microsoft to you, and you agree as a
|
||||
condition of benefiting from it that no Microsoft rights are received from
|
||||
suppliers, distributors, or otherwise from any other person in connection with
|
||||
this promise.
|
||||
|
||||
Definitions:
|
||||
|
||||
"Covered Code" means those Microsoft .NET libraries and runtime components as
|
||||
made available by Microsoft at https://github.com/Microsoft/referencesource.
|
||||
|
||||
".NET Patents" are those patent claims, both currently owned by Microsoft and
|
||||
acquired in the future, that are necessarily infringed by Covered Code. .NET
|
||||
Patents do not include any patent claims that are infringed by any Enabling
|
||||
Technology, that are infringed only as a consequence of modification of
|
||||
Covered Code, or that are infringed only by the combination of Covered Code
|
||||
with third party code.
|
||||
|
||||
".NET Runtime" means any compliant implementation in software of (a) all of
|
||||
the required parts of the mandatory provisions of Standard ECMA-335 – Common
|
||||
Language Infrastructure (CLI); and (b) if implemented, any additional
|
||||
functionality in Microsoft's .NET Framework, as described in Microsoft's API
|
||||
documentation on its MSDN website. For example, .NET Runtimes include
|
||||
Microsoft's .NET Framework and those portions of the Mono Project compliant
|
||||
with (a) and (b).
|
||||
|
||||
"Enabling Technology" means underlying or enabling technology that may be
|
||||
used, combined, or distributed in connection with Microsoft's .NET Framework
|
||||
or other .NET Runtimes, such as hardware, operating systems, and applications
|
||||
that run on .NET Framework or other .NET Runtimes.
|
||||
|
||||
|
||||
|
||||
---- Nito.AsyncEx (from LICENSE.TXT) ----
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 StephenCleary
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
</system:String>
|
||||
<system:String x:Key="SourceHanSansLicense"
|
||||
xml:space="preserve">Copyright 2014-2021 Adobe (http://www.adobe.com/), with Reserved Font Name 'Source'. Source is a trademark of Adobe in the United States and/or other countries.
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
http://scripts.sil.org/OFL
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide development of collaborative font projects, to support the font creation efforts of academic and linguistic communities, and to provide a free and open framework in which fonts may be shared and improved in partnership with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and redistributed freely as long as they are not sold by themselves. The fonts, including any derivative works, can be bundled, embedded, redistributed and/or sold with any software provided that any reserved names are not used by derivative works. The fonts and derivatives, however, cannot be released under any other type of license. The requirement for fonts to remain under this license does not apply to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright Holder(s) under this license and clearly marked as such. This may include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting, or substituting -- in part or in whole -- any of the components of the Original Version, by changing formats or by porting the Font Software to a new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of the Font Software, to use, study, copy, merge, embed, modify, redistribute, and sell modified and unmodified copies of the Font Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components, in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled, redistributed and/or sold with any software, provided that each copy contains the above copyright notice and this license. These can be included either as stand-alone text files, human-readable headers or in the appropriate machine-readable metadata fields within text or binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font Name(s) unless explicit written permission is granted by the corresponding Copyright Holder. This restriction only applies to the primary font name as presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font Software shall not be used to promote, endorse or advertise any Modified Version, except to acknowledge the contribution(s) of the Copyright Holder(s) and the Author(s) or with their explicit written permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole, must be distributed entirely under this license, and must not be distributed under any other license. The requirement for fonts to remain under this license does not apply to any document created using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE.</system:String>
|
||||
|
||||
|
||||
</ResourceDictionary>
|
516
Livia/Resources/Strings/Strings.xaml
Normal file
|
@ -0,0 +1,516 @@
|
|||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:system="clr-namespace:System;assembly=System.Runtime">
|
||||
<system:String x:Key="InstanceIsRunningError">程序已在运行</system:String>
|
||||
<system:String x:Key="Error">错误</system:String>
|
||||
<system:String x:Key="Warning">警告</system:String>
|
||||
<system:String x:Key="Info">提示</system:String>
|
||||
<system:String x:Key="Confirm">确认</system:String>
|
||||
<system:String x:Key="Cancel">取消</system:String>
|
||||
<system:String x:Key="Clear">清空</system:String>
|
||||
<system:String x:Key="Processing">正在处理</system:String>
|
||||
<system:String x:Key="Importing">正在处理</system:String>
|
||||
<system:String x:Key="DataProcessComplete">处理成功</system:String>
|
||||
<system:String x:Key="DataImportComplete">处理成功</system:String>
|
||||
<system:String x:Key="DeleteSuccess">删除成功</system:String>
|
||||
<system:String x:Key="SystemCrashing">系统错误,软件即将关闭。</system:String>
|
||||
<system:String x:Key="Updating">正在升级</system:String>
|
||||
<system:String x:Key="InvalidArgumentError">无效的参数:{0}</system:String>
|
||||
<system:String x:Key="ParsingArgumentError">解析参数错误:{0}</system:String>
|
||||
<system:String x:Key="ArgumentNotFoundError">未包含必需参数:{0}</system:String>
|
||||
<system:String x:Key="Version">版本</system:String>
|
||||
|
||||
<system:String x:Key="InvalidInputDirectoryError">无效的输入目录</system:String>
|
||||
<system:String x:Key="IdleTimeout">长时间没有操作,已自动登出。</system:String>
|
||||
<system:String x:Key="IdleTimeoutWarning">长时间没有操作,将于大约{0}秒后登出。</system:String>
|
||||
|
||||
<system:String x:Key="ConfirmLogoutWarning">确认登出?</system:String>
|
||||
<system:String x:Key="ConfirmExitWarning">确认退出?</system:String>
|
||||
<system:String x:Key="ConfirmCancelWarning">确认取消?</system:String>
|
||||
<system:String x:Key="ConfirmDeleteWarning">确认删除数据?</system:String>
|
||||
|
||||
<system:String x:Key="DefaultWindowLevelButtonTest">默认窗宽窗位</system:String>
|
||||
|
||||
<system:String x:Key="BrainLobesRotationViewerName">脑叶灌注</system:String>
|
||||
<system:String x:Key="ArterialTerritoriesRotationViewerName">动脉供血区域灌注</system:String>
|
||||
<system:String x:Key="AspectsRotationViewerName">ASPECTS区域灌注</system:String>
|
||||
<system:String x:Key="LimbicSystemRotationViewerName">边缘系统区域灌注</system:String>
|
||||
|
||||
<system:String x:Key="CerebrumCerebellumPerfusionDataGridName">大脑小脑灌注</system:String>
|
||||
<system:String x:Key="BrainLobesPerfusionDataGridName">脑叶灌注</system:String>
|
||||
<system:String x:Key="ArterialTerritoriesPerfusionDataGridName">动脉供血区域灌注</system:String>
|
||||
<system:String x:Key="AspectsPerfusionDataGridName">ASPECTS区域灌注</system:String>
|
||||
<system:String x:Key="LimbicSystemPerfusionDataGridName">边缘系统区域灌注</system:String>
|
||||
<system:String x:Key="PerfusionDataGridSwitchButtonText">参数切换</system:String>
|
||||
<system:String x:Key="PerfusionDataGridTabName">灌注数据</system:String>
|
||||
<system:String x:Key="AslLabelTip">切换右侧标签,此按钮将同步更新</system:String>
|
||||
<system:String x:Key="JumpPerfusionDataTip">查看详细表格数据</system:String>
|
||||
|
||||
<system:String x:Key="LoadDataButtonText">读取文件</system:String>
|
||||
<system:String x:Key="ExportReportButtonText">生成报告</system:String>
|
||||
<system:String x:Key="ImportDataButtonText">导入结果</system:String>
|
||||
<system:String x:Key="ExportDataButtonText">导出结果</system:String>
|
||||
<system:String x:Key="PushToPacsButtonText">推送报告</system:String>
|
||||
<system:String x:Key="CheckReportsButtonText">查看报告</system:String>
|
||||
<system:String x:Key="RemainingQuota">待处理次数:</system:String>
|
||||
<system:String x:Key="UnlimitedQuota">无限制</system:String>
|
||||
<system:String x:Key="FolderNotEmptyError">请选择一个空白文件夹</system:String>
|
||||
|
||||
<system:String x:Key="SearchButtonText">数据查询</system:String>
|
||||
<system:String x:Key="SearchConfirmButtonText">查询</system:String>
|
||||
<system:String x:Key="JobListButtonText">历史记录</system:String>
|
||||
<system:String x:Key="JobListLoadDataButtonText">查看结果</system:String>
|
||||
<system:String x:Key="JobListDownloadArchiveButtonText">原始数据</system:String>
|
||||
<system:String x:Key="JobListDownloadSortedButtonText">中间文件</system:String>
|
||||
<system:String x:Key="JobListCancelButtonText">取消处理</system:String>
|
||||
<system:String x:Key="JobListSelectSeriesButtonText">选择序列</system:String>
|
||||
<system:String x:Key="JobListDeleteSeriesButtonText">删除数据</system:String>
|
||||
<system:String x:Key="PatientName">姓名</system:String>
|
||||
<system:String x:Key="PatientId">ID</system:String>
|
||||
<system:String x:Key="PatientAge">年龄</system:String>
|
||||
<system:String x:Key="PatientSex">性别</system:String>
|
||||
<system:String x:Key="StudyDate">检查时间</system:String>
|
||||
<system:String x:Key="ImportTime">处理时间</system:String>
|
||||
<system:String x:Key="Status">状态</system:String>
|
||||
<system:String x:Key="Sequence">包含序列</system:String>
|
||||
<system:String x:Key="Operation">操作</system:String>
|
||||
<system:String x:Key="ServerJobStatus1">正在导入</system:String>
|
||||
<system:String x:Key="ServerJobStatus2">正在清洗</system:String>
|
||||
<system:String x:Key="ServerJobStatus3">正在处理</system:String>
|
||||
<system:String x:Key="ServerJobStatus4">处理成功</system:String>
|
||||
<system:String x:Key="ServerJobStatus5">处理失败</system:String>
|
||||
<system:String x:Key="ServerJobStatus6">等待设置</system:String>
|
||||
<system:String x:Key="ServerJobStatus7">排队等待</system:String>
|
||||
|
||||
<system:String x:Key="SearchTipsText">%代表任意0 ~ 多个字符,例如查询animage可以使用an%、%ima%或%ge</system:String>
|
||||
<system:String x:Key="PatientAgeStart">年龄起</system:String>
|
||||
<system:String x:Key="PatientAgeEnd">年龄止</system:String>
|
||||
<system:String x:Key="ImportTimeStart">处理时间起</system:String>
|
||||
<system:String x:Key="ImportTimeEnd">处理时间止</system:String>
|
||||
<system:String x:Key="StudyDateStart">检查时间起</system:String>
|
||||
<system:String x:Key="StudyDateEnd">检查时间止</system:String>
|
||||
|
||||
<system:String x:Key="SelectSeriesWindowTitle">选择需要处理的序列</system:String>
|
||||
<system:String x:Key="SelectSeriesSuccess">序列选择成功,开始处理!</system:String>
|
||||
|
||||
<system:String x:Key="CheckReportWindowTitle">查看报告</system:String>
|
||||
<system:String x:Key="SelectReportModuleWindowTitle">生成报告</system:String>
|
||||
<system:String x:Key="ReportName">报告名称</system:String>
|
||||
<system:String x:Key="DefaultReportName">报告1</system:String>
|
||||
<system:String x:Key="ReportNameHint">建议修改默认名称</system:String>
|
||||
<system:String x:Key="ShowRoiLocation">显示定位信息</system:String>
|
||||
<system:String x:Key="AnonymousMode">匿名模式</system:String>
|
||||
<system:String x:Key="DeleteReportButtonText">删除报告</system:String>
|
||||
<system:String x:Key="PushReportButtonText">推送报告</system:String>
|
||||
<system:String x:Key="DownloadReportButtonText">下载报告</system:String>
|
||||
<system:String x:Key="ReportNameWarning">报告名称需要包含1-20个字符,且不能含有 \ / : * ? " < > |</system:String>
|
||||
<system:String x:Key="ReportModuleAal">详细解剖区域</system:String>
|
||||
<system:String x:Key="ReportModuleAtlas">灌注图谱</system:String>
|
||||
<system:String x:Key="ReportModuleColorImage">彩图</system:String>
|
||||
<system:String x:Key="ReportModuleFocus">病灶叠加图</system:String>
|
||||
<system:String x:Key="ReportModuleLeftAnteriorRoi">左侧前循环病灶</system:String>
|
||||
<system:String x:Key="ReportModuleRightAnteriorRoi">右侧前循环病灶</system:String>
|
||||
<system:String x:Key="ReportModulePosteriorRoi">后循环病灶</system:String>
|
||||
<system:String x:Key="DownloadReportSuccess">报告下载成功!</system:String>
|
||||
|
||||
<system:String x:Key="ValidateSerialKeyWindowTitle">授权验证</system:String>
|
||||
<system:String x:Key="SerialKey">序列号</system:String>
|
||||
<system:String x:Key="AuthenticationKey">授权码</system:String>
|
||||
<system:String x:Key="ValidateSerialKeyFailed">验证失败</system:String>
|
||||
|
||||
<system:String x:Key="Score">评分:</system:String>
|
||||
<system:String x:Key="NoRoiLocationDataMessage">没有有效的定位信息</system:String>
|
||||
|
||||
<system:String x:Key="Left">左侧</system:String>
|
||||
<system:String x:Key="Right">右侧</system:String>
|
||||
<system:String x:Key="BothSides">双侧</system:String>
|
||||
|
||||
<system:String x:Key="AxialOverview">轴状位一览</system:String>
|
||||
|
||||
<system:String x:Key="TotalVolume">总体积:</system:String>
|
||||
<system:String x:Key="SelectedVolume">已选体积:</system:String>
|
||||
<system:String x:Key="Count">数量:</system:String>
|
||||
<system:String x:Key="RoiLocationDisplayModeVolume">病灶区域体积</system:String>
|
||||
<system:String x:Key="RoiLocationDisplayModeRoiPercentage">占病灶百分比</system:String>
|
||||
<system:String x:Key="RoiLocationDisplayModeRegionPercentage">占脑区百分比</system:String>
|
||||
<system:String x:Key="RoiTwoColumnModeTrue">左右分开显示</system:String>
|
||||
<system:String x:Key="RoiTwoColumnModeFalse">左右合并显示</system:String>
|
||||
<system:String x:Key="UnfoldButtonStateTrue">全部展开</system:String>
|
||||
<system:String x:Key="UnfoldButtonStateFalse">全部合并</system:String>
|
||||
|
||||
<system:String x:Key="IspOverIsc">IP/IC</system:String>
|
||||
<system:String x:Key="IspOverSumOfIscAndIsp">IP/(IP + IC)</system:String>
|
||||
<system:String x:Key="AstrokeDiagnosis1">IP区/IC区 > 1.8</system:String>
|
||||
<system:String x:Key="AstrokeDiagnosis2">建议溶栓</system:String>
|
||||
|
||||
<system:String x:Key="AspectsCbfIscScore">CBF评分</system:String>
|
||||
<system:String x:Key="AspectsAdcIscScore">ADC评分</system:String>
|
||||
|
||||
<system:String x:Key="Left/Right">左/右</system:String>
|
||||
<system:String x:Key="Region">区域</system:String>
|
||||
<system:String x:Key="AbsoluteCbf">CBF值</system:String>
|
||||
<system:String x:Key="RelativeToCerebellum">相对同侧小脑差异</system:String>
|
||||
<system:String x:Key="RelativeToCerebrum">相对同侧大脑差异</system:String>
|
||||
<system:String x:Key="LeftRightDifference">左右差异</system:String>
|
||||
<system:String x:Key="UnableToLocateRegionWarning">未找到区域</system:String>
|
||||
<system:String x:Key="LocateRoiButtonText">定位区域</system:String>
|
||||
<system:String x:Key="ShowBoundaryButtonText">显示轮廓</system:String>
|
||||
|
||||
<system:String x:Key="ProcessingError">处理失败</system:String>
|
||||
<system:String x:Key="ExportFailed">导出失败</system:String>
|
||||
<system:String x:Key="ServerUnauthorizedError">请重新登录</system:String>
|
||||
<system:String x:Key="InvalidDataError">数据错误</system:String>
|
||||
<system:String x:Key="ApplicationDidNotQuitNormallyWarning">上一次程序未正常退出!</system:String>
|
||||
|
||||
<system:String x:Key="PasswordBoxHint">请输入密码</system:String>
|
||||
<system:String x:Key="UsernameTextBlockHint">请输入用户名</system:String>
|
||||
<system:String x:Key="WindowTitle">Livia</system:String>
|
||||
<system:String x:Key="LoginControlTitle">用户登录</system:String>
|
||||
<system:String x:Key="LoginControlMessage">欢迎使用本软件</system:String>
|
||||
<system:String x:Key="LoginControlMessage2">.</system:String>
|
||||
<system:String x:Key="LoginControlFootnote">科技部十三五国家重点研发计划(课题编号:2019YFC0120901)</system:String>
|
||||
<system:String x:Key="LoginButtonText">登录</system:String>
|
||||
<system:String x:Key="UserName">用户名</system:String>
|
||||
<system:String x:Key="Password">密码</system:String>
|
||||
<system:String x:Key="Logout">退出登录</system:String>
|
||||
|
||||
<system:String x:Key="CheckUpdateButtonText">检查更新</system:String>
|
||||
<system:String x:Key="CannotGetUpdateInfoMessage">无法检查更新,请确保计算机网络连接正常。</system:String>
|
||||
<system:String x:Key="NewVersionAvailableInfoMessage">有新版本,是否立刻更新?</system:String>
|
||||
<system:String x:Key="NoNewVersionInfoMessage">当前已是最新版本</system:String>
|
||||
<system:String x:Key="RestartNeededInfoMessage">程序已更新,请重新打开软件。</system:String>
|
||||
<system:String x:Key="InstallUpdateButtonText">安装更新</system:String>
|
||||
|
||||
<system:String x:Key="IncorrectPasswordError">账号或密码错误!</system:String>
|
||||
<system:String x:Key="SocketExceptionError">无法连接服务器!</system:String>
|
||||
<system:String x:Key="LoginError">登录失败!</system:String>
|
||||
|
||||
<system:String x:Key="SettingsWindowName">设置中心</system:String>
|
||||
<system:String x:Key="SoftwareVersionInfoTabName">软件信息</system:String>
|
||||
<system:String x:Key="GeneralTabName">通用</system:String>
|
||||
<system:String x:Key="ManufacturerInformationTabName">制造商信息</system:String>
|
||||
<system:String x:Key="LicenseTabName">软件安装许可协议</system:String>
|
||||
<system:String x:Key="OpenSourceLibraryLicenseTabName">开源库协议</system:String>
|
||||
<system:String x:Key="ReleaseNotesTabName">更新说明</system:String>
|
||||
|
||||
<system:String x:Key="DeveloperSettings">管理员设置</system:String>
|
||||
<system:String x:Key="RestartIsRequireForChangesToTakeEffect">设置在重启软件后生效</system:String>
|
||||
<system:String x:Key="ServerAddress">服务器地址</system:String>
|
||||
<system:String x:Key="ServerPort">服务器端口</system:String>
|
||||
<system:String x:Key="UserLocalhostButtonText">使用本机</system:String>
|
||||
<system:String x:Key="UserCloudServerButtonText">使用云服务器</system:String>
|
||||
<system:String x:Key="UserTestServerButtonText">使用测试服务器</system:String>
|
||||
<system:String x:Key="UseRegistrationVersion">使用注册版本</system:String>
|
||||
<system:String x:Key="LogoutAfter">空闲登出时间(分钟,0不自动登出)</system:String>
|
||||
<system:String x:Key="ShowArchiveDownloadButton">显示下载原始数据按钮</system:String>
|
||||
<system:String x:Key="ShowPushToPacsButton">显示推送报告按钮</system:String>
|
||||
<system:String x:Key="AutoRefreshServerJobList">自动刷新历史记录</system:String>
|
||||
<system:String x:Key="ShowSelectReportModuleDialog">选择报告模块</system:String>
|
||||
<system:String x:Key="ShowDicomNodeTab">显示DICOM节点信息</system:String>
|
||||
<system:String x:Key="ShowReportModuleAalCheckbox">报告选择模块显示详细解剖区域选项</system:String>
|
||||
<system:String x:Key="ShowUpdateButton">允许检测更新</system:String>
|
||||
|
||||
<system:String x:Key="DisplaySettings">显示设置</system:String>
|
||||
<system:String x:Key="FontFamily">字体</system:String>
|
||||
<system:String x:Key="FontWeight">字重</system:String>
|
||||
|
||||
<system:String x:Key="Language">语言</system:String>
|
||||
<system:String x:Key="CultureInfo">日期格式</system:String>
|
||||
<system:String x:Key="UpdateChannel">软件渠道</system:String>
|
||||
|
||||
<system:String x:Key="SecurityTabName">安全</system:String>
|
||||
<system:String x:Key="EditPassword">修改密码</system:String>
|
||||
<system:String x:Key="PasswordRequirement">密码长度6至15位且须包含数字和字母</system:String>
|
||||
<system:String x:Key="OldPassword">旧密码</system:String>
|
||||
<system:String x:Key="ConfirmPassword">确认密码</system:String>
|
||||
<system:String x:Key="NewPassword">新密码</system:String>
|
||||
<system:String x:Key="ConfirmPasswordFailedError">两次输入的密码不一致!</system:String>
|
||||
<system:String x:Key="EditPasswordSuccess">修改成功!</system:String>
|
||||
|
||||
<system:String x:Key="DicomNodeTabName">DICOM节点信息</system:String>
|
||||
<system:String x:Key="DicomNodeHost">DICOM节点地址</system:String>
|
||||
<system:String x:Key="DicomNodePort">DICOM节点端口</system:String>
|
||||
<system:String x:Key="DicomNodeAet">DICOM节点AET</system:String>
|
||||
|
||||
<system:String x:Key="ReportSettings">报告设置</system:String>
|
||||
<system:String x:Key="OpenReportOnFinish">导出后打开报告</system:String>
|
||||
|
||||
<system:String x:Key="PushToPacsSuccess">推送成功!</system:String>
|
||||
|
||||
<system:String x:Key="OpenLogDirectoryButtonText">打开日志目录</system:String>
|
||||
<system:String x:Key="OpenTempDirectoryButtonText">打开临时目录</system:String>
|
||||
<system:String x:Key="ClearAuthenticationKeyButtonText">清除授权码</system:String>
|
||||
|
||||
<system:String x:Key="RoiAtlasNotFoundError">未发现所需要的勾画标签图</system:String>
|
||||
<system:String x:Key="PathNotFoundError">未找到文件夹</system:String>
|
||||
|
||||
<system:String x:Key="ServerErrorUnknown">未知服务器错误</system:String>
|
||||
<system:String x:Key="ServerError10001">PACS无法建立连接</system:String>
|
||||
<system:String x:Key="ServerError10002">SCP服务提供者返回错误码</system:String>
|
||||
<system:String x:Key="ServerError10003">SCP访问超时/异常</system:String>
|
||||
<system:String x:Key="ServerError10004">缺少必要的序列</system:String>
|
||||
<system:String x:Key="ServerError10005">加密锁次数不足</system:String>
|
||||
<system:String x:Key="ServerError10006">没有加密锁或加密锁异常</system:String>
|
||||
<system:String x:Key="ServerError10007">加密锁密码错误</system:String>
|
||||
<system:String x:Key="ServerError10008">图像分析运行时错误</system:String>
|
||||
<system:String x:Key="ServerError10009">输入的Key不存在</system:String>
|
||||
<system:String x:Key="ServerError10010">用户没有足够的次数</system:String>
|
||||
<system:String x:Key="ServerError10011">用户旧密码错误</system:String>
|
||||
<system:String x:Key="ServerError10012">新密码与旧密码相同</system:String>
|
||||
<system:String x:Key="ServerError10013">密码缺少字母或数字</system:String>
|
||||
<system:String x:Key="ServerError10014">密码长度应在6位与15位之间</system:String>
|
||||
<system:String x:Key="ServerError10015">存储服务器异常</system:String>
|
||||
<system:String x:Key="ServerError10016">DICOM文件不标准</system:String>
|
||||
<system:String x:Key="ServerError10017">未发现有DICOM序列</system:String>
|
||||
<system:String x:Key="ServerError10018">拒绝访问</system:String>
|
||||
<system:String x:Key="ServerError10019">用户未授权</system:String>
|
||||
<system:String x:Key="ServerError10020">资源不存在</system:String>
|
||||
<system:String x:Key="ServerError10021">密码已错误{0}次,请在{1}秒后重试</system:String>
|
||||
<system:String x:Key="ServerError10022">用户名或密码错误!</system:String>
|
||||
<system:String x:Key="ServerError10023">处理结果已经过期,资源被删除</system:String>
|
||||
<system:String x:Key="ServerError10024">处理的数据被终止</system:String>
|
||||
<system:String x:Key="ServerError10025">报告生成失败</system:String>
|
||||
<system:String x:Key="ServerError10026">任务发布失败</system:String>
|
||||
<system:String x:Key="ServerError10028">数据不存在</system:String>
|
||||
<system:String x:Key="ServerError10032">操作被拒绝</system:String>
|
||||
<system:String x:Key="ServerError10035">报告名称已存在,是否覆盖?</system:String>
|
||||
<system:String x:Key="ServerError10036">不支持的报告格式类型</system:String>
|
||||
<system:String x:Key="ServerError10037">数据存储未完成,请稍后重试</system:String>
|
||||
<system:String x:Key="ServerError10038">数据未授权</system:String>
|
||||
<system:String x:Key="ServerError20000">文件清洗异常</system:String>
|
||||
<system:String x:Key="ServerError20001">DICOM文件缺少标签</system:String>
|
||||
<system:String x:Key="ServerError20002">Siemens Mosaic图像缺少必要的CSA头部信息</system:String>
|
||||
<system:String x:Key="ServerError20003">不支持的文件类型</system:String>
|
||||
<system:String x:Key="ServerError20004">RGB DICOM文件</system:String>
|
||||
<system:String x:Key="ServerError20005">缺少必要的序列</system:String>
|
||||
<system:String x:Key="ServerError20006">DICOM 图像缺层</system:String>
|
||||
<system:String x:Key="ServerError20007">DICOM 图像层数过少</system:String>
|
||||
<system:String x:Key="ServerError20008">DICOM 图像空间不一致</system:String>
|
||||
<system:String x:Key="ServerError20009">DWI图像缺少弥散B值</system:String>
|
||||
<system:String x:Key="ServerError20010">DICOM 图像类型未明确</system:String>
|
||||
<system:String x:Key="ServerError20011">DICOM 图像缺少方位信息</system:String>
|
||||
<system:String x:Key="ServerError20012">未找到CBF或者M0序列</system:String>
|
||||
<system:String x:Key="ServerError20013">PLD长度与序列数量不相等</system:String>
|
||||
<system:String x:Key="ServerError20014">PLD长度与重复次数不相等</system:String>
|
||||
<system:String x:Key="ServerError20015">Control像与Label像数量不相等</system:String>
|
||||
<system:String x:Key="ServerError20016">图像序列不规范</system:String>
|
||||
<system:String x:Key="ServerError20017">上传的文件太多,一次只能处理一组序列</system:String>
|
||||
<system:String x:Key="ServerError20018">拥有相同模态的多组数据,请删除一组后重试</system:String>
|
||||
<system:String x:Key="ServerError20019">DWI序列需要包含两个不同的B值</system:String>
|
||||
<system:String x:Key="ServerError20020">不支持的磁场强度</system:String>
|
||||
<system:String x:Key="ServerError20021">ASL序列太多</system:String>
|
||||
<system:String x:Key="ServerError20022">ASL缺少图像</system:String>
|
||||
<system:String x:Key="ServerError20023">不支持的comments标签</system:String>
|
||||
<system:String x:Key="ServerError20024">配准失败</system:String>
|
||||
|
||||
<!-- ReSharper disable InconsistentNaming -->
|
||||
<!-- ReSharper disable MarkupTextTypo -->
|
||||
<!-- ReSharper disable IdentifierTypo -->
|
||||
<system:String x:Key="ServerString##ASPECTS_Caudate">尾状核</system:String>
|
||||
<system:String x:Key="ServerString##Internal_Capsule">内囊</system:String>
|
||||
<system:String x:Key="ServerString##ASPECTS_Lentiform_Nucleus">豆状核</system:String>
|
||||
<system:String x:Key="ServerString##ASPECTS_Insula">脑岛</system:String>
|
||||
<system:String x:Key="ServerString##M1">M1</system:String>
|
||||
<system:String x:Key="ServerString##M2">M2</system:String>
|
||||
<system:String x:Key="ServerString##M3">M3</system:String>
|
||||
<system:String x:Key="ServerString##M4">M4</system:String>
|
||||
<system:String x:Key="ServerString##M5">M5</system:String>
|
||||
<system:String x:Key="ServerString##M6">M6</system:String>
|
||||
<system:String x:Key="ServerString##Precentral">中央前回</system:String>
|
||||
<system:String x:Key="ServerString##Frontal_Sup_2">额上回</system:String>
|
||||
<system:String x:Key="ServerString##Frontal_Mid_2">额中回</system:String>
|
||||
<system:String x:Key="ServerString##Frontal_Inf_Oper">岛盖部额下回</system:String>
|
||||
<system:String x:Key="ServerString##Frontal_Inf_Tri">三角部额下回</system:String>
|
||||
<system:String x:Key="ServerString##Frontal_Inf_Orb_2">眶部额下回</system:String>
|
||||
<system:String x:Key="ServerString##Rolandic_Oper">中央沟盖</system:String>
|
||||
<system:String x:Key="ServerString##Supp_Motor_Area">运动辅助区</system:String>
|
||||
<system:String x:Key="ServerString##Olfactory">嗅皮质</system:String>
|
||||
<system:String x:Key="ServerString##Frontal_Sup_Medial">内侧额上回</system:String>
|
||||
<system:String x:Key="ServerString##Frontal_Med_Orb">眶内额上回</system:String>
|
||||
<system:String x:Key="ServerString##Rectus">直回</system:String>
|
||||
<system:String x:Key="ServerString##OFCmed">眶内回</system:String>
|
||||
<system:String x:Key="ServerString##OFCant">眶前回</system:String>
|
||||
<system:String x:Key="ServerString##OFCpost">眶后回</system:String>
|
||||
<system:String x:Key="ServerString##OFClat">眶外回</system:String>
|
||||
<system:String x:Key="ServerString##Insula">脑岛</system:String>
|
||||
<system:String x:Key="ServerString##Cingulate_Ant">前扣带回和扣带旁回</system:String>
|
||||
<system:String x:Key="ServerString##Cingulate_Mid">中扣带回和扣带旁回</system:String>
|
||||
<system:String x:Key="ServerString##Cingulate_Post">后扣带回</system:String>
|
||||
<system:String x:Key="ServerString##Hippocampus">海马</system:String>
|
||||
<system:String x:Key="ServerString##ParaHippocampal">海马旁回</system:String>
|
||||
<system:String x:Key="ServerString##Amygdala">杏仁核</system:String>
|
||||
<system:String x:Key="ServerString##Calcarine">距状裂周围皮层</system:String>
|
||||
<system:String x:Key="ServerString##Cuneus">楔叶</system:String>
|
||||
<system:String x:Key="ServerString##Lingual">舌回</system:String>
|
||||
<system:String x:Key="ServerString##Occipital_Sup">枕上回</system:String>
|
||||
<system:String x:Key="ServerString##Occipital_Mid">枕中回</system:String>
|
||||
<system:String x:Key="ServerString##Occipital_Inf">枕下回</system:String>
|
||||
<system:String x:Key="ServerString##Fusiform">梭状回</system:String>
|
||||
<system:String x:Key="ServerString##Postcentral">中央后回</system:String>
|
||||
<system:String x:Key="ServerString##Parietal_Sup">顶上小叶</system:String>
|
||||
<system:String x:Key="ServerString##Parietal_Inf">除角回和缘上回的顶下小叶</system:String>
|
||||
<system:String x:Key="ServerString##SupraMarginal">缘上回</system:String>
|
||||
<system:String x:Key="ServerString##Angular">角回</system:String>
|
||||
<system:String x:Key="ServerString##Precuneus">楔前叶</system:String>
|
||||
<system:String x:Key="ServerString##Paracentral_Lobule">旁中央小叶</system:String>
|
||||
<system:String x:Key="ServerString##Caudate">尾状核</system:String>
|
||||
<system:String x:Key="ServerString##Putamen">壳核</system:String>
|
||||
<system:String x:Key="ServerString##Pallidum">苍白球</system:String>
|
||||
<system:String x:Key="ServerString##Thalamus">丘脑</system:String>
|
||||
<system:String x:Key="ServerString##Heschl">颞横回</system:String>
|
||||
<system:String x:Key="ServerString##Temporal_Sup">颞上回</system:String>
|
||||
<system:String x:Key="ServerString##Temporal_Pole_Sup">颞极:颞上回</system:String>
|
||||
<system:String x:Key="ServerString##Temporal_Mid">颞中回</system:String>
|
||||
<system:String x:Key="ServerString##Temporal_Pole_Mid">颞极:颞中回</system:String>
|
||||
<system:String x:Key="ServerString##Temporal_Inf">颞下回</system:String>
|
||||
<system:String x:Key="ServerString##Cerebellum_Crus1">小脑脚1区</system:String>
|
||||
<system:String x:Key="ServerString##Cerebellum_Crus2">小脑脚2区</system:String>
|
||||
<system:String x:Key="ServerString##Cerebellum_3">小脑半球3区</system:String>
|
||||
<system:String x:Key="ServerString##Cerebellum_4_5">小脑半球4、5区</system:String>
|
||||
<system:String x:Key="ServerString##Cerebellum_6">小脑半球6区</system:String>
|
||||
<system:String x:Key="ServerString##Cerebellum_7b">小脑半球7b区</system:String>
|
||||
<system:String x:Key="ServerString##Cerebellum_8">小脑半球8区</system:String>
|
||||
<system:String x:Key="ServerString##Cerebellum_9">小脑半球9区</system:String>
|
||||
<system:String x:Key="ServerString##Cerebellum_10">小脑半球10区</system:String>
|
||||
<system:String x:Key="ServerString##Vermis_1_2">小脑蚓部1、2区</system:String>
|
||||
<system:String x:Key="ServerString##Vermis_3">小脑蚓部3区</system:String>
|
||||
<system:String x:Key="ServerString##Vermis_4_5">小脑蚓部4、5区</system:String>
|
||||
<system:String x:Key="ServerString##Vermis_6">小脑蚓部6区</system:String>
|
||||
<system:String x:Key="ServerString##Vermis_7">小脑蚓部7区</system:String>
|
||||
<system:String x:Key="ServerString##Vermis_8">小脑蚓部8区</system:String>
|
||||
<system:String x:Key="ServerString##Vermis_9">小脑蚓部9区</system:String>
|
||||
<system:String x:Key="ServerString##Vermis_10">小脑蚓部10区</system:String>
|
||||
<system:String x:Key="ServerString##Thal_AV">丘脑前腹侧核</system:String>
|
||||
<system:String x:Key="ServerString##Thal_LP">丘脑后外侧核</system:String>
|
||||
<system:String x:Key="ServerString##Thal_VA">丘脑腹前核</system:String>
|
||||
<system:String x:Key="ServerString##Thal_VL">丘脑腹外侧核</system:String>
|
||||
<system:String x:Key="ServerString##Thal_VPL">丘脑腹后外侧核</system:String>
|
||||
<system:String x:Key="ServerString##Thal_IL">丘脑板内核</system:String>
|
||||
<system:String x:Key="ServerString##Thal_Re">丘脑核仁</system:String>
|
||||
<system:String x:Key="ServerString##Thal_MDm">丘脑背内侧核大细胞部</system:String>
|
||||
<system:String x:Key="ServerString##Thal_MDl">丘脑背内侧核小细胞部</system:String>
|
||||
<system:String x:Key="ServerString##Thal_LGN">丘脑外侧膝状体</system:String>
|
||||
<system:String x:Key="ServerString##Thal_MGN">丘脑内侧膝状体</system:String>
|
||||
<system:String x:Key="ServerString##Thal_PuI">丘脑枕下部</system:String>
|
||||
<system:String x:Key="ServerString##Thal_PuM">丘脑枕内侧部</system:String>
|
||||
<system:String x:Key="ServerString##Thal_PuA">丘脑枕前侧部</system:String>
|
||||
<system:String x:Key="ServerString##Thal_PuL">丘脑枕外侧部</system:String>
|
||||
<system:String x:Key="ServerString##ACC_sub">膝下部前扣带回皮质</system:String>
|
||||
<system:String x:Key="ServerString##ACC_pre">膝前部前扣带回皮质</system:String>
|
||||
<system:String x:Key="ServerString##ACC_sup">胼胝体上部前扣带回皮质</system:String>
|
||||
<system:String x:Key="ServerString##N_Acc">伏隔核</system:String>
|
||||
<system:String x:Key="ServerString##VTA">中脑腹侧被盖区</system:String>
|
||||
<system:String x:Key="ServerString##SN_pc">黑质致密部</system:String>
|
||||
<system:String x:Key="ServerString##SN_pr">黑质网状部</system:String>
|
||||
<system:String x:Key="ServerString##Red_N">红核</system:String>
|
||||
<system:String x:Key="ServerString##LC">蓝斑</system:String>
|
||||
<system:String x:Key="ServerString##Raphe_D">中缝背核</system:String>
|
||||
<system:String x:Key="ServerString##Raphe_M">中缝正中核</system:String>
|
||||
<system:String x:Key="ServerString##Middle_cerebellar_peduncle">小脑中脚</system:String>
|
||||
<system:String x:Key="ServerString##Pontine_crossing_tract">脑桥交叉束(MCP的一部分)</system:String>
|
||||
<system:String x:Key="ServerString##Genu_of_corpus_callosum">胼胝体膝部</system:String>
|
||||
<system:String x:Key="ServerString##Body_of_corpus_callosum">胼胝体体部</system:String>
|
||||
<system:String x:Key="ServerString##Splenium_of_corpus_callosum">胼胝体压部</system:String>
|
||||
<system:String x:Key="ServerString##Fornix_column_and_body_of_fornix">穹窿(穹窿的柱和体)</system:String>
|
||||
<system:String x:Key="ServerString##Corticospinal_tract">皮质脊髓束</system:String>
|
||||
<system:String x:Key="ServerString##Medial_lemniscus">内侧丘系</system:String>
|
||||
<system:String x:Key="ServerString##Inferior_cerebellar_peduncle">小脑下脚</system:String>
|
||||
<system:String x:Key="ServerString##Superior_cerebellar_peduncle">小脑上脚</system:String>
|
||||
<system:String x:Key="ServerString##Cerebral_peduncle">大脑脚</system:String>
|
||||
<system:String x:Key="ServerString##Anterior_limb_of_internal_capsule">内囊前肢</system:String>
|
||||
<system:String x:Key="ServerString##Posterior_limb_of_internal_capsule">内囊后肢</system:String>
|
||||
<system:String x:Key="ServerString##Retrolenticular_part_of_internal_capsule">内囊透镜后部</system:String>
|
||||
<system:String x:Key="ServerString##Anterior_corona_radiata">前辐射冠</system:String>
|
||||
<system:String x:Key="ServerString##Superior_corona_radiata">上辐射冠</system:String>
|
||||
<system:String x:Key="ServerString##Posterior_corona_radiata">后辐射冠</system:String>
|
||||
<system:String x:Key="ServerString##Posterior_thalamic_radiation">后丘脑辐射(包括视辐射)</system:String>
|
||||
<system:String x:Key="ServerString##Sagittal_stratum">矢状层(包括下纵束和下额枕束)</system:String>
|
||||
<system:String x:Key="ServerString##External_capsule">外囊</system:String>
|
||||
<system:String x:Key="ServerString##Cingulum_cingulate_gyrus">扣带(扣带回)</system:String>
|
||||
<system:String x:Key="ServerString##Cingulum_hippocampus">扣带(海马)</system:String>
|
||||
<system:String x:Key="ServerString##Fornix_cres_Stria_terminalis">穹窿/终纹</system:String>
|
||||
<system:String x:Key="ServerString##Superior_longitudinal_fasciculus">上纵束</system:String>
|
||||
<system:String x:Key="ServerString##Superior_fronto_occipital_fasciculus">额枕上束</system:String>
|
||||
<system:String x:Key="ServerString##Uncinate_fasciculus">钩束</system:String>
|
||||
<system:String x:Key="ServerString##Tapetum">毯(内矢状层)</system:String>
|
||||
<system:String x:Key="ServerString##Frontal">额叶</system:String>
|
||||
<system:String x:Key="ServerString##Parietal">顶叶</system:String>
|
||||
<system:String x:Key="ServerString##Temporal">颞叶</system:String>
|
||||
<system:String x:Key="ServerString##Occipital">枕叶</system:String>
|
||||
<system:String x:Key="ServerString##Limbic_system">边缘系统</system:String>
|
||||
<system:String x:Key="ServerString##Basal_Ganglia">基底神经节</system:String>
|
||||
<system:String x:Key="ServerString##Cerebellum">小脑</system:String>
|
||||
<system:String x:Key="ServerString##Vermis">小脑蚓</system:String>
|
||||
<system:String x:Key="ServerString##Encephalocoel">脑室旁</system:String>
|
||||
<system:String x:Key="ServerString##Brainstem">脑干</system:String>
|
||||
<system:String x:Key="ServerString##Pons">桥脑</system:String>
|
||||
<system:String x:Key="ServerString##Midbrain">中脑</system:String>
|
||||
<system:String x:Key="ServerString##PCA">大脑后动脉供血区</system:String>
|
||||
<system:String x:Key="ServerString##ACAPerf">前动脉深穿支</system:String>
|
||||
<system:String x:Key="ServerString##ACHA">脉络膜前动脉</system:String>
|
||||
<system:String x:Key="ServerString##LeptoACA">前动脉脑膜支</system:String>
|
||||
<system:String x:Key="ServerString##LeptoMCA">中动脉脑膜支</system:String>
|
||||
<system:String x:Key="ServerString##LeptoPCA">后动脉脑膜支</system:String>
|
||||
<system:String x:Key="ServerString##MCAPerf">中动脉深穿支</system:String>
|
||||
<system:String x:Key="ServerString##POCA">后交通动脉</system:String>
|
||||
<system:String x:Key="ServerString##Cerebrum">大脑</system:String>
|
||||
<system:String x:Key="ServerString##Hypothalamus">下丘脑</system:String>
|
||||
<system:String x:Key="ServerString##Hypophysis">垂体</system:String>
|
||||
<system:String x:Key="ServerString##Stalk">垂体秆</system:String>
|
||||
<system:String x:Key="ServerString##Preoptic">脑视前区</system:String>
|
||||
<system:String x:Key="ServerString##Tuberal">脑结节区</system:String>
|
||||
<system:String x:Key="ServerString##Supraoptic">脑视上区</system:String>
|
||||
<system:String x:Key="ServerString##Mammilary">脑乳头区</system:String>
|
||||
<system:String x:Key="ServerString##HypophysisAnt">垂体前叶</system:String>
|
||||
<system:String x:Key="ServerString##HypophysisPos">垂体后叶</system:String>
|
||||
<system:String x:Key="ServerString##SubstantiaNigra">黑质</system:String>
|
||||
<system:String x:Key="ServerString##CorpusStriatum">纹状体</system:String>
|
||||
<system:String x:Key="ServerString##Whole">全脑</system:String>
|
||||
<system:String x:Key="ServerString##Anterior_Circulation">前循环</system:String>
|
||||
<system:String x:Key="ServerString##Posterior_Circulation">后循环</system:String>
|
||||
<system:String x:Key="ServerString##GrayMatter">灰质</system:String>
|
||||
<system:String x:Key="ServerString##WhiteMatter">白质</system:String>
|
||||
<system:String x:Key="ServerString##Cingulate_Ant_Ros">前扣带和旁扣带脑回</system:String>
|
||||
<system:String x:Key="ServerString##Cingulate_Ant_Mid">内侧和旁扣带脑回</system:String>
|
||||
<system:String x:Key="ServerString##Other">其他</system:String>
|
||||
<system:String x:Key="ServerString##L">左</system:String>
|
||||
<system:String x:Key="ServerString##R">右</system:String>
|
||||
<system:String x:Key="ServerString##ADC-IC">ADC-IC</system:String>
|
||||
<system:String x:Key="ServerString##ADC < 620">ADC < 620</system:String>
|
||||
<system:String x:Key="ServerString##ATT < 0.7">ATT < 0.7</system:String>
|
||||
<system:String x:Key="ServerString##0.7 ≤ ATT < 0.9">0.7 ≤ ATT < 0.9</system:String>
|
||||
<system:String x:Key="ServerString##0.9 ≤ ATT < 1.0">0.9 ≤ ATT < 1.0</system:String>
|
||||
<system:String x:Key="ServerString##1.0 ≤ ATT < 1.3">1.0 ≤ ATT < 1.3</system:String>
|
||||
<system:String x:Key="ServerString##1.3 ≤ ATT < 1.5">1.3 ≤ ATT < 1.5</system:String>
|
||||
<system:String x:Key="ServerString##1.5 ≤ ATT < 2.5">1.5 ≤ ATT < 2.5</system:String>
|
||||
<system:String x:Key="ServerString##ATT ≥ 2.5">ATT ≥ 2.5</system:String>
|
||||
<system:String x:Key="ServerString##CBF-IC">CBF-IC</system:String>
|
||||
<system:String x:Key="ServerString##CBF < 10">CBF < 10</system:String>
|
||||
<system:String x:Key="ServerString##10 ≤ CBF < 20">10 ≤ CBF < 20</system:String>
|
||||
<system:String x:Key="ServerString##20 ≤ CBF < 30">20 ≤ CBF < 30</system:String>
|
||||
<system:String x:Key="ServerString##30 ≤ CBF < 40">30 ≤ CBF < 40</system:String>
|
||||
<system:String x:Key="ServerString##40 ≤ CBF < 70">40 ≤ CBF < 70</system:String>
|
||||
<system:String x:Key="ServerString##70 ≤ CBF < 100">70 ≤ CBF < 100</system:String>
|
||||
<system:String x:Key="ServerString##CBF ≥ 100">CBF ≥ 100</system:String>
|
||||
<system:String x:Key="ServerString##CBF-high-perfusion">高灌注</system:String>
|
||||
<system:String x:Key="ServerString##CBF-low-perfusion">低灌注</system:String>
|
||||
<system:String x:Key="ServerString##CBF-low-perfusion-white">白质低灌注</system:String>
|
||||
<system:String x:Key="ServerString##IC">IC</system:String>
|
||||
<system:String x:Key="ServerString##IP">IP</system:String>
|
||||
<system:String x:Key="ServerString##OP">OP</system:String>
|
||||
<system:String x:Key="ServerString##aCBV < 0.5">aCBV < 0.5</system:String>
|
||||
<system:String x:Key="ServerString##0.5 ≤ aCBV < 0.6">0.5 ≤ aCBV < 0.6</system:String>
|
||||
<system:String x:Key="ServerString##0.6 ≤ aCBV < 0.8">0.6 ≤ aCBV < 0.8</system:String>
|
||||
<system:String x:Key="ServerString##0.8 ≤ aCBV < 1.0">0.8 ≤ aCBV < 1.0</system:String>
|
||||
<system:String x:Key="ServerString##1.0 ≤ aCBV < 1.3">1.0 ≤ aCBV < 1.3</system:String>
|
||||
<system:String x:Key="ServerString##1.3 ≤ aCBV < 1.5">1.3 ≤ aCBV < 1.5</system:String>
|
||||
<system:String x:Key="ServerString##aCBV ≥ 1.5">aCBV ≥ 1.5</system:String>
|
||||
<system:String x:Key="ServerString##AnteriorCirculation">前循环</system:String>
|
||||
<system:String x:Key="ServerString##Left">左侧</system:String>
|
||||
<system:String x:Key="ServerString##PosteriorCirculation">后循环</system:String>
|
||||
<system:String x:Key="ServerString##Right">右侧</system:String>
|
||||
<system:String x:Key="ImageRotationViewerTip">切换右侧标签,此内容将同步更新</system:String>
|
||||
<!-- ReSharper restore MarkupTextTypo -->
|
||||
<!-- ReSharper restore InconsistentNaming -->
|
||||
<!-- ReSharper restore IdentifierTypo -->
|
||||
</ResourceDictionary>
|
525
Livia/Resources/Strings/en.xaml
Normal file
|
@ -0,0 +1,525 @@
|
|||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:system="clr-namespace:System;assembly=System.Runtime">
|
||||
<!-- ReSharper disable MarkupTextTypo -->
|
||||
<system:String x:Key="ManufacturerInformation"
|
||||
xml:space="preserve">Manufacturer Name: AnImage Technology (Beijing) Co,Ltd.
|
||||
Production Address: Room 205, 206, 207, unit 201, building 3(3A), yard 16, Baoshennan street, Daxing District, Beijing
|
||||
After Sales Service Unit: AnImage Technology (Beijing) Co,Ltd.
|
||||
Contact: 010-69465675</system:String>
|
||||
<!-- ReSharper restore MarkupTextTypo -->
|
||||
|
||||
<system:String x:Key="InstanceIsRunningError">Program is already running.</system:String>
|
||||
<system:String x:Key="Error">Error</system:String>
|
||||
<system:String x:Key="Warning">Warning</system:String>
|
||||
<system:String x:Key="Info">Info</system:String>
|
||||
<system:String x:Key="Confirm">Confirm</system:String>
|
||||
<system:String x:Key="Cancel">Cancel</system:String>
|
||||
<system:String x:Key="Clear">Clear</system:String>
|
||||
<system:String x:Key="Processing">Processing</system:String>
|
||||
<system:String x:Key="DataProcessComplete">Process was completed successfully.</system:String>
|
||||
<system:String x:Key="DataImportComplete">Process was completed successfully.</system:String>
|
||||
<system:String x:Key="DeleteSuccess">Deleted successfully.</system:String>
|
||||
<system:String x:Key="SystemCrashing">System error, program is terminating.</system:String>
|
||||
<system:String x:Key="Updating">Updating</system:String>
|
||||
<system:String x:Key="InvalidArgumentError">Invalid argument:{0}</system:String>
|
||||
<system:String x:Key="ParsingArgumentError">Error parsing argument:{0}</system:String>
|
||||
<system:String x:Key="ArgumentNotFoundError">Argument not found:{0}</system:String>
|
||||
<system:String x:Key="Version">Version</system:String>
|
||||
<system:String x:Key="Importing">Processing</system:String>
|
||||
<system:String x:Key="LoginControlMessage2">.</system:String>
|
||||
<system:String x:Key="ShowUpdateButton">Allow detection of updates</system:String>
|
||||
|
||||
<system:String x:Key="InvalidInputDirectoryError">Invalid input directory</system:String>
|
||||
<system:String x:Key="IdleTimeout">You are logged out for idling.</system:String>
|
||||
<system:String x:Key="IdleTimeoutWarning">You will be logged out for idling in about {0} seconds.</system:String>
|
||||
|
||||
<system:String x:Key="ConfirmLogoutWarning">Are you sure you want to log out?</system:String>
|
||||
<system:String x:Key="ConfirmExitWarning">Are you sure you want to exit?</system:String>
|
||||
<system:String x:Key="ConfirmCancelWarning">Are you sure you want to cancel?</system:String>
|
||||
<system:String x:Key="ConfirmDeleteWarning">Are you sure you want to delete this?</system:String>
|
||||
|
||||
<system:String x:Key="DefaultWindowLevelButtonTest">Default window center/window width</system:String>
|
||||
|
||||
<system:String x:Key="BrainLobesRotationViewerName">Brain Lobes Perfusion</system:String>
|
||||
<system:String x:Key="ArterialTerritoriesRotationViewerName">Arterial Territories Perfusion</system:String>
|
||||
<system:String x:Key="AspectsRotationViewerName">ASPECTS Perfusion</system:String>
|
||||
<system:String x:Key="LimbicSystemRotationViewerName">Limbic System Perfusion</system:String>
|
||||
|
||||
<system:String x:Key="CerebrumCerebellumPerfusionDataGridName">Cerebrum/Cerebellum</system:String>
|
||||
<system:String x:Key="BrainLobesPerfusionDataGridName">Brain Lobes</system:String>
|
||||
<system:String x:Key="ArterialTerritoriesPerfusionDataGridName">Arterial Territories</system:String>
|
||||
<system:String x:Key="AspectsPerfusionDataGridName">ASPECTS</system:String>
|
||||
<system:String x:Key="LimbicSystemPerfusionDataGridName">Limbic System</system:String>
|
||||
<system:String x:Key="PerfusionDataGridSwitchButtonText">Switch Parameter</system:String>
|
||||
<system:String x:Key="PerfusionDataGridTabName">Perfusion Data</system:String>
|
||||
<system:String x:Key="AslLabelTip">This button will update when tab on the right is switched.</system:String>
|
||||
<system:String x:Key="JumpPerfusionDataTip">View detailed table data</system:String>
|
||||
|
||||
<system:String x:Key="LoadDataButtonText">Load Files</system:String>
|
||||
<system:String x:Key="ExportReportButtonText">Generate Report</system:String>
|
||||
<system:String x:Key="ImportDataButtonText">Import Result</system:String>
|
||||
<system:String x:Key="ExportDataButtonText">Export Result</system:String>
|
||||
<system:String x:Key="PushToPacsButtonText">Push to PACS</system:String>
|
||||
<system:String x:Key="CheckReportsButtonText">View Report</system:String>
|
||||
<system:String x:Key="RemainingQuota">Remaining quota:</system:String>
|
||||
<system:String x:Key="UnlimitedQuota">Unlimited</system:String>
|
||||
<system:String x:Key="FolderNotEmptyError">Please select an empty folder.</system:String>
|
||||
|
||||
<system:String x:Key="SearchButtonText">Search</system:String>
|
||||
<system:String x:Key="SearchConfirmButtonText">Search</system:String>
|
||||
<system:String x:Key="JobListButtonText">History</system:String>
|
||||
<system:String x:Key="JobListLoadDataButtonText">View Result</system:String>
|
||||
<system:String x:Key="JobListDownloadArchiveButtonText">Download Data</system:String>
|
||||
<system:String x:Key="JobListDownloadSortedButtonText">Download Sorted</system:String>
|
||||
<system:String x:Key="JobListCancelButtonText">Cancel</system:String>
|
||||
<system:String x:Key="JobListSelectSeriesButtonText">Select Series</system:String>
|
||||
<system:String x:Key="JobListDeleteSeriesButtonText">Delete Series</system:String>
|
||||
<system:String x:Key="PatientName">Name</system:String>
|
||||
<system:String x:Key="PatientId">ID</system:String>
|
||||
<system:String x:Key="PatientAge">Age</system:String>
|
||||
<system:String x:Key="PatientSex">Sex</system:String>
|
||||
<system:String x:Key="StudyDate">Study Date</system:String>
|
||||
<system:String x:Key="ImportTime">Import Time</system:String>
|
||||
<system:String x:Key="Status">Status</system:String>
|
||||
<system:String x:Key="Sequence">Sequences</system:String>
|
||||
<system:String x:Key="Operation">Operation</system:String>
|
||||
<system:String x:Key="ServerJobStatus1">Importing</system:String>
|
||||
<system:String x:Key="ServerJobStatus2">Sorting data</system:String>
|
||||
<system:String x:Key="ServerJobStatus3">Processing</system:String>
|
||||
<system:String x:Key="ServerJobStatus4">Success</system:String>
|
||||
<system:String x:Key="ServerJobStatus5">Failure</system:String>
|
||||
<system:String x:Key="ServerJobStatus6">Awaiting Confirmation</system:String>
|
||||
<system:String x:Key="ServerJobStatus7">Queueing</system:String>
|
||||
|
||||
<system:String x:Key="SearchTipsText">% stands for 0 or more characters. E.g., you can search an%、%ima% or %ge for animage</system:String>
|
||||
<system:String x:Key="PatientAgeStart">Age from</system:String>
|
||||
<system:String x:Key="PatientAgeEnd">Age to</system:String>
|
||||
<system:String x:Key="ImportTimeStart">Import time from</system:String>
|
||||
<system:String x:Key="ImportTimeEnd">Import time to</system:String>
|
||||
<system:String x:Key="StudyDateStart">Study date from</system:String>
|
||||
<system:String x:Key="StudyDateEnd">Study date to</system:String>
|
||||
|
||||
<system:String x:Key="SelectSeriesWindowTitle">Select series to process</system:String>
|
||||
<system:String x:Key="SelectSeriesSuccess">Start to process!</system:String>
|
||||
|
||||
<system:String x:Key="CheckReportWindowTitle">View Report</system:String>
|
||||
<system:String x:Key="SelectReportModuleWindowTitle">Generate Report</system:String>
|
||||
<system:String x:Key="ReportName">Report Name</system:String>
|
||||
<system:String x:Key="DefaultReportName">Report1</system:String>
|
||||
<system:String x:Key="ReportNameHint">Please change default name.</system:String>
|
||||
<system:String x:Key="ShowRoiLocation">Show Roi Location</system:String>
|
||||
<system:String x:Key="AnonymousMode">Anonymous Mode</system:String>
|
||||
<system:String x:Key="DeleteReportButtonText">Delete Report</system:String>
|
||||
<system:String x:Key="PushReportButtonText">Push Report</system:String>
|
||||
<system:String x:Key="DownloadReportButtonText">Download Report</system:String>
|
||||
<system:String x:Key="ReportNameWarning">Report name must contain 1-20 characters and none for the following characters \ / : * ? " < > |</system:String>
|
||||
<system:String x:Key="ReportModuleAal">AAL atlas</system:String>
|
||||
<system:String x:Key="ReportModuleAtlas">Atlas</system:String>
|
||||
<system:String x:Key="ReportModuleColorImage">Color Image</system:String>
|
||||
<system:String x:Key="ReportModuleFocus">Focus</system:String>
|
||||
<system:String x:Key="ReportModuleLeftAnteriorRoi">Left anterior circulation ROI</system:String>
|
||||
<system:String x:Key="ReportModuleRightAnteriorRoi">Right anterior circulation ROI</system:String>
|
||||
<system:String x:Key="ReportModulePosteriorRoi">Posterior circulation ROI</system:String>
|
||||
<system:String x:Key="DownloadReportSuccess">Report downloaded successfully!</system:String>
|
||||
|
||||
<system:String x:Key="ValidateSerialKeyWindowTitle">Authentication</system:String>
|
||||
<system:String x:Key="SerialKey">Serial Number</system:String>
|
||||
<system:String x:Key="AuthenticationKey">Authentication Key</system:String>
|
||||
<system:String x:Key="ValidateSerialKeyFailed">Authentication Failed!</system:String>
|
||||
|
||||
<system:String x:Key="Score">Score:</system:String>
|
||||
<system:String x:Key="NoRoiLocationDataMessage">No location information</system:String>
|
||||
|
||||
<system:String x:Key="Left">Left</system:String>
|
||||
<system:String x:Key="Right">Right</system:String>
|
||||
<system:String x:Key="BothSides">Both sides</system:String>
|
||||
|
||||
<system:String x:Key="AxialOverview">Axial Overview</system:String>
|
||||
|
||||
<system:String x:Key="TotalVolume">Total Vol:</system:String>
|
||||
<system:String x:Key="SelectedVolume">Selected Vol:</system:String>
|
||||
<system:String x:Key="Count">Count:</system:String>
|
||||
<system:String x:Key="RoiLocationDisplayModeVolume">Volume</system:String>
|
||||
<system:String x:Key="RoiLocationDisplayModeRoiPercentage">ROI %</system:String>
|
||||
<system:String x:Key="RoiLocationDisplayModeRegionPercentage">Brain region %</system:String>
|
||||
<system:String x:Key="RoiTwoColumnModeTrue">L/R Separated</system:String>
|
||||
<system:String x:Key="RoiTwoColumnModeFalse">L/R Combined</system:String>
|
||||
<system:String x:Key="UnfoldButtonStateTrue">Unfold All</system:String>
|
||||
<system:String x:Key="UnfoldButtonStateFalse">Fold All</system:String>
|
||||
|
||||
<system:String x:Key="IspOverIsc">IP/IC</system:String>
|
||||
<system:String x:Key="IspOverSumOfIscAndIsp">IP/(IP + IC)</system:String>
|
||||
<system:String x:Key="AstrokeDiagnosis1">IP/IC > 1.8</system:String>
|
||||
<!-- ReSharper disable once MarkupTextTypo -->
|
||||
<system:String x:Key="AstrokeDiagnosis2">Consider thrombolysis</system:String>
|
||||
|
||||
<system:String x:Key="AspectsCbfIscScore">CBF IC Score</system:String>
|
||||
<system:String x:Key="AspectsAdcIscScore">ADC IC Score</system:String>
|
||||
|
||||
<system:String x:Key="Left/Right">Left/Right</system:String>
|
||||
<system:String x:Key="Region">Region</system:String>
|
||||
<system:String x:Key="AbsoluteCbf">CBF Value</system:String>
|
||||
<system:String x:Key="RelativeToCerebellum">Relative To Cerebellum</system:String>
|
||||
<system:String x:Key="RelativeToCerebrum">Relative To Cerebrum</system:String>
|
||||
<system:String x:Key="LeftRightDifference">L/R Diff</system:String>
|
||||
<system:String x:Key="UnableToLocateRegionWarning">Unable to locate region</system:String>
|
||||
<system:String x:Key="LocateRoiButtonText">Locate Roi</system:String>
|
||||
<system:String x:Key="ShowBoundaryButtonText">Show Boundary</system:String>
|
||||
|
||||
<system:String x:Key="ProcessingError">Processing Failed!</system:String>
|
||||
<system:String x:Key="ExportFailed">Export Failed!</system:String>
|
||||
<system:String x:Key="ServerUnauthorizedError">Please log in again!</system:String>
|
||||
<system:String x:Key="InvalidDataError">Data Error!</system:String>
|
||||
<system:String x:Key="ApplicationDidNotQuitNormallyWarning">Application did not exit normally!</system:String>
|
||||
|
||||
<system:String x:Key="PasswordBoxHint">Please enter password.</system:String>
|
||||
<system:String x:Key="UsernameTextBlockHint">Please enter username.</system:String>
|
||||
<system:String x:Key="WindowTitle">Livia</system:String>
|
||||
<system:String x:Key="LoginControlTitle">Login</system:String>
|
||||
<system:String x:Key="LoginControlMessage">Welcome</system:String>
|
||||
<system:String x:Key="LoginControlFootnote">The 13th Five-Year National Key Research and Development Plan of the Ministry of Science and Technology (Project No.: 2019YFC0120901)</system:String>
|
||||
<system:String x:Key="LoginButtonText">Login</system:String>
|
||||
<system:String x:Key="UserName">Username</system:String>
|
||||
<system:String x:Key="Password">Password</system:String>
|
||||
<system:String x:Key="Logout">Logout</system:String>
|
||||
|
||||
<system:String x:Key="CheckUpdateButtonText">Check for update.</system:String>
|
||||
<system:String x:Key="CannotGetUpdateInfoMessage">Unable to obtain the latest update information, please check your internet connection.</system:String>
|
||||
<system:String x:Key="NewVersionAvailableInfoMessage">New version available, update now?</system:String>
|
||||
<system:String x:Key="NoNewVersionInfoMessage">This software us up to date.</system:String>
|
||||
<system:String x:Key="RestartNeededInfoMessage">Software updated, please restart.</system:String>
|
||||
<system:String x:Key="InstallUpdateButtonText">Install Update</system:String>
|
||||
|
||||
<system:String x:Key="IncorrectPasswordError">Incorrect username/password!</system:String>
|
||||
<system:String x:Key="SocketExceptionError">Cannot connect to server!</system:String>
|
||||
<system:String x:Key="LoginError">Login failed!</system:String>
|
||||
|
||||
<system:String x:Key="SettingsWindowName">Settings</system:String>
|
||||
<system:String x:Key="SoftwareVersionInfoTabName">Software Information</system:String>
|
||||
<system:String x:Key="GeneralTabName">General</system:String>
|
||||
<system:String x:Key="ManufacturerInformationTabName">Manufacturer Information</system:String>
|
||||
<system:String x:Key="LicenseTabName">User License</system:String>
|
||||
<system:String x:Key="OpenSourceLibraryLicenseTabName">Open Source Library License</system:String>
|
||||
<system:String x:Key="ReleaseNotesTabName">Release Notes</system:String>
|
||||
|
||||
<system:String x:Key="DeveloperSettings">Developer Settings</system:String>
|
||||
<system:String x:Key="RestartIsRequireForChangesToTakeEffect">Restart is required for changes to take effect.</system:String>
|
||||
<system:String x:Key="ServerAddress">Server Address</system:String>
|
||||
<system:String x:Key="ServerPort">Server Port</system:String>
|
||||
<system:String x:Key="UserLocalhostButtonText">Localhost</system:String>
|
||||
<system:String x:Key="UserCloudServerButtonText">Cloud Server</system:String>
|
||||
<system:String x:Key="UserTestServerButtonText">Use test server</system:String>
|
||||
<system:String x:Key="UseRegistrationVersion">Use Registration Version</system:String>
|
||||
<system:String x:Key="LogoutAfter">Logout after(min,0 for never)</system:String>
|
||||
<system:String x:Key="ShowArchiveDownloadButton">Show Archive Download Button</system:String>
|
||||
<system:String x:Key="ShowPushToPacsButton">Show Push To Pacs Button</system:String>
|
||||
<system:String x:Key="AutoRefreshServerJobList">Auto Refresh Server Job List</system:String>
|
||||
<system:String x:Key="ShowSelectReportModuleDialog">Show Select Report Module Dialog</system:String>
|
||||
<system:String x:Key="ShowDicomNodeTab">Show Dicom Node Tab</system:String>
|
||||
<system:String x:Key="ShowReportModuleAalCheckbox">Show Report Module Aal Checkbox</system:String>
|
||||
|
||||
<system:String x:Key="DisplaySettings">Display Settings</system:String>
|
||||
<system:String x:Key="FontFamily">Font Family</system:String>
|
||||
<system:String x:Key="FontWeight">Font Weight</system:String>
|
||||
|
||||
<system:String x:Key="Language">Language</system:String>
|
||||
<system:String x:Key="CultureInfo">Culture Info</system:String>
|
||||
<system:String x:Key="UpdateChannel">Update Channel</system:String>
|
||||
|
||||
<system:String x:Key="SecurityTabName">Security Settings</system:String>
|
||||
<system:String x:Key="EditPassword">Change Password</system:String>
|
||||
<system:String x:Key="PasswordRequirement">Password must contain letters and numbers and be 6-15 characters.</system:String>
|
||||
<system:String x:Key="OldPassword">Old Password</system:String>
|
||||
<system:String x:Key="ConfirmPassword">Confirm Password</system:String>
|
||||
<system:String x:Key="NewPassword">New Password</system:String>
|
||||
<system:String x:Key="ConfirmPasswordFailedError">Passwords do not match!</system:String>
|
||||
<system:String x:Key="EditPasswordSuccess">Password changed!</system:String>
|
||||
|
||||
<system:String x:Key="DicomNodeTabName">DICOM Node Info</system:String>
|
||||
<system:String x:Key="DicomNodeHost">DICOM Node Host</system:String>
|
||||
<system:String x:Key="DicomNodePort">DICOM Node Port</system:String>
|
||||
<system:String x:Key="DicomNodeAet">DICOM Node AET</system:String>
|
||||
|
||||
<system:String x:Key="ReportSettings">Report Settings</system:String>
|
||||
<system:String x:Key="OpenReportOnFinish">Open report after downloading.</system:String>
|
||||
|
||||
<system:String x:Key="PushToPacsSuccess">Successfully pushed to PACS!</system:String>
|
||||
|
||||
<system:String x:Key="OpenLogDirectoryButtonText">Open logs folder</system:String>
|
||||
<system:String x:Key="OpenTempDirectoryButtonText">Open temp folder</system:String>
|
||||
<system:String x:Key="ClearAuthenticationKeyButtonText">Clear</system:String>
|
||||
|
||||
<system:String x:Key="RoiAtlasNotFoundError">Roi atlas file not found!</system:String>
|
||||
<system:String x:Key="PathNotFoundError">Path not found!</system:String>
|
||||
|
||||
<system:String x:Key="ServerErrorUnknown">Server Error Unknown</system:String>
|
||||
<system:String x:Key="ServerError10001">Cannot connect to PACS</system:String>
|
||||
<system:String x:Key="ServerError10002">SCP server return error</system:String>
|
||||
<system:String x:Key="ServerError10003">SCP timeout/error</system:String>
|
||||
<system:String x:Key="ServerError10004">Missing required sequence</system:String>
|
||||
<system:String x:Key="ServerError10005">Dongle out of quota</system:String>
|
||||
<system:String x:Key="ServerError10006">Dongle not found or dongle error</system:String>
|
||||
<system:String x:Key="ServerError10007">Dongle incorrect password</system:String>
|
||||
<system:String x:Key="ServerError10008">Error processing images</system:String>
|
||||
<system:String x:Key="ServerError10009">Key not found</system:String>
|
||||
<system:String x:Key="ServerError10010">Out of quota</system:String>
|
||||
<system:String x:Key="ServerError10011">Incorrect old password</system:String>
|
||||
<system:String x:Key="ServerError10012">New password is same as old password.</system:String>
|
||||
<system:String x:Key="ServerError10013">Password does not contain letters or numbers.</system:String>
|
||||
<system:String x:Key="ServerError10014">Password must be 6-15 characters.</system:String>
|
||||
<system:String x:Key="ServerError10015">Storage server error</system:String>
|
||||
<system:String x:Key="ServerError10016">DICOM file error</system:String>
|
||||
<system:String x:Key="ServerError10017">No DICOM sequence found</system:String>
|
||||
<system:String x:Key="ServerError10018">Access denied</system:String>
|
||||
<system:String x:Key="ServerError10019">Unauthorized user</system:String>
|
||||
<system:String x:Key="ServerError10020">Resource not found</system:String>
|
||||
<system:String x:Key="ServerError10021">Failed {0} times,please try again in {1} seconds</system:String>
|
||||
<system:String x:Key="ServerError10022">Incorrect username/password!</system:String>
|
||||
<system:String x:Key="ServerError10023">Result expired,resource has been deleted</system:String>
|
||||
<system:String x:Key="ServerError10024">Process is terminated</system:String>
|
||||
<system:String x:Key="ServerError10025">Generate report failed</system:String>
|
||||
<system:String x:Key="ServerError10026">Failed to generate task</system:String>
|
||||
<system:String x:Key="ServerError10028">Data not found</system:String>
|
||||
<system:String x:Key="ServerError10032">Operation denied</system:String>
|
||||
<system:String x:Key="ServerError10035">Report name exists, do you wish to overwrite it?</system:String>
|
||||
<system:String x:Key="ServerError10036">Unknown report format type</system:String>
|
||||
<system:String x:Key="ServerError10037">Data is being stored, please try again later</system:String>
|
||||
<system:String x:Key="ServerError10038">Unauthorized data</system:String>
|
||||
<system:String x:Key="ServerError20000">Data sorting error</system:String>
|
||||
<system:String x:Key="ServerError20001">DICOM missing tags</system:String>
|
||||
<system:String x:Key="ServerError20002">Siemens mosaic missing CSA header</system:String>
|
||||
<system:String x:Key="ServerError20003">Unsupported file type</system:String>
|
||||
<system:String x:Key="ServerError20004">RGB DICOM file not supported</system:String>
|
||||
<system:String x:Key="ServerError20005">Missing required sequence</system:String>
|
||||
<system:String x:Key="ServerError20006">DICOM image missing slice</system:String>
|
||||
<system:String x:Key="ServerError20007">DICOM slice number too low</system:String>
|
||||
<system:String x:Key="ServerError20008">DICOM image space inconsistent</system:String>
|
||||
<system:String x:Key="ServerError20009">DWI image missing B value</system:String>
|
||||
<system:String x:Key="ServerError20010">DICOM image type unspecified</system:String>
|
||||
<system:String x:Key="ServerError20011">DICOM image missing orientation info</system:String>
|
||||
<system:String x:Key="ServerError20012">CBf or M0 sequence not found</system:String>
|
||||
<system:String x:Key="ServerError20013">PLD and sequence mismatch</system:String>
|
||||
<system:String x:Key="ServerError20014">PLD and measurement list mismatch</system:String>
|
||||
<system:String x:Key="ServerError20015">Control and label image number mismatch</system:String>
|
||||
<system:String x:Key="ServerError20016">Bad Image</system:String>
|
||||
<system:String x:Key="ServerError20017">Too many files uploaded.</system:String>
|
||||
<system:String x:Key="ServerError20018">Contains multiple sequences of the same modality, please delete one.</system:String>
|
||||
<system:String x:Key="ServerError20019">DWI sequence requires 2 different B values</system:String>
|
||||
<system:String x:Key="ServerError20020">Unsupported MRI field strength</system:String>
|
||||
<system:String x:Key="ServerError20021">Too many ASL sequence</system:String>
|
||||
<system:String x:Key="ServerError20022">ASL sequence missing image</system:String>
|
||||
<system:String x:Key="ServerError20023">Unsupported comments tag</system:String>
|
||||
<system:String x:Key="ServerError20024">Registration failed</system:String>
|
||||
|
||||
<!-- ReSharper disable InconsistentNaming -->
|
||||
<!-- ReSharper disable MarkupTextTypo -->
|
||||
<!-- ReSharper disable IdentifierTypo -->
|
||||
<system:String x:Key="ServerString##ASPECTS_Caudate">ASPECTS_Caudate</system:String>
|
||||
<system:String x:Key="ServerString##Internal_Capsule">Internal_Capsule</system:String>
|
||||
<system:String x:Key="ServerString##ASPECTS_Lentiform_Nucleus">ASPECTS_Lentiform_Nucleus</system:String>
|
||||
<system:String x:Key="ServerString##ASPECTS_Insula">ASPECTS_Insula</system:String>
|
||||
<system:String x:Key="ServerString##M1">M1</system:String>
|
||||
<system:String x:Key="ServerString##M2">M2</system:String>
|
||||
<system:String x:Key="ServerString##M3">M3</system:String>
|
||||
<system:String x:Key="ServerString##M4">M4</system:String>
|
||||
<system:String x:Key="ServerString##M5">M5</system:String>
|
||||
<system:String x:Key="ServerString##M6">M6</system:String>
|
||||
<system:String x:Key="ServerString##Precentral">Precentral</system:String>
|
||||
<system:String x:Key="ServerString##Frontal_Sup_2">Frontal_Sup_2</system:String>
|
||||
<system:String x:Key="ServerString##Frontal_Mid_2">Frontal_Mid_2</system:String>
|
||||
<system:String x:Key="ServerString##Frontal_Inf_Oper">Frontal_Inf_Oper</system:String>
|
||||
<system:String x:Key="ServerString##Frontal_Inf_Tri">Frontal_Inf_Tri</system:String>
|
||||
<system:String x:Key="ServerString##Frontal_Inf_Orb_2">Frontal_Inf_Orb_2</system:String>
|
||||
<system:String x:Key="ServerString##Rolandic_Oper">Rolandic_Oper</system:String>
|
||||
<system:String x:Key="ServerString##Supp_Motor_Area">Supp_Motor_Area</system:String>
|
||||
<system:String x:Key="ServerString##Olfactory">Olfactory</system:String>
|
||||
<system:String x:Key="ServerString##Frontal_Sup_Medial">Frontal_Sup_Medial</system:String>
|
||||
<system:String x:Key="ServerString##Frontal_Med_Orb">Frontal_Med_Orb</system:String>
|
||||
<system:String x:Key="ServerString##Rectus">Rectus</system:String>
|
||||
<system:String x:Key="ServerString##OFCmed">OFCmed</system:String>
|
||||
<system:String x:Key="ServerString##OFCant">OFCant</system:String>
|
||||
<system:String x:Key="ServerString##OFCpost">OFCpost</system:String>
|
||||
<system:String x:Key="ServerString##OFClat">OFClat</system:String>
|
||||
<system:String x:Key="ServerString##Insula">Insula</system:String>
|
||||
<system:String x:Key="ServerString##Cingulate_Ant">Cingulate_Ant</system:String>
|
||||
<system:String x:Key="ServerString##Cingulate_Mid">Cingulate_Mid</system:String>
|
||||
<system:String x:Key="ServerString##Cingulate_Post">Cingulate_Post</system:String>
|
||||
<system:String x:Key="ServerString##Hippocampus">Hippocampus</system:String>
|
||||
<system:String x:Key="ServerString##ParaHippocampal">ParaHippocampal</system:String>
|
||||
<system:String x:Key="ServerString##Amygdala">Amygdala</system:String>
|
||||
<system:String x:Key="ServerString##Calcarine">Calcarine</system:String>
|
||||
<system:String x:Key="ServerString##Cuneus">Cuneus</system:String>
|
||||
<system:String x:Key="ServerString##Lingual">Lingual</system:String>
|
||||
<system:String x:Key="ServerString##Occipital_Sup">Occipital_Sup</system:String>
|
||||
<system:String x:Key="ServerString##Occipital_Mid">Occipital_Mid</system:String>
|
||||
<system:String x:Key="ServerString##Occipital_Inf">Occipital_Inf</system:String>
|
||||
<system:String x:Key="ServerString##Fusiform">Fusiform</system:String>
|
||||
<system:String x:Key="ServerString##Postcentral">Postcentral</system:String>
|
||||
<system:String x:Key="ServerString##Parietal_Sup">Parietal_Sup</system:String>
|
||||
<system:String x:Key="ServerString##Parietal_Inf">Parietal_Inf</system:String>
|
||||
<system:String x:Key="ServerString##SupraMarginal">SupraMarginal</system:String>
|
||||
<system:String x:Key="ServerString##Angular">Angular</system:String>
|
||||
<system:String x:Key="ServerString##Precuneus">Precuneus</system:String>
|
||||
<system:String x:Key="ServerString##Paracentral_Lobule">Paracentral_Lobule</system:String>
|
||||
<system:String x:Key="ServerString##Caudate">Caudate</system:String>
|
||||
<system:String x:Key="ServerString##Putamen">Putamen</system:String>
|
||||
<system:String x:Key="ServerString##Pallidum">Pallidum</system:String>
|
||||
<system:String x:Key="ServerString##Thalamus">Thalamus</system:String>
|
||||
<system:String x:Key="ServerString##Heschl">Heschl</system:String>
|
||||
<system:String x:Key="ServerString##Temporal_Sup">Temporal_Sup</system:String>
|
||||
<system:String x:Key="ServerString##Temporal_Pole_Sup">Temporal_Pole_Sup</system:String>
|
||||
<system:String x:Key="ServerString##Temporal_Mid">Temporal_Mid</system:String>
|
||||
<system:String x:Key="ServerString##Temporal_Pole_Mid">Temporal_Pole_Mid</system:String>
|
||||
<system:String x:Key="ServerString##Temporal_Inf">Temporal_Inf</system:String>
|
||||
<system:String x:Key="ServerString##Cerebellum_Crus1">Cerebellum_Crus1</system:String>
|
||||
<system:String x:Key="ServerString##Cerebellum_Crus2">Cerebellum_Crus2</system:String>
|
||||
<system:String x:Key="ServerString##Cerebellum_3">Cerebellum_3</system:String>
|
||||
<system:String x:Key="ServerString##Cerebellum_4_5">Cerebellum_4_5</system:String>
|
||||
<system:String x:Key="ServerString##Cerebellum_6">Cerebellum_6</system:String>
|
||||
<system:String x:Key="ServerString##Cerebellum_7b">Cerebellum_7b</system:String>
|
||||
<system:String x:Key="ServerString##Cerebellum_8">Cerebellum_8</system:String>
|
||||
<system:String x:Key="ServerString##Cerebellum_9">Cerebellum_9</system:String>
|
||||
<system:String x:Key="ServerString##Cerebellum_10">Cerebellum_10</system:String>
|
||||
<system:String x:Key="ServerString##Vermis_1_2">Vermis_1_2</system:String>
|
||||
<system:String x:Key="ServerString##Vermis_3">Vermis_3</system:String>
|
||||
<system:String x:Key="ServerString##Vermis_4_5">Vermis_4_5</system:String>
|
||||
<system:String x:Key="ServerString##Vermis_6">Vermis_6</system:String>
|
||||
<system:String x:Key="ServerString##Vermis_7">Vermis_7</system:String>
|
||||
<system:String x:Key="ServerString##Vermis_8">Vermis_8</system:String>
|
||||
<system:String x:Key="ServerString##Vermis_9">Vermis_9</system:String>
|
||||
<system:String x:Key="ServerString##Vermis_10">Vermis_10</system:String>
|
||||
<system:String x:Key="ServerString##Thal_AV">Thal_AV</system:String>
|
||||
<system:String x:Key="ServerString##Thal_LP">Thal_LP</system:String>
|
||||
<system:String x:Key="ServerString##Thal_VA">Thal_VA</system:String>
|
||||
<system:String x:Key="ServerString##Thal_VL">Thal_VL</system:String>
|
||||
<system:String x:Key="ServerString##Thal_VPL">Thal_VPL</system:String>
|
||||
<system:String x:Key="ServerString##Thal_IL">Thal_IL</system:String>
|
||||
<system:String x:Key="ServerString##Thal_Re">Thal_Re</system:String>
|
||||
<system:String x:Key="ServerString##Thal_MDm">Thal_MDm</system:String>
|
||||
<system:String x:Key="ServerString##Thal_MDl">Thal_MDl</system:String>
|
||||
<system:String x:Key="ServerString##Thal_LGN">Thal_LGN</system:String>
|
||||
<system:String x:Key="ServerString##Thal_MGN">Thal_MGN</system:String>
|
||||
<system:String x:Key="ServerString##Thal_PuI">Thal_PuI</system:String>
|
||||
<system:String x:Key="ServerString##Thal_PuM">Thal_PuM</system:String>
|
||||
<system:String x:Key="ServerString##Thal_PuA">Thal_PuA</system:String>
|
||||
<system:String x:Key="ServerString##Thal_PuL">Thal_PuL</system:String>
|
||||
<system:String x:Key="ServerString##ACC_sub">ACC_sub</system:String>
|
||||
<system:String x:Key="ServerString##ACC_pre">ACC_pre</system:String>
|
||||
<system:String x:Key="ServerString##ACC_sup">ACC_sup</system:String>
|
||||
<system:String x:Key="ServerString##N_Acc">N_Acc</system:String>
|
||||
<system:String x:Key="ServerString##VTA">VTA</system:String>
|
||||
<system:String x:Key="ServerString##SN_pc">SN_pc</system:String>
|
||||
<system:String x:Key="ServerString##SN_pr">SN_pr</system:String>
|
||||
<system:String x:Key="ServerString##Red_N">Red_N</system:String>
|
||||
<system:String x:Key="ServerString##LC">LC</system:String>
|
||||
<system:String x:Key="ServerString##Raphe_D">Raphe_D</system:String>
|
||||
<system:String x:Key="ServerString##Raphe_M">Raphe_M</system:String>
|
||||
<system:String x:Key="ServerString##Middle_cerebellar_peduncle">Middle_cerebellar_peduncle</system:String>
|
||||
<system:String x:Key="ServerString##Pontine_crossing_tract">Pontine_crossing_tract</system:String>
|
||||
<system:String x:Key="ServerString##Genu_of_corpus_callosum">Genu_of_corpus_callosum</system:String>
|
||||
<system:String x:Key="ServerString##Body_of_corpus_callosum">Body_of_corpus_callosum</system:String>
|
||||
<system:String x:Key="ServerString##Splenium_of_corpus_callosum">Splenium_of_corpus_callosum</system:String>
|
||||
<system:String x:Key="ServerString##Fornix_column_and_body_of_fornix">Fornix_column_and_body_of_fornix</system:String>
|
||||
<system:String x:Key="ServerString##Corticospinal_tract">Corticospinal_tract</system:String>
|
||||
<system:String x:Key="ServerString##Medial_lemniscus">Medial_lemniscus</system:String>
|
||||
<system:String x:Key="ServerString##Inferior_cerebellar_peduncle">Inferior_cerebellar_peduncle</system:String>
|
||||
<system:String x:Key="ServerString##Superior_cerebellar_peduncle">Superior_cerebellar_peduncle</system:String>
|
||||
<system:String x:Key="ServerString##Cerebral_peduncle">Cerebral_peduncle</system:String>
|
||||
<system:String x:Key="ServerString##Anterior_limb_of_internal_capsule">Anterior_limb_of_internal_capsule</system:String>
|
||||
<system:String x:Key="ServerString##Posterior_limb_of_internal_capsule">Posterior_limb_of_internal_capsule</system:String>
|
||||
<system:String x:Key="ServerString##Retrolenticular_part_of_internal_capsule">Retrolenticular_part_of_internal_capsule</system:String>
|
||||
<system:String x:Key="ServerString##Anterior_corona_radiata">Anterior_corona_radiata</system:String>
|
||||
<system:String x:Key="ServerString##Superior_corona_radiata">Superior_corona_radiata</system:String>
|
||||
<system:String x:Key="ServerString##Posterior_corona_radiata">Posterior_corona_radiata</system:String>
|
||||
<system:String x:Key="ServerString##Posterior_thalamic_radiation">Posterior_thalamic_radiation</system:String>
|
||||
<system:String x:Key="ServerString##Sagittal_stratum">Sagittal_stratum</system:String>
|
||||
<system:String x:Key="ServerString##External_capsule">External_capsule</system:String>
|
||||
<system:String x:Key="ServerString##Cingulum_cingulate_gyrus">Cingulum_cingulate_gyrus</system:String>
|
||||
<system:String x:Key="ServerString##Cingulum_hippocampus">Cingulum_hippocampus</system:String>
|
||||
<system:String x:Key="ServerString##Fornix_cres_Stria_terminalis">Fornix_cres_Stria_terminalis</system:String>
|
||||
<system:String x:Key="ServerString##Superior_longitudinal_fasciculus">Superior_longitudinal_fasciculus</system:String>
|
||||
<system:String x:Key="ServerString##Superior_fronto_occipital_fasciculus">Superior_fronto_occipital_fasciculus</system:String>
|
||||
<system:String x:Key="ServerString##Uncinate_fasciculus">Uncinate_fasciculus</system:String>
|
||||
<system:String x:Key="ServerString##Tapetum">Tapetum</system:String>
|
||||
<system:String x:Key="ServerString##Frontal">Frontal</system:String>
|
||||
<system:String x:Key="ServerString##Parietal">Parietal</system:String>
|
||||
<system:String x:Key="ServerString##Temporal">Temporal</system:String>
|
||||
<system:String x:Key="ServerString##Occipital">Occipital</system:String>
|
||||
<system:String x:Key="ServerString##Limbic_system">Limbic_system</system:String>
|
||||
<system:String x:Key="ServerString##Basal_Ganglia">Basal_Ganglia</system:String>
|
||||
<system:String x:Key="ServerString##Cerebellum">Cerebellum</system:String>
|
||||
<system:String x:Key="ServerString##Vermis">Vermis</system:String>
|
||||
<system:String x:Key="ServerString##Encephalocoel">Encephalocoel</system:String>
|
||||
<system:String x:Key="ServerString##Brainstem">Brainstem</system:String>
|
||||
<system:String x:Key="ServerString##Pons">Pons</system:String>
|
||||
<system:String x:Key="ServerString##Midbrain">Midbrain</system:String>
|
||||
<system:String x:Key="ServerString##PCA">PCA</system:String>
|
||||
<system:String x:Key="ServerString##ACAPerf">ACAPerf</system:String>
|
||||
<system:String x:Key="ServerString##ACHA">ACHA</system:String>
|
||||
<system:String x:Key="ServerString##LeptoACA">LeptoACA</system:String>
|
||||
<system:String x:Key="ServerString##LeptoMCA">LeptoMCA</system:String>
|
||||
<system:String x:Key="ServerString##LeptoPCA">LeptoPCA</system:String>
|
||||
<system:String x:Key="ServerString##MCAPerf">MCAPerf</system:String>
|
||||
<system:String x:Key="ServerString##POCA">POCA</system:String>
|
||||
<system:String x:Key="ServerString##Cerebrum">Cerebrum</system:String>
|
||||
<system:String x:Key="ServerString##Hypothalamus">Hypothalamus</system:String>
|
||||
<system:String x:Key="ServerString##Hypophysis">Hypophysis</system:String>
|
||||
<system:String x:Key="ServerString##Stalk">Stalk</system:String>
|
||||
<system:String x:Key="ServerString##Preoptic">Preoptic</system:String>
|
||||
<system:String x:Key="ServerString##Tuberal">Tuberal</system:String>
|
||||
<system:String x:Key="ServerString##Supraoptic">Supraoptic</system:String>
|
||||
<system:String x:Key="ServerString##Mammilary">Mammilary</system:String>
|
||||
<system:String x:Key="ServerString##HypophysisAnt">HypophysisAnt</system:String>
|
||||
<system:String x:Key="ServerString##HypophysisPos">HypophysisPos</system:String>
|
||||
<system:String x:Key="ServerString##SubstantiaNigra">SubstantiaNigra</system:String>
|
||||
<system:String x:Key="ServerString##CorpusStriatum">CorpusStriatum</system:String>
|
||||
<system:String x:Key="ServerString##Whole">Whole</system:String>
|
||||
<system:String x:Key="ServerString##Anterior_Circulation">Anterior_Circulation</system:String>
|
||||
<system:String x:Key="ServerString##Posterior_Circulation">Posterior_Circulation</system:String>
|
||||
<system:String x:Key="ServerString##GrayMatter">GrayMatter</system:String>
|
||||
<system:String x:Key="ServerString##WhiteMatter">WhiteMatter</system:String>
|
||||
<system:String x:Key="ServerString##Cingulate_Ant_Ros">Cingulate_Ant_Ros</system:String>
|
||||
<system:String x:Key="ServerString##Cingulate_Ant_Mid">Cingulate_Ant_Mid</system:String>
|
||||
<system:String x:Key="ServerString##Other">Other</system:String>
|
||||
<system:String x:Key="ServerString##L">L</system:String>
|
||||
<system:String x:Key="ServerString##R">R</system:String>
|
||||
<system:String x:Key="ServerString##ADC-IC">ADC-IC</system:String>
|
||||
<system:String x:Key="ServerString##ADC < 620">ADC < 620</system:String>
|
||||
<system:String x:Key="ServerString##ATT < 0.7">ATT < 0.7</system:String>
|
||||
<system:String x:Key="ServerString##0.7 ≤ ATT < 0.9">0.7 ≤ ATT < 0.9</system:String>
|
||||
<system:String x:Key="ServerString##0.9 ≤ ATT < 1.0">0.9 ≤ ATT < 1.0</system:String>
|
||||
<system:String x:Key="ServerString##1.0 ≤ ATT < 1.3">1.0 ≤ ATT < 1.3</system:String>
|
||||
<system:String x:Key="ServerString##1.3 ≤ ATT < 1.5">1.3 ≤ ATT < 1.5</system:String>
|
||||
<system:String x:Key="ServerString##1.5 ≤ ATT < 2.5">1.5 ≤ ATT < 2.5</system:String>
|
||||
<system:String x:Key="ServerString##ATT ≥ 2.5">ATT ≥ 2.5</system:String>
|
||||
<system:String x:Key="ServerString##CBF-IC">CBF-IC</system:String>
|
||||
<system:String x:Key="ServerString##CBF < 10">CBF < 10</system:String>
|
||||
<system:String x:Key="ServerString##10 ≤ CBF < 20">10 ≤ CBF < 20</system:String>
|
||||
<system:String x:Key="ServerString##20 ≤ CBF < 30">20 ≤ CBF < 30</system:String>
|
||||
<system:String x:Key="ServerString##30 ≤ CBF < 40">30 ≤ CBF < 40</system:String>
|
||||
<system:String x:Key="ServerString##40 ≤ CBF < 70">40 ≤ CBF < 70</system:String>
|
||||
<system:String x:Key="ServerString##70 ≤ CBF < 100">70 ≤ CBF < 100</system:String>
|
||||
<system:String x:Key="ServerString##CBF ≥ 100">CBF ≥ 100</system:String>
|
||||
<system:String x:Key="ServerString##CBF-high-perfusion">CBF-high-perfusion</system:String>
|
||||
<system:String x:Key="ServerString##CBF-low-perfusion">CBF-low-perfusion</system:String>
|
||||
<system:String x:Key="ServerString##CBF-low-perfusion-white">CBF-low-perfusion-white</system:String>
|
||||
<system:String x:Key="ServerString##IC">IC</system:String>
|
||||
<system:String x:Key="ServerString##IP">IP</system:String>
|
||||
<system:String x:Key="ServerString##OP">OP</system:String>
|
||||
<system:String x:Key="ServerString##aCBV < 0.5">aCBV < 0.5</system:String>
|
||||
<system:String x:Key="ServerString##0.5 ≤ aCBV < 0.6">0.5 ≤ aCBV < 0.6</system:String>
|
||||
<system:String x:Key="ServerString##0.6 ≤ aCBV < 0.8">0.6 ≤ aCBV < 0.8</system:String>
|
||||
<system:String x:Key="ServerString##0.8 ≤ aCBV < 1.0">0.8 ≤ aCBV < 1.0</system:String>
|
||||
<system:String x:Key="ServerString##1.0 ≤ aCBV < 1.3">1.0 ≤ aCBV < 1.3</system:String>
|
||||
<system:String x:Key="ServerString##1.3 ≤ aCBV < 1.5">1.3 ≤ aCBV < 1.5</system:String>
|
||||
<system:String x:Key="ServerString##aCBV ≥ 1.5">aCBV ≥ 1.5</system:String>
|
||||
<system:String x:Key="ServerString##AnteriorCirculation">AnteriorCirculation</system:String>
|
||||
<system:String x:Key="ServerString##Left">Left</system:String>
|
||||
<system:String x:Key="ServerString##PosteriorCirculation">PosteriorCirculation</system:String>
|
||||
<system:String x:Key="ServerString##Right">Right</system:String>
|
||||
<system:String x:Key="ImageRotationViewerTip">Content will update when tab on the right is switched.</system:String>
|
||||
<!-- ReSharper restore MarkupTextTypo -->
|
||||
<!-- ReSharper restore InconsistentNaming -->
|
||||
<!-- ReSharper restore IdentifierTypo -->
|
||||
</ResourceDictionary>
|
678
Livia/Resources/Themes/LiviaButton.xaml
Normal file
|
@ -0,0 +1,678 @@
|
|||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
xmlns:attachedProperties="clr-namespace:Livia.Views.AttachedProperties">
|
||||
|
||||
<Style TargetType="{x:Type ButtonBase}"
|
||||
x:Key="LiviaTextButtonBase">
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type ButtonBase}">
|
||||
<Grid>
|
||||
<Button Style="{StaticResource MaterialDesignRaisedButton}"
|
||||
Name="Button"
|
||||
Height="40"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
materialDesign:ButtonAssist.CornerRadius="20"
|
||||
materialDesign:ElevationAssist.Elevation="Dp0"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
Background="{TemplateBinding Background}"
|
||||
Foreground="{TemplateBinding Foreground}">
|
||||
<TextBlock Style="{StaticResource MaterialDesignButtonTextBlock}"
|
||||
Margin="20, 0, 20, 1.5"
|
||||
VerticalAlignment="Center"
|
||||
Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(attachedProperties:StringItemProperties.ItemName)}"
|
||||
Name="ButtonText" />
|
||||
</Button>
|
||||
</Grid>
|
||||
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsMouseOver"
|
||||
Value="True">
|
||||
<Setter TargetName="Button"
|
||||
Property="BorderBrush"
|
||||
Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(attachedProperties:SolidColorBrushProperties.SolidColorBrush1)}" />
|
||||
<Setter TargetName="Button"
|
||||
Property="Background"
|
||||
Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(attachedProperties:SolidColorBrushProperties.SolidColorBrush2)}" />
|
||||
<Setter TargetName="Button"
|
||||
Property="Foreground"
|
||||
Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(attachedProperties:SolidColorBrushProperties.SolidColorBrush3)}" />
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<Style TargetType="{x:Type ButtonBase}"
|
||||
BasedOn="{StaticResource LiviaTextButtonBase}"
|
||||
x:Key="LiviaPrimaryTextButton">
|
||||
<Setter Property="BorderBrush"
|
||||
Value="{StaticResource PrimaryColorHighlightBrush}" />
|
||||
<Setter Property="Background"
|
||||
Value="{StaticResource PrimaryColorHighlightBrush}" />
|
||||
<Setter Property="Foreground"
|
||||
Value="{StaticResource White1Brush}" />
|
||||
<Setter Property="attachedProperties:StringItemProperties.ItemName"
|
||||
Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(attachedProperties:StringItemProperties.ItemName)}" />
|
||||
<Setter Property="attachedProperties:SolidColorBrushProperties.SolidColorBrush1"
|
||||
Value="{StaticResource PrimaryColorAlternativeBrush}" />
|
||||
<Setter Property="attachedProperties:SolidColorBrushProperties.SolidColorBrush2"
|
||||
Value="{StaticResource PrimaryColorAlternativeBrush}" />
|
||||
<Setter Property="attachedProperties:SolidColorBrushProperties.SolidColorBrush3"
|
||||
Value="{StaticResource White1Brush}" />
|
||||
</Style>
|
||||
|
||||
<Style TargetType="{x:Type ButtonBase}"
|
||||
BasedOn="{StaticResource LiviaTextButtonBase}"
|
||||
x:Key="LiviaDarkTextButton">
|
||||
<Setter Property="BorderBrush"
|
||||
Value="{StaticResource PrimaryColorHighlightBrush}" />
|
||||
<Setter Property="Background"
|
||||
Value="Transparent" />
|
||||
<Setter Property="Foreground"
|
||||
Value="{StaticResource White1Brush}" />
|
||||
<Setter Property="attachedProperties:StringItemProperties.ItemName"
|
||||
Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(attachedProperties:StringItemProperties.ItemName)}" />
|
||||
<Setter Property="attachedProperties:SolidColorBrushProperties.SolidColorBrush1"
|
||||
Value="{StaticResource PrimaryColorAlternativeBrush}" />
|
||||
<Setter Property="attachedProperties:SolidColorBrushProperties.SolidColorBrush2"
|
||||
Value="{StaticResource PrimaryColorAlternativeBrush}" />
|
||||
<Setter Property="attachedProperties:SolidColorBrushProperties.SolidColorBrush3"
|
||||
Value="{StaticResource White1Brush}" />
|
||||
</Style>
|
||||
|
||||
<Style TargetType="{x:Type ButtonBase}"
|
||||
BasedOn="{StaticResource LiviaTextButtonBase}"
|
||||
x:Key="LiviaWhiteTextButton">
|
||||
<Setter Property="BorderBrush"
|
||||
Value="{StaticResource PrimaryColorBrush}" />
|
||||
<Setter Property="Background"
|
||||
Value="{StaticResource White1Brush}" />
|
||||
<Setter Property="Foreground"
|
||||
Value="{StaticResource PrimaryColorBrush}" />
|
||||
<Setter Property="attachedProperties:StringItemProperties.ItemName"
|
||||
Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(attachedProperties:StringItemProperties.ItemName)}" />
|
||||
<Setter Property="attachedProperties:SolidColorBrushProperties.SolidColorBrush1"
|
||||
Value="{StaticResource PrimaryColorBrush}" />
|
||||
<Setter Property="attachedProperties:SolidColorBrushProperties.SolidColorBrush2"
|
||||
Value="{StaticResource PrimaryColorAlternativeBrush}" />
|
||||
<Setter Property="attachedProperties:SolidColorBrushProperties.SolidColorBrush3"
|
||||
Value="{StaticResource White1Brush}" />
|
||||
</Style>
|
||||
|
||||
<Style TargetType="{x:Type ButtonBase}"
|
||||
BasedOn="{StaticResource LiviaTextButtonBase}"
|
||||
x:Key="LiviaLoginTextButton">
|
||||
<Setter Property="BorderBrush"
|
||||
Value="{StaticResource LoginColorBrush}" />
|
||||
<Setter Property="Background"
|
||||
Value="{StaticResource LoginColorBrush}" />
|
||||
<Setter Property="Foreground"
|
||||
Value="{StaticResource White1Brush}" />
|
||||
<Setter Property="attachedProperties:StringItemProperties.ItemName"
|
||||
Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(attachedProperties:StringItemProperties.ItemName)}" />
|
||||
<Setter Property="attachedProperties:SolidColorBrushProperties.SolidColorBrush1"
|
||||
Value="{StaticResource LoginColorBrush}" />
|
||||
<Setter Property="attachedProperties:SolidColorBrushProperties.SolidColorBrush2"
|
||||
Value="{StaticResource LoginAlternativeColorBrush}" />
|
||||
<Setter Property="attachedProperties:SolidColorBrushProperties.SolidColorBrush3"
|
||||
Value="{StaticResource White1Brush}" />
|
||||
</Style>
|
||||
|
||||
<Style TargetType="{x:Type ButtonBase}"
|
||||
BasedOn="{StaticResource LiviaTextButtonBase}"
|
||||
x:Key="LiviaAlertTextButton">
|
||||
<Setter Property="BorderBrush"
|
||||
Value="{StaticResource AlertColorBrush}" />
|
||||
<Setter Property="Background"
|
||||
Value="{StaticResource White1Brush}" />
|
||||
<Setter Property="Foreground"
|
||||
Value="{StaticResource AlertColorBrush}" />
|
||||
<Setter Property="attachedProperties:StringItemProperties.ItemName"
|
||||
Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(attachedProperties:StringItemProperties.ItemName)}" />
|
||||
<Setter Property="attachedProperties:SolidColorBrushProperties.SolidColorBrush1"
|
||||
Value="{StaticResource AlertColorBrush}" />
|
||||
<Setter Property="attachedProperties:SolidColorBrushProperties.SolidColorBrush2"
|
||||
Value="{StaticResource AlertAlternativeColorBrush}" />
|
||||
<Setter Property="attachedProperties:SolidColorBrushProperties.SolidColorBrush3"
|
||||
Value="{StaticResource White1Brush}" />
|
||||
</Style>
|
||||
|
||||
<Style TargetType="{x:Type ButtonBase}"
|
||||
x:Key="LiviaTinyIconButtonBase">
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type ButtonBase}">
|
||||
<Border Name="Border"
|
||||
Width="20"
|
||||
Height="20"
|
||||
BorderThickness="1"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
CornerRadius="36"
|
||||
Background="{TemplateBinding Background}">
|
||||
<Button Name="Button"
|
||||
Style="{StaticResource MaterialDesignRaisedButton}"
|
||||
Padding="0"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Height="20"
|
||||
Width="20"
|
||||
materialDesign:ElevationAssist.Elevation="Dp0"
|
||||
BorderThickness="0"
|
||||
Command="{TemplateBinding Command}"
|
||||
CommandParameter="{TemplateBinding CommandParameter}"
|
||||
Background="Transparent"
|
||||
Foreground="{TemplateBinding Foreground}">
|
||||
<materialDesign:PackIcon Kind="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(attachedProperties:IconKindProperties.IconKind)}"
|
||||
Padding="20"
|
||||
Margin="0"
|
||||
Width="16"
|
||||
Height="16"
|
||||
Name="ButtonIcon" />
|
||||
</Button>
|
||||
</Border>
|
||||
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsMouseOver"
|
||||
Value="True">
|
||||
<Setter TargetName="Border"
|
||||
Property="Background"
|
||||
Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(attachedProperties:SolidColorBrushProperties.SolidColorBrush1)}" />
|
||||
<Setter TargetName="Border"
|
||||
Property="BorderBrush"
|
||||
Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(attachedProperties:SolidColorBrushProperties.SolidColorBrush2)}" />
|
||||
<Setter TargetName="Button"
|
||||
Property="Foreground"
|
||||
Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(attachedProperties:SolidColorBrushProperties.SolidColorBrush3)}" />
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<Style TargetType="{x:Type ButtonBase}"
|
||||
BasedOn="{StaticResource LiviaTinyIconButtonBase}"
|
||||
x:Key="LiviaPrimaryTinyIconButton">
|
||||
<Setter Property="ToolTipService.InitialShowDelay"
|
||||
Value="100" />
|
||||
<Setter Property="Background"
|
||||
Value="Transparent" />
|
||||
<Setter Property="Foreground"
|
||||
Value="{StaticResource DataGridAlternatingRowBackgroundBrush}" />
|
||||
<Setter Property="BorderBrush"
|
||||
Value="Transparent" />
|
||||
<Setter Property="attachedProperties:IconKindProperties.IconKind"
|
||||
Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(attachedProperties:IconKindProperties.IconKind)}" />
|
||||
<Setter Property="attachedProperties:SolidColorBrushProperties.SolidColorBrush1"
|
||||
Value="Transparent" />
|
||||
<Setter Property="attachedProperties:SolidColorBrushProperties.SolidColorBrush2"
|
||||
Value="Transparent" />
|
||||
<Setter Property="attachedProperties:SolidColorBrushProperties.SolidColorBrush3"
|
||||
Value="{StaticResource PrimaryColorHighlightBrush}" />
|
||||
</Style>
|
||||
|
||||
<Style TargetType="{x:Type ButtonBase}"
|
||||
x:Key="LiviaIconButtonBase">
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type ButtonBase}">
|
||||
<Border Name="Border"
|
||||
Width="36"
|
||||
Height="36"
|
||||
BorderThickness="1"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
CornerRadius="36"
|
||||
Background="{TemplateBinding Background}">
|
||||
<Button Name="Button"
|
||||
Style="{StaticResource MaterialDesignRaisedButton}"
|
||||
Padding="0"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Height="36"
|
||||
Width="36"
|
||||
materialDesign:ElevationAssist.Elevation="Dp0"
|
||||
BorderThickness="0"
|
||||
Command="{TemplateBinding Command}"
|
||||
CommandParameter="{TemplateBinding CommandParameter}"
|
||||
Background="Transparent"
|
||||
Foreground="{TemplateBinding Foreground}">
|
||||
<materialDesign:PackIcon Kind="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(attachedProperties:IconKindProperties.IconKind)}"
|
||||
Padding="0"
|
||||
Margin="0"
|
||||
Width="16"
|
||||
Height="16"
|
||||
Name="ButtonIcon" />
|
||||
</Button>
|
||||
</Border>
|
||||
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsMouseOver"
|
||||
Value="True">
|
||||
<Setter TargetName="Border"
|
||||
Property="Background"
|
||||
Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(attachedProperties:SolidColorBrushProperties.SolidColorBrush1)}" />
|
||||
<Setter TargetName="Border"
|
||||
Property="BorderBrush"
|
||||
Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(attachedProperties:SolidColorBrushProperties.SolidColorBrush2)}" />
|
||||
<Setter TargetName="Button"
|
||||
Property="Foreground"
|
||||
Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(attachedProperties:SolidColorBrushProperties.SolidColorBrush3)}" />
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<Style TargetType="{x:Type ButtonBase}"
|
||||
BasedOn="{StaticResource LiviaIconButtonBase}"
|
||||
x:Key="LiviaAlertIconButton">
|
||||
<Setter Property="Background"
|
||||
Value="Transparent" />
|
||||
<Setter Property="Foreground"
|
||||
Value="{StaticResource AlertColorBrush}" />
|
||||
<Setter Property="BorderBrush"
|
||||
Value="Transparent" />
|
||||
<Setter Property="attachedProperties:IconKindProperties.IconKind"
|
||||
Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(attachedProperties:IconKindProperties.IconKind)}" />
|
||||
<Setter Property="attachedProperties:SolidColorBrushProperties.SolidColorBrush1"
|
||||
Value="Transparent" />
|
||||
<Setter Property="attachedProperties:SolidColorBrushProperties.SolidColorBrush2"
|
||||
Value="Transparent" />
|
||||
<Setter Property="attachedProperties:SolidColorBrushProperties.SolidColorBrush3"
|
||||
Value="{StaticResource AlertAlternativeColorBrush}" />
|
||||
</Style>
|
||||
<Style TargetType="{x:Type ButtonBase}"
|
||||
BasedOn="{StaticResource LiviaIconButtonBase}"
|
||||
x:Key="LiviaPrimaryIconButton">
|
||||
<Setter Property="Background"
|
||||
Value="Transparent" />
|
||||
<Setter Property="Foreground"
|
||||
Value="{StaticResource PrimaryTextColorBrush}" />
|
||||
<Setter Property="BorderBrush"
|
||||
Value="Transparent" />
|
||||
<Setter Property="attachedProperties:IconKindProperties.IconKind"
|
||||
Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(attachedProperties:IconKindProperties.IconKind)}" />
|
||||
<Setter Property="attachedProperties:SolidColorBrushProperties.SolidColorBrush1"
|
||||
Value="Transparent" />
|
||||
<Setter Property="attachedProperties:SolidColorBrushProperties.SolidColorBrush2"
|
||||
Value="Transparent" />
|
||||
<Setter Property="attachedProperties:SolidColorBrushProperties.SolidColorBrush3"
|
||||
Value="{StaticResource White1Brush}" />
|
||||
</Style>
|
||||
|
||||
<Style TargetType="{x:Type ButtonBase}"
|
||||
x:Key="LiviaTextProcessingButtonBase">
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type ButtonBase}">
|
||||
<Grid>
|
||||
<Button Style="{StaticResource MaterialDesignRaisedButton}"
|
||||
Name="Button"
|
||||
Height="40"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
materialDesign:ButtonAssist.CornerRadius="20"
|
||||
materialDesign:ElevationAssist.Elevation="Dp0"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
Background="{TemplateBinding Background}"
|
||||
Foreground="{TemplateBinding Foreground}">
|
||||
<StackPanel Orientation="Horizontal"
|
||||
Margin="20, 0, 20, 1.5">
|
||||
<TextBlock Style="{StaticResource MaterialDesignButtonTextBlock}"
|
||||
Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(attachedProperties:StringItemProperties.ItemName)}"
|
||||
Name="ButtonText" />
|
||||
<Grid Width="{Binding ActualWidth, ElementName=ButtonText}"
|
||||
Name="ProgressBar">
|
||||
<ProgressBar IsIndeterminate="True"
|
||||
Foreground="{TemplateBinding Foreground}"
|
||||
Style="{StaticResource MaterialDesignCircularProgressBar}" />
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
</Grid>
|
||||
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsMouseOver"
|
||||
Value="True">
|
||||
<Setter TargetName="Button"
|
||||
Property="BorderBrush"
|
||||
Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(attachedProperties:SolidColorBrushProperties.SolidColorBrush1)}" />
|
||||
<Setter TargetName="Button"
|
||||
Property="Background"
|
||||
Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(attachedProperties:SolidColorBrushProperties.SolidColorBrush2)}" />
|
||||
<Setter TargetName="Button"
|
||||
Property="Foreground"
|
||||
Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(attachedProperties:SolidColorBrushProperties.SolidColorBrush3)}" />
|
||||
</Trigger>
|
||||
|
||||
<Trigger Property="IsEnabled"
|
||||
Value="True">
|
||||
<Setter TargetName="ProgressBar"
|
||||
Property="Visibility"
|
||||
Value="Collapsed" />
|
||||
</Trigger>
|
||||
<Trigger Property="IsEnabled"
|
||||
Value="False">
|
||||
<Setter TargetName="ButtonText"
|
||||
Property="Visibility"
|
||||
Value="Collapsed" />
|
||||
</Trigger>
|
||||
|
||||
</ControlTemplate.Triggers>
|
||||
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<Style TargetType="{x:Type ButtonBase}"
|
||||
BasedOn="{StaticResource LiviaTextProcessingButtonBase}"
|
||||
x:Key="LiviaPrimaryProcessingTextButton">
|
||||
<Setter Property="BorderBrush"
|
||||
Value="{StaticResource PrimaryColorHighlightBrush}" />
|
||||
<Setter Property="Background"
|
||||
Value="{StaticResource PrimaryColorHighlightBrush}" />
|
||||
<Setter Property="Foreground"
|
||||
Value="{StaticResource White1Brush}" />
|
||||
<Setter Property="attachedProperties:StringItemProperties.ItemName"
|
||||
Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(attachedProperties:StringItemProperties.ItemName)}" />
|
||||
<Setter Property="attachedProperties:SolidColorBrushProperties.SolidColorBrush1"
|
||||
Value="{StaticResource PrimaryColorAlternativeBrush}" />
|
||||
<Setter Property="attachedProperties:SolidColorBrushProperties.SolidColorBrush2"
|
||||
Value="{StaticResource PrimaryColorAlternativeBrush}" />
|
||||
<Setter Property="attachedProperties:SolidColorBrushProperties.SolidColorBrush3"
|
||||
Value="{StaticResource White1Brush}" />
|
||||
</Style>
|
||||
|
||||
<Style TargetType="{x:Type ButtonBase}"
|
||||
BasedOn="{StaticResource LiviaTextProcessingButtonBase}"
|
||||
x:Key="LiviaDarkProcessingTextButton">
|
||||
<Setter Property="BorderBrush"
|
||||
Value="{StaticResource PrimaryColorHighlightBrush}" />
|
||||
<Setter Property="Background"
|
||||
Value="Transparent" />
|
||||
<Setter Property="Foreground"
|
||||
Value="{StaticResource White1Brush}" />
|
||||
<Setter Property="attachedProperties:StringItemProperties.ItemName"
|
||||
Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(attachedProperties:StringItemProperties.ItemName)}" />
|
||||
<Setter Property="attachedProperties:SolidColorBrushProperties.SolidColorBrush1"
|
||||
Value="{StaticResource PrimaryColorAlternativeBrush}" />
|
||||
<Setter Property="attachedProperties:SolidColorBrushProperties.SolidColorBrush2"
|
||||
Value="{StaticResource PrimaryColorAlternativeBrush}" />
|
||||
<Setter Property="attachedProperties:SolidColorBrushProperties.SolidColorBrush3"
|
||||
Value="{StaticResource White1Brush}" />
|
||||
</Style>
|
||||
|
||||
<Style TargetType="{x:Type ButtonBase}"
|
||||
BasedOn="{StaticResource LiviaTextProcessingButtonBase}"
|
||||
x:Key="LiviaAlertProcessingTextButton">
|
||||
<Setter Property="BorderBrush"
|
||||
Value="{StaticResource AlertColorBrush}" />
|
||||
<Setter Property="Background"
|
||||
Value="{StaticResource White1Brush}" />
|
||||
<Setter Property="Foreground"
|
||||
Value="{StaticResource AlertColorBrush}" />
|
||||
<Setter Property="attachedProperties:StringItemProperties.ItemName"
|
||||
Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(attachedProperties:StringItemProperties.ItemName)}" />
|
||||
<Setter Property="attachedProperties:SolidColorBrushProperties.SolidColorBrush1"
|
||||
Value="{StaticResource AlertColorBrush}" />
|
||||
<Setter Property="attachedProperties:SolidColorBrushProperties.SolidColorBrush2"
|
||||
Value="{StaticResource AlertAlternativeColorBrush}" />
|
||||
<Setter Property="attachedProperties:SolidColorBrushProperties.SolidColorBrush3"
|
||||
Value="{StaticResource White1Brush}" />
|
||||
</Style>
|
||||
|
||||
<Style TargetType="{x:Type ButtonBase}"
|
||||
x:Key="LiviaProcessingIconButtonBase">
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type ButtonBase}">
|
||||
<Grid>
|
||||
<Button Style="{StaticResource MaterialDesignRaisedButton}"
|
||||
Name="Button"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Height="24"
|
||||
materialDesign:ElevationAssist.Elevation="Dp0"
|
||||
BorderThickness="0"
|
||||
Background="{TemplateBinding Background}"
|
||||
Foreground="{TemplateBinding Foreground}">
|
||||
<StackPanel>
|
||||
<materialDesign:PackIcon Kind="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(attachedProperties:IconKindProperties.IconKind)}"
|
||||
Width="16"
|
||||
Height="16"
|
||||
Name="ButtonIcon" />
|
||||
<ProgressBar IsIndeterminate="True"
|
||||
Width="16"
|
||||
Height="16"
|
||||
Name="ProgressBar"
|
||||
Foreground="{TemplateBinding Foreground}"
|
||||
Style="{StaticResource MaterialDesignCircularProgressBar}" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
</Grid>
|
||||
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsMouseOver"
|
||||
Value="True">
|
||||
<Setter TargetName="Button"
|
||||
Property="Background"
|
||||
Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(attachedProperties:SolidColorBrushProperties.SolidColorBrush1)}" />
|
||||
</Trigger>
|
||||
|
||||
<Trigger Property="IsEnabled"
|
||||
Value="True">
|
||||
<Setter TargetName="ProgressBar"
|
||||
Property="Visibility"
|
||||
Value="Collapsed" />
|
||||
</Trigger>
|
||||
<Trigger Property="IsEnabled"
|
||||
Value="False">
|
||||
<Setter TargetName="ButtonIcon"
|
||||
Property="Visibility"
|
||||
Value="Collapsed" />
|
||||
</Trigger>
|
||||
|
||||
</ControlTemplate.Triggers>
|
||||
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<Style TargetType="{x:Type ButtonBase}"
|
||||
BasedOn="{StaticResource LiviaProcessingIconButtonBase}"
|
||||
x:Key="LiviaPrimaryProcessingIconButton">
|
||||
<Setter Property="Background"
|
||||
Value="{StaticResource PrimaryColorBrush}" />
|
||||
<Setter Property="Foreground"
|
||||
Value="{StaticResource White1Brush}" />
|
||||
<Setter Property="attachedProperties:IconKindProperties.IconKind"
|
||||
Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(attachedProperties:IconKindProperties.IconKind)}" />
|
||||
<Setter Property="attachedProperties:SolidColorBrushProperties.SolidColorBrush1"
|
||||
Value="{StaticResource PrimaryColorAlternativeBrush}" />
|
||||
</Style>
|
||||
|
||||
<Style TargetType="{x:Type ButtonBase}"
|
||||
x:Key="LiviaGridProcessingTextButtonBase">
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type ButtonBase}">
|
||||
<Grid>
|
||||
<Button Style="{StaticResource MaterialDesignRaisedButton}"
|
||||
Name="Button"
|
||||
HorizontalAlignment="Center"
|
||||
Padding="0"
|
||||
Margin="0"
|
||||
VerticalAlignment="Center"
|
||||
materialDesign:ElevationAssist.Elevation="Dp0"
|
||||
BorderThickness="0"
|
||||
Background="Transparent"
|
||||
Foreground="{TemplateBinding Foreground}">
|
||||
<StackPanel>
|
||||
<TextBlock Style="{StaticResource MaterialDesignButtonTextBlock}"
|
||||
Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(attachedProperties:StringItemProperties.ItemName)}"
|
||||
VerticalAlignment="Center"
|
||||
Name="ButtonText" />
|
||||
<ProgressBar IsIndeterminate="True"
|
||||
Name="ProgressBar"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{TemplateBinding Foreground}"
|
||||
Style="{StaticResource MaterialDesignCircularProgressBar}" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
</Grid>
|
||||
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsMouseOver"
|
||||
Value="True">
|
||||
<Setter TargetName="Button"
|
||||
Property="Foreground"
|
||||
Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(attachedProperties:SolidColorBrushProperties.SolidColorBrush1)}" />
|
||||
</Trigger>
|
||||
|
||||
<Trigger Property="IsEnabled"
|
||||
Value="True">
|
||||
<Setter TargetName="ProgressBar"
|
||||
Property="Visibility"
|
||||
Value="Collapsed" />
|
||||
</Trigger>
|
||||
<Trigger Property="IsEnabled"
|
||||
Value="False">
|
||||
<Setter TargetName="ButtonText"
|
||||
Property="Visibility"
|
||||
Value="Collapsed" />
|
||||
</Trigger>
|
||||
|
||||
</ControlTemplate.Triggers>
|
||||
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<Style TargetType="{x:Type ButtonBase}"
|
||||
BasedOn="{StaticResource LiviaGridProcessingTextButtonBase}"
|
||||
x:Key="LiviaPrimaryGridProcessingTextButton">
|
||||
<Setter Property="Foreground"
|
||||
Value="{StaticResource PrimaryGridTextButtonColorBrush}" />
|
||||
<Setter Property="attachedProperties:SolidColorBrushProperties.SolidColorBrush1"
|
||||
Value="{StaticResource White1Brush}" />
|
||||
</Style>
|
||||
|
||||
<Style TargetType="{x:Type ButtonBase}"
|
||||
BasedOn="{StaticResource LiviaGridProcessingTextButtonBase}"
|
||||
x:Key="LiviaPrimaryGridProcessingAlertTextButton">
|
||||
<Setter Property="Foreground"
|
||||
Value="{StaticResource AlertColorBrush}" />
|
||||
<Setter Property="attachedProperties:SolidColorBrushProperties.SolidColorBrush1"
|
||||
Value="{StaticResource White1Brush}" />
|
||||
</Style>
|
||||
|
||||
<Style TargetType="{x:Type ButtonBase}"
|
||||
BasedOn="{StaticResource LiviaIconButtonBase}"
|
||||
x:Key="LiviaStretchIconButton">
|
||||
<Setter Property="attachedProperties:IconKindProperties.IconKind"
|
||||
Value="fullscreen" />
|
||||
<Setter Property="HorizontalAlignment"
|
||||
Value="Right" />
|
||||
<Setter Property="VerticalAlignment"
|
||||
Value="Bottom" />
|
||||
<Setter Property="Margin"
|
||||
Value="5" />
|
||||
<Setter Property="Visibility"
|
||||
Value="{Binding Path=IsMouseOver, RelativeSource={RelativeSource AncestorType={x:Type Grid}, Mode=FindAncestor}, Converter={StaticResource BooleanToVisibilityConverter}}" />
|
||||
<Setter Property="Background"
|
||||
Value="{StaticResource PrimaryColorAlternative2Brush}" />
|
||||
<Setter Property="Foreground"
|
||||
Value="{StaticResource White1Brush}" />
|
||||
<Setter Property="BorderBrush"
|
||||
Value="{StaticResource Gray4Brush}" />
|
||||
<Setter Property="attachedProperties:SolidColorBrushProperties.SolidColorBrush1"
|
||||
Value="{StaticResource PrimaryColorAlternative2Brush}" />
|
||||
<Setter Property="attachedProperties:SolidColorBrushProperties.SolidColorBrush2"
|
||||
Value="{StaticResource PrimaryColorHighlightBrush}" />
|
||||
<Setter Property="attachedProperties:SolidColorBrushProperties.SolidColorBrush3"
|
||||
Value="{StaticResource PrimaryColorHighlightBrush}" />
|
||||
<Setter Property="Command"
|
||||
Value="{x:Static materialDesign:DialogHost.OpenDialogCommand}" />
|
||||
</Style>
|
||||
|
||||
<Style TargetType="{x:Type ButtonBase}"
|
||||
x:Key="LiviaTextIconButtonBase">
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type ButtonBase}">
|
||||
<Grid>
|
||||
<Button Name="Button"
|
||||
materialDesign:RippleAssist.IsDisabled="True"
|
||||
BorderThickness="0"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Padding="0"
|
||||
Margin="0"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
Background="{TemplateBinding Background}"
|
||||
Foreground="{TemplateBinding Foreground}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<materialDesign:PackIcon Kind="{Binding (attachedProperties:IconKindProperties.IconKind), RelativeSource={RelativeSource TemplatedParent}}"
|
||||
Name="ButtonIcon"
|
||||
Margin="0, 2, 5, 0"
|
||||
VerticalAlignment="Center" />
|
||||
<TextBlock Style="{StaticResource MaterialDesignButtonTextBlock}"
|
||||
Text="{Binding (attachedProperties:StringItemProperties.ItemName), RelativeSource={RelativeSource TemplatedParent}}"
|
||||
Name="ButtonText"
|
||||
VerticalAlignment="Center" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
</Grid>
|
||||
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsMouseOver"
|
||||
Value="True">
|
||||
<Setter TargetName="Button"
|
||||
Property="Foreground"
|
||||
Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(attachedProperties:SolidColorBrushProperties.SolidColorBrush1)}" />
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<Style TargetType="{x:Type ButtonBase}"
|
||||
BasedOn="{StaticResource LiviaTextIconButtonBase}"
|
||||
x:Key="LiviaPrimaryTextIconButton">
|
||||
<Setter Property="Background"
|
||||
Value="Transparent" />
|
||||
<Setter Property="Foreground"
|
||||
Value="{StaticResource PrimaryTextColorBrush}" />
|
||||
<Setter Property="BorderBrush"
|
||||
Value="Transparent" />
|
||||
<Setter Property="attachedProperties:SolidColorBrushProperties.SolidColorBrush1"
|
||||
Value="{StaticResource White1Brush}" />
|
||||
</Style>
|
||||
|
||||
<Style TargetType="{x:Type ButtonBase}"
|
||||
BasedOn="{StaticResource LiviaTextIconButtonBase}"
|
||||
x:Key="LiviaAlertTextIconButton">
|
||||
<Setter Property="Background"
|
||||
Value="Transparent" />
|
||||
<Setter Property="Foreground"
|
||||
Value="{StaticResource AlertColorBrush}" />
|
||||
<Setter Property="BorderBrush"
|
||||
Value="Transparent" />
|
||||
<Setter Property="attachedProperties:SolidColorBrushProperties.SolidColorBrush1"
|
||||
Value="{StaticResource AlertAlternativeColorBrush}" />
|
||||
</Style>
|
||||
|
||||
</ResourceDictionary>
|
35
Livia/Resources/Themes/LiviaCard.xaml
Normal file
|
@ -0,0 +1,35 @@
|
|||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
xmlns:attachedProperties="clr-namespace:Livia.Views.AttachedProperties">
|
||||
<Style TargetType="{x:Type materialDesign:Card}"
|
||||
x:Key="LiviaCard">
|
||||
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type materialDesign:Card}">
|
||||
<materialDesign:Card UniformCornerRadius="6"
|
||||
Background="Transparent">
|
||||
<StackPanel>
|
||||
<Border Height="64"
|
||||
CornerRadius="6, 6, 0, 0"
|
||||
Background="{StaticResource PrimaryColorBrush}">
|
||||
<TextBlock Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(attachedProperties:StringItemProperties.ItemName)}"
|
||||
Style="{StaticResource MaterialDesignBody1TextBlock}"
|
||||
Margin="20, 0"
|
||||
Foreground="{StaticResource White1Brush}"
|
||||
VerticalAlignment="Center" />
|
||||
</Border>
|
||||
<Border CornerRadius="0, 0, 6, 6"
|
||||
Background="{StaticResource White1Brush}">
|
||||
<ContentPresenter x:Name="ContentPresenter"
|
||||
Margin="20" />
|
||||
</Border>
|
||||
</StackPanel>
|
||||
</materialDesign:Card>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
|
||||
</Style>
|
||||
</ResourceDictionary>
|
190
Livia/Resources/Themes/LiviaDataGrid.xaml
Normal file
|
@ -0,0 +1,190 @@
|
|||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes">
|
||||
|
||||
<Style x:Key="LiviaDataGridCell"
|
||||
TargetType="{x:Type DataGridCell}">
|
||||
<Setter Property="Padding"
|
||||
Value="5" />
|
||||
<Setter Property="Background"
|
||||
Value="Transparent" />
|
||||
<Setter Property="BorderBrush"
|
||||
Value="Transparent" />
|
||||
<Setter Property="BorderThickness"
|
||||
Value="0" />
|
||||
<Setter Property="Margin"
|
||||
Value="0" />
|
||||
<Setter Property="Validation.ErrorTemplate"
|
||||
Value="{x:Null}" />
|
||||
<Setter Property="Foreground"
|
||||
Value="{Binding Foreground, RelativeSource={RelativeSource AncestorType=DataGridRow}}" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type DataGridCell}">
|
||||
<Grid>
|
||||
<Border Background="{TemplateBinding Background}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
SnapsToDevicePixels="True" />
|
||||
<ContentPresenter Margin="{TemplateBinding Padding}"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Center"
|
||||
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Style.Triggers>
|
||||
<Trigger Property="IsEnabled"
|
||||
Value="False">
|
||||
<Setter Property="Opacity"
|
||||
Value=".56" />
|
||||
</Trigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
|
||||
<Style x:Key="LiviaDataGridColumnHeader"
|
||||
TargetType="{x:Type DataGridColumnHeader}">
|
||||
<Setter Property="Background"
|
||||
Value="{StaticResource DataGridHeaderBackgroundBrush}" />
|
||||
<Setter Property="Foreground"
|
||||
Value="{StaticResource White1Brush}" />
|
||||
<Setter Property="FontSize"
|
||||
Value="16" />
|
||||
|
||||
<Setter Property="VerticalContentAlignment"
|
||||
Value="Center" />
|
||||
<Setter Property="HorizontalContentAlignment"
|
||||
Value="Center" />
|
||||
<Setter Property="TextBlock.FontWeight"
|
||||
Value="Medium" />
|
||||
<Setter Property="Padding"
|
||||
Value="{Binding RelativeSource={RelativeSource Self}, Path=(materialDesign:DataGridAssist.ColumnHeaderPadding)}" />
|
||||
<Setter Property="BorderBrush"
|
||||
Value="{Binding HorizontalGridLinesBrush, RelativeSource={RelativeSource AncestorType=DataGrid}}" />
|
||||
<Setter Property="BorderThickness"
|
||||
Value="{Binding GridLinesVisibility, RelativeSource={RelativeSource AncestorType=DataGrid}, Converter={StaticResource GridLinesVisibilityBorderToThicknessConverter}}" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type DataGridColumnHeader}">
|
||||
<ControlTemplate.Resources>
|
||||
<Storyboard x:Key="GoToVisible">
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="SortDirectionIndicator"
|
||||
Storyboard.TargetProperty="Width">
|
||||
<EasingDoubleKeyFrame KeyTime="0:0:0.0"
|
||||
Value="0" />
|
||||
<EasingDoubleKeyFrame KeyTime="0:0:0.2"
|
||||
Value="12" />
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
<Storyboard x:Key="GoToHidden">
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="SortDirectionIndicator"
|
||||
Storyboard.TargetProperty="Width">
|
||||
<EasingDoubleKeyFrame KeyTime="0:0:0.0"
|
||||
Value="12" />
|
||||
<EasingDoubleKeyFrame KeyTime="0:0:0.2"
|
||||
Value="0" />
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</ControlTemplate.Resources>
|
||||
<Grid Background="Transparent">
|
||||
<Border Padding="{TemplateBinding Padding}"
|
||||
Background="{TemplateBinding Background}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}">
|
||||
<StackPanel HorizontalAlignment="Center"
|
||||
Orientation="Horizontal">
|
||||
<ContentPresenter VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||
RecognizesAccessKey="True"
|
||||
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}">
|
||||
<ContentPresenter.Resources>
|
||||
<Style TargetType="TextBlock">
|
||||
<Setter Property="TextTrimming"
|
||||
Value="WordEllipsis" />
|
||||
<Setter Property="Padding"
|
||||
Value="8,0,0,0" />
|
||||
<Setter Property="materialDesign:TextBlockAssist.AutoToolTip"
|
||||
Value="True" />
|
||||
</Style>
|
||||
</ContentPresenter.Resources>
|
||||
</ContentPresenter>
|
||||
|
||||
<materialDesign:ListSortDirectionIndicator x:Name="SortDirectionIndicator"
|
||||
Width="0"
|
||||
Height="12"
|
||||
Margin="5"
|
||||
HorizontalAlignment="Left"
|
||||
IsTabStop="False"
|
||||
Foreground="{TemplateBinding Foreground}"
|
||||
ListSortDirection="{TemplateBinding SortDirection}"
|
||||
Visibility="{Binding CanUserSortColumns, RelativeSource={RelativeSource AncestorType=DataGrid}, Converter={StaticResource BooleanToVisibilityConverter}}" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
<Thumb x:Name="PART_LeftHeaderGripper"
|
||||
HorizontalAlignment="Left"
|
||||
Style="{StaticResource MaterialDesignGridColumnHeaderGripper}" />
|
||||
<Thumb x:Name="PART_RightHeaderGripper"
|
||||
HorizontalAlignment="Right"
|
||||
Style="{StaticResource MaterialDesignGridColumnHeaderGripper}" />
|
||||
</Grid>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger SourceName="SortDirectionIndicator"
|
||||
Property="IsNeutral"
|
||||
Value="False">
|
||||
<Trigger.EnterActions>
|
||||
<BeginStoryboard Name="GoToVisible"
|
||||
Storyboard="{StaticResource GoToVisible}" />
|
||||
</Trigger.EnterActions>
|
||||
<Trigger.ExitActions>
|
||||
<RemoveStoryboard BeginStoryboardName="GoToVisible" />
|
||||
<BeginStoryboard Storyboard="{StaticResource GoToHidden}" />
|
||||
</Trigger.ExitActions>
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<Style x:Key="ServerJobListColumnHeader"
|
||||
TargetType="{x:Type DataGridColumnHeader}"
|
||||
BasedOn="{StaticResource LiviaDataGridColumnHeader}">
|
||||
<Setter Property="Background"
|
||||
Value="{StaticResource PrimaryColorBrush}" />
|
||||
<Setter Property="Foreground"
|
||||
Value="{StaticResource PrimaryTextColorBrush}" />
|
||||
</Style>
|
||||
|
||||
<!--TODO::wait for MaterialDesignInXAML update and change DataGridAssist.SelectedCellBorderBrush-->
|
||||
<Style x:Key="LiviaDataGrid"
|
||||
TargetType="{x:Type DataGrid}"
|
||||
BasedOn="{StaticResource MaterialDesignDataGrid}">
|
||||
|
||||
<Setter Property="CanUserAddRows"
|
||||
Value="False" />
|
||||
<Setter Property="CanUserReorderColumns"
|
||||
Value="False" />
|
||||
<Setter Property="CanUserDeleteRows"
|
||||
Value="False" />
|
||||
<Setter Property="AutoGenerateColumns"
|
||||
Value="False" />
|
||||
<Setter Property="AlternationCount"
|
||||
Value="2" />
|
||||
<Setter Property="GridLinesVisibility"
|
||||
Value="All" />
|
||||
<Setter Property="HeadersVisibility"
|
||||
Value="Column" />
|
||||
<Setter Property="CanUserResizeColumns"
|
||||
Value="False" />
|
||||
</Style>
|
||||
|
||||
<Style x:Key="ServerJobListText"
|
||||
TargetType="{x:Type TextBlock}"
|
||||
BasedOn="{StaticResource MaterialDesignBody1TextBlock}">
|
||||
<Setter Property="Margin"
|
||||
Value="10" />
|
||||
<Setter Property="VerticalAlignment"
|
||||
Value="Center" />
|
||||
</Style>
|
||||
|
||||
</ResourceDictionary>
|
36
Livia/Resources/Themes/LiviaDatePicker.xaml
Normal file
|
@ -0,0 +1,36 @@
|
|||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes">
|
||||
<Style x:Key="LiviaCalendarDayButton"
|
||||
TargetType="CalendarDayButton"
|
||||
BasedOn="{StaticResource MaterialDesignCalendarDayButton}">
|
||||
<Setter Property="materialDesign:CalendarAssist.SelectionColor"
|
||||
Value="{StaticResource PrimaryColorHighlightBrush}" />
|
||||
<Setter Property="materialDesign:CalendarAssist.SelectionForegroundColor"
|
||||
Value="{StaticResource Gray1Brush}" />
|
||||
</Style>
|
||||
<Style x:Key="LiviaCalendarButton"
|
||||
TargetType="CalendarButton"
|
||||
BasedOn="{StaticResource MaterialDesignCalendarButton}">
|
||||
<Setter Property="materialDesign:CalendarAssist.SelectionColor"
|
||||
Value="{StaticResource PrimaryColorHighlightBrush}" />
|
||||
<Setter Property="materialDesign:CalendarAssist.SelectionForegroundColor"
|
||||
Value="{StaticResource Gray1Brush}" />
|
||||
</Style>
|
||||
<Style x:Key="LiviaCalendar"
|
||||
TargetType="{x:Type Calendar}"
|
||||
BasedOn="{StaticResource MaterialDesignDatePickerCalendarPortrait}">
|
||||
<Setter Property="Background"
|
||||
Value="{StaticResource CalendarBackground}" />
|
||||
<Setter Property="Foreground"
|
||||
Value="{StaticResource Gray3Brush}" />
|
||||
<Setter Property="materialDesign:CalendarAssist.HeaderBackground"
|
||||
Value="{StaticResource ServerJobListRowBackground}" />
|
||||
<Setter Property="materialDesign:CalendarAssist.HeaderForeground"
|
||||
Value="{StaticResource Gray1Brush}" />
|
||||
<Setter Property="CalendarDayButtonStyle"
|
||||
Value="{StaticResource LiviaCalendarDayButton}" />
|
||||
<Setter Property="CalendarButtonStyle"
|
||||
Value="{StaticResource LiviaCalendarButton}" />
|
||||
</Style>
|
||||
</ResourceDictionary>
|
21
Livia/Resources/Themes/LiviaDialogDataTemplate.xaml
Normal file
|
@ -0,0 +1,21 @@
|
|||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:viewModels="clr-namespace:Livia.ViewModels"
|
||||
xmlns:controls="clr-namespace:Livia.Views.Controls">
|
||||
|
||||
<DataTemplate DataType="{x:Type viewModels:ImageRotationViewerControlViewModel}">
|
||||
<controls:ImageRotationViewerControl DataContext="{Binding }"
|
||||
Height="1000" />
|
||||
</DataTemplate>
|
||||
|
||||
<DataTemplate DataType="{x:Type viewModels:MosaicImageControlViewModel}">
|
||||
<controls:MosaicImageControl DataContext="{Binding}"
|
||||
Height="1000" />
|
||||
</DataTemplate>
|
||||
|
||||
<DataTemplate DataType="{x:Type viewModels:PerfusionDataGridControlViewModel}">
|
||||
<controls:PerfusionDataGridControl DataContext="{Binding}"
|
||||
Width="1500" />
|
||||
</DataTemplate>
|
||||
|
||||
</ResourceDictionary>
|
23
Livia/Resources/Themes/LiviaResourceDictionary.xaml
Normal file
|
@ -0,0 +1,23 @@
|
|||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<materialDesign:BundledTheme BaseTheme="Light"
|
||||
PrimaryColor="Blue"
|
||||
SecondaryColor="Red" />
|
||||
<ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Defaults.xaml" />
|
||||
<ResourceDictionary Source="pack://application:,,,/Livia;component/Resources/Strings/Strings.xaml" />
|
||||
<ResourceDictionary Source="pack://application:,,,/Livia;component/Resources/Strings/Licenses.xaml" />
|
||||
<ResourceDictionary Source="pack://application:,,,/Livia;component/Resources/Themes/LiviaTheme.xaml" />
|
||||
<ResourceDictionary Source="pack://application:,,,/Livia;component/Resources/Themes/LiviaWindow.xaml" />
|
||||
<ResourceDictionary Source="pack://application:,,,/Livia;component/Resources/Themes/LiviaDialogDataTemplate.xaml" />
|
||||
<ResourceDictionary Source="pack://application:,,,/Livia;component/Resources/Themes/LiviaButton.xaml" />
|
||||
<ResourceDictionary Source="pack://application:,,,/Livia;component/Resources/Themes/LiviaCard.xaml" />
|
||||
<ResourceDictionary Source="pack://application:,,,/Livia;component/Resources/Themes/LiviaDataGrid.xaml" />
|
||||
<ResourceDictionary Source="pack://application:,,,/Livia;component/Resources/Themes/LiviaDatePicker.xaml" />
|
||||
<ResourceDictionary Source="pack://application:,,,/Livia;component/Resources/Themes/LiviaScrollViewer.xaml" />
|
||||
<ResourceDictionary Source="pack://application:,,,/Livia;component/Resources/Themes/MaterialTitleBarFloatingActionButton.xaml" />
|
||||
<ResourceDictionary Source="pack://application:,,,/Livia;component/Resources/Themes/MainWindowTabButton.xaml" />
|
||||
<ResourceDictionary Source="pack://application:,,,/Livia;component/Resources/Themes/SettingsWindowItem.xaml" />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
181
Livia/Resources/Themes/LiviaScrollViewer.xaml
Normal file
|
@ -0,0 +1,181 @@
|
|||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes">
|
||||
|
||||
<ControlTemplate x:Key="LiviaScrollViewerTemplate"
|
||||
TargetType="{x:Type ScrollViewer}">
|
||||
<Grid x:Name="Grid"
|
||||
Background="{TemplateBinding Background}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Rectangle x:Name="PART_Corner"
|
||||
Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
Fill="{DynamicResource MaterialDesignSelection}"
|
||||
Visibility="{Binding Path=(materialDesign:ScrollViewerAssist.CornerRectangleVisibility), RelativeSource={RelativeSource TemplatedParent}}" />
|
||||
|
||||
<ScrollContentPresenter x:Name="PART_ScrollContentPresenter"
|
||||
Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
CanContentScroll="{TemplateBinding CanContentScroll}"
|
||||
CanHorizontallyScroll="False"
|
||||
CanVerticallyScroll="False"
|
||||
Content="{TemplateBinding Content}"
|
||||
ContentTemplate="{TemplateBinding ContentTemplate}" />
|
||||
|
||||
<Separator x:Name="PART_TopSeparator"
|
||||
Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
Margin="0"
|
||||
VerticalAlignment="Top"
|
||||
Background="{DynamicResource MaterialDesignSelection}"
|
||||
Opacity="0.4"
|
||||
Visibility="Visible" />
|
||||
|
||||
<Separator x:Name="PART_BottomSeparator"
|
||||
Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
Margin="0"
|
||||
VerticalAlignment="Bottom"
|
||||
Background="{DynamicResource MaterialDesignSelection}"
|
||||
Opacity="0.4"
|
||||
Visibility="Hidden" />
|
||||
|
||||
<ScrollBar x:Name="PART_VerticalScrollBar"
|
||||
Grid.Row="0"
|
||||
Grid.Column="1"
|
||||
Background="{StaticResource White1Brush}"
|
||||
AutomationProperties.AutomationId="VerticalScrollBar"
|
||||
Cursor="Arrow"
|
||||
Maximum="{TemplateBinding ScrollableHeight}"
|
||||
Minimum="0"
|
||||
ViewportSize="{TemplateBinding ViewportHeight}"
|
||||
Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}"
|
||||
Value="{Binding VerticalOffset, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}" />
|
||||
|
||||
<ScrollBar x:Name="PART_HorizontalScrollBar"
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Background="{StaticResource White1Brush}"
|
||||
AutomationProperties.AutomationId="HorizontalScrollBar"
|
||||
Cursor="Arrow"
|
||||
Maximum="{TemplateBinding ScrollableWidth}"
|
||||
Minimum="0"
|
||||
Orientation="Horizontal"
|
||||
ViewportSize="{TemplateBinding ViewportWidth}"
|
||||
Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}"
|
||||
Value="{Binding HorizontalOffset, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}" />
|
||||
</Grid>
|
||||
<ControlTemplate.Triggers>
|
||||
<MultiTrigger>
|
||||
<MultiTrigger.Conditions>
|
||||
<Condition Property="materialDesign:ScrollViewerAssist.ShowSeparators"
|
||||
Value="True" />
|
||||
<Condition Property="ComputedVerticalScrollBarVisibility"
|
||||
Value="Visible" />
|
||||
</MultiTrigger.Conditions>
|
||||
<Setter TargetName="PART_BottomSeparator"
|
||||
Property="Visibility"
|
||||
Value="Visible" />
|
||||
</MultiTrigger>
|
||||
|
||||
<Trigger Property="VerticalOffset"
|
||||
Value="0">
|
||||
<Setter TargetName="PART_TopSeparator"
|
||||
Property="Visibility"
|
||||
Value="Hidden" />
|
||||
</Trigger>
|
||||
|
||||
<Trigger Property="materialDesign:ScrollViewerAssist.ShowSeparators"
|
||||
Value="False">
|
||||
<Setter TargetName="PART_TopSeparator"
|
||||
Property="Visibility"
|
||||
Value="Hidden" />
|
||||
</Trigger>
|
||||
|
||||
<Trigger Property="materialDesign:ScrollViewerAssist.IgnorePadding"
|
||||
Value="False">
|
||||
<Setter TargetName="PART_ScrollContentPresenter"
|
||||
Property="Margin"
|
||||
Value="{Binding Padding, RelativeSource={RelativeSource TemplatedParent}}" />
|
||||
</Trigger>
|
||||
|
||||
<MultiTrigger>
|
||||
<MultiTrigger.Conditions>
|
||||
<Condition Property="materialDesign:ScrollViewerAssist.IsAutoHideEnabled"
|
||||
Value="True" />
|
||||
<Condition Property="ComputedVerticalScrollBarVisibility"
|
||||
Value="Visible" />
|
||||
<Condition Property="IsMouseOver"
|
||||
Value="False" />
|
||||
</MultiTrigger.Conditions>
|
||||
<MultiTrigger.EnterActions>
|
||||
<BeginStoryboard>
|
||||
<Storyboard>
|
||||
<DoubleAnimation Storyboard.TargetName="PART_VerticalScrollBar"
|
||||
Storyboard.TargetProperty="Opacity"
|
||||
To="0"
|
||||
Duration="0:0:0.2" />
|
||||
</Storyboard>
|
||||
</BeginStoryboard>
|
||||
</MultiTrigger.EnterActions>
|
||||
<MultiTrigger.ExitActions>
|
||||
<BeginStoryboard>
|
||||
<Storyboard>
|
||||
<DoubleAnimation Storyboard.TargetName="PART_VerticalScrollBar"
|
||||
Storyboard.TargetProperty="Opacity"
|
||||
To="1"
|
||||
Duration="0:0:0.2" />
|
||||
</Storyboard>
|
||||
</BeginStoryboard>
|
||||
</MultiTrigger.ExitActions>
|
||||
</MultiTrigger>
|
||||
|
||||
<MultiTrigger>
|
||||
<MultiTrigger.Conditions>
|
||||
<Condition Property="materialDesign:ScrollViewerAssist.IsAutoHideEnabled"
|
||||
Value="True" />
|
||||
<Condition Property="ComputedHorizontalScrollBarVisibility"
|
||||
Value="Visible" />
|
||||
<Condition Property="IsMouseOver"
|
||||
Value="False" />
|
||||
</MultiTrigger.Conditions>
|
||||
<MultiTrigger.EnterActions>
|
||||
<BeginStoryboard>
|
||||
<Storyboard>
|
||||
<DoubleAnimation Storyboard.TargetName="PART_HorizontalScrollBar"
|
||||
Storyboard.TargetProperty="Opacity"
|
||||
To="0"
|
||||
Duration="0:0:0.2" />
|
||||
</Storyboard>
|
||||
</BeginStoryboard>
|
||||
</MultiTrigger.EnterActions>
|
||||
<MultiTrigger.ExitActions>
|
||||
<BeginStoryboard>
|
||||
<Storyboard>
|
||||
<DoubleAnimation Storyboard.TargetName="PART_HorizontalScrollBar"
|
||||
Storyboard.TargetProperty="Opacity"
|
||||
To="1"
|
||||
Duration="0:0:0.2" />
|
||||
</Storyboard>
|
||||
</BeginStoryboard>
|
||||
</MultiTrigger.ExitActions>
|
||||
</MultiTrigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
|
||||
<Style x:Key="LiviaScrollViewer"
|
||||
TargetType="ScrollViewer">
|
||||
<Setter Property="Template"
|
||||
Value="{StaticResource LiviaScrollViewerTemplate}" />
|
||||
<Setter Property="VerticalScrollBarVisibility"
|
||||
Value="Auto"/>
|
||||
</Style>
|
||||
</ResourceDictionary>
|
236
Livia/Resources/Themes/LiviaTabItem.xaml
Normal file
|
@ -0,0 +1,236 @@
|
|||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes">
|
||||
<Style x:Key="FocusVisual">
|
||||
<Setter Property="Control.Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate>
|
||||
<Rectangle Margin="2" StrokeDashArray="1 2" Stroke="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" SnapsToDevicePixels="true" StrokeThickness="1"/>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
<Style TargetType="{x:Type TabItem}">
|
||||
<Setter Property="Background" Value="Transparent"/>
|
||||
<Setter Property="FocusVisualStyle" Value="{StaticResource FocusVisual}"/>
|
||||
<Setter Property="Foreground" Value="{Binding (TextElement.Foreground), RelativeSource={RelativeSource AncestorType={x:Type TabControl}}}"/>
|
||||
<Setter Property="Height" Value="48"/>
|
||||
<Setter Property="MinWidth" Value="90"/>
|
||||
<Setter Property="Padding" Value="16,12"/>
|
||||
<Setter Property="materialDesign:DialogHost.RestoreFocusElement" Value="{Binding RelativeSource={RelativeSource AncestorType={x:Type TabControl}, Mode=FindAncestor}}"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type TabItem}">
|
||||
<ControlTemplate.Resources>
|
||||
<Storyboard x:Key="SelectHorizontalTabItem">
|
||||
<DoubleAnimation Storyboard.TargetName="ScaleTransform"
|
||||
Storyboard.TargetProperty="ScaleY"
|
||||
From="0"
|
||||
To="1"
|
||||
Duration="0" />
|
||||
<DoubleAnimation Storyboard.TargetName="ScaleTransform"
|
||||
Storyboard.TargetProperty="ScaleX"
|
||||
From="0"
|
||||
To="1"
|
||||
Duration="0:0:0.3">
|
||||
<DoubleAnimation.EasingFunction>
|
||||
<SineEase EasingMode="EaseOut" />
|
||||
</DoubleAnimation.EasingFunction>
|
||||
</DoubleAnimation>
|
||||
<DoubleAnimation BeginTime="0:0:0.3"
|
||||
Storyboard.TargetName="PART_BackgroundSelection"
|
||||
Storyboard.TargetProperty="Opacity"
|
||||
To="0.12"
|
||||
Duration="0" />
|
||||
</Storyboard>
|
||||
<Storyboard x:Key="SelectVerticalTabItem">
|
||||
<DoubleAnimation Storyboard.TargetName="ScaleTransform"
|
||||
Storyboard.TargetProperty="ScaleX"
|
||||
From="0"
|
||||
To="1"
|
||||
Duration="0" />
|
||||
<DoubleAnimation Storyboard.TargetName="ScaleTransform"
|
||||
Storyboard.TargetProperty="ScaleY"
|
||||
From="0"
|
||||
To="1"
|
||||
Duration="0:0:0.3">
|
||||
<DoubleAnimation.EasingFunction>
|
||||
<SineEase EasingMode="EaseOut" />
|
||||
</DoubleAnimation.EasingFunction>
|
||||
</DoubleAnimation>
|
||||
<DoubleAnimation BeginTime="0:0:0.3"
|
||||
Storyboard.TargetName="PART_BackgroundSelection"
|
||||
Storyboard.TargetProperty="Opacity"
|
||||
To="0.12"
|
||||
Duration="0" />
|
||||
</Storyboard>
|
||||
</ControlTemplate.Resources>
|
||||
<Grid x:Name="Root">
|
||||
<materialDesign:ColorZone x:Name="ColorZoneHeader" Background="{Binding (materialDesign:ColorZoneAssist.Background), RelativeSource={RelativeSource Mode=TemplatedParent}}"
|
||||
Foreground="{Binding (materialDesign:ColorZoneAssist.Foreground), RelativeSource={RelativeSource Mode=TemplatedParent}}" Focusable="False" HorizontalAlignment="Stretch" Mode="Custom" VerticalAlignment="Stretch">
|
||||
<materialDesign:Ripple x:Name="contentPresenter"
|
||||
ContentStringFormat="{TemplateBinding HeaderStringFormat}"
|
||||
ContentTemplate="{TemplateBinding HeaderTemplate}"
|
||||
Content="{TemplateBinding Header}"
|
||||
ContentTemplateSelector="{TemplateBinding HeaderTemplateSelector}"
|
||||
Focusable="False"
|
||||
TextBlock.FontWeight="Medium"
|
||||
TextBlock.FontSize="14"
|
||||
TextBlock.Foreground="{StaticResource PrimaryTextColorBrush}"
|
||||
HorizontalContentAlignment="Center" Opacity=".82"
|
||||
Padding="{TemplateBinding Padding}" RecognizesAccessKey="True"
|
||||
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalContentAlignment="Center"/>
|
||||
</materialDesign:ColorZone>
|
||||
<Border x:Name="SelectionHighlightBorder" BorderBrush="{Binding Foreground, ElementName=ColorZoneHeader}" BorderThickness="0,0,0,2" RenderTransformOrigin="0.5,0.5" Visibility="Hidden">
|
||||
<Border.RenderTransform>
|
||||
<ScaleTransform x:Name="ScaleTransform" ScaleX="0" ScaleY="0"/>
|
||||
</Border.RenderTransform>
|
||||
<Rectangle x:Name="PART_BackgroundSelection" Fill="{TemplateBinding Background}" IsHitTestVisible="False" Opacity="0.0"/>
|
||||
</Border>
|
||||
</Grid>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsEnabled" Value="False">
|
||||
<Setter Property="Opacity" Value="0.38"/>
|
||||
</Trigger>
|
||||
<Trigger Property="IsSelected" Value="True">
|
||||
<Setter Property="Visibility" TargetName="SelectionHighlightBorder" Value="Visible"/>
|
||||
<Setter Property="Opacity" TargetName="contentPresenter" Value="1"/>
|
||||
<Setter Property="Foreground" TargetName="contentPresenter" Value="{StaticResource White1Brush}"/>
|
||||
<Setter Property="materialDesign:RippleAssist.IsDisabled" TargetName="contentPresenter" Value="True"/>
|
||||
</Trigger>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter TargetName="ColorZoneHeader" Property="Cursor" Value="Hand"></Setter>
|
||||
</Trigger>
|
||||
<MultiTrigger>
|
||||
<MultiTrigger.Conditions>
|
||||
<Condition Property="IsSelected" Value="False"></Condition>
|
||||
<Condition Property="IsMouseOver" Value="True"></Condition>
|
||||
</MultiTrigger.Conditions>
|
||||
<MultiTrigger.Setters>
|
||||
<Setter TargetName="ColorZoneHeader" Property="Foreground" Value="{StaticResource White1Brush}"></Setter>
|
||||
<Setter TargetName="contentPresenter" Property="Foreground" Value="{StaticResource White1Brush}"></Setter>
|
||||
</MultiTrigger.Setters>
|
||||
</MultiTrigger>
|
||||
<DataTrigger Binding="{Binding TabStripPlacement, RelativeSource={RelativeSource AncestorType={x:Type TabControl}}}" Value="Bottom">
|
||||
<Setter Property="BorderThickness" TargetName="SelectionHighlightBorder" Value="0,2,0,0"/>
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding TabStripPlacement, RelativeSource={RelativeSource AncestorType={x:Type TabControl}}}" Value="Left">
|
||||
<Setter Property="BorderThickness" TargetName="SelectionHighlightBorder" Value="0,0,2,0"/>
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding TabStripPlacement, RelativeSource={RelativeSource AncestorType={x:Type TabControl}}}" Value="Right">
|
||||
<Setter Property="BorderThickness" TargetName="SelectionHighlightBorder" Value="2,0,0,0"/>
|
||||
</DataTrigger>
|
||||
<MultiDataTrigger>
|
||||
<MultiDataTrigger.Conditions>
|
||||
<Condition Binding="{Binding IsSelected, RelativeSource={RelativeSource Mode=Self}}" Value="True"/>
|
||||
<Condition Binding="{Binding TabStripPlacement, RelativeSource={RelativeSource AncestorType={x:Type TabControl}}}" Value="Top"/>
|
||||
</MultiDataTrigger.Conditions>
|
||||
<MultiDataTrigger.EnterActions>
|
||||
<BeginStoryboard Storyboard="{StaticResource SelectHorizontalTabItem}"/>
|
||||
</MultiDataTrigger.EnterActions>
|
||||
</MultiDataTrigger>
|
||||
<MultiDataTrigger>
|
||||
<MultiDataTrigger.Conditions>
|
||||
<Condition Binding="{Binding IsSelected, RelativeSource={RelativeSource Mode=Self}}" Value="True"/>
|
||||
<Condition Binding="{Binding TabStripPlacement, RelativeSource={RelativeSource AncestorType={x:Type TabControl}}}" Value="Bottom"/>
|
||||
</MultiDataTrigger.Conditions>
|
||||
<MultiDataTrigger.EnterActions>
|
||||
<BeginStoryboard Storyboard="{StaticResource SelectHorizontalTabItem}"/>
|
||||
</MultiDataTrigger.EnterActions>
|
||||
</MultiDataTrigger>
|
||||
<MultiDataTrigger>
|
||||
<MultiDataTrigger.Conditions>
|
||||
<Condition Binding="{Binding IsSelected, RelativeSource={RelativeSource Mode=Self}}" Value="True"/>
|
||||
<Condition Binding="{Binding TabStripPlacement, RelativeSource={RelativeSource AncestorType={x:Type TabControl}}}" Value="Left"/>
|
||||
</MultiDataTrigger.Conditions>
|
||||
<MultiDataTrigger.EnterActions>
|
||||
<BeginStoryboard Storyboard="{StaticResource SelectVerticalTabItem}"/>
|
||||
</MultiDataTrigger.EnterActions>
|
||||
</MultiDataTrigger>
|
||||
<MultiDataTrigger>
|
||||
<MultiDataTrigger.Conditions>
|
||||
<Condition Binding="{Binding IsSelected, RelativeSource={RelativeSource Mode=Self}}" Value="True"/>
|
||||
<Condition Binding="{Binding TabStripPlacement, RelativeSource={RelativeSource AncestorType={x:Type TabControl}}}" Value="Right"/>
|
||||
</MultiDataTrigger.Conditions>
|
||||
<MultiDataTrigger.EnterActions>
|
||||
<BeginStoryboard Storyboard="{StaticResource SelectVerticalTabItem}"/>
|
||||
</MultiDataTrigger.EnterActions>
|
||||
</MultiDataTrigger>
|
||||
<Trigger Property="materialDesign:TabAssist.HasFilledTab" Value="False">
|
||||
<Setter Property="materialDesign:ColorZoneAssist.Foreground" TargetName="ColorZoneHeader" Value="{DynamicResource MaterialDesignBody}"/>
|
||||
</Trigger>
|
||||
<MultiTrigger>
|
||||
<MultiTrigger.Conditions>
|
||||
<Condition Property="materialDesign:TabAssist.HasFilledTab" Value="False"/>
|
||||
<Condition Property="IsSelected" Value="True"/>
|
||||
<Condition Property="materialDesign:ColorZoneAssist.Mode" Value="Standard"/>
|
||||
</MultiTrigger.Conditions>
|
||||
<Setter Property="materialDesign:ColorZoneAssist.Foreground" TargetName="ColorZoneHeader" Value="{DynamicResource MaterialDesignBody}"/>
|
||||
</MultiTrigger>
|
||||
<MultiTrigger>
|
||||
<MultiTrigger.Conditions>
|
||||
<Condition Property="materialDesign:TabAssist.HasFilledTab" Value="False"/>
|
||||
<Condition Property="IsSelected" Value="True"/>
|
||||
<Condition Property="materialDesign:ColorZoneAssist.Mode" Value="PrimaryLight"/>
|
||||
</MultiTrigger.Conditions>
|
||||
<Setter Property="materialDesign:ColorZoneAssist.Foreground" TargetName="ColorZoneHeader" Value="{DynamicResource PrimaryHueLightBrush}"/>
|
||||
</MultiTrigger>
|
||||
<MultiTrigger>
|
||||
<MultiTrigger.Conditions>
|
||||
<Condition Property="materialDesign:TabAssist.HasFilledTab" Value="False"/>
|
||||
<Condition Property="IsSelected" Value="True"/>
|
||||
<Condition Property="materialDesign:ColorZoneAssist.Mode" Value="PrimaryMid"/>
|
||||
</MultiTrigger.Conditions>
|
||||
<Setter Property="materialDesign:ColorZoneAssist.Foreground" TargetName="ColorZoneHeader" Value="{DynamicResource PrimaryHueMidBrush}"/>
|
||||
</MultiTrigger>
|
||||
<MultiTrigger>
|
||||
<MultiTrigger.Conditions>
|
||||
<Condition Property="materialDesign:TabAssist.HasFilledTab" Value="False"/>
|
||||
<Condition Property="IsSelected" Value="True"/>
|
||||
<Condition Property="materialDesign:ColorZoneAssist.Mode" Value="PrimaryDark"/>
|
||||
</MultiTrigger.Conditions>
|
||||
<Setter Property="materialDesign:ColorZoneAssist.Foreground" TargetName="ColorZoneHeader" Value="{DynamicResource PrimaryHueDarkBrush}"/>
|
||||
</MultiTrigger>
|
||||
<MultiTrigger>
|
||||
<MultiTrigger.Conditions>
|
||||
<Condition Property="materialDesign:TabAssist.HasFilledTab" Value="False"/>
|
||||
<Condition Property="IsSelected" Value="True"/>
|
||||
<Condition Property="materialDesign:ColorZoneAssist.Mode" Value="SecondaryLight"/>
|
||||
</MultiTrigger.Conditions>
|
||||
<Setter Property="materialDesign:ColorZoneAssist.Foreground" TargetName="ColorZoneHeader" Value="{DynamicResource SecondaryHueLightBrush}"/>
|
||||
</MultiTrigger>
|
||||
<MultiTrigger>
|
||||
<MultiTrigger.Conditions>
|
||||
<Condition Property="materialDesign:TabAssist.HasFilledTab" Value="False"/>
|
||||
<Condition Property="IsSelected" Value="True"/>
|
||||
<Condition Property="materialDesign:ColorZoneAssist.Mode" Value="SecondaryMid"/>
|
||||
</MultiTrigger.Conditions>
|
||||
<Setter Property="materialDesign:ColorZoneAssist.Foreground" TargetName="ColorZoneHeader" Value="{DynamicResource SecondaryHueMidBrush}"/>
|
||||
</MultiTrigger>
|
||||
<MultiTrigger>
|
||||
<MultiTrigger.Conditions>
|
||||
<Condition Property="materialDesign:TabAssist.HasFilledTab" Value="False"/>
|
||||
<Condition Property="IsSelected" Value="True"/>
|
||||
<Condition Property="materialDesign:ColorZoneAssist.Mode" Value="SecondaryDark"/>
|
||||
</MultiTrigger.Conditions>
|
||||
<Setter Property="materialDesign:ColorZoneAssist.Foreground" TargetName="ColorZoneHeader" Value="{DynamicResource SecondaryHueDarkBrush}"/>
|
||||
</MultiTrigger>
|
||||
<MultiTrigger>
|
||||
<MultiTrigger.Conditions>
|
||||
<Condition Property="materialDesign:TabAssist.HasFilledTab" Value="False"/>
|
||||
<Condition Property="IsSelected" Value="True"/>
|
||||
<Condition Property="materialDesign:ColorZoneAssist.Mode" Value="Custom"/>
|
||||
</MultiTrigger.Conditions>
|
||||
<Setter Property="materialDesign:ColorZoneAssist.Foreground" TargetName="ColorZoneHeader" Value="{Binding (materialDesign:ColorZoneAssist.Foreground), RelativeSource={RelativeSource Mode=TemplatedParent}}"/>
|
||||
</MultiTrigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Setter Property="materialDesign:ColorZoneAssist.Background" Value="Transparent"/>
|
||||
<Setter Property="materialDesign:ColorZoneAssist.Foreground" Value="{Binding (materialDesign:ColorZoneAssist.Foreground), RelativeSource={RelativeSource AncestorType={x:Type TabControl}}}"/>
|
||||
<Setter Property="materialDesign:ColorZoneAssist.Mode" Value="{Binding (materialDesign:ColorZoneAssist.Mode), RelativeSource={RelativeSource AncestorType={x:Type TabControl}}}"/>
|
||||
<Setter Property="materialDesign:RippleAssist.Feedback" Value="{DynamicResource MaterialDesignFlatButtonRipple}"/>
|
||||
<Setter Property="materialDesign:TabAssist.HasFilledTab" Value="{Binding (materialDesign:TabAssist.HasFilledTab), RelativeSource={RelativeSource AncestorType={x:Type TabControl}}}"/>
|
||||
</Style>
|
||||
</ResourceDictionary>
|
88
Livia/Resources/Themes/LiviaTheme.xaml
Normal file
|
@ -0,0 +1,88 @@
|
|||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:po="http://schemas.microsoft.com/winfx/2006/xaml/presentation/options">
|
||||
|
||||
<SolidColorBrush x:Key="MaterialDesignSelection"
|
||||
Color="Transparent"
|
||||
po:Freeze="True" />
|
||||
|
||||
<SolidColorBrush x:Key="PrimaryColorBrush">#1E2A3C</SolidColorBrush>
|
||||
<SolidColorBrush x:Key="PrimaryColorLightBrush">#233D63</SolidColorBrush>
|
||||
<SolidColorBrush x:Key="PrimaryColorLight2Brush">#25344a</SolidColorBrush>
|
||||
<SolidColorBrush x:Key="PrimaryColorDarkBrush">#121325</SolidColorBrush>
|
||||
<SolidColorBrush x:Key="PrimaryColorHighlightBrush">#0066D3</SolidColorBrush>
|
||||
<SolidColorBrush x:Key="PrimaryColorHoverBrush">#0043A8</SolidColorBrush>
|
||||
<SolidColorBrush x:Key="PrimaryColorAlternativeBrush">#0043A8</SolidColorBrush>
|
||||
<SolidColorBrush x:Key="PrimaryColorAlternative2Brush">#001F3F</SolidColorBrush>
|
||||
|
||||
<SolidColorBrush x:Key="MaterialDesignCheckBoxOff" Color="#96B1DA" />
|
||||
|
||||
<SolidColorBrush x:Key="PrimaryColorHighlightOpaqueBrush">#4C0066D3</SolidColorBrush>
|
||||
|
||||
<SolidColorBrush x:Key="PrimaryTextColorBrush">#96B1DA</SolidColorBrush>
|
||||
|
||||
<SolidColorBrush x:Key="PrimaryGridTextButtonColorBrush">#1C84F3</SolidColorBrush>
|
||||
|
||||
<SolidColorBrush x:Key="PrimaryToolTipBorderBrush">#E7E7E7</SolidColorBrush>
|
||||
|
||||
|
||||
<SolidColorBrush x:Key="HighlightTextColorBrush">#37D300</SolidColorBrush>
|
||||
|
||||
<SolidColorBrush x:Key="ServerJobListRowBackground">#233249</SolidColorBrush>
|
||||
<SolidColorBrush x:Key="CalendarBackground">#2F425E</SolidColorBrush>
|
||||
<SolidColorBrush x:Key="SuccessTextColorBrush">#37D300</SolidColorBrush>
|
||||
<SolidColorBrush x:Key="FailedTextColorBrush">#C53B00</SolidColorBrush>
|
||||
<SolidColorBrush x:Key="ProcessingTextColorBrush">#0066D3</SolidColorBrush>
|
||||
|
||||
<SolidColorBrush x:Key="DataGridHeaderBackgroundBrush">#233D63</SolidColorBrush>
|
||||
<SolidColorBrush x:Key="DataGridRowBackgroundBrush">#3C6096</SolidColorBrush>
|
||||
<SolidColorBrush x:Key="DataGridAlternatingRowBackgroundBrush">#264A81</SolidColorBrush>
|
||||
<SolidColorBrush x:Key="DataGridButtonBackgroundBrush">#2A384C</SolidColorBrush>
|
||||
<SolidColorBrush x:Key="DataGridWarningBrush">#D3CB00</SolidColorBrush>
|
||||
<SolidColorBrush x:Key="DataGridCriticalBrush">#FF5E4D</SolidColorBrush>
|
||||
|
||||
<SolidColorBrush x:Key="IscColorBrush">#C53B00</SolidColorBrush>
|
||||
|
||||
<SolidColorBrush x:Key="SecondaryColorBrush">#9430A2</SolidColorBrush>
|
||||
<SolidColorBrush x:Key="SecondaryAlternativeColorBrush">#B856C6</SolidColorBrush>
|
||||
|
||||
<SolidColorBrush x:Key="LoginColorBrush">#FFA53C</SolidColorBrush>
|
||||
<SolidColorBrush x:Key="LoginAlternativeColorBrush">#FFC582</SolidColorBrush>
|
||||
|
||||
<SolidColorBrush x:Key="BackgroundColorBrush">#121325</SolidColorBrush>
|
||||
|
||||
<SolidColorBrush x:Key="White1Brush">#FFFFFF</SolidColorBrush>
|
||||
|
||||
<Color x:Key="White2">#FAFAFA</Color>
|
||||
<SolidColorBrush x:Key="White2Brush">#FAFAFA</SolidColorBrush>
|
||||
|
||||
<Color x:Key="Gray1">#EBEBEB</Color>
|
||||
<SolidColorBrush x:Key="Gray1Brush">#EBEBEB</SolidColorBrush>
|
||||
<Color x:Key="Gray1Transparent">#00EBEBEB</Color>
|
||||
|
||||
<SolidColorBrush x:Key="Gray2Brush">#F5F5F5</SolidColorBrush>
|
||||
<SolidColorBrush x:Key="Gray3Brush">#BBBBBB</SolidColorBrush>
|
||||
<SolidColorBrush x:Key="Gray4Brush">#666666</SolidColorBrush>
|
||||
|
||||
<Color x:Key="Black1">#333333</Color>
|
||||
<SolidColorBrush x:Key="Black1Brush">#333333</SolidColorBrush>
|
||||
<SolidColorBrush x:Key="Black2">#FF000000</SolidColorBrush>
|
||||
|
||||
<SolidColorBrush x:Key="AlertColorBrush">#A8071A</SolidColorBrush>
|
||||
<SolidColorBrush x:Key="AlertAlternativeColorBrush">#F03F3F</SolidColorBrush>
|
||||
|
||||
<SolidColorBrush x:Key="ProgressBarBackgroundBrush">#8F7199C7</SolidColorBrush>
|
||||
|
||||
<SolidColorBrush x:Key="AspectsScoreColorBrush0">#FF0001</SolidColorBrush>
|
||||
<SolidColorBrush x:Key="AspectsScoreColorBrush1">#FF3D00</SolidColorBrush>
|
||||
<SolidColorBrush x:Key="AspectsScoreColorBrush2">#FF7A00</SolidColorBrush>
|
||||
<SolidColorBrush x:Key="AspectsScoreColorBrush3">#FFA000</SolidColorBrush>
|
||||
<SolidColorBrush x:Key="AspectsScoreColorBrush4">#FFBE04</SolidColorBrush>
|
||||
<SolidColorBrush x:Key="AspectsScoreColorBrush5">#FCF107</SolidColorBrush>
|
||||
<SolidColorBrush x:Key="AspectsScoreColorBrush6">#CFEF0C</SolidColorBrush>
|
||||
<SolidColorBrush x:Key="AspectsScoreColorBrush7">#C4EF0C</SolidColorBrush>
|
||||
<SolidColorBrush x:Key="AspectsScoreColorBrush8">#9BDD00</SolidColorBrush>
|
||||
<SolidColorBrush x:Key="AspectsScoreColorBrush9">#51DD00</SolidColorBrush>
|
||||
<SolidColorBrush x:Key="AspectsScoreColorBrush10">#00B500</SolidColorBrush>
|
||||
|
||||
</ResourceDictionary>
|
98
Livia/Resources/Themes/LiviaWindow.xaml
Normal file
|
@ -0,0 +1,98 @@
|
|||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:converters="clr-namespace:Livia.Views.Converters"
|
||||
xmlns:properties="clr-namespace:Livia.Properties">
|
||||
|
||||
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Defaults.xaml" />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
|
||||
<Style TargetType="{x:Type Window}"
|
||||
x:Key="LiviaWindow">
|
||||
<Style.Resources>
|
||||
<converters:StringToFontFamilyConverter x:Key="StringToFontFamilyConverter" />
|
||||
<Style x:Key="{x:Type ToolTip}"
|
||||
BasedOn="{StaticResource MaterialDesignToolTip}"
|
||||
TargetType="ToolTip">
|
||||
<Setter Property="FontFamily"
|
||||
Value="{Binding FontFamily, Source={x:Static properties:ViewSettings.Default}, Converter={StaticResource StringToFontFamilyConverter}}" />
|
||||
</Style>
|
||||
</Style.Resources>
|
||||
<Setter Property="Background"
|
||||
Value="{DynamicResource MaterialDesignPaper}" />
|
||||
<Setter Property="TextElement.Foreground"
|
||||
Value="{DynamicResource MaterialDesignBody}" />
|
||||
<Setter Property="TextOptions.TextFormattingMode"
|
||||
Value="Ideal" />
|
||||
<Setter Property="TextOptions.TextRenderingMode"
|
||||
Value="Auto" />
|
||||
<Setter Property="FontFamily"
|
||||
Value="{Binding FontFamily, Source={x:Static properties:ViewSettings.Default}, Converter={StaticResource StringToFontFamilyConverter}}" />
|
||||
<Setter Property="WindowStyle"
|
||||
Value="None" />
|
||||
<Setter Property="ResizeMode"
|
||||
Value="CanResizeWithGrip" />
|
||||
<Setter Property="AllowsTransparency"
|
||||
Value="True" />
|
||||
</Style>
|
||||
|
||||
<Style TargetType="{x:Type Window}"
|
||||
x:Key="LiviaDialog">
|
||||
<Style.Resources>
|
||||
<converters:StringToFontFamilyConverter x:Key="StringToFontFamilyConverter" />
|
||||
</Style.Resources>
|
||||
<Setter Property="Background"
|
||||
Value="{DynamicResource MaterialDesignPaper}" />
|
||||
<Setter Property="TextOptions.TextFormattingMode"
|
||||
Value="Ideal" />
|
||||
<Setter Property="TextOptions.TextRenderingMode"
|
||||
Value="Auto" />
|
||||
<Setter Property="FontFamily"
|
||||
Value="{Binding FontFamily, Source={x:Static properties:ViewSettings.Default}, Converter={StaticResource StringToFontFamilyConverter}}" />
|
||||
<Setter Property="WindowStyle"
|
||||
Value="None" />
|
||||
<Setter Property="AllowsTransparency"
|
||||
Value="True" />
|
||||
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type Window}">
|
||||
<Border BorderThickness="1"
|
||||
CornerRadius="6"
|
||||
Background="{StaticResource White1Brush}"
|
||||
BorderBrush="{StaticResource Gray3Brush}">
|
||||
|
||||
<AdornerDecorator>
|
||||
<ContentPresenter />
|
||||
</AdornerDecorator>
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
<Style TargetType="ToolTip" x:Key="CustomToolTipStyle">
|
||||
<Setter Property="Padding" Value="1"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="ToolTip">
|
||||
<Border CornerRadius="5,5,5,5"
|
||||
BorderThickness="1"
|
||||
BorderBrush="{StaticResource PrimaryToolTipBorderBrush}"
|
||||
Background="{StaticResource PrimaryColorDarkBrush}">
|
||||
<TextBlock FontWeight="Bold"
|
||||
FontSize="13"
|
||||
TextWrapping="Wrap"
|
||||
Margin="10"
|
||||
Foreground="{StaticResource PrimaryToolTipBorderBrush}"
|
||||
MaxWidth="200"
|
||||
Width="auto"
|
||||
HorizontalAlignment="Center"
|
||||
TextAlignment="Left"
|
||||
Text="{TemplateBinding Content}" />
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</ResourceDictionary>
|