Merge branch 'release/0.9'
11
.editorconfig
Normal file
@ -0,0 +1,11 @@
|
||||
[*.cs]
|
||||
|
||||
# IDE0011: Add braces
|
||||
csharp_prefer_braces = when_multiline
|
||||
csharp_style_var_for_built_in_types=true:silent
|
||||
csharp_style_var_when_type_is_apparent=true:silent
|
||||
csharp_style_var_elsewhere=true:silent
|
||||
|
||||
dotnet_diagnostic.IDE0055.severity = none
|
||||
|
||||
dotnet_diagnostic.IDE0130.severity = none
|
190
.gitignore
vendored
@ -1,189 +1,5 @@
|
||||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
|
||||
# User-specific files
|
||||
*.suo
|
||||
.VS
|
||||
*.user
|
||||
*.sln.docstates
|
||||
|
||||
# Build results
|
||||
[Dd]ebug/
|
||||
[Dd]ebugPublic/
|
||||
[Rr]elease/
|
||||
x64/
|
||||
build/
|
||||
bld/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
|
||||
# Roslyn cache directories
|
||||
*.ide/
|
||||
|
||||
# MSTest test Results
|
||||
[Tt]est[Rr]esult*/
|
||||
[Bb]uild[Ll]og.*
|
||||
|
||||
#NUNIT
|
||||
*.VisualState.xml
|
||||
TestResult.xml
|
||||
|
||||
# Build Results of an ATL Project
|
||||
[Dd]ebugPS/
|
||||
[Rr]eleasePS/
|
||||
dlldata.c
|
||||
|
||||
*_i.c
|
||||
*_p.c
|
||||
*_i.h
|
||||
*.ilk
|
||||
*.meta
|
||||
*.obj
|
||||
*.pch
|
||||
*.pdb
|
||||
*.pgc
|
||||
*.pgd
|
||||
*.rsp
|
||||
*.sbr
|
||||
*.tlb
|
||||
*.tli
|
||||
*.tlh
|
||||
*.tmp
|
||||
*.tmp_proj
|
||||
*.log
|
||||
*.vspscc
|
||||
*.vssscc
|
||||
.builds
|
||||
*.pidb
|
||||
*.svclog
|
||||
*.scc
|
||||
|
||||
# Chutzpah Test files
|
||||
_Chutzpah*
|
||||
|
||||
# Visual C++ cache files
|
||||
ipch/
|
||||
*.aps
|
||||
*.ncb
|
||||
*.opensdf
|
||||
*.sdf
|
||||
*.cachefile
|
||||
|
||||
# Visual Studio profiler
|
||||
*.psess
|
||||
*.vsp
|
||||
*.vspx
|
||||
|
||||
# TFS 2012 Local Workspace
|
||||
$tf/
|
||||
|
||||
# Guidance Automation Toolkit
|
||||
*.gpState
|
||||
|
||||
# ReSharper is a .NET coding add-in
|
||||
_ReSharper*/
|
||||
*.[Rr]e[Ss]harper
|
||||
*.DotSettings.user
|
||||
|
||||
# JustCode is a .NET coding addin-in
|
||||
.JustCode
|
||||
|
||||
# TeamCity is a build add-in
|
||||
_TeamCity*
|
||||
|
||||
# DotCover is a Code Coverage Tool
|
||||
*.dotCover
|
||||
|
||||
# NCrunch
|
||||
_NCrunch_*
|
||||
.*crunch*.local.xml
|
||||
|
||||
# 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
|
||||
## TODO: Comment the next line if you want to checkin your
|
||||
## web deploy settings but do note that will include unencrypted
|
||||
## passwords
|
||||
#*.pubxml
|
||||
|
||||
# NuGet Packages Directory
|
||||
packages/*
|
||||
## TODO: If the tool you use requires repositories.config
|
||||
## uncomment the next line
|
||||
#!packages/repositories.config
|
||||
|
||||
# Enable "build/" folder in the NuGet Packages folder since
|
||||
# NuGet packages use it for MSBuild targets.
|
||||
# This line needs to be after the ignore of the build folder
|
||||
# (and the packages folder if the line above has been uncommented)
|
||||
!packages/build/
|
||||
|
||||
# Windows Azure Build Output
|
||||
csx/
|
||||
*.build.csdef
|
||||
|
||||
# Windows Store app package directory
|
||||
AppPackages/
|
||||
|
||||
# Others
|
||||
sql/
|
||||
*.Cache
|
||||
ClientBin/
|
||||
[Ss]tyle[Cc]op.*
|
||||
~$*
|
||||
*~
|
||||
*.dbmdl
|
||||
*.dbproj.schemaview
|
||||
*.pfx
|
||||
*.publishsettings
|
||||
node_modules/
|
||||
|
||||
# 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
|
||||
|
||||
# SQL Server files
|
||||
*.mdf
|
||||
*.ldf
|
||||
|
||||
# Business Intelligence projects
|
||||
*.rdl.data
|
||||
*.bim.layout
|
||||
*.bim_*.settings
|
||||
|
||||
# Microsoft Fakes
|
||||
FakesAssemblies/
|
||||
|
||||
# LightSwitch generated files
|
||||
GeneratedArtifacts/
|
||||
_Pvt_Extensions/
|
||||
ModelManifest.xml
|
||||
bin
|
||||
obj
|
@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<configuration>
|
||||
<startup>
|
||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
|
||||
</startup>
|
||||
</configuration>
|
97
App.xaml.cs
@ -1,97 +0,0 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Windows;
|
||||
using System.Windows.Markup;
|
||||
using Newtonsoft.Json;
|
||||
using PettingZoo.Model;
|
||||
using PettingZoo.View;
|
||||
using PettingZoo.ViewModel;
|
||||
using SimpleInjector;
|
||||
|
||||
namespace PettingZoo
|
||||
{
|
||||
public partial class App
|
||||
{
|
||||
public void ApplicationStartup(object sender, StartupEventArgs e)
|
||||
{
|
||||
// WPF defaults to US for date formatting in bindings, this fixes it
|
||||
FrameworkElement.LanguageProperty.OverrideMetadata(typeof(FrameworkElement), new FrameworkPropertyMetadata(
|
||||
XmlLanguage.GetLanguage(CultureInfo.CurrentCulture.IetfLanguageTag)));
|
||||
|
||||
var container = Bootstrap();
|
||||
RunApplication(container);
|
||||
}
|
||||
|
||||
|
||||
private static Container Bootstrap()
|
||||
{
|
||||
var container = new Container();
|
||||
|
||||
container.RegisterSingleton(() => new UserSettings(new AppDataSettingsSerializer("Settings.json")));
|
||||
|
||||
container.Register<IConnectionFactory, RabbitMQClientConnectionFactory>();
|
||||
container.Register<IConnectionInfoBuilder, WindowConnectionInfoBuilder>();
|
||||
|
||||
container.Register<MainWindow>();
|
||||
container.Register<MainViewModel>();
|
||||
|
||||
// Note: don't run Verify! It'll create a MainWindow which will then become
|
||||
// Application.Current.MainWindow and prevent the process from shutting down.
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
|
||||
private static void RunApplication(Container container)
|
||||
{
|
||||
var mainWindow = container.GetInstance<MainWindow>();
|
||||
mainWindow.Closed += (sender, args) => container.Dispose();
|
||||
|
||||
mainWindow.Show();
|
||||
}
|
||||
|
||||
|
||||
private class AppDataSettingsSerializer : IUserSettingsSerializer
|
||||
{
|
||||
private readonly string path;
|
||||
private readonly string fullPath;
|
||||
|
||||
|
||||
public AppDataSettingsSerializer(string filename)
|
||||
{
|
||||
var companyName = GetProductInfo<AssemblyCompanyAttribute>().Company;
|
||||
var productName = GetProductInfo<AssemblyProductAttribute>().Product;
|
||||
|
||||
path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
|
||||
companyName, productName);
|
||||
fullPath = Path.Combine(path, filename);
|
||||
}
|
||||
|
||||
|
||||
public void Read(UserSettings settings)
|
||||
{
|
||||
if (File.Exists(fullPath))
|
||||
JsonConvert.PopulateObject(File.ReadAllText(fullPath), settings);
|
||||
}
|
||||
|
||||
|
||||
public void Write(UserSettings settings)
|
||||
{
|
||||
Directory.CreateDirectory(path);
|
||||
File.WriteAllText(fullPath, JsonConvert.SerializeObject(settings, Formatting.Indented));
|
||||
}
|
||||
|
||||
|
||||
private T GetProductInfo<T>()
|
||||
{
|
||||
var attributes = GetType().Assembly.GetCustomAttributes(typeof(T), true);
|
||||
if (attributes.Length == 0)
|
||||
throw new Exception("Missing product information in assembly");
|
||||
|
||||
return (T)attributes[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
6
GitVersion.yml
Normal file
@ -0,0 +1,6 @@
|
||||
mode: ContinuousDeployment
|
||||
branches:
|
||||
master:
|
||||
mode: ContinuousDelivery
|
||||
ignore:
|
||||
sha: []
|
483
Icons.xaml
@ -1,483 +0,0 @@
|
||||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
<DrawingImage x:Key="ClearIcon">
|
||||
<DrawingImage.Drawing>
|
||||
<DrawingGroup>
|
||||
<DrawingGroup>
|
||||
<DrawingGroup.ClipGeometry>
|
||||
<RectangleGeometry Rect="0,0,58,58" />
|
||||
</DrawingGroup.ClipGeometry>
|
||||
<DrawingGroup>
|
||||
<DrawingGroup>
|
||||
<GeometryDrawing Brush="#FFEFEBDE">
|
||||
<GeometryDrawing.Geometry>
|
||||
<PathGeometry FillRule="Nonzero" Figures="M46.5,14L46.5,14 32.5,0 1.5,0 1.5,58 46.5,58z" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<DrawingGroup>
|
||||
<GeometryDrawing Brush="#FFD5D0BB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<PathGeometry FillRule="Nonzero" Figures="M11.5,23L36.5,23C37.052,23 37.5,22.553 37.5,22 37.5,21.447 37.052,21 36.5,21L11.5,21C10.948,21 10.5,21.447 10.5,22 10.5,22.553 10.948,23 11.5,23z" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FFD5D0BB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<PathGeometry FillRule="Nonzero" Figures="M11.5,15L21.5,15C22.052,15 22.5,14.553 22.5,14 22.5,13.447 22.052,13 21.5,13L11.5,13C10.948,13 10.5,13.447 10.5,14 10.5,14.553 10.948,15 11.5,15z" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FFD5D0BB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<PathGeometry FillRule="Nonzero" Figures="M36.5,29L11.5,29C10.948,29 10.5,29.447 10.5,30 10.5,30.553 10.948,31 11.5,31L36.5,31C37.052,31 37.5,30.553 37.5,30 37.5,29.447 37.052,29 36.5,29z" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FFD5D0BB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<PathGeometry FillRule="Nonzero" Figures="M36.5,37L11.5,37C10.948,37 10.5,37.447 10.5,38 10.5,38.553 10.948,39 11.5,39L36.5,39C37.052,39 37.5,38.553 37.5,38 37.5,37.447 37.052,37 36.5,37z" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FFD5D0BB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<PathGeometry FillRule="Nonzero" Figures="M36.5,45L11.5,45C10.948,45 10.5,45.447 10.5,46 10.5,46.553 10.948,47 11.5,47L36.5,47C37.052,47 37.5,46.553 37.5,46 37.5,45.447 37.052,45 36.5,45z" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
</DrawingGroup>
|
||||
<GeometryDrawing Brush="#FFD5D0BB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<PathGeometry FillRule="Nonzero" Figures="M32.5,0L32.5,0 32.5,14 46.5,14z" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
</DrawingGroup>
|
||||
<DrawingGroup>
|
||||
<GeometryDrawing Brush="#FFED7161">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="12" RadiusY="12" Center="44.5,46" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FFFFFFFF">
|
||||
<GeometryDrawing.Geometry>
|
||||
<PathGeometry FillRule="Nonzero" Figures="M45.914,46L49.45,42.464C49.841,42.073 49.841,41.441 49.45,41.05 49.059,40.659 48.427,40.659 48.036,41.05L44.5,44.586 40.964,41.05C40.573,40.659 39.941,40.659 39.55,41.05 39.159,41.441 39.159,42.073 39.55,42.464L43.086,46 39.55,49.536C39.159,49.927 39.159,50.559 39.55,50.95 39.745,51.145 40.001,51.243 40.257,51.243 40.513,51.243 40.769,51.145 40.964,50.95L44.5,47.414 48.036,50.95C48.231,51.145 48.487,51.243 48.743,51.243 48.999,51.243 49.255,51.145 49.45,50.95 49.841,50.559 49.841,49.927 49.45,49.536L45.914,46z" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
</DrawingGroup>
|
||||
</DrawingGroup>
|
||||
</DrawingGroup>
|
||||
</DrawingGroup>
|
||||
</DrawingImage.Drawing>
|
||||
</DrawingImage>
|
||||
|
||||
<DrawingImage x:Key="ConnectIcon">
|
||||
<DrawingImage.Drawing>
|
||||
<DrawingGroup>
|
||||
<DrawingGroup>
|
||||
<DrawingGroup.ClipGeometry>
|
||||
<RectangleGeometry Rect="0,0,59,59" />
|
||||
</DrawingGroup.ClipGeometry>
|
||||
<DrawingGroup>
|
||||
<DrawingGroup>
|
||||
<GeometryDrawing Brush="#FF556080">
|
||||
<GeometryDrawing.Geometry>
|
||||
<PathGeometry FillRule="Nonzero" Figures="M54.392,19L3.608,19C1.616,19,0,17.384,0,15.392L0,3.608C0,1.616,1.616,0,3.608,0L54.391,0C56.384,0,58,1.616,58,3.608L58,15.391C58,17.384,56.384,19,54.392,19z" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF38454F">
|
||||
<GeometryDrawing.Geometry>
|
||||
<PathGeometry FillRule="Nonzero" Figures="M54.392,38L3.608,38C1.616,38,0,36.384,0,34.392L0,22.608C0,20.616,1.616,19,3.608,19L54.391,19C56.384,19,58,20.616,58,22.608L58,34.391C58,36.384,56.384,38,54.392,38z" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF556080">
|
||||
<GeometryDrawing.Geometry>
|
||||
<PathGeometry FillRule="Nonzero" Figures="M54.392,57L3.608,57C1.616,57,0,55.384,0,53.392L0,41.608C0,39.616,1.616,38,3.608,38L54.391,38C56.384,38,58,39.616,58,41.608L58,53.391C58,55.384,56.384,57,54.392,57z" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="3.5" RadiusY="3.5" Center="9.5,9.5" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="49,8" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="45,8" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="51,11" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="47,11" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="41,8" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="43,11" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="37,8" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="39,11" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="33,8" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="35,11" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="3.5" RadiusY="3.5" Center="9.5,28.5" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="49,27" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="45,27" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="51,30" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="47,30" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="41,27" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="43,30" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="37,27" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="39,30" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="33,27" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="35,30" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="3.5" RadiusY="3.5" Center="9.5,47.5" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="49,46" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="45,46" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="51,49" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="47,49" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="41,46" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="43,49" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="37,46" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="39,49" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="33,46" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="35,49" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
</DrawingGroup>
|
||||
<DrawingGroup>
|
||||
<GeometryDrawing Brush="#FF26B999">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="12" RadiusY="12" Center="47,47" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FFFFFFFF">
|
||||
<GeometryDrawing.Geometry>
|
||||
<PathGeometry FillRule="Nonzero" Figures="M53.571,41.179C53.116,40.863,52.494,40.975,52.179,41.429L46.583,49.469 42.634,46.227C42.208,45.876 41.577,45.939 41.227,46.366 40.876,46.793 40.938,47.423 41.366,47.773L46.152,51.702C46.332,51.849 46.556,51.929 46.786,51.929 46.831,51.929 46.877,51.926 46.923,51.92 47.199,51.881 47.447,51.73 47.607,51.501L53.821,42.572C54.136,42.118,54.024,41.495,53.571,41.179z" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
</DrawingGroup>
|
||||
</DrawingGroup>
|
||||
</DrawingGroup>
|
||||
</DrawingGroup>
|
||||
</DrawingImage.Drawing>
|
||||
</DrawingImage>
|
||||
|
||||
<DrawingImage x:Key="DisconnectIcon">
|
||||
<DrawingImage.Drawing>
|
||||
<DrawingGroup>
|
||||
<DrawingGroup>
|
||||
<DrawingGroup.ClipGeometry>
|
||||
<RectangleGeometry Rect="0,0,59,59" />
|
||||
</DrawingGroup.ClipGeometry>
|
||||
<DrawingGroup>
|
||||
<DrawingGroup>
|
||||
<GeometryDrawing Brush="#FF556080">
|
||||
<GeometryDrawing.Geometry>
|
||||
<PathGeometry FillRule="Nonzero" Figures="M54.392,19L3.608,19C1.616,19,0,17.384,0,15.392L0,3.608C0,1.616,1.616,0,3.608,0L54.391,0C56.384,0,58,1.616,58,3.608L58,15.391C58,17.384,56.384,19,54.392,19z" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF38454F">
|
||||
<GeometryDrawing.Geometry>
|
||||
<PathGeometry FillRule="Nonzero" Figures="M54.392,38L3.608,38C1.616,38,0,36.384,0,34.392L0,22.608C0,20.616,1.616,19,3.608,19L54.391,19C56.384,19,58,20.616,58,22.608L58,34.391C58,36.384,56.384,38,54.392,38z" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF556080">
|
||||
<GeometryDrawing.Geometry>
|
||||
<PathGeometry FillRule="Nonzero" Figures="M54.392,57L3.608,57C1.616,57,0,55.384,0,53.392L0,41.608C0,39.616,1.616,38,3.608,38L54.391,38C56.384,38,58,39.616,58,41.608L58,53.391C58,55.384,56.384,57,54.392,57z" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="3.5" RadiusY="3.5" Center="9.5,9.5" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="49,8" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="45,8" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="51,11" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="47,11" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="41,8" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="43,11" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="37,8" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="39,11" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="33,8" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="35,11" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="3.5" RadiusY="3.5" Center="9.5,28.5" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="49,27" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="45,27" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="51,30" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="47,30" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="41,27" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="43,30" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="37,27" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="39,30" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="33,27" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="35,30" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="3.5" RadiusY="3.5" Center="9.5,47.5" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="49,46" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="45,46" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="51,49" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="47,49" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="41,46" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="43,49" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="37,46" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="39,49" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="33,46" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="35,49" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
</DrawingGroup>
|
||||
<DrawingGroup>
|
||||
<GeometryDrawing Brush="#FFED7161">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="12" RadiusY="12" Center="47,47" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FFFFFFFF">
|
||||
<GeometryDrawing.Geometry>
|
||||
<PathGeometry FillRule="Nonzero" Figures="M48.414,47L51.95,43.464C52.341,43.073 52.341,42.441 51.95,42.05 51.559,41.659 50.927,41.659 50.536,42.05L47,45.586 43.464,42.05C43.073,41.659 42.441,41.659 42.05,42.05 41.659,42.441 41.659,43.073 42.05,43.464L45.586,47 42.05,50.536C41.659,50.927 41.659,51.559 42.05,51.95 42.245,52.145 42.501,52.243 42.757,52.243 43.013,52.243 43.269,52.145 43.464,51.95L47,48.414 50.536,51.95C50.731,52.145 50.987,52.243 51.243,52.243 51.499,52.243 51.755,52.145 51.95,51.95 52.341,51.559 52.341,50.927 51.95,50.536L48.414,47z" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
</DrawingGroup>
|
||||
</DrawingGroup>
|
||||
</DrawingGroup>
|
||||
</DrawingGroup>
|
||||
</DrawingImage.Drawing>
|
||||
</DrawingImage>
|
||||
</ResourceDictionary>
|
@ -1,60 +0,0 @@
|
||||
<DrawingGroup xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
<DrawingGroup x:Name="DrawingLayer">
|
||||
<DrawingGroup.ClipGeometry>
|
||||
<RectangleGeometry Rect="0,0,58,58" />
|
||||
</DrawingGroup.ClipGeometry>
|
||||
<DrawingGroup>
|
||||
<DrawingGroup>
|
||||
<GeometryDrawing Brush="#FFEFEBDE">
|
||||
<GeometryDrawing.Geometry>
|
||||
<PathGeometry FillRule="Nonzero" Figures="M46.5,14L46.5,14 32.5,0 1.5,0 1.5,58 46.5,58z" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<DrawingGroup>
|
||||
<GeometryDrawing Brush="#FFD5D0BB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<PathGeometry FillRule="Nonzero" Figures="M11.5,23L36.5,23C37.052,23 37.5,22.553 37.5,22 37.5,21.447 37.052,21 36.5,21L11.5,21C10.948,21 10.5,21.447 10.5,22 10.5,22.553 10.948,23 11.5,23z" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FFD5D0BB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<PathGeometry FillRule="Nonzero" Figures="M11.5,15L21.5,15C22.052,15 22.5,14.553 22.5,14 22.5,13.447 22.052,13 21.5,13L11.5,13C10.948,13 10.5,13.447 10.5,14 10.5,14.553 10.948,15 11.5,15z" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FFD5D0BB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<PathGeometry FillRule="Nonzero" Figures="M36.5,29L11.5,29C10.948,29 10.5,29.447 10.5,30 10.5,30.553 10.948,31 11.5,31L36.5,31C37.052,31 37.5,30.553 37.5,30 37.5,29.447 37.052,29 36.5,29z" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FFD5D0BB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<PathGeometry FillRule="Nonzero" Figures="M36.5,37L11.5,37C10.948,37 10.5,37.447 10.5,38 10.5,38.553 10.948,39 11.5,39L36.5,39C37.052,39 37.5,38.553 37.5,38 37.5,37.447 37.052,37 36.5,37z" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FFD5D0BB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<PathGeometry FillRule="Nonzero" Figures="M36.5,45L11.5,45C10.948,45 10.5,45.447 10.5,46 10.5,46.553 10.948,47 11.5,47L36.5,47C37.052,47 37.5,46.553 37.5,46 37.5,45.447 37.052,45 36.5,45z" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
</DrawingGroup>
|
||||
<GeometryDrawing Brush="#FFD5D0BB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<PathGeometry FillRule="Nonzero" Figures="M32.5,0L32.5,0 32.5,14 46.5,14z" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
</DrawingGroup>
|
||||
<DrawingGroup>
|
||||
<GeometryDrawing Brush="#FFED7161">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="12" RadiusY="12" Center="44.5,46" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FFFFFFFF">
|
||||
<GeometryDrawing.Geometry>
|
||||
<PathGeometry FillRule="Nonzero" Figures="M45.914,46L49.45,42.464C49.841,42.073 49.841,41.441 49.45,41.05 49.059,40.659 48.427,40.659 48.036,41.05L44.5,44.586 40.964,41.05C40.573,40.659 39.941,40.659 39.55,41.05 39.159,41.441 39.159,42.073 39.55,42.464L43.086,46 39.55,49.536C39.159,49.927 39.159,50.559 39.55,50.95 39.745,51.145 40.001,51.243 40.257,51.243 40.513,51.243 40.769,51.145 40.964,50.95L44.5,47.414 48.036,50.95C48.231,51.145 48.487,51.243 48.743,51.243 48.999,51.243 49.255,51.145 49.45,50.95 49.841,50.559 49.841,49.927 49.45,49.536L45.914,46z" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
</DrawingGroup>
|
||||
</DrawingGroup>
|
||||
</DrawingGroup>
|
||||
</DrawingGroup>
|
@ -1,203 +0,0 @@
|
||||
<DrawingGroup xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
<DrawingGroup x:Name="DrawingLayer">
|
||||
<DrawingGroup.ClipGeometry>
|
||||
<RectangleGeometry Rect="0,0,59,59" />
|
||||
</DrawingGroup.ClipGeometry>
|
||||
<DrawingGroup>
|
||||
<DrawingGroup>
|
||||
<GeometryDrawing Brush="#FF556080">
|
||||
<GeometryDrawing.Geometry>
|
||||
<PathGeometry FillRule="Nonzero" Figures="M54.392,19L3.608,19C1.616,19,0,17.384,0,15.392L0,3.608C0,1.616,1.616,0,3.608,0L54.391,0C56.384,0,58,1.616,58,3.608L58,15.391C58,17.384,56.384,19,54.392,19z" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF38454F">
|
||||
<GeometryDrawing.Geometry>
|
||||
<PathGeometry FillRule="Nonzero" Figures="M54.392,38L3.608,38C1.616,38,0,36.384,0,34.392L0,22.608C0,20.616,1.616,19,3.608,19L54.391,19C56.384,19,58,20.616,58,22.608L58,34.391C58,36.384,56.384,38,54.392,38z" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF556080">
|
||||
<GeometryDrawing.Geometry>
|
||||
<PathGeometry FillRule="Nonzero" Figures="M54.392,57L3.608,57C1.616,57,0,55.384,0,53.392L0,41.608C0,39.616,1.616,38,3.608,38L54.391,38C56.384,38,58,39.616,58,41.608L58,53.391C58,55.384,56.384,57,54.392,57z" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="3.5" RadiusY="3.5" Center="9.5,9.5" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="49,8" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="45,8" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="51,11" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="47,11" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="41,8" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="43,11" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="37,8" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="39,11" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="33,8" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="35,11" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="3.5" RadiusY="3.5" Center="9.5,28.5" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="49,27" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="45,27" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="51,30" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="47,30" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="41,27" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="43,30" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="37,27" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="39,30" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="33,27" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="35,30" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="3.5" RadiusY="3.5" Center="9.5,47.5" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="49,46" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="45,46" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="51,49" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="47,49" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="41,46" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="43,49" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="37,46" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="39,49" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="33,46" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="35,49" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
</DrawingGroup>
|
||||
<DrawingGroup>
|
||||
<GeometryDrawing Brush="#FF26B999">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="12" RadiusY="12" Center="47,47" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FFFFFFFF">
|
||||
<GeometryDrawing.Geometry>
|
||||
<PathGeometry FillRule="Nonzero" Figures="M53.571,41.179C53.116,40.863,52.494,40.975,52.179,41.429L46.583,49.469 42.634,46.227C42.208,45.876 41.577,45.939 41.227,46.366 40.876,46.793 40.938,47.423 41.366,47.773L46.152,51.702C46.332,51.849 46.556,51.929 46.786,51.929 46.831,51.929 46.877,51.926 46.923,51.92 47.199,51.881 47.447,51.73 47.607,51.501L53.821,42.572C54.136,42.118,54.024,41.495,53.571,41.179z" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
</DrawingGroup>
|
||||
</DrawingGroup>
|
||||
</DrawingGroup>
|
||||
</DrawingGroup>
|
@ -1,203 +0,0 @@
|
||||
<DrawingGroup xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
<DrawingGroup x:Name="DrawingLayer">
|
||||
<DrawingGroup.ClipGeometry>
|
||||
<RectangleGeometry Rect="0,0,59,59" />
|
||||
</DrawingGroup.ClipGeometry>
|
||||
<DrawingGroup>
|
||||
<DrawingGroup>
|
||||
<GeometryDrawing Brush="#FF556080">
|
||||
<GeometryDrawing.Geometry>
|
||||
<PathGeometry FillRule="Nonzero" Figures="M54.392,19L3.608,19C1.616,19,0,17.384,0,15.392L0,3.608C0,1.616,1.616,0,3.608,0L54.391,0C56.384,0,58,1.616,58,3.608L58,15.391C58,17.384,56.384,19,54.392,19z" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF38454F">
|
||||
<GeometryDrawing.Geometry>
|
||||
<PathGeometry FillRule="Nonzero" Figures="M54.392,38L3.608,38C1.616,38,0,36.384,0,34.392L0,22.608C0,20.616,1.616,19,3.608,19L54.391,19C56.384,19,58,20.616,58,22.608L58,34.391C58,36.384,56.384,38,54.392,38z" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF556080">
|
||||
<GeometryDrawing.Geometry>
|
||||
<PathGeometry FillRule="Nonzero" Figures="M54.392,57L3.608,57C1.616,57,0,55.384,0,53.392L0,41.608C0,39.616,1.616,38,3.608,38L54.391,38C56.384,38,58,39.616,58,41.608L58,53.391C58,55.384,56.384,57,54.392,57z" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="3.5" RadiusY="3.5" Center="9.5,9.5" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="49,8" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="45,8" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="51,11" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="47,11" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="41,8" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="43,11" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="37,8" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="39,11" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="33,8" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="35,11" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="3.5" RadiusY="3.5" Center="9.5,28.5" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="49,27" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="45,27" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="51,30" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="47,30" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="41,27" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="43,30" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="37,27" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="39,30" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="33,27" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="35,30" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="3.5" RadiusY="3.5" Center="9.5,47.5" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="49,46" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="45,46" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="51,49" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="47,49" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="41,46" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="43,49" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="37,46" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="39,49" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="33,46" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FF8697CB">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="1" RadiusY="1" Center="35,49" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
</DrawingGroup>
|
||||
<DrawingGroup>
|
||||
<GeometryDrawing Brush="#FFED7161">
|
||||
<GeometryDrawing.Geometry>
|
||||
<EllipseGeometry RadiusX="12" RadiusY="12" Center="47,47" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
<GeometryDrawing Brush="#FFFFFFFF">
|
||||
<GeometryDrawing.Geometry>
|
||||
<PathGeometry FillRule="Nonzero" Figures="M48.414,47L51.95,43.464C52.341,43.073 52.341,42.441 51.95,42.05 51.559,41.659 50.927,41.659 50.536,42.05L47,45.586 43.464,42.05C43.073,41.659 42.441,41.659 42.05,42.05 41.659,42.441 41.659,43.073 42.05,43.464L45.586,47 42.05,50.536C41.659,50.927 41.659,51.559 42.05,51.95 42.245,52.145 42.501,52.243 42.757,52.243 43.013,52.243 43.269,52.145 43.464,51.95L47,48.414 50.536,51.95C50.731,52.145 50.987,52.243 51.243,52.243 51.499,52.243 51.755,52.145 51.95,51.95 52.341,51.559 52.341,50.927 51.95,50.536L48.414,47z" />
|
||||
</GeometryDrawing.Geometry>
|
||||
</GeometryDrawing>
|
||||
</DrawingGroup>
|
||||
</DrawingGroup>
|
||||
</DrawingGroup>
|
||||
</DrawingGroup>
|
@ -1,25 +0,0 @@
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace PettingZoo.Infrastructure
|
||||
{
|
||||
public class BaseViewModel : INotifyPropertyChanged
|
||||
{
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
protected virtual void RaisePropertyChanged([CallerMemberName] string propertyName = null)
|
||||
{
|
||||
var handler = PropertyChanged;
|
||||
if (handler != null)
|
||||
handler(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
|
||||
|
||||
protected virtual void RaiseOtherPropertyChanged(string propertyName)
|
||||
{
|
||||
var handler = PropertyChanged;
|
||||
if (handler != null)
|
||||
handler(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
}
|
||||
}
|
@ -1,81 +0,0 @@
|
||||
using System;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace PettingZoo.Infrastructure
|
||||
{
|
||||
public class DelegateCommand<T> : ICommand
|
||||
{
|
||||
private readonly Func<T, bool> canExecute;
|
||||
private readonly Action<T> execute;
|
||||
|
||||
public event EventHandler CanExecuteChanged;
|
||||
|
||||
|
||||
public DelegateCommand(Action<T> execute) : this(execute, null)
|
||||
{
|
||||
}
|
||||
|
||||
public DelegateCommand(Action<T> execute, Func<T, bool> canExecute)
|
||||
{
|
||||
this.execute = execute;
|
||||
this.canExecute = canExecute;
|
||||
}
|
||||
|
||||
|
||||
public bool CanExecute(object parameter)
|
||||
{
|
||||
return canExecute == null || canExecute((T)parameter);
|
||||
}
|
||||
|
||||
|
||||
public void Execute(object parameter)
|
||||
{
|
||||
execute((T)parameter);
|
||||
}
|
||||
|
||||
|
||||
public void RaiseCanExecuteChanged()
|
||||
{
|
||||
if (CanExecuteChanged != null)
|
||||
CanExecuteChanged(this, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public class DelegateCommand : ICommand
|
||||
{
|
||||
private readonly Func<bool> canExecute;
|
||||
private readonly Action execute;
|
||||
|
||||
public event EventHandler CanExecuteChanged;
|
||||
|
||||
|
||||
public DelegateCommand(Action execute) : this(execute, null) { }
|
||||
|
||||
public DelegateCommand(Action execute, Func<bool> canExecute)
|
||||
{
|
||||
this.execute = execute;
|
||||
this.canExecute = canExecute;
|
||||
}
|
||||
|
||||
|
||||
public bool CanExecute(object parameter)
|
||||
{
|
||||
return canExecute == null || canExecute();
|
||||
}
|
||||
|
||||
|
||||
public void Execute(object parameter)
|
||||
{
|
||||
execute();
|
||||
}
|
||||
|
||||
|
||||
public void RaiseCanExecuteChanged()
|
||||
{
|
||||
if (CanExecuteChanged != null)
|
||||
CanExecuteChanged(this, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
namespace PettingZoo.Model
|
||||
{
|
||||
public class ConnectionInfo
|
||||
{
|
||||
public string Host { get; set; }
|
||||
public string VirtualHost { get; set; }
|
||||
public int Port { get; set; }
|
||||
public string Username { get; set; }
|
||||
public string Password { get; set; }
|
||||
|
||||
public string Exchange { get; set; }
|
||||
public string RoutingKey { get; set; }
|
||||
}
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace PettingZoo.Model
|
||||
{
|
||||
public enum ConnectionStatus
|
||||
{
|
||||
Disconnected,
|
||||
Connecting,
|
||||
Connected,
|
||||
Error
|
||||
}
|
||||
|
||||
|
||||
public class StatusChangedEventArgs : EventArgs
|
||||
{
|
||||
public ConnectionStatus Status { get; private set; }
|
||||
public string Context { get; private set; }
|
||||
|
||||
|
||||
public StatusChangedEventArgs(ConnectionStatus status, string context)
|
||||
{
|
||||
Status = status;
|
||||
Context = context;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class MessageReceivedEventArgs : EventArgs
|
||||
{
|
||||
public MessageInfo MessageInfo { get; private set; }
|
||||
|
||||
|
||||
public MessageReceivedEventArgs(MessageInfo messageInfo)
|
||||
{
|
||||
MessageInfo = messageInfo;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public interface IConnection : IDisposable
|
||||
{
|
||||
event EventHandler<StatusChangedEventArgs> StatusChanged;
|
||||
event EventHandler<MessageReceivedEventArgs> MessageReceived;
|
||||
}
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
namespace PettingZoo.Model
|
||||
{
|
||||
public interface IConnectionFactory
|
||||
{
|
||||
IConnection CreateConnection(ConnectionInfo connectionInfo);
|
||||
}
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
namespace PettingZoo.Model
|
||||
{
|
||||
public interface IConnectionInfoBuilder
|
||||
{
|
||||
ConnectionInfo Build();
|
||||
}
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace PettingZoo.Model
|
||||
{
|
||||
public class MessageInfo
|
||||
{
|
||||
public DateTime Timestamp { get; set; }
|
||||
public string Exchange { get; set; }
|
||||
public string RoutingKey { get; set; }
|
||||
public byte[] Body { get; set; }
|
||||
|
||||
public Dictionary<string, string> Properties;
|
||||
|
||||
public string ContentType
|
||||
{
|
||||
get
|
||||
{
|
||||
return Properties != null && Properties.ContainsKey(RabbitMQProperties.ContentType)
|
||||
? Properties[RabbitMQProperties.ContentType]
|
||||
: "";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public MessageInfo()
|
||||
{
|
||||
Timestamp = DateTime.Now;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,190 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using PettingZoo.Properties;
|
||||
using RabbitMQ.Client;
|
||||
using RabbitMQ.Client.Events;
|
||||
|
||||
namespace PettingZoo.Model
|
||||
{
|
||||
public class RabbitMQClientConnection : IConnection
|
||||
{
|
||||
private const int ConnectRetryDelay = 5000;
|
||||
|
||||
private readonly CancellationTokenSource connectionTaskToken;
|
||||
private RabbitMQ.Client.IConnection connection;
|
||||
private IModel model;
|
||||
|
||||
|
||||
public event EventHandler<StatusChangedEventArgs> StatusChanged;
|
||||
public event EventHandler<MessageReceivedEventArgs> MessageReceived;
|
||||
|
||||
|
||||
public RabbitMQClientConnection(ConnectionInfo connectionInfo)
|
||||
{
|
||||
connectionTaskToken = new CancellationTokenSource();
|
||||
var connectionToken = connectionTaskToken.Token;
|
||||
|
||||
Task.Factory.StartNew(() => TryConnection(connectionInfo, connectionToken), connectionToken);
|
||||
}
|
||||
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
connectionTaskToken.Cancel();
|
||||
|
||||
if (model != null)
|
||||
{
|
||||
model.Dispose();
|
||||
model = null;
|
||||
}
|
||||
|
||||
if (connection != null)
|
||||
{
|
||||
connection.Dispose();
|
||||
connection = null;
|
||||
}
|
||||
|
||||
StatusChanged = null;
|
||||
MessageReceived = null;
|
||||
}
|
||||
|
||||
|
||||
private void TryConnection(ConnectionInfo connectionInfo, CancellationToken cancellationToken)
|
||||
{
|
||||
var factory = new ConnectionFactory
|
||||
{
|
||||
HostName = connectionInfo.Host,
|
||||
Port = connectionInfo.Port,
|
||||
VirtualHost = connectionInfo.VirtualHost,
|
||||
UserName = connectionInfo.Username,
|
||||
Password = connectionInfo.Password
|
||||
};
|
||||
|
||||
var statusContext = String.Format("{0}:{1}{2}", connectionInfo.Host, connectionInfo.Port, connectionInfo.VirtualHost);
|
||||
|
||||
while (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
DoStatusChanged(ConnectionStatus.Connecting, statusContext);
|
||||
try
|
||||
{
|
||||
connection = factory.CreateConnection();
|
||||
model = connection.CreateModel();
|
||||
|
||||
var queueName = model.QueueDeclare().QueueName;
|
||||
model.QueueBind(queueName, connectionInfo.Exchange, connectionInfo.RoutingKey);
|
||||
|
||||
|
||||
var consumer = new EventingBasicConsumer(model);
|
||||
consumer.Received += ClientReceived;
|
||||
|
||||
model.BasicConsume(queueName, true, consumer);
|
||||
DoStatusChanged(ConnectionStatus.Connected, statusContext);
|
||||
|
||||
break;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
DoStatusChanged(ConnectionStatus.Error, e.Message);
|
||||
Task.Delay(ConnectRetryDelay, cancellationToken).Wait(cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void ClientReceived(object sender, BasicDeliverEventArgs args)
|
||||
{
|
||||
if (MessageReceived == null)
|
||||
return;
|
||||
|
||||
MessageReceived(this, new MessageReceivedEventArgs(
|
||||
new MessageInfo
|
||||
{
|
||||
Exchange = args.Exchange,
|
||||
RoutingKey = args.RoutingKey,
|
||||
Body = args.Body,
|
||||
Properties = ConvertProperties(args.BasicProperties)
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
private void DoStatusChanged(ConnectionStatus status, string context = null)
|
||||
{
|
||||
if (StatusChanged != null)
|
||||
StatusChanged(this, new StatusChangedEventArgs(status, context));
|
||||
}
|
||||
|
||||
|
||||
private static Dictionary<string, string> ConvertProperties(IBasicProperties basicProperties)
|
||||
{
|
||||
var properties = new Dictionary<string, string>();
|
||||
|
||||
if (basicProperties.IsDeliveryModePresent())
|
||||
{
|
||||
string deliveryMode;
|
||||
|
||||
switch (basicProperties.DeliveryMode)
|
||||
{
|
||||
case 1:
|
||||
deliveryMode = Resources.DeliveryModeNonPersistent;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
deliveryMode = Resources.DeliveryModePersistent;
|
||||
break;
|
||||
|
||||
default:
|
||||
deliveryMode = basicProperties.DeliveryMode.ToString(CultureInfo.InvariantCulture);
|
||||
break;
|
||||
}
|
||||
|
||||
properties.Add(RabbitMQProperties.DeliveryMode, deliveryMode);
|
||||
}
|
||||
|
||||
if (basicProperties.IsContentTypePresent())
|
||||
properties.Add(RabbitMQProperties.ContentType, basicProperties.ContentType);
|
||||
|
||||
if (basicProperties.IsContentEncodingPresent())
|
||||
properties.Add(RabbitMQProperties.ContentEncoding, basicProperties.ContentEncoding);
|
||||
|
||||
if (basicProperties.IsPriorityPresent())
|
||||
properties.Add(RabbitMQProperties.Priority, basicProperties.Priority.ToString(CultureInfo.InvariantCulture));
|
||||
|
||||
if (basicProperties.IsCorrelationIdPresent())
|
||||
properties.Add(RabbitMQProperties.Priority, basicProperties.CorrelationId);
|
||||
|
||||
if (basicProperties.IsReplyToPresent())
|
||||
properties.Add(RabbitMQProperties.ReplyTo, basicProperties.ReplyTo);
|
||||
|
||||
if (basicProperties.IsExpirationPresent())
|
||||
properties.Add(RabbitMQProperties.Expiration, basicProperties.Expiration);
|
||||
|
||||
if (basicProperties.IsMessageIdPresent())
|
||||
properties.Add(RabbitMQProperties.MessageId, basicProperties.MessageId);
|
||||
|
||||
if (basicProperties.IsTimestampPresent())
|
||||
properties.Add(RabbitMQProperties.Timestamp, basicProperties.Timestamp.UnixTime.ToString(CultureInfo.InvariantCulture));
|
||||
|
||||
if (basicProperties.IsTypePresent())
|
||||
properties.Add(RabbitMQProperties.Type, basicProperties.Type);
|
||||
|
||||
if (basicProperties.IsUserIdPresent())
|
||||
properties.Add(RabbitMQProperties.UserId, basicProperties.UserId);
|
||||
|
||||
if (basicProperties.IsAppIdPresent())
|
||||
properties.Add(RabbitMQProperties.UserId, basicProperties.AppId);
|
||||
|
||||
if (basicProperties.IsClusterIdPresent())
|
||||
properties.Add(RabbitMQProperties.ClusterId, basicProperties.ClusterId);
|
||||
|
||||
foreach (var header in basicProperties.Headers)
|
||||
properties.Add(header.Key, Encoding.UTF8.GetString((byte[])header.Value));
|
||||
|
||||
return properties;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
namespace PettingZoo.Model
|
||||
{
|
||||
public class RabbitMQClientConnectionFactory : IConnectionFactory
|
||||
{
|
||||
public IConnection CreateConnection(ConnectionInfo connectionInfo)
|
||||
{
|
||||
return new RabbitMQClientConnection(connectionInfo);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
namespace PettingZoo.Model
|
||||
{
|
||||
static class RabbitMQProperties
|
||||
{
|
||||
public const string ContentType = "content-type";
|
||||
public const string ContentEncoding = "content-encoding";
|
||||
public const string DeliveryMode = "delivery-mode";
|
||||
public const string Priority = "priority";
|
||||
public const string CorrelationId = "correlation-id";
|
||||
public const string ReplyTo = "reply-to";
|
||||
public const string Expiration = "expiration";
|
||||
public const string MessageId = "message-id";
|
||||
public const string Timestamp = "timestamp";
|
||||
public const string Type = "type";
|
||||
public const string UserId = "user-id";
|
||||
public const string AppId = "app-id";
|
||||
public const string ClusterId = "cluster-id";
|
||||
}
|
||||
}
|
@ -1,58 +0,0 @@
|
||||
namespace PettingZoo.Model
|
||||
{
|
||||
public interface IUserSettingsSerializer
|
||||
{
|
||||
void Read(UserSettings settings);
|
||||
void Write(UserSettings settings);
|
||||
}
|
||||
|
||||
|
||||
public class ConnectionWindowSettings
|
||||
{
|
||||
public string LastHost { get; set; }
|
||||
public string LastVirtualHost { get; set; }
|
||||
public int LastPort { get; set; }
|
||||
public string LastUsername { get; set; }
|
||||
public string LastPassword { get; set; }
|
||||
|
||||
public string LastExchange { get; set; }
|
||||
public string LastRoutingKey { get; set; }
|
||||
|
||||
|
||||
public ConnectionWindowSettings()
|
||||
{
|
||||
LastHost = "localhost";
|
||||
LastPort = 5672;
|
||||
LastVirtualHost = "/";
|
||||
LastUsername = "guest";
|
||||
LastPassword = "guest";
|
||||
|
||||
LastExchange = "amqp";
|
||||
LastRoutingKey = "#";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class UserSettings
|
||||
{
|
||||
public ConnectionWindowSettings ConnectionWindow { get; private set; }
|
||||
|
||||
|
||||
private readonly IUserSettingsSerializer serializer;
|
||||
|
||||
|
||||
public UserSettings(IUserSettingsSerializer serializer)
|
||||
{
|
||||
ConnectionWindow = new ConnectionWindowSettings();
|
||||
|
||||
this.serializer = serializer;
|
||||
serializer.Read(this);
|
||||
}
|
||||
|
||||
|
||||
public void Save()
|
||||
{
|
||||
serializer.Write(this);
|
||||
}
|
||||
}
|
||||
}
|
21
PettingZoo.Core/Connection/ConnectionParams.cs
Normal file
@ -0,0 +1,21 @@
|
||||
namespace PettingZoo.Core.Connection
|
||||
{
|
||||
public class ConnectionParams
|
||||
{
|
||||
public string Host { get; }
|
||||
public string VirtualHost { get; }
|
||||
public int Port { get; }
|
||||
public string Username { get; }
|
||||
public string Password { get; }
|
||||
|
||||
|
||||
public ConnectionParams(string host, string virtualHost, int port, string username, string password)
|
||||
{
|
||||
Host = host;
|
||||
VirtualHost = virtualHost;
|
||||
Port = port;
|
||||
Username = username;
|
||||
Password = password;
|
||||
}
|
||||
}
|
||||
}
|
38
PettingZoo.Core/Connection/IConnection.cs
Normal file
@ -0,0 +1,38 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace PettingZoo.Core.Connection
|
||||
{
|
||||
public interface IConnection : IAsyncDisposable
|
||||
{
|
||||
event EventHandler<StatusChangedEventArgs> StatusChanged;
|
||||
|
||||
ISubscriber Subscribe(string exchange, string routingKey);
|
||||
ISubscriber Subscribe();
|
||||
|
||||
Task Publish(PublishMessageInfo messageInfo);
|
||||
}
|
||||
|
||||
|
||||
public enum ConnectionStatus
|
||||
{
|
||||
Disconnected,
|
||||
Connecting,
|
||||
Connected,
|
||||
Error
|
||||
}
|
||||
|
||||
|
||||
public class StatusChangedEventArgs : EventArgs
|
||||
{
|
||||
public ConnectionStatus Status { get; }
|
||||
public string? Context { get; }
|
||||
|
||||
|
||||
public StatusChangedEventArgs(ConnectionStatus status, string? context)
|
||||
{
|
||||
Status = status;
|
||||
Context = context;
|
||||
}
|
||||
}
|
||||
}
|
7
PettingZoo.Core/Connection/IConnectionFactory.cs
Normal file
@ -0,0 +1,7 @@
|
||||
namespace PettingZoo.Core.Connection
|
||||
{
|
||||
public interface IConnectionFactory
|
||||
{
|
||||
IConnection CreateConnection(ConnectionParams connectionInfo);
|
||||
}
|
||||
}
|
27
PettingZoo.Core/Connection/ISubscriber.cs
Normal file
@ -0,0 +1,27 @@
|
||||
using System;
|
||||
|
||||
namespace PettingZoo.Core.Connection
|
||||
{
|
||||
public interface ISubscriber : IAsyncDisposable
|
||||
{
|
||||
string? QueueName { get; }
|
||||
string? Exchange {get; }
|
||||
string? RoutingKey { get; }
|
||||
|
||||
event EventHandler<MessageReceivedEventArgs>? MessageReceived;
|
||||
|
||||
void Start();
|
||||
}
|
||||
|
||||
|
||||
public class MessageReceivedEventArgs : EventArgs
|
||||
{
|
||||
public ReceivedMessageInfo MessageInfo { get; }
|
||||
|
||||
|
||||
public MessageReceivedEventArgs(ReceivedMessageInfo messageInfo)
|
||||
{
|
||||
MessageInfo = messageInfo;
|
||||
}
|
||||
}
|
||||
}
|
74
PettingZoo.Core/Connection/MessageInfo.cs
Normal file
@ -0,0 +1,74 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace PettingZoo.Core.Connection
|
||||
{
|
||||
public class BaseMessageInfo
|
||||
{
|
||||
public string Exchange { get; }
|
||||
public string RoutingKey { get; }
|
||||
public byte[] Body { get; }
|
||||
public MessageProperties Properties { get; }
|
||||
|
||||
public BaseMessageInfo(string exchange, string routingKey, byte[] body, MessageProperties properties)
|
||||
{
|
||||
Exchange = exchange;
|
||||
RoutingKey = routingKey;
|
||||
Body = body;
|
||||
Properties = properties;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class ReceivedMessageInfo : BaseMessageInfo
|
||||
{
|
||||
public DateTime ReceivedTimestamp { get; }
|
||||
|
||||
public ReceivedMessageInfo(string exchange, string routingKey, byte[] body, MessageProperties properties, DateTime receivedTimestamp)
|
||||
: base(exchange, routingKey, body, properties)
|
||||
{
|
||||
ReceivedTimestamp = receivedTimestamp;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class PublishMessageInfo : BaseMessageInfo
|
||||
{
|
||||
public PublishMessageInfo(string exchange, string routingKey, byte[] body, MessageProperties properties)
|
||||
: base(exchange, routingKey, body, properties)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public enum MessageDeliveryMode
|
||||
{
|
||||
NonPersistent = 1,
|
||||
Persistent = 2
|
||||
}
|
||||
|
||||
|
||||
public class MessageProperties
|
||||
{
|
||||
private static readonly IReadOnlyDictionary<string, string> EmptyHeaders = new Dictionary<string, string>();
|
||||
|
||||
public MessageProperties(IReadOnlyDictionary<string, string>? headers)
|
||||
{
|
||||
Headers = headers ?? EmptyHeaders;
|
||||
}
|
||||
|
||||
public string? AppId { get; init; }
|
||||
public string? ContentEncoding { get; init; }
|
||||
public string? ContentType { get; init; }
|
||||
public string? CorrelationId { get; init; }
|
||||
public MessageDeliveryMode? DeliveryMode { get; init; }
|
||||
public string? Expiration { get; init; }
|
||||
public IReadOnlyDictionary<string, string> Headers { get; }
|
||||
public string? MessageId { get; init; }
|
||||
public byte? Priority { get; init; }
|
||||
public string? ReplyTo { get; init; }
|
||||
public DateTime? Timestamp { get; init; }
|
||||
public string? Type { get; init; }
|
||||
public string? UserId { get; init; }
|
||||
}
|
||||
}
|
25
PettingZoo.Core/Generator/IExampleGenerator.cs
Normal file
@ -0,0 +1,25 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace PettingZoo.Core.Generator
|
||||
{
|
||||
public interface IExampleSource : IDisposable
|
||||
{
|
||||
IExampleFolder GetRootFolder();
|
||||
}
|
||||
|
||||
|
||||
public interface IExampleFolder
|
||||
{
|
||||
public string Name { get; }
|
||||
|
||||
public IReadOnlyList<IExampleFolder> Folders { get; }
|
||||
public IReadOnlyList<IExampleMessage> Messages { get; }
|
||||
}
|
||||
|
||||
|
||||
public interface IExampleMessage
|
||||
{
|
||||
string Generate();
|
||||
}
|
||||
}
|
28
PettingZoo.Core/PettingZoo.Core.csproj
Normal file
@ -0,0 +1,28 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<Version>0.1</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Update="Rendering\MessagePropertiesRendererStrings.Designer.cs">
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>MessagePropertiesRendererStrings.resx</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Update="Rendering\MessagePropertiesRendererStrings.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>MessagePropertiesRendererStrings.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
@ -3,25 +3,23 @@ using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace PettingZoo.Model
|
||||
namespace PettingZoo.Core.Rendering
|
||||
{
|
||||
public class MessageBodyRenderer
|
||||
{
|
||||
public static Dictionary<string, Func<byte[], string>> ContentTypeHandlers = new Dictionary<string, Func<byte[], string>>
|
||||
public static Dictionary<string, Func<byte[], string>> ContentTypeHandlers = new()
|
||||
{
|
||||
{ "application/json", RenderJson }
|
||||
};
|
||||
|
||||
|
||||
public static string Render(byte[] body, string contentType = "")
|
||||
public static string Render(byte[] body, string? contentType)
|
||||
{
|
||||
Func<byte[], string> handler;
|
||||
|
||||
if (ContentTypeHandlers.TryGetValue(contentType, out handler))
|
||||
return handler(body);
|
||||
return (contentType != null) && ContentTypeHandlers.TryGetValue(contentType, out var handler)
|
||||
? handler(body)
|
||||
: Encoding.UTF8.GetString(body);
|
||||
|
||||
// ToDo hex output if required
|
||||
return Encoding.UTF8.GetString(body);
|
||||
}
|
||||
|
||||
|
60
PettingZoo.Core/Rendering/MessagePropertiesRenderer.cs
Normal file
@ -0,0 +1,60 @@
|
||||
using System.Collections.Generic;
|
||||
using PettingZoo.Core.Connection;
|
||||
|
||||
namespace PettingZoo.Core.Rendering
|
||||
{
|
||||
public class MessagePropertiesRenderer
|
||||
{
|
||||
public static IDictionary<string, string> Render(MessageProperties properties)
|
||||
{
|
||||
var result = new Dictionary<string, string>();
|
||||
|
||||
if (properties.AppId != null)
|
||||
result.Add(MessagePropertiesRendererStrings.AppId, properties.AppId);
|
||||
|
||||
if (properties.ContentEncoding != null)
|
||||
result.Add(MessagePropertiesRendererStrings.ContentEncoding, properties.ContentEncoding);
|
||||
|
||||
if (properties.ContentType != null)
|
||||
result.Add(MessagePropertiesRendererStrings.ContentType, properties.ContentType);
|
||||
|
||||
if (properties.CorrelationId != null)
|
||||
result.Add(MessagePropertiesRendererStrings.CorrelationId, properties.CorrelationId);
|
||||
|
||||
if (properties.DeliveryMode != null)
|
||||
result.Add(MessagePropertiesRendererStrings.DeliveryMode,
|
||||
properties.DeliveryMode == MessageDeliveryMode.Persistent
|
||||
? MessagePropertiesRendererStrings.DeliveryModePersistent
|
||||
: MessagePropertiesRendererStrings.DeliveryModeNonPersistent);
|
||||
|
||||
if (properties.Expiration != null)
|
||||
result.Add(MessagePropertiesRendererStrings.Expiration, properties.Expiration);
|
||||
|
||||
if (properties.MessageId != null)
|
||||
result.Add(MessagePropertiesRendererStrings.MessageId, properties.MessageId);
|
||||
|
||||
if (properties.Priority != null)
|
||||
result.Add(MessagePropertiesRendererStrings.Priority, properties.Priority.Value.ToString());
|
||||
|
||||
if (properties.ReplyTo != null)
|
||||
result.Add(MessagePropertiesRendererStrings.ReplyTo, properties.ReplyTo);
|
||||
|
||||
if (properties.Timestamp != null)
|
||||
result.Add(MessagePropertiesRendererStrings.Timestamp, properties.Timestamp.Value.ToString("G"));
|
||||
|
||||
if (properties.Type != null)
|
||||
result.Add(MessagePropertiesRendererStrings.Type, properties.Type);
|
||||
|
||||
if (properties.UserId != null)
|
||||
result.Add(MessagePropertiesRendererStrings.UserId, properties.UserId);
|
||||
|
||||
foreach (var (key, value) in properties.Headers)
|
||||
{
|
||||
if (!result.TryAdd(key, value))
|
||||
result.TryAdd(MessagePropertiesRendererStrings.HeaderPrefix + key, value);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
198
PettingZoo.Core/Rendering/MessagePropertiesRendererStrings.Designer.cs
generated
Normal file
@ -0,0 +1,198 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <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 PettingZoo.Core.Rendering {
|
||||
using System;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
/// </summary>
|
||||
// This class was auto-generated by the StronglyTypedResourceBuilder
|
||||
// class via a tool like ResGen or Visual Studio.
|
||||
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||
// with the /str option, or rebuild your VS project.
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
internal class MessagePropertiesRendererStrings {
|
||||
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
|
||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||
|
||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal MessagePropertiesRendererStrings() {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the cached ResourceManager instance used by this class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Resources.ResourceManager ResourceManager {
|
||||
get {
|
||||
if (object.ReferenceEquals(resourceMan, null)) {
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("PettingZoo.Core.Rendering.MessagePropertiesRendererStrings", typeof(MessagePropertiesRendererStrings).Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the current thread's CurrentUICulture property for all
|
||||
/// resource lookups using this strongly typed resource class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Globalization.CultureInfo Culture {
|
||||
get {
|
||||
return resourceCulture;
|
||||
}
|
||||
set {
|
||||
resourceCulture = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to App ID.
|
||||
/// </summary>
|
||||
internal static string AppId {
|
||||
get {
|
||||
return ResourceManager.GetString("AppId", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Content encoding.
|
||||
/// </summary>
|
||||
internal static string ContentEncoding {
|
||||
get {
|
||||
return ResourceManager.GetString("ContentEncoding", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Content type.
|
||||
/// </summary>
|
||||
internal static string ContentType {
|
||||
get {
|
||||
return ResourceManager.GetString("ContentType", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Correlation ID.
|
||||
/// </summary>
|
||||
internal static string CorrelationId {
|
||||
get {
|
||||
return ResourceManager.GetString("CorrelationId", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Delivery mode.
|
||||
/// </summary>
|
||||
internal static string DeliveryMode {
|
||||
get {
|
||||
return ResourceManager.GetString("DeliveryMode", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Transient (1).
|
||||
/// </summary>
|
||||
internal static string DeliveryModeNonPersistent {
|
||||
get {
|
||||
return ResourceManager.GetString("DeliveryModeNonPersistent", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Persistent (2).
|
||||
/// </summary>
|
||||
internal static string DeliveryModePersistent {
|
||||
get {
|
||||
return ResourceManager.GetString("DeliveryModePersistent", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Expiration.
|
||||
/// </summary>
|
||||
internal static string Expiration {
|
||||
get {
|
||||
return ResourceManager.GetString("Expiration", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Header: .
|
||||
/// </summary>
|
||||
internal static string HeaderPrefix {
|
||||
get {
|
||||
return ResourceManager.GetString("HeaderPrefix", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Message ID.
|
||||
/// </summary>
|
||||
internal static string MessageId {
|
||||
get {
|
||||
return ResourceManager.GetString("MessageId", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Priority.
|
||||
/// </summary>
|
||||
internal static string Priority {
|
||||
get {
|
||||
return ResourceManager.GetString("Priority", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Reply To.
|
||||
/// </summary>
|
||||
internal static string ReplyTo {
|
||||
get {
|
||||
return ResourceManager.GetString("ReplyTo", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Timestamp.
|
||||
/// </summary>
|
||||
internal static string Timestamp {
|
||||
get {
|
||||
return ResourceManager.GetString("Timestamp", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Type.
|
||||
/// </summary>
|
||||
internal static string Type {
|
||||
get {
|
||||
return ResourceManager.GetString("Type", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to User ID.
|
||||
/// </summary>
|
||||
internal static string UserId {
|
||||
get {
|
||||
return ResourceManager.GetString("UserId", resourceCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
165
PettingZoo.Core/Rendering/MessagePropertiesRendererStrings.resx
Normal file
@ -0,0 +1,165 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="AppId" xml:space="preserve">
|
||||
<value>App ID</value>
|
||||
</data>
|
||||
<data name="ContentEncoding" xml:space="preserve">
|
||||
<value>Content encoding</value>
|
||||
</data>
|
||||
<data name="ContentType" xml:space="preserve">
|
||||
<value>Content type</value>
|
||||
</data>
|
||||
<data name="CorrelationId" xml:space="preserve">
|
||||
<value>Correlation ID</value>
|
||||
</data>
|
||||
<data name="DeliveryMode" xml:space="preserve">
|
||||
<value>Delivery mode</value>
|
||||
</data>
|
||||
<data name="DeliveryModeNonPersistent" xml:space="preserve">
|
||||
<value>Transient (1)</value>
|
||||
</data>
|
||||
<data name="DeliveryModePersistent" xml:space="preserve">
|
||||
<value>Persistent (2)</value>
|
||||
</data>
|
||||
<data name="Expiration" xml:space="preserve">
|
||||
<value>Expiration</value>
|
||||
</data>
|
||||
<data name="HeaderPrefix" xml:space="preserve">
|
||||
<value>Header: </value>
|
||||
</data>
|
||||
<data name="MessageId" xml:space="preserve">
|
||||
<value>Message ID</value>
|
||||
</data>
|
||||
<data name="Priority" xml:space="preserve">
|
||||
<value>Priority</value>
|
||||
</data>
|
||||
<data name="ReplyTo" xml:space="preserve">
|
||||
<value>Reply To</value>
|
||||
</data>
|
||||
<data name="Timestamp" xml:space="preserve">
|
||||
<value>Timestamp</value>
|
||||
</data>
|
||||
<data name="Type" xml:space="preserve">
|
||||
<value>Type</value>
|
||||
</data>
|
||||
<data name="UserId" xml:space="preserve">
|
||||
<value>User ID</value>
|
||||
</data>
|
||||
</root>
|
90
PettingZoo.Core/Settings/IConnectionSettingsRepository.cs
Normal file
@ -0,0 +1,90 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace PettingZoo.Core.Settings
|
||||
{
|
||||
public interface IConnectionSettingsRepository
|
||||
{
|
||||
Task<StoredConnectionSettings> GetLastUsed();
|
||||
Task StoreLastUsed(bool storePassword, ConnectionSettings connectionSettings);
|
||||
|
||||
Task<IEnumerable<StoredConnectionSettings>> GetStored();
|
||||
Task<StoredConnectionSettings> Add(string displayName, bool storePassword, ConnectionSettings connectionSettings);
|
||||
Task<StoredConnectionSettings> Update(Guid id, string displayName, bool storePassword, ConnectionSettings connectionSettings);
|
||||
Task Delete(Guid id);
|
||||
}
|
||||
|
||||
|
||||
public class ConnectionSettings
|
||||
{
|
||||
public string Host { get; }
|
||||
public string VirtualHost { get; }
|
||||
public int Port { get; }
|
||||
public string Username { get; }
|
||||
public string Password { get; }
|
||||
|
||||
public bool Subscribe { get; }
|
||||
public string Exchange { get; }
|
||||
public string RoutingKey { get; }
|
||||
|
||||
|
||||
public static readonly ConnectionSettings Default = new("localhost", "/", 5672, "guest", "guest", false, "", "#");
|
||||
|
||||
|
||||
public ConnectionSettings(string host, string virtualHost, int port, string username, string password,
|
||||
bool subscribe, string exchange, string routingKey)
|
||||
{
|
||||
Host = host;
|
||||
VirtualHost = virtualHost;
|
||||
Port = port;
|
||||
Username = username;
|
||||
Password = password;
|
||||
|
||||
Subscribe = subscribe;
|
||||
Exchange = exchange;
|
||||
RoutingKey = routingKey;
|
||||
}
|
||||
|
||||
|
||||
public bool SameParameters(ConnectionSettings value, bool comparePassword = true)
|
||||
{
|
||||
return Host == value.Host &&
|
||||
VirtualHost == value.VirtualHost &&
|
||||
Port == value.Port &&
|
||||
Username == value.Username &&
|
||||
(!comparePassword || Password == value.Password) &&
|
||||
Subscribe == value.Subscribe &&
|
||||
Exchange == value.Exchange &&
|
||||
RoutingKey == value.RoutingKey;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class StoredConnectionSettings : ConnectionSettings
|
||||
{
|
||||
public Guid Id { get; }
|
||||
public string DisplayName { get; }
|
||||
public bool StorePassword { get; }
|
||||
|
||||
|
||||
public StoredConnectionSettings(Guid id, string displayName, bool storePassword, string host, string virtualHost, int port, string username,
|
||||
string password, bool subscribe, string exchange, string routingKey)
|
||||
: base(host, virtualHost, port, username, password, subscribe, exchange, routingKey)
|
||||
{
|
||||
Id = id;
|
||||
DisplayName = displayName;
|
||||
StorePassword = storePassword;
|
||||
}
|
||||
|
||||
|
||||
public StoredConnectionSettings(Guid id, string displayName, bool storePassword, ConnectionSettings connectionSettings)
|
||||
: base(connectionSettings.Host, connectionSettings.VirtualHost, connectionSettings.Port, connectionSettings.Username,
|
||||
connectionSettings.Password, connectionSettings.Subscribe, connectionSettings.Exchange, connectionSettings.RoutingKey)
|
||||
{
|
||||
Id = id;
|
||||
DisplayName = displayName;
|
||||
StorePassword = storePassword;
|
||||
}
|
||||
}
|
||||
}
|
30
PettingZoo.Core/Settings/IUISettingsRepository.cs
Normal file
@ -0,0 +1,30 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace PettingZoo.Core.Settings
|
||||
{
|
||||
public interface IUISettingsRepository
|
||||
{
|
||||
Task<MainWindowPositionSettings?> GetMainWindowPosition();
|
||||
Task StoreMainWindowPosition(MainWindowPositionSettings settings);
|
||||
}
|
||||
|
||||
|
||||
public class MainWindowPositionSettings
|
||||
{
|
||||
public int Top { get; }
|
||||
public int Left { get; }
|
||||
public int Width { get; }
|
||||
public int Height { get; }
|
||||
public bool Maximized { get; }
|
||||
|
||||
|
||||
public MainWindowPositionSettings(int top, int left, int width, int height, bool maximized)
|
||||
{
|
||||
Top = top;
|
||||
Left = left;
|
||||
Width = width;
|
||||
Height = height;
|
||||
Maximized = maximized;
|
||||
}
|
||||
}
|
||||
}
|
18
PettingZoo.RabbitMQ/PettingZoo.RabbitMQ.csproj
Normal file
@ -0,0 +1,18 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<Version>0.1</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
<PackageReference Include="RabbitMQ.Client" Version="6.2.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\PettingZoo.Core\PettingZoo.Core.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
156
PettingZoo.RabbitMQ/RabbitMQClientConnection.cs
Normal file
@ -0,0 +1,156 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using PettingZoo.Core.Connection;
|
||||
using RabbitMQ.Client;
|
||||
|
||||
namespace PettingZoo.RabbitMQ
|
||||
{
|
||||
public class RabbitMQClientConnection : Core.Connection.IConnection
|
||||
{
|
||||
private const int ConnectRetryDelay = 5000;
|
||||
|
||||
private readonly CancellationTokenSource connectionTaskToken = new();
|
||||
private readonly Task connectionTask;
|
||||
private readonly object connectionLock = new();
|
||||
private global::RabbitMQ.Client.IConnection? connection;
|
||||
private IModel? model;
|
||||
|
||||
|
||||
public event EventHandler<StatusChangedEventArgs>? StatusChanged;
|
||||
|
||||
|
||||
public RabbitMQClientConnection(ConnectionParams connectionParams)
|
||||
{
|
||||
connectionTask = Task.Factory.StartNew(() => TryConnection(connectionParams, connectionTaskToken.Token), CancellationToken.None);
|
||||
}
|
||||
|
||||
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
connectionTaskToken.Cancel();
|
||||
if (!connectionTask.IsCompleted)
|
||||
await connectionTask;
|
||||
|
||||
lock (connectionLock)
|
||||
{
|
||||
if (model != null)
|
||||
{
|
||||
model.Dispose();
|
||||
model = null;
|
||||
}
|
||||
|
||||
if (connection != null)
|
||||
{
|
||||
connection.Dispose();
|
||||
connection = null;
|
||||
}
|
||||
}
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
|
||||
public ISubscriber Subscribe(string exchange, string routingKey)
|
||||
{
|
||||
return CreateSubscriber(exchange, routingKey);
|
||||
}
|
||||
|
||||
|
||||
public ISubscriber Subscribe()
|
||||
{
|
||||
return CreateSubscriber(null, null);
|
||||
}
|
||||
|
||||
|
||||
private ISubscriber CreateSubscriber(string? exchange, string? routingKey)
|
||||
{
|
||||
lock (connectionLock)
|
||||
{
|
||||
var subscriber = new RabbitMQClientSubscriber(model, exchange, routingKey);
|
||||
if (model != null)
|
||||
return subscriber;
|
||||
|
||||
|
||||
void ConnectSubscriber(object? sender, StatusChangedEventArgs args)
|
||||
{
|
||||
if (args.Status != ConnectionStatus.Connected)
|
||||
return;
|
||||
|
||||
lock (connectionLock)
|
||||
{
|
||||
if (model == null)
|
||||
return;
|
||||
|
||||
subscriber.Connected(model);
|
||||
}
|
||||
|
||||
StatusChanged -= ConnectSubscriber;
|
||||
}
|
||||
|
||||
|
||||
StatusChanged += ConnectSubscriber;
|
||||
return subscriber;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public Task Publish(PublishMessageInfo messageInfo)
|
||||
{
|
||||
if (model == null)
|
||||
throw new InvalidOperationException("Not connected");
|
||||
|
||||
model.BasicPublish(messageInfo.Exchange, messageInfo.RoutingKey, false,
|
||||
RabbitMQClientPropertiesConverter.Convert(messageInfo.Properties, model.CreateBasicProperties()),
|
||||
messageInfo.Body);
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
|
||||
private void TryConnection(ConnectionParams connectionParams, CancellationToken cancellationToken)
|
||||
{
|
||||
var factory = new ConnectionFactory
|
||||
{
|
||||
HostName = connectionParams.Host,
|
||||
Port = connectionParams.Port,
|
||||
VirtualHost = connectionParams.VirtualHost,
|
||||
UserName = connectionParams.Username,
|
||||
Password = connectionParams.Password
|
||||
};
|
||||
|
||||
var statusContext = $"{connectionParams.Host}:{connectionParams.Port}{connectionParams.VirtualHost}";
|
||||
|
||||
while (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
DoStatusChanged(ConnectionStatus.Connecting, statusContext);
|
||||
try
|
||||
{
|
||||
connection = factory.CreateConnection();
|
||||
model = connection.CreateModel();
|
||||
|
||||
DoStatusChanged(ConnectionStatus.Connected, statusContext);
|
||||
break;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
DoStatusChanged(ConnectionStatus.Error, e.Message);
|
||||
|
||||
try
|
||||
{
|
||||
Task.Delay(ConnectRetryDelay, cancellationToken).Wait(cancellationToken);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void DoStatusChanged(ConnectionStatus status, string? context = null)
|
||||
{
|
||||
StatusChanged?.Invoke(this, new StatusChangedEventArgs(status, context));
|
||||
}
|
||||
}
|
||||
}
|
12
PettingZoo.RabbitMQ/RabbitMQClientConnectionFactory.cs
Normal file
@ -0,0 +1,12 @@
|
||||
using PettingZoo.Core.Connection;
|
||||
|
||||
namespace PettingZoo.RabbitMQ
|
||||
{
|
||||
public class RabbitMQClientConnectionFactory : IConnectionFactory
|
||||
{
|
||||
public IConnection CreateConnection(ConnectionParams connectionParams)
|
||||
{
|
||||
return new RabbitMQClientConnection(connectionParams);
|
||||
}
|
||||
}
|
||||
}
|
137
PettingZoo.RabbitMQ/RabbitMQClientPropertiesConverter.cs
Normal file
@ -0,0 +1,137 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using PettingZoo.Core.Connection;
|
||||
using RabbitMQ.Client;
|
||||
|
||||
namespace PettingZoo.RabbitMQ
|
||||
{
|
||||
public static class RabbitMQClientPropertiesConverter
|
||||
{
|
||||
public static MessageProperties Convert(IBasicProperties basicProperties)
|
||||
{
|
||||
return new MessageProperties(basicProperties.Headers?.ToDictionary(p => p.Key, p => Encoding.UTF8.GetString((byte[])p.Value)))
|
||||
{
|
||||
DeliveryMode = basicProperties.IsDeliveryModePresent()
|
||||
? basicProperties.DeliveryMode == 2 ? MessageDeliveryMode.Persistent :
|
||||
MessageDeliveryMode.NonPersistent
|
||||
: null,
|
||||
|
||||
ContentType = basicProperties.IsContentTypePresent()
|
||||
? basicProperties.ContentType
|
||||
: null,
|
||||
|
||||
ContentEncoding = basicProperties.IsContentEncodingPresent()
|
||||
? basicProperties.ContentEncoding
|
||||
: null,
|
||||
|
||||
Priority = basicProperties.IsPriorityPresent()
|
||||
? basicProperties.Priority
|
||||
: null,
|
||||
|
||||
CorrelationId = basicProperties.IsCorrelationIdPresent()
|
||||
? basicProperties.CorrelationId
|
||||
: null,
|
||||
|
||||
ReplyTo = basicProperties.IsReplyToPresent()
|
||||
? basicProperties.ReplyTo
|
||||
: null,
|
||||
|
||||
Expiration = basicProperties.IsExpirationPresent()
|
||||
? basicProperties.Expiration
|
||||
: null,
|
||||
|
||||
MessageId = basicProperties.IsMessageIdPresent()
|
||||
? basicProperties.MessageId
|
||||
: null,
|
||||
|
||||
Timestamp = basicProperties.IsTimestampPresent()
|
||||
? DateTimeOffset.FromUnixTimeMilliseconds(basicProperties.Timestamp.UnixTime).LocalDateTime
|
||||
: null,
|
||||
|
||||
Type = basicProperties.IsTypePresent()
|
||||
? basicProperties.Type
|
||||
: null,
|
||||
|
||||
UserId = basicProperties.IsUserIdPresent()
|
||||
? basicProperties.UserId
|
||||
: null,
|
||||
|
||||
AppId = basicProperties.IsAppIdPresent()
|
||||
? basicProperties.AppId
|
||||
: null
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
public static IBasicProperties Convert(MessageProperties properties, IBasicProperties targetProperties)
|
||||
{
|
||||
if (properties.DeliveryMode != null)
|
||||
targetProperties.DeliveryMode = properties.DeliveryMode == MessageDeliveryMode.Persistent ? (byte)2 : (byte)1;
|
||||
else
|
||||
targetProperties.ClearDeliveryMode();
|
||||
|
||||
if (properties.ContentType != null)
|
||||
targetProperties.ContentType = properties.ContentType;
|
||||
else
|
||||
targetProperties.ClearContentType();
|
||||
|
||||
if (properties.ContentEncoding != null)
|
||||
targetProperties.ContentEncoding = properties.ContentEncoding;
|
||||
else
|
||||
targetProperties.ClearContentEncoding();
|
||||
|
||||
if (properties.Priority != null)
|
||||
targetProperties.Priority = properties.Priority.Value;
|
||||
else
|
||||
targetProperties.ClearPriority();
|
||||
|
||||
if (properties.CorrelationId != null)
|
||||
targetProperties.CorrelationId = properties.CorrelationId;
|
||||
else
|
||||
targetProperties.ClearCorrelationId();
|
||||
|
||||
if (properties.ReplyTo != null)
|
||||
targetProperties.ReplyTo = properties.ReplyTo;
|
||||
else
|
||||
targetProperties.ClearReplyTo();
|
||||
|
||||
if (properties.Expiration != null)
|
||||
targetProperties.Expiration = properties.Expiration;
|
||||
else
|
||||
targetProperties.ClearExpiration();
|
||||
|
||||
if (properties.MessageId != null)
|
||||
targetProperties.MessageId = properties.MessageId;
|
||||
else
|
||||
targetProperties.ClearMessageId();
|
||||
|
||||
if (properties.Timestamp != null)
|
||||
targetProperties.Timestamp = new AmqpTimestamp(new DateTimeOffset(properties.Timestamp.Value).ToUnixTimeMilliseconds());
|
||||
else
|
||||
targetProperties.ClearTimestamp();
|
||||
|
||||
if (properties.Type != null)
|
||||
targetProperties.Type = properties.Type;
|
||||
else
|
||||
targetProperties.ClearType();
|
||||
|
||||
if (properties.UserId != null)
|
||||
targetProperties.UserId = properties.UserId;
|
||||
else
|
||||
targetProperties.ClearUserId();
|
||||
|
||||
if (properties.AppId != null)
|
||||
targetProperties.AppId = properties.AppId;
|
||||
else
|
||||
targetProperties.ClearAppId();
|
||||
|
||||
if (properties.Headers.Count > 0)
|
||||
targetProperties.Headers = properties.Headers.ToDictionary(p => p.Key, p => (object)Encoding.UTF8.GetBytes(p.Value));
|
||||
else
|
||||
targetProperties.ClearHeaders();
|
||||
|
||||
return targetProperties;
|
||||
}
|
||||
}
|
||||
}
|
83
PettingZoo.RabbitMQ/RabbitMQClientSubscriber.cs
Normal file
@ -0,0 +1,83 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using PettingZoo.Core.Connection;
|
||||
using RabbitMQ.Client;
|
||||
using RabbitMQ.Client.Events;
|
||||
|
||||
namespace PettingZoo.RabbitMQ
|
||||
{
|
||||
public class RabbitMQClientSubscriber : ISubscriber
|
||||
{
|
||||
private IModel? model;
|
||||
|
||||
private string? consumerTag;
|
||||
private bool started;
|
||||
|
||||
public string? QueueName { get; private set; }
|
||||
public string? Exchange { get; }
|
||||
public string? RoutingKey { get; }
|
||||
public event EventHandler<MessageReceivedEventArgs>? MessageReceived;
|
||||
|
||||
|
||||
public RabbitMQClientSubscriber(IModel? model, string? exchange, string? routingKey)
|
||||
{
|
||||
this.model = model;
|
||||
Exchange = exchange;
|
||||
RoutingKey = routingKey;
|
||||
}
|
||||
|
||||
|
||||
public ValueTask DisposeAsync()
|
||||
{
|
||||
GC.SuppressFinalize(this);
|
||||
|
||||
if (model != null && consumerTag != null && model.IsOpen)
|
||||
model.BasicCancelNoWait(consumerTag);
|
||||
|
||||
return default;
|
||||
}
|
||||
|
||||
|
||||
public void Start()
|
||||
{
|
||||
started = true;
|
||||
if (model == null)
|
||||
return;
|
||||
|
||||
QueueName = model.QueueDeclare().QueueName;
|
||||
if (Exchange != null && RoutingKey != null)
|
||||
model.QueueBind(QueueName, Exchange, RoutingKey);
|
||||
|
||||
var consumer = new EventingBasicConsumer(model);
|
||||
consumer.Received += ClientReceived;
|
||||
|
||||
consumerTag = model.BasicConsume(QueueName, true, consumer);
|
||||
}
|
||||
|
||||
|
||||
public void Connected(IModel newModel)
|
||||
{
|
||||
model = newModel;
|
||||
|
||||
if (started)
|
||||
Start();
|
||||
}
|
||||
|
||||
|
||||
private void ClientReceived(object? sender, BasicDeliverEventArgs args)
|
||||
{
|
||||
MessageReceived?.Invoke(this, new MessageReceivedEventArgs(
|
||||
new ReceivedMessageInfo(
|
||||
args.Exchange,
|
||||
args.RoutingKey,
|
||||
args.Body.ToArray(),
|
||||
RabbitMQClientPropertiesConverter.Convert(args.BasicProperties),
|
||||
args.BasicProperties.Timestamp.UnixTime > 0
|
||||
? DateTimeOffset.FromUnixTimeSeconds(args.BasicProperties.Timestamp.UnixTime).LocalDateTime
|
||||
: DateTime.Now
|
||||
)
|
||||
));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
35
PettingZoo.Settings.LiteDB/BaseLiteDBRepository.cs
Normal file
@ -0,0 +1,35 @@
|
||||
using LiteDB;
|
||||
using LiteDB.Async;
|
||||
|
||||
namespace PettingZoo.Settings.LiteDB
|
||||
{
|
||||
public class BaseLiteDBRepository
|
||||
{
|
||||
private readonly string databaseFilename;
|
||||
|
||||
protected static readonly BsonMapper Mapper = new()
|
||||
{
|
||||
EmptyStringToNull = false
|
||||
};
|
||||
|
||||
|
||||
public BaseLiteDBRepository(string databaseName)
|
||||
{
|
||||
var appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
|
||||
if (appDataPath == null)
|
||||
throw new IOException("Could not resolve application data path");
|
||||
|
||||
var databasePath = Path.Combine(appDataPath, @"PettingZoo");
|
||||
if (!Directory.CreateDirectory(databasePath).Exists)
|
||||
throw new IOException($"Failed to create directory: {databasePath}");
|
||||
|
||||
databaseFilename = Path.Combine(databasePath, $"{databaseName}.litedb");
|
||||
}
|
||||
|
||||
|
||||
protected ILiteDatabaseAsync GetDatabase()
|
||||
{
|
||||
return new LiteDatabaseAsync(databaseFilename, Mapper);
|
||||
}
|
||||
}
|
||||
}
|
133
PettingZoo.Settings.LiteDB/LiteDBConnectionSettingsRepository.cs
Normal file
@ -0,0 +1,133 @@
|
||||
using PettingZoo.Core.Settings;
|
||||
|
||||
namespace PettingZoo.Settings.LiteDB
|
||||
{
|
||||
public class LiteDBConnectionSettingsRepository : BaseLiteDBRepository, IConnectionSettingsRepository
|
||||
{
|
||||
private static readonly Guid LastUsedId = new("1624147f-76b2-4b5e-8e6f-2ef1730a0a99");
|
||||
|
||||
private const string CollectionLastUsed = "lastUsed";
|
||||
private const string CollectionStored = "stored";
|
||||
|
||||
|
||||
public LiteDBConnectionSettingsRepository() : base(@"connectionSettings")
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
public async Task<StoredConnectionSettings> GetLastUsed()
|
||||
{
|
||||
using var database = GetDatabase();
|
||||
var collection = database.GetCollection<ConnectionSettingsRecord>(CollectionLastUsed);
|
||||
|
||||
var lastUsed = await collection.FindOneAsync(r => true);
|
||||
if (lastUsed == null)
|
||||
return new StoredConnectionSettings(Guid.Empty, "", true, ConnectionSettings.Default);
|
||||
|
||||
return new StoredConnectionSettings(
|
||||
Guid.Empty,
|
||||
"",
|
||||
lastUsed.Password != null,
|
||||
lastUsed.Host,
|
||||
lastUsed.VirtualHost,
|
||||
lastUsed.Port,
|
||||
lastUsed.Username,
|
||||
lastUsed.Password ?? "",
|
||||
lastUsed.Subscribe,
|
||||
lastUsed.Exchange,
|
||||
lastUsed.RoutingKey);
|
||||
}
|
||||
|
||||
|
||||
public async Task StoreLastUsed(bool storePassword, ConnectionSettings connectionSettings)
|
||||
{
|
||||
using var database = GetDatabase();
|
||||
var collection = database.GetCollection<ConnectionSettingsRecord>(CollectionLastUsed);
|
||||
|
||||
await collection.UpsertAsync(ConnectionSettingsRecord.FromConnectionSettings(LastUsedId, connectionSettings, "", storePassword));
|
||||
}
|
||||
|
||||
|
||||
public async Task<IEnumerable<StoredConnectionSettings>> GetStored()
|
||||
{
|
||||
using var database = GetDatabase();
|
||||
var collection = database.GetCollection<ConnectionSettingsRecord>(CollectionStored);
|
||||
|
||||
return (await collection.FindAllAsync())
|
||||
.Select(r => new StoredConnectionSettings(r.Id, r.DisplayName, r.Password != null, r.Host, r.VirtualHost, r.Port, r.Username, r.Password ?? "", r.Subscribe, r.Exchange, r.RoutingKey))
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
|
||||
public async Task<StoredConnectionSettings> Add(string displayName, bool storePassword, ConnectionSettings connectionSettings)
|
||||
{
|
||||
using var database = GetDatabase();
|
||||
var collection = database.GetCollection<ConnectionSettingsRecord>(CollectionStored);
|
||||
|
||||
var id = Guid.NewGuid();
|
||||
await collection.InsertAsync(ConnectionSettingsRecord.FromConnectionSettings(id, connectionSettings, displayName, storePassword));
|
||||
|
||||
return new StoredConnectionSettings(id, displayName, storePassword, connectionSettings);
|
||||
}
|
||||
|
||||
|
||||
public async Task<StoredConnectionSettings> Update(Guid id, string displayName, bool storePassword, ConnectionSettings connectionSettings)
|
||||
{
|
||||
using var database = GetDatabase();
|
||||
var collection = database.GetCollection<ConnectionSettingsRecord>(CollectionStored);
|
||||
|
||||
await collection.UpdateAsync(ConnectionSettingsRecord.FromConnectionSettings(id, connectionSettings, displayName, storePassword));
|
||||
return new StoredConnectionSettings(id, displayName, storePassword, connectionSettings);
|
||||
}
|
||||
|
||||
|
||||
public async Task Delete(Guid id)
|
||||
{
|
||||
using var database = GetDatabase();
|
||||
var collection = database.GetCollection<ConnectionSettingsRecord>(CollectionStored);
|
||||
|
||||
await collection.DeleteAsync(id);
|
||||
}
|
||||
|
||||
|
||||
// ReSharper disable MemberCanBePrivate.Local - for LiteDB
|
||||
// ReSharper disable PropertyCanBeMadeInitOnly.Local
|
||||
private class ConnectionSettingsRecord
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
|
||||
public string DisplayName { get; set; } = null!;
|
||||
public string Host { get; set; } = null!;
|
||||
public string VirtualHost { get; set; } = null!;
|
||||
public int Port { get; set; }
|
||||
public string Username { get; set; } = null!;
|
||||
public string? Password { get; set; }
|
||||
|
||||
public bool Subscribe { get; set; }
|
||||
public string Exchange { get; set; } = null!;
|
||||
public string RoutingKey { get; set; } = null!;
|
||||
|
||||
|
||||
public static ConnectionSettingsRecord FromConnectionSettings(Guid id, ConnectionSettings connectionSettings, string displayName, bool storePassword)
|
||||
{
|
||||
return new ConnectionSettingsRecord
|
||||
{
|
||||
Id = id,
|
||||
DisplayName = displayName,
|
||||
|
||||
Host = connectionSettings.Host,
|
||||
VirtualHost = connectionSettings.VirtualHost,
|
||||
Port = connectionSettings.Port,
|
||||
Username = connectionSettings.Username,
|
||||
Password = storePassword ? connectionSettings.Password : null,
|
||||
|
||||
Subscribe = connectionSettings.Subscribe,
|
||||
Exchange = connectionSettings.Exchange,
|
||||
RoutingKey = connectionSettings.RoutingKey
|
||||
};
|
||||
}
|
||||
}
|
||||
// ReSharper restore PropertyCanBeMadeInitOnly.Local
|
||||
// ReSharper restore MemberCanBePrivate.Local
|
||||
}
|
||||
}
|
84
PettingZoo.Settings.LiteDB/LiteDBUISettingsRepository.cs
Normal file
@ -0,0 +1,84 @@
|
||||
using PettingZoo.Core.Settings;
|
||||
|
||||
namespace PettingZoo.Settings.LiteDB
|
||||
{
|
||||
public class LiteDBUISettingsRepository : BaseLiteDBRepository, IUISettingsRepository
|
||||
{
|
||||
private const string CollectionSettings = "settings";
|
||||
|
||||
|
||||
public LiteDBUISettingsRepository() : base(@"uiSettings")
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
public async Task<MainWindowPositionSettings?> GetMainWindowPosition()
|
||||
{
|
||||
using var database = GetDatabase();
|
||||
var collection = database.GetCollection(CollectionSettings);
|
||||
|
||||
var settings = await collection.FindByIdAsync(MainWindowPositionSettingsRecord.SettingsKey);
|
||||
if (settings == null)
|
||||
return null;
|
||||
|
||||
var position = Mapper.ToObject<MainWindowPositionSettingsRecord>(settings);
|
||||
return new MainWindowPositionSettings(
|
||||
position.Top,
|
||||
position.Left,
|
||||
position.Width,
|
||||
position.Height,
|
||||
position.Maximized);
|
||||
}
|
||||
|
||||
|
||||
public async Task StoreMainWindowPosition(MainWindowPositionSettings settings)
|
||||
{
|
||||
using var database = GetDatabase();
|
||||
var collection = database.GetCollection(CollectionSettings);
|
||||
|
||||
await collection.UpsertAsync(
|
||||
Mapper.ToDocument(new MainWindowPositionSettingsRecord
|
||||
{
|
||||
Top = settings.Top,
|
||||
Left = settings.Left,
|
||||
Width = settings.Width,
|
||||
Height = settings.Height,
|
||||
Maximized = settings.Maximized
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ReSharper disable MemberCanBePrivate.Local - for LiteDB
|
||||
// ReSharper disable PropertyCanBeMadeInitOnly.Local
|
||||
private class BaseSettingsRecord
|
||||
{
|
||||
// ReSharper disable once UnusedAutoPropertyAccessor.Local
|
||||
public Guid Id { get; }
|
||||
|
||||
protected BaseSettingsRecord(Guid id)
|
||||
{
|
||||
Id = id;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private class MainWindowPositionSettingsRecord : BaseSettingsRecord
|
||||
{
|
||||
public static readonly Guid SettingsKey = new("fc71cf99-0744-4f5d-ada8-6a78d1df7b62");
|
||||
|
||||
|
||||
public int Top { get; set; }
|
||||
public int Left { get; set; }
|
||||
public int Width { get; set; }
|
||||
public int Height { get; set; }
|
||||
public bool Maximized { get; set; }
|
||||
|
||||
public MainWindowPositionSettingsRecord() : base(SettingsKey)
|
||||
{
|
||||
}
|
||||
}
|
||||
// ReSharper restore PropertyCanBeMadeInitOnly.Local
|
||||
// ReSharper restore MemberCanBePrivate.Local
|
||||
}
|
||||
}
|
18
PettingZoo.Settings.LiteDB/PettingZoo.Settings.LiteDB.csproj
Normal file
@ -0,0 +1,18 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="LiteDB" Version="5.0.11" />
|
||||
<PackageReference Include="LiteDB.Async" Version="0.0.11" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\PettingZoo.Core\PettingZoo.Core.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
16
PettingZoo.Tapeti/PettingZoo.Tapeti.csproj
Normal file
@ -0,0 +1,16 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<Version>0.1</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.Reflection.MetadataLoadContext" Version="6.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\PettingZoo.Core\PettingZoo.Core.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
187
PettingZoo.Tapeti/TapetiClassLibraryExampleSource.cs
Normal file
@ -0,0 +1,187 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using Newtonsoft.Json;
|
||||
using PettingZoo.Core.Generator;
|
||||
|
||||
namespace PettingZoo.Tapeti
|
||||
{
|
||||
public class TapetiClassLibraryExampleSource : IExampleSource
|
||||
{
|
||||
private readonly string classLibraryFilename;
|
||||
private readonly IEnumerable<string> extraAssemblies;
|
||||
private Lazy<AssemblySource> assemblySource;
|
||||
|
||||
|
||||
public TapetiClassLibraryExampleSource(string classLibraryFilename, IEnumerable<string> extraAssemblies)
|
||||
{
|
||||
this.classLibraryFilename = classLibraryFilename;
|
||||
this.extraAssemblies = extraAssemblies;
|
||||
|
||||
assemblySource = new Lazy<AssemblySource>(AssemblySourceFactory);
|
||||
}
|
||||
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (assemblySource.IsValueCreated)
|
||||
assemblySource.Value.Dispose();
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
|
||||
public IExampleFolder GetRootFolder()
|
||||
{
|
||||
return assemblySource.Value.RootFolder;
|
||||
}
|
||||
|
||||
|
||||
private AssemblySource AssemblySourceFactory()
|
||||
{
|
||||
var runtimeAssemblies = Directory.GetFiles(RuntimeEnvironment.GetRuntimeDirectory(), "*.dll");
|
||||
|
||||
var paths = runtimeAssemblies
|
||||
.Concat(extraAssemblies)
|
||||
.Append(classLibraryFilename);
|
||||
|
||||
// TODO can we use a custom resolver to detect missing references?
|
||||
var resolver = new PathAssemblyResolver(paths);
|
||||
var loadContext = new MetadataLoadContext(resolver);
|
||||
try
|
||||
{
|
||||
var assembly = loadContext.LoadFromAssemblyPath(classLibraryFilename);
|
||||
var rootFolder = new Folder(@"Root");
|
||||
|
||||
|
||||
foreach (var assemblyType in assembly.GetTypes())
|
||||
AddType(assemblyType, rootFolder);
|
||||
|
||||
|
||||
return new AssemblySource
|
||||
{
|
||||
LoadContext = loadContext,
|
||||
RootFolder = rootFolder
|
||||
};
|
||||
}
|
||||
catch
|
||||
{
|
||||
loadContext.Dispose();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void AddType(Type type, Folder rootFolder)
|
||||
{
|
||||
if (!type.IsClass)
|
||||
return;
|
||||
|
||||
var assemblyName = type.Assembly.GetName().Name + ".";
|
||||
var typeNamespace = type.Namespace ?? "";
|
||||
|
||||
if (typeNamespace.StartsWith(assemblyName))
|
||||
typeNamespace = typeNamespace.Substring(assemblyName.Length);
|
||||
|
||||
var folder = CreateFolder(rootFolder, typeNamespace);
|
||||
folder.AddMessage(new Message(type));
|
||||
}
|
||||
|
||||
|
||||
private static Folder CreateFolder(Folder rootFolder, string typeNamespace)
|
||||
{
|
||||
var parts = typeNamespace.Split('.');
|
||||
if (parts.Length == 0)
|
||||
return rootFolder;
|
||||
|
||||
var folder = rootFolder;
|
||||
|
||||
foreach (var part in parts)
|
||||
folder = folder.CreateFolder(part);
|
||||
|
||||
return folder;
|
||||
}
|
||||
|
||||
|
||||
private class Folder : IExampleFolder
|
||||
{
|
||||
private readonly List<Folder> folders = new();
|
||||
private readonly List<IExampleMessage> messages = new();
|
||||
|
||||
|
||||
public string Name { get; }
|
||||
public IReadOnlyList<IExampleFolder> Folders => folders;
|
||||
public IReadOnlyList<IExampleMessage> Messages => messages;
|
||||
|
||||
|
||||
public Folder(string name)
|
||||
{
|
||||
Name = name;
|
||||
}
|
||||
|
||||
|
||||
public Folder CreateFolder(string name)
|
||||
{
|
||||
var folder = folders.FirstOrDefault(f => f.Name == name);
|
||||
if (folder != null)
|
||||
return folder;
|
||||
|
||||
folder = new Folder(name);
|
||||
folders.Add(folder);
|
||||
return folder;
|
||||
}
|
||||
|
||||
|
||||
public void AddMessage(IExampleMessage message)
|
||||
{
|
||||
messages.Add(message);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private class Message : IExampleMessage
|
||||
{
|
||||
private readonly Type type;
|
||||
|
||||
|
||||
public Message(Type type)
|
||||
{
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
|
||||
public string Generate()
|
||||
{
|
||||
/*
|
||||
We can't create an instance of the type to serialize easily, as most will depend on
|
||||
assemblies not included in the NuGet package, so we'll parse the Type ourselves.
|
||||
This is still much easier than using MetadataReader, as we can more easily check against
|
||||
standard types like Nullable.
|
||||
|
||||
The only external dependencies should be the attributes, like [RequiredGuid]. The messaging models
|
||||
themselves should not inherit from classes outside of their assembly, or include properties
|
||||
with types from other assemblies. With that assumption, walking the class structure should be safe.
|
||||
The extraAssemblies passed to TapetiClassLibraryExampleSource can also be used to give it a better chance.
|
||||
*/
|
||||
var serialized = TypeToJObjectConverter.Convert(type);
|
||||
return serialized.ToString(Formatting.Indented);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private class AssemblySource : IDisposable
|
||||
{
|
||||
public MetadataLoadContext LoadContext { get; init; }
|
||||
public IExampleFolder RootFolder { get; init; }
|
||||
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
LoadContext.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
90
PettingZoo.Tapeti/TypeToJObjectConverter.cs
Normal file
@ -0,0 +1,90 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace PettingZoo.Tapeti
|
||||
{
|
||||
internal class TypeToJObjectConverter
|
||||
{
|
||||
public static JObject Convert(Type type)
|
||||
{
|
||||
var result = new JObject();
|
||||
|
||||
foreach (var propertyInfo in type.GetProperties(BindingFlags.Public | BindingFlags.Instance))
|
||||
{
|
||||
// Note: unfortunately we can not call GetCustomAttributes here, as that would
|
||||
// trigger assemblies not included in the package to be loaded
|
||||
|
||||
var value = PropertyToJToken(propertyInfo.PropertyType);
|
||||
result.Add(propertyInfo.Name, value);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
private static readonly Dictionary<Type, JToken> TypeMap = new()
|
||||
{
|
||||
{ typeof(short), 0 },
|
||||
{ typeof(ushort), 0 },
|
||||
{ typeof(int), 0 },
|
||||
{ typeof(uint), 0 },
|
||||
{ typeof(long), 0 },
|
||||
{ typeof(ulong), 0 },
|
||||
{ typeof(decimal), 0.0 },
|
||||
{ typeof(float), 0.0 },
|
||||
{ typeof(bool), false }
|
||||
};
|
||||
|
||||
|
||||
private static JToken PropertyToJToken(Type propertyType)
|
||||
{
|
||||
var actualType = Nullable.GetUnderlyingType(propertyType) ?? propertyType;
|
||||
|
||||
|
||||
// String is also a class
|
||||
if (actualType == typeof(string))
|
||||
return "";
|
||||
|
||||
|
||||
if (actualType.IsClass)
|
||||
{
|
||||
// IEnumerable<T>
|
||||
var enumerableInterface = actualType.GetInterfaces()
|
||||
.FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IEnumerable<>));
|
||||
|
||||
if (enumerableInterface != null)
|
||||
return new JArray(Convert(enumerableInterface.GetGenericArguments()[0]));
|
||||
|
||||
|
||||
return Convert(actualType);
|
||||
}
|
||||
|
||||
if (actualType.IsArray)
|
||||
return new JArray(Convert(actualType.GetElementType()));
|
||||
|
||||
if (actualType.IsEnum)
|
||||
return Enum.GetNames(actualType).FirstOrDefault();
|
||||
|
||||
|
||||
// Special cases for runtime generated values
|
||||
if (actualType == typeof(DateTime))
|
||||
{
|
||||
// Strip the milliseconds for a cleaner result
|
||||
var now = DateTime.UtcNow;
|
||||
return new DateTime(now.Ticks - now.Ticks % TimeSpan.TicksPerSecond, now.Kind);
|
||||
}
|
||||
|
||||
if (actualType == typeof(Guid))
|
||||
return Guid.NewGuid().ToString();
|
||||
|
||||
return TypeMap.TryGetValue(actualType, out var mappedToken)
|
||||
? mappedToken
|
||||
: $"(unknown type: {actualType.Name})";
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
@ -1,188 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{24819D09-C747-4356-B686-D9DE9CAA6F59}</ProjectGuid>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>PettingZoo</RootNamespace>
|
||||
<AssemblyName>PettingZoo</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<PublishUrl>publish\</PublishUrl>
|
||||
<Install>true</Install>
|
||||
<InstallFrom>Disk</InstallFrom>
|
||||
<UpdateEnabled>false</UpdateEnabled>
|
||||
<UpdateMode>Foreground</UpdateMode>
|
||||
<UpdateInterval>7</UpdateInterval>
|
||||
<UpdateIntervalUnits>Days</UpdateIntervalUnits>
|
||||
<UpdatePeriodically>false</UpdatePeriodically>
|
||||
<UpdateRequired>false</UpdateRequired>
|
||||
<MapFileExtensions>true</MapFileExtensions>
|
||||
<ApplicationRevision>0</ApplicationRevision>
|
||||
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>
|
||||
<IsWebBootstrapper>false</IsWebBootstrapper>
|
||||
<UseApplicationTrust>false</UseApplicationTrust>
|
||||
<BootstrapperEnabled>true</BootstrapperEnabled>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<StartupObject>
|
||||
</StartupObject>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<ApplicationIcon>PettingZoo.ico</ApplicationIcon>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="AutoMapper, Version=4.2.1.0, Culture=neutral, PublicKeyToken=be96cd2c38ef1005, processorArchitecture=MSIL">
|
||||
<HintPath>packages\AutoMapper.4.2.1\lib\net45\AutoMapper.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Newtonsoft.Json, Version=8.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>packages\Newtonsoft.Json.8.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="RabbitMQ.Client, Version=3.6.2.0, Culture=neutral, PublicKeyToken=89e7d7c5feba84ce, processorArchitecture=MSIL">
|
||||
<HintPath>packages\RabbitMQ.Client.3.6.2\lib\net45\RabbitMQ.Client.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="SimpleInjector, Version=3.1.5.0, Culture=neutral, PublicKeyToken=984cb50dea722e99, processorArchitecture=MSIL">
|
||||
<HintPath>packages\SimpleInjector.3.1.5\lib\net45\SimpleInjector.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.ComponentModel.DataAnnotations" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="System.Xaml">
|
||||
<RequiredTargetFramework>4.0</RequiredTargetFramework>
|
||||
</Reference>
|
||||
<Reference Include="WindowsBase" />
|
||||
<Reference Include="PresentationCore" />
|
||||
<Reference Include="PresentationFramework" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ApplicationDefinition Include="App.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</ApplicationDefinition>
|
||||
<Page Include="Icons.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Style.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="View\ConnectionWindow.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="View\MainWindow.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Compile Include="App.xaml.cs">
|
||||
<DependentUpon>App.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Infrastructure\DelegateCommand.cs" />
|
||||
<Compile Include="Infrastructure\BaseViewModel.cs" />
|
||||
<Compile Include="Infrastructure\GridLayout.cs" />
|
||||
<Compile Include="Infrastructure\ListBoxAutoScroll.cs" />
|
||||
<Compile Include="Infrastructure\PasswordBoxAssistant.cs" />
|
||||
<Compile Include="Model\UserSettings.cs" />
|
||||
<Compile Include="Model\IConnection.cs" />
|
||||
<Compile Include="Model\IConnectionFactory.cs" />
|
||||
<Compile Include="Model\IConnectionInfoBuilder.cs" />
|
||||
<Compile Include="Model\ConnectionInfo.cs" />
|
||||
<Compile Include="Model\MessageBodyRenderer.cs" />
|
||||
<Compile Include="Model\MessageInfo.cs" />
|
||||
<Compile Include="Model\RabbitMQClientConnection.cs" />
|
||||
<Compile Include="Model\RabbitMQClientConnectionFactory.cs" />
|
||||
<Compile Include="Model\RabbitMQProperties.cs" />
|
||||
<Compile Include="ViewModel\ConnectionViewModel.cs" />
|
||||
<Compile Include="ViewModel\MainViewModel.cs" />
|
||||
<Compile Include="View\ConnectionWindow.xaml.cs">
|
||||
<DependentUpon>ConnectionWindow.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="View\MainWindow.xaml.cs">
|
||||
<DependentUpon>MainWindow.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Properties\AssemblyInfo.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Properties\Resources.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DesignTime>True</DesignTime>
|
||||
<DependentUpon>Resources.resx</DependentUpon>
|
||||
</Compile>
|
||||
<EmbeddedResource Include="Properties\Resources.resx">
|
||||
<Generator>PublicResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
<None Include="packages.config" />
|
||||
<AppDesigner Include="Properties\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="App.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<BootstrapperPackage Include=".NETFramework,Version=v4.5">
|
||||
<Visible>False</Visible>
|
||||
<ProductName>Microsoft .NET Framework 4.5 %28x86 and x64%29</ProductName>
|
||||
<Install>true</Install>
|
||||
</BootstrapperPackage>
|
||||
<BootstrapperPackage Include="Microsoft.Net.Client.3.5">
|
||||
<Visible>False</Visible>
|
||||
<ProductName>.NET Framework 3.5 SP1 Client Profile</ProductName>
|
||||
<Install>false</Install>
|
||||
</BootstrapperPackage>
|
||||
<BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
|
||||
<Visible>False</Visible>
|
||||
<ProductName>.NET Framework 3.5 SP1</ProductName>
|
||||
<Install>false</Install>
|
||||
</BootstrapperPackage>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Resource Include="PettingZoo.ico" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
@ -1,9 +1,22 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 2013
|
||||
VisualStudioVersion = 12.0.40629.0
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.0.31912.275
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PettingZoo", "PettingZoo.csproj", "{24819D09-C747-4356-B686-D9DE9CAA6F59}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PettingZoo", "PettingZoo\PettingZoo.csproj", "{24819D09-C747-4356-B686-D9DE9CAA6F59}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{A64E3FB8-7606-4A05-BF10-D83FD0E80D2D}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
.editorconfig = .editorconfig
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PettingZoo.Core", "PettingZoo.Core\PettingZoo.Core.csproj", "{AD20CA14-6272-4C50-819D-F9FE6A963DB1}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PettingZoo.RabbitMQ", "PettingZoo.RabbitMQ\PettingZoo.RabbitMQ.csproj", "{220149F3-A8D6-44ED-B3B6-DFE506EB018A}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PettingZoo.Tapeti", "PettingZoo.Tapeti\PettingZoo.Tapeti.csproj", "{1763AB04-59D9-4663-B207-D6302FFAACD5}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PettingZoo.Settings.LiteDB", "PettingZoo.Settings.LiteDB\PettingZoo.Settings.LiteDB.csproj", "{7157B09C-FDD9-4928-B14D-C25B784CA865}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
@ -15,8 +28,27 @@ Global
|
||||
{24819D09-C747-4356-B686-D9DE9CAA6F59}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{24819D09-C747-4356-B686-D9DE9CAA6F59}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{24819D09-C747-4356-B686-D9DE9CAA6F59}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{AD20CA14-6272-4C50-819D-F9FE6A963DB1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{AD20CA14-6272-4C50-819D-F9FE6A963DB1}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{AD20CA14-6272-4C50-819D-F9FE6A963DB1}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{AD20CA14-6272-4C50-819D-F9FE6A963DB1}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{220149F3-A8D6-44ED-B3B6-DFE506EB018A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{220149F3-A8D6-44ED-B3B6-DFE506EB018A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{220149F3-A8D6-44ED-B3B6-DFE506EB018A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{220149F3-A8D6-44ED-B3B6-DFE506EB018A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{1763AB04-59D9-4663-B207-D6302FFAACD5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{1763AB04-59D9-4663-B207-D6302FFAACD5}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{1763AB04-59D9-4663-B207-D6302FFAACD5}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{1763AB04-59D9-4663-B207-D6302FFAACD5}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{7157B09C-FDD9-4928-B14D-C25B784CA865}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{7157B09C-FDD9-4928-B14D-C25B784CA865}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{7157B09C-FDD9-4928-B14D-C25B784CA865}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{7157B09C-FDD9-4928-B14D-C25B784CA865}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {07CE270E-9E57-49CD-8D5C-79C7B7A98517}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
@ -1,4 +1,7 @@
|
||||
<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:Boolean x:Key="/Default/CodeInspection/CodeAnnotations/NamespacesWithAnnotations/=PettingZoo_002EAnnotations/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=DB/@EntryIndexedValue">DB</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=DBUI/@EntryIndexedValue">DBUI</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=UI/@EntryIndexedValue">UI</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=MQ/@EntryIndexedValue">MQ</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=WPF/@EntryIndexedValue">WPF</s:String></wpf:ResourceDictionary>
|
@ -1,12 +1,12 @@
|
||||
<Application xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
x:Class="PettingZoo.App"
|
||||
Startup="ApplicationStartup">
|
||||
ShutdownMode="OnMainWindowClose"
|
||||
DispatcherUnhandledException="App_OnDispatcherUnhandledException">
|
||||
<Application.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="Style.xaml"/>
|
||||
<ResourceDictionary Source="Icons.xaml"/>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
||||
</Application.Resources>
|
86
PettingZoo/App.xaml.cs
Normal file
@ -0,0 +1,86 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Threading;
|
||||
using PettingZoo.Core.Settings;
|
||||
using PettingZoo.UI.Main;
|
||||
using SimpleInjector;
|
||||
using Point = System.Windows.Point;
|
||||
|
||||
namespace PettingZoo
|
||||
{
|
||||
public partial class App
|
||||
{
|
||||
private readonly Container container;
|
||||
|
||||
|
||||
public App()
|
||||
{
|
||||
throw new InvalidOperationException("Default main should not be used");
|
||||
}
|
||||
|
||||
|
||||
public App(Container container)
|
||||
{
|
||||
this.container = container;
|
||||
}
|
||||
|
||||
|
||||
protected override async void OnStartup(StartupEventArgs e)
|
||||
{
|
||||
var uitSettingsRepository = container.GetInstance<IUISettingsRepository>();
|
||||
var position = await uitSettingsRepository.GetMainWindowPosition();
|
||||
|
||||
var mainWindow = container.GetInstance<MainWindow>();
|
||||
|
||||
if (position != null)
|
||||
{
|
||||
var positionBounds = new Rect(
|
||||
new Point(position.Left, position.Top),
|
||||
new Point(position.Left + position.Width, position.Top + position.Height));
|
||||
|
||||
if (InScreenBounds(positionBounds))
|
||||
{
|
||||
mainWindow.WindowStartupLocation = WindowStartupLocation.Manual;
|
||||
mainWindow.Top = positionBounds.Top;
|
||||
mainWindow.Left = positionBounds.Left;
|
||||
mainWindow.Width = positionBounds.Width;
|
||||
mainWindow.Height = positionBounds.Height;
|
||||
}
|
||||
|
||||
mainWindow.WindowState = position.Maximized ? WindowState.Maximized : WindowState.Normal;
|
||||
}
|
||||
|
||||
mainWindow.Closing += (_, _) =>
|
||||
{
|
||||
var newPosition = new MainWindowPositionSettings(
|
||||
(int)mainWindow.RestoreBounds.Top,
|
||||
(int)mainWindow.RestoreBounds.Left,
|
||||
(int)mainWindow.RestoreBounds.Width,
|
||||
(int)mainWindow.RestoreBounds.Height,
|
||||
mainWindow.WasMaximized);
|
||||
|
||||
Task.Run(() => uitSettingsRepository.StoreMainWindowPosition(newPosition));
|
||||
};
|
||||
|
||||
mainWindow.Show();
|
||||
}
|
||||
|
||||
|
||||
private static bool InScreenBounds(Rect bounds)
|
||||
{
|
||||
var boundsRectangle = new Rectangle((int)bounds.Left, (int)bounds.Top, (int)bounds.Width, (int)bounds.Height);
|
||||
|
||||
// There doesn't appear to be any way to get this information other than from System.Windows.From/PInvoke at the time of writing
|
||||
return System.Windows.Forms.Screen.AllScreens.Any(screen => screen.Bounds.IntersectsWith(boundsRectangle));
|
||||
}
|
||||
|
||||
|
||||
private void App_OnDispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
|
||||
{
|
||||
_ = MessageBox.Show($"Unhandled exception: {e.Exception.Message}", "Petting Zoo - Exception", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,4 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Generator: Adobe Illustrator 18.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 58 58" style="enable-background:new 0 0 58 58;" xml:space="preserve">
|
||||
@ -23,34 +22,4 @@
|
||||
s0.512-0.098,0.707-0.293c0.391-0.391,0.391-1.023,0-1.414L45.914,46z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.6 KiB |
@ -1,5 +1,4 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Generator: Adobe Illustrator 18.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 59 59" style="enable-background:new 0 0 59 59;" xml:space="preserve">
|
||||
@ -52,34 +51,4 @@
|
||||
c0.045,0,0.091-0.003,0.137-0.009c0.276-0.039,0.524-0.19,0.684-0.419l6.214-8.929C54.136,42.118,54.024,41.495,53.571,41.179z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 3.2 KiB |
59
PettingZoo/Images/Connecting.svg
Normal file
@ -0,0 +1,59 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
version="1.1"
|
||||
id="Capa_1"
|
||||
x="0px"
|
||||
y="0px"
|
||||
viewBox="0 0 22 26"
|
||||
xml:space="preserve"
|
||||
sodipodi:docname="Working.svg"
|
||||
inkscape:version="1.1.1 (3bf5ae0d25, 2021-09-20)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><defs
|
||||
id="defs61">
|
||||
|
||||
|
||||
</defs><sodipodi:namedview
|
||||
id="namedview59"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
showgrid="false"
|
||||
inkscape:zoom="14.344828"
|
||||
inkscape:cx="-7.0060096"
|
||||
inkscape:cy="1.6382212"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1017"
|
||||
inkscape:window-x="1912"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="Capa_1" />
|
||||
<g
|
||||
id="g24"
|
||||
transform="translate(-36,-30)">
|
||||
<path
|
||||
style="fill:#d0e8f9"
|
||||
d="m 48,43 h -2 v 0 c -3.633,-1.453 -6,-4.861 -6,-8.64 v -3.36 h 14 v 3.36 c 0,3.779 -2.367,7.187 -6,8.64 z"
|
||||
id="path14" />
|
||||
<path
|
||||
style="fill:#d0e8f9"
|
||||
d="m 46,43 h 2 v 0 c 3.633,1.453 6,4.861 6,8.64 v 3.36 h -14 v -3.36 c 0,-3.779 2.367,-7.187 6,-8.64 z"
|
||||
id="path16" />
|
||||
<g
|
||||
id="g22">
|
||||
<path
|
||||
style="fill:#556080"
|
||||
d="m 37,32 h 1 2 17 c 0.553,0 1,-0.448 1,-1 0,-0.552 -0.447,-1 -1,-1 h -17 -2 -1 c -0.553,0 -1,0.448 -1,1 0,0.552 0.447,1 1,1 z"
|
||||
id="path18" />
|
||||
<path
|
||||
style="fill:#556080"
|
||||
d="m 56.999,53.998 h -18 c -0.003,0 -0.006,0.002 -0.01,0.002 h -1.989 c -0.553,0 -1,0.448 -1,1 0,0.552 0.447,1 1,1 h 3 c 0.003,0 0.006,-0.002 0.01,-0.002 h 16.989 c 0.553,0 1,-0.448 1,-1 0,-0.552 -0.447,-1 -1,-1 z"
|
||||
id="path20" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.8 KiB |
@ -1,5 +1,4 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Generator: Adobe Illustrator 18.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 59 59" style="enable-background:new 0 0 59 59;" xml:space="preserve">
|
||||
@ -53,34 +52,4 @@
|
||||
s0.512-0.098,0.707-0.293c0.391-0.391,0.391-1.023,0-1.414L48.414,47z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.2 KiB |
19
PettingZoo/Images/Dock.svg
Normal file
@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 58 58" style="enable-background:new 0 0 58 58;" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<rect y="16" style="fill:#ECF0F1;" width="50" height="35"/>
|
||||
<rect y="2" style="fill:#546A79;" width="50" height="14"/>
|
||||
<circle style="fill:#ED7161;" cx="7" cy="9" r="3"/>
|
||||
<circle style="fill:#EFC41A;" cx="16" cy="9" r="3"/>
|
||||
<circle style="fill:#4FBA6E;" cx="25" cy="9" r="3"/>
|
||||
</g>
|
||||
<g>
|
||||
<rect x="36" y="34" style="fill:#48A0DC;" width="22" height="22"/>
|
||||
<rect x="46" y="40" style="fill:#FFFFFF;" width="2" height="16"/>
|
||||
<polygon style="fill:#FFFFFF;" points="52.293,46.707 47,41.414 41.707,46.707 40.293,45.293 47,38.586 53.707,45.293 "/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 938 B |
65
PettingZoo/Images/Error.svg
Normal file
@ -0,0 +1,65 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
version="1.1"
|
||||
id="Capa_1"
|
||||
x="0px"
|
||||
y="0px"
|
||||
viewBox="0 0 24 24"
|
||||
xml:space="preserve"
|
||||
sodipodi:docname="Error.svg"
|
||||
inkscape:version="1.1.1 (3bf5ae0d25, 2021-09-20)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><defs
|
||||
id="defs130">
|
||||
|
||||
|
||||
</defs><sodipodi:namedview
|
||||
id="namedview128"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
showgrid="false"
|
||||
inkscape:zoom="14.344828"
|
||||
inkscape:cx="-14.395433"
|
||||
inkscape:cy="-2.9627404"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1017"
|
||||
inkscape:window-x="1912"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="Capa_1" />
|
||||
<g
|
||||
id="g95"
|
||||
transform="translate(-34,-32)">
|
||||
|
||||
<circle
|
||||
style="fill:#ed7161"
|
||||
cx="46"
|
||||
cy="44"
|
||||
r="12"
|
||||
id="circle89" /><path
|
||||
style="fill:#ffffff"
|
||||
d="m 47.414,44 3.536,-3.536 c 0.391,-0.391 0.391,-1.023 0,-1.414 -0.391,-0.391 -1.023,-0.391 -1.414,0 l -3.536,3.536 -3.536,-3.536 c -0.391,-0.391 -1.023,-0.391 -1.414,0 -0.391,0.391 -0.391,1.023 0,1.414 l 3.536,3.536 -3.536,3.536 c -0.391,0.391 -0.391,1.023 0,1.414 0.195,0.195 0.451,0.293 0.707,0.293 0.256,0 0.512,-0.098 0.707,-0.293 l 3.536,-3.536 3.536,3.536 c 0.195,0.195 0.451,0.293 0.707,0.293 0.256,0 0.512,-0.098 0.707,-0.293 0.391,-0.391 0.391,-1.023 0,-1.414 z"
|
||||
id="path91" />
|
||||
</g>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</svg>
|
After Width: | Height: | Size: 1.6 KiB |
50
PettingZoo/Images/Ok.svg
Normal file
@ -0,0 +1,50 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
version="1.1"
|
||||
id="Capa_1"
|
||||
x="0px"
|
||||
y="0px"
|
||||
viewBox="0 0 24 24"
|
||||
xml:space="preserve"
|
||||
sodipodi:docname="Ok.svg"
|
||||
inkscape:version="1.1.1 (3bf5ae0d25, 2021-09-20)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><defs
|
||||
id="defs13" /><sodipodi:namedview
|
||||
id="namedview11"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
showgrid="false"
|
||||
inkscape:zoom="7.1724138"
|
||||
inkscape:cx="-11.362981"
|
||||
inkscape:cy="7.1802885"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1017"
|
||||
inkscape:window-x="1912"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="Capa_1" />
|
||||
<g
|
||||
id="g8"
|
||||
transform="translate(-34,-32)">
|
||||
<g
|
||||
id="g6">
|
||||
<circle
|
||||
style="fill:#26b999"
|
||||
cx="46"
|
||||
cy="44"
|
||||
r="12"
|
||||
id="circle2" />
|
||||
<path
|
||||
style="fill:#ffffff"
|
||||
d="m 52.571,38.179 c -0.455,-0.316 -1.077,-0.204 -1.392,0.25 l -5.596,8.04 -3.949,-3.242 c -0.426,-0.351 -1.057,-0.288 -1.407,0.139 -0.351,0.427 -0.289,1.057 0.139,1.407 l 4.786,3.929 c 0.18,0.147 0.404,0.227 0.634,0.227 0.045,0 0.091,-0.003 0.137,-0.009 0.276,-0.039 0.524,-0.19 0.684,-0.419 l 6.214,-8.929 c 0.315,-0.454 0.203,-1.077 -0.25,-1.393 z"
|
||||
id="path4" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB |
32
PettingZoo/Images/Publish.svg
Normal file
@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 58.707 58.707" style="enable-background:new 0 0 58.707 58.707;" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<polygon style="fill:#EFEBDE;" points="46.072,14 32.072,0 1.072,0 1.072,58 46.072,58 "/>
|
||||
<g>
|
||||
<path style="fill:#D5D0BB;" d="M11.072,23h25c0.552,0,1-0.447,1-1s-0.448-1-1-1h-25c-0.552,0-1,0.447-1,1S10.52,23,11.072,23z"/>
|
||||
<path style="fill:#D5D0BB;" d="M11.072,15h10c0.552,0,1-0.447,1-1s-0.448-1-1-1h-10c-0.552,0-1,0.447-1,1S10.52,15,11.072,15z"/>
|
||||
<path style="fill:#D5D0BB;" d="M36.072,29h-25c-0.552,0-1,0.447-1,1s0.448,1,1,1h25c0.552,0,1-0.447,1-1S36.624,29,36.072,29z"/>
|
||||
<path style="fill:#D5D0BB;" d="M36.072,37h-25c-0.552,0-1,0.447-1,1s0.448,1,1,1h25c0.552,0,1-0.447,1-1S36.624,37,36.072,37z"/>
|
||||
<path style="fill:#D5D0BB;" d="M36.072,45h-25c-0.552,0-1,0.447-1,1s0.448,1,1,1h25c0.552,0,1-0.447,1-1S36.624,45,36.072,45z"/>
|
||||
</g>
|
||||
<polygon style="fill:#D5D0BB;" points="32.072,0 32.072,14 46.072,14 "/>
|
||||
</g>
|
||||
<g>
|
||||
<polygon style="fill:#EDDCC7;" points="36.201,49.214 36.194,49.222 34.205,56.511 38.852,51.865 "/>
|
||||
<path style="fill:#D75A4A;" d="M55.451,35.266l-1.247-1.247c-0.775-0.775-2.032-0.775-2.807,0L47.815,37.6l2.651,2.651
|
||||
L55.451,35.266z"/>
|
||||
|
||||
<rect x="41.459" y="36.521" transform="matrix(0.7071 0.7071 -0.7071 0.7071 44.3228 -17.5395)" style="fill:#F29C21;" width="3.749" height="16.424"/>
|
||||
<polygon style="fill:#D6C4B1;" points="41.85,54.879 41.858,54.871 38.852,51.865 34.205,56.511 34.072,57 "/>
|
||||
<path style="fill:#A34740;" d="M53.472,43.257l3.582-3.582c0.775-0.775,0.775-2.032,0-2.807l-1.602-1.602l-4.985,4.985
|
||||
L53.472,43.257z"/>
|
||||
|
||||
<rect x="44.036" y="39.349" transform="matrix(-0.7071 -0.7071 0.7071 -0.7071 45.1717 113.8333)" style="fill:#E18C25;" width="4.251" height="16.424"/>
|
||||
<path style="fill:#5E5E5E;" d="M33.365,58.707c-0.256,0-0.512-0.098-0.707-0.293c-0.391-0.391-0.391-1.023,0-1.414l2.207-2.207
|
||||
c0.391-0.391,1.023-0.391,1.414,0s0.391,1.023,0,1.414l-2.207,2.207C33.877,58.609,33.621,58.707,33.365,58.707z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.2 KiB |
18
PettingZoo/Images/PublishSend.svg
Normal file
@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 58 58" style="enable-background:new 0 0 58 58;" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<polygon style="fill:#DCD6CD;" points="0,5.5 0,44.5 28,44.5 56,44.5 56,5.5 "/>
|
||||
<path style="fill:#E8E3D9;" d="M30.965,27.607c-1.637,1.462-4.292,1.462-5.93,0l-2.087-1.843C16.419,31.591,0,44.5,0,44.5h21.607
|
||||
h12.787H56c0,0-16.419-12.909-22.948-18.736L30.965,27.607z"/>
|
||||
<path style="fill:#EFEBDE;" d="M0,5.5l25.035,22.107c1.637,1.462,4.292,1.462,5.93,0L56,5.5H0z"/>
|
||||
</g>
|
||||
<g>
|
||||
<rect x="36" y="30.5" style="fill:#48A0DC;" width="22" height="22"/>
|
||||
<rect x="46" y="36.5" style="fill:#FFFFFF;" width="2" height="16"/>
|
||||
<polygon style="fill:#FFFFFF;" points="52.293,43.207 47,37.914 41.707,43.207 40.293,41.793 47,35.086 53.707,41.793 "/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.0 KiB |
32
PettingZoo/Images/Subscribe.svg
Normal file
@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 58.195 58.195" style="enable-background:new 0 0 58.195 58.195;" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<polygon style="fill:#EFEBDE;" points="45,14.097 31,0.097 0,0.097 0,58.097 45,58.097 "/>
|
||||
<g>
|
||||
<path style="fill:#D5D0BB;" d="M10,23.097h25c0.552,0,1-0.447,1-1s-0.448-1-1-1H10c-0.552,0-1,0.447-1,1S9.448,23.097,10,23.097z
|
||||
"/>
|
||||
<path style="fill:#D5D0BB;" d="M10,15.097h10c0.552,0,1-0.447,1-1s-0.448-1-1-1H10c-0.552,0-1,0.447-1,1S9.448,15.097,10,15.097z
|
||||
"/>
|
||||
<path style="fill:#D5D0BB;" d="M35,29.097H10c-0.552,0-1,0.447-1,1s0.448,1,1,1h25c0.552,0,1-0.447,1-1S35.552,29.097,35,29.097z
|
||||
"/>
|
||||
<path style="fill:#D5D0BB;" d="M35,37.097H10c-0.552,0-1,0.447-1,1s0.448,1,1,1h25c0.552,0,1-0.447,1-1S35.552,37.097,35,37.097z
|
||||
"/>
|
||||
<path style="fill:#D5D0BB;" d="M35,45.097H10c-0.552,0-1,0.447-1,1s0.448,1,1,1h25c0.552,0,1-0.447,1-1S35.552,45.097,35,45.097z
|
||||
"/>
|
||||
</g>
|
||||
<polygon style="fill:#D5D0BB;" points="31,0.097 31,14.097 45,14.097 "/>
|
||||
</g>
|
||||
<g>
|
||||
<path style="fill:#FFFFFF;" d="M57,48.289l-0.107,0.163c-7.121,10.876-18.773,10.876-25.893,0l0,0l0.107-0.163
|
||||
C38.227,37.412,49.879,37.412,57,48.289L57,48.289z"/>
|
||||
<circle style="fill:#556080;" cx="43.764" cy="46.007" r="5.909"/>
|
||||
<path style="fill:#8697CB;" d="M43.947,57.609c-5.254,0-10.148-3.058-13.783-8.609l-0.358-0.547l0.465-0.711
|
||||
c3.635-5.552,8.53-8.609,13.784-8.609c5.253,0,10.148,3.057,13.783,8.609l0.358,0.547l-0.465,0.711
|
||||
C54.095,54.552,49.2,57.609,43.947,57.609z M32.203,48.448c3.206,4.624,7.356,7.161,11.744,7.161c4.436,0,8.63-2.594,11.85-7.317
|
||||
c-3.206-4.624-7.356-7.161-11.743-7.161C39.617,41.132,35.423,43.725,32.203,48.448z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.9 KiB |
19
PettingZoo/Images/Undock.svg
Normal file
@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 58 58" style="enable-background:new 0 0 58 58;" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<rect y="16" style="fill:#ECF0F1;" width="50" height="35"/>
|
||||
<rect y="2" style="fill:#546A79;" width="50" height="14"/>
|
||||
<circle style="fill:#ED7161;" cx="7" cy="9" r="3"/>
|
||||
<circle style="fill:#EFC41A;" cx="16" cy="9" r="3"/>
|
||||
<circle style="fill:#4FBA6E;" cx="25" cy="9" r="3"/>
|
||||
</g>
|
||||
<g>
|
||||
<rect x="36" y="34" style="fill:#21AE5E;" width="22" height="22"/>
|
||||
<rect x="46" y="35.586" style="fill:#FFFFFF;" width="2" height="16"/>
|
||||
<polygon style="fill:#FFFFFF;" points="47,53 40,46.293 41.476,44.879 47,50.172 52.524,44.879 54,46.293 "/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 930 B |
190
PettingZoo/PettingZoo.csproj
Normal file
@ -0,0 +1,190 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>net6.0-windows</TargetFramework>
|
||||
<Version>0.1</Version>
|
||||
<UseWPF>true</UseWPF>
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
<Authors>Mark van Renswoude</Authors>
|
||||
<Product>Petting Zoo</Product>
|
||||
<Description>Petting Zoo - a live RabbitMQ message viewer</Description>
|
||||
<Copyright />
|
||||
<Nullable>enable</Nullable>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<StartupObject>PettingZoo.Program</StartupObject>
|
||||
<PackageIcon></PackageIcon>
|
||||
<PackageIconUrl />
|
||||
<ApplicationIcon>PettingZoo.ico</ApplicationIcon>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="Images\Connecting.svg" />
|
||||
<None Remove="Images\Dock.svg" />
|
||||
<None Remove="Images\Error.svg" />
|
||||
<None Remove="Images\Ok.svg" />
|
||||
<None Remove="Images\PublishSend.svg" />
|
||||
<None Remove="Images\Undock.svg" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Resource Include="Images\Clear.svg" />
|
||||
<Resource Include="Images\Connect.svg" />
|
||||
<Resource Include="Images\Disconnect.svg" />
|
||||
<Resource Include="Images\Dock.svg" />
|
||||
<Resource Include="Images\Error.svg" />
|
||||
<Resource Include="Images\Ok.svg" />
|
||||
<Resource Include="Images\Publish.svg" />
|
||||
<Resource Include="Images\PublishSend.svg" />
|
||||
<Resource Include="Images\Subscribe.svg" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
<PackageReference Include="SharpVectors" Version="1.7.7" />
|
||||
<PackageReference Include="SimpleInjector" Version="5.3.2" />
|
||||
<PackageReference Include="System.Reactive" Version="5.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\PettingZoo.Core\PettingZoo.Core.csproj" />
|
||||
<ProjectReference Include="..\PettingZoo.RabbitMQ\PettingZoo.RabbitMQ.csproj" />
|
||||
<ProjectReference Include="..\PettingZoo.Settings.LiteDB\PettingZoo.Settings.LiteDB.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Resource Include="Images\Undock.svg" />
|
||||
<Resource Include="Images\Connecting.svg" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Update="UI\Connection\ConnectionDisplayNameStrings.Designer.cs">
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>ConnectionDisplayNameStrings.resx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Update="UI\Connection\ConnectionWindowStrings.Designer.cs">
|
||||
<DependentUpon>ConnectionWindowStrings.resx</DependentUpon>
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
</Compile>
|
||||
<Compile Update="UI\Example\ExamplePickerDialogStrings.Designer.cs">
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>ExamplePickerDialogStrings.resx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Update="UI\Main\MainWindowStrings.Designer.cs">
|
||||
<DependentUpon>MainWindowStrings.resx</DependentUpon>
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
</Compile>
|
||||
<Compile Update="UI\Subscribe\SubscribeWindowStrings.Designer.cs">
|
||||
<DependentUpon>SubscribeWindowStrings.resx</DependentUpon>
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
</Compile>
|
||||
<Compile Update="UI\Tab\Publisher\PayloadEditorStrings.Designer.cs">
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>PayloadEditorStrings.resx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Update="UI\Tab\Publisher\TapetiPublisherViewStrings.Designer.cs">
|
||||
<DependentUpon>TapetiPublisherViewStrings.resx</DependentUpon>
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
</Compile>
|
||||
<Compile Update="UI\Tab\Publisher\TapetiPublisherView.xaml.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Update="UI\Tab\Publisher\RawPublisherViewStrings.Designer.cs">
|
||||
<DependentUpon>RawPublisherViewStrings.resx</DependentUpon>
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
</Compile>
|
||||
<Compile Update="UI\Tab\Publisher\PublisherViewStrings.Designer.cs">
|
||||
<DependentUpon>PublisherViewStrings.resx</DependentUpon>
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
</Compile>
|
||||
<Compile Update="UI\Tab\Subscriber\SubscriberViewStrings.Designer.cs">
|
||||
<DependentUpon>SubscriberViewStrings.resx</DependentUpon>
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
</Compile>
|
||||
<Compile Update="UI\Tab\Undocked\UndockedTabHostStrings.Designer.cs">
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>UndockedTabHostStrings.resx</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Update="UI\Connection\ConnectionDisplayNameStrings.resx">
|
||||
<Generator>PublicResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>ConnectionDisplayNameStrings.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Update="UI\Connection\ConnectionWindowStrings.resx">
|
||||
<LastGenOutput>ConnectionWindowStrings.Designer.cs</LastGenOutput>
|
||||
<Generator>PublicResXFileCodeGenerator</Generator>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Update="UI\Example\ExamplePickerDialogStrings.resx">
|
||||
<Generator>PublicResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>ExamplePickerDialogStrings.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Update="UI\Main\MainWindowStrings.resx">
|
||||
<LastGenOutput>MainWindowStrings.Designer.cs</LastGenOutput>
|
||||
<Generator>PublicResXFileCodeGenerator</Generator>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Update="UI\Subscribe\SubscribeWindowStrings.resx">
|
||||
<LastGenOutput>SubscribeWindowStrings.Designer.cs</LastGenOutput>
|
||||
<Generator>PublicResXFileCodeGenerator</Generator>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Update="UI\Tab\Publisher\PayloadEditorStrings.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>PayloadEditorStrings.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Update="UI\Tab\Publisher\TapetiPublisherViewStrings.resx">
|
||||
<LastGenOutput>TapetiPublisherViewStrings.Designer.cs</LastGenOutput>
|
||||
<Generator>PublicResXFileCodeGenerator</Generator>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Update="UI\Tab\Publisher\RawPublisherViewStrings.resx">
|
||||
<LastGenOutput>RawPublisherViewStrings.Designer.cs</LastGenOutput>
|
||||
<Generator>PublicResXFileCodeGenerator</Generator>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Update="UI\Tab\Publisher\PublisherViewStrings.resx">
|
||||
<LastGenOutput>PublisherViewStrings.Designer.cs</LastGenOutput>
|
||||
<Generator>PublicResXFileCodeGenerator</Generator>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Update="UI\Tab\Subscriber\SubscriberViewStrings.resx">
|
||||
<LastGenOutput>SubscriberViewStrings.Designer.cs</LastGenOutput>
|
||||
<Generator>PublicResXFileCodeGenerator</Generator>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Update="UI\Tab\Undocked\UndockedTabHostStrings.resx">
|
||||
<Generator>PublicResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>UndockedTabHostStrings.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Page Update="UI\Connection\ConnectionWindow.xaml">
|
||||
<XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime>
|
||||
</Page>
|
||||
<Page Update="UI\Main\MainWindow.xaml">
|
||||
<XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime>
|
||||
</Page>
|
||||
<Page Update="UI\Subscribe\SubscribeWindow.xaml">
|
||||
<XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime>
|
||||
</Page>
|
||||
<Page Update="UI\Tab\Publisher\PublisherView.xaml">
|
||||
<XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime>
|
||||
</Page>
|
||||
<Page Update="UI\Tab\Publisher\TapetiPublisherView.xaml">
|
||||
<XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Update="UI\Tab\Subscriber\SubscriberView.xaml">
|
||||
<XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB |
78
PettingZoo/Program.cs
Normal file
@ -0,0 +1,78 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Markup;
|
||||
using PettingZoo.Core.Connection;
|
||||
using PettingZoo.Core.Settings;
|
||||
using PettingZoo.RabbitMQ;
|
||||
using PettingZoo.Settings.LiteDB;
|
||||
using PettingZoo.UI.Connection;
|
||||
using PettingZoo.UI.Main;
|
||||
using PettingZoo.UI.Subscribe;
|
||||
using SimpleInjector;
|
||||
|
||||
namespace PettingZoo
|
||||
{
|
||||
public static class Program
|
||||
{
|
||||
[STAThread]
|
||||
public static void Main()
|
||||
{
|
||||
// WPF defaults to US for date formatting in bindings, this fixes it
|
||||
FrameworkElement.LanguageProperty.OverrideMetadata(typeof(FrameworkElement), new FrameworkPropertyMetadata(
|
||||
XmlLanguage.GetLanguage(CultureInfo.CurrentCulture.IetfLanguageTag)));
|
||||
|
||||
var container = Bootstrap();
|
||||
RunApplication(container);
|
||||
}
|
||||
|
||||
|
||||
private static Container Bootstrap()
|
||||
{
|
||||
var container = new Container();
|
||||
|
||||
// See comments in RunApplication
|
||||
container.Options.EnableAutoVerification = false;
|
||||
|
||||
container.Register<IConnectionFactory, RabbitMQClientConnectionFactory>();
|
||||
container.Register<IConnectionDialog, WindowConnectionDialog>();
|
||||
container.Register<ISubscribeDialog, WindowSubscribeDialog>();
|
||||
container.Register<IConnectionSettingsRepository, LiteDBConnectionSettingsRepository>();
|
||||
container.Register<IUISettingsRepository, LiteDBUISettingsRepository>();
|
||||
|
||||
container.Register<MainWindow>();
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
|
||||
private static void RunApplication(Container container)
|
||||
{
|
||||
try
|
||||
{
|
||||
var app = new App(container);
|
||||
app.InitializeComponent();
|
||||
|
||||
#if DEBUG
|
||||
// Verify container after initialization to prevent issues loading the resource dictionaries
|
||||
container.Verify();
|
||||
|
||||
// This causes the MainWindow and Windows properties to be populated however, which we don't want
|
||||
// because then the app does not close properly when using OnMainWindowClose, so clean up the mess
|
||||
app.MainWindow = null;
|
||||
foreach (var window in app.Windows)
|
||||
((Window)window).Close();
|
||||
|
||||
// All this is the reason we only perform verification in debug builds
|
||||
#endif
|
||||
|
||||
app.Run();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
MessageBox.Show($"Fatal exception: {e.Message}", @"PettingZoo", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:infrastructure="clr-namespace:PettingZoo.Infrastructure">
|
||||
xmlns:ui="clr-namespace:PettingZoo.UI">
|
||||
<!-- Global styling -->
|
||||
<Style x:Key="WindowStyle" TargetType="{x:Type Window}">
|
||||
<Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
|
||||
@ -10,6 +10,11 @@
|
||||
<Setter Property="Padding" Value="8,4"/>
|
||||
</Style>
|
||||
|
||||
<Style TargetType="{x:Type TextBox}">
|
||||
<Setter Property="Padding" Value="3" />
|
||||
</Style>
|
||||
|
||||
|
||||
<!-- Explicit styling -->
|
||||
<Style x:Key="SidePanel" TargetType="{x:Type Border}">
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
@ -28,9 +33,13 @@
|
||||
<Style x:Key="FooterButton" TargetType="{x:Type Button}" BasedOn="{StaticResource {x:Type Button}}">
|
||||
<Setter Property="Margin" Value="8,0,0,0" />
|
||||
</Style>
|
||||
|
||||
|
||||
<Style x:Key="Form" TargetType="{x:Type infrastructure:GridLayout}">
|
||||
|
||||
<Style x:Key="FooterButtonLeft" TargetType="{x:Type Button}" BasedOn="{StaticResource {x:Type Button}}">
|
||||
<Setter Property="Margin" Value="0,0,8,0" />
|
||||
</Style>
|
||||
|
||||
|
||||
<Style x:Key="Form" TargetType="{x:Type ui:GridLayout}">
|
||||
<Setter Property="ChildMargin" Value="4"/>
|
||||
</Style>
|
||||
|
||||
@ -59,6 +68,25 @@
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
|
||||
<Style x:Key="Routingkey" TargetType="{x:Type TextBlock}">
|
||||
<Style x:Key="RoutingKey">
|
||||
</Style>
|
||||
|
||||
<Style x:Key="TypeSelection" TargetType="{x:Type ToggleButton}">
|
||||
<Setter Property="Margin" Value="0 0 8 0" />
|
||||
<Setter Property="Padding" Value="8 4 8 4" />
|
||||
<Setter Property="VerticalAlignment" Value="Center" />
|
||||
</Style>
|
||||
|
||||
|
||||
<Style x:Key="SectionLabel" TargetType="{x:Type Label}">
|
||||
<Setter Property="FontWeight" Value="Bold"/>
|
||||
</Style>
|
||||
|
||||
|
||||
<Style x:Key="Payload" TargetType="{x:Type TextBox}">
|
||||
<Setter Property="AcceptsReturn" Value="True" />
|
||||
<Setter Property="AcceptsTab" Value="True" />
|
||||
<Setter Property="VerticalScrollBarVisibility" Value="Visible" />
|
||||
<Setter Property="FontFamily" Value="Consolas,Courier New" />
|
||||
</Style>
|
||||
</ResourceDictionary>
|
13
PettingZoo/TODO.md
Normal file
@ -0,0 +1,13 @@
|
||||
Must-have
|
||||
---------
|
||||
|
||||
|
||||
Should-have
|
||||
-----------
|
||||
- Save / load publisher messages (either as templates or to disk)
|
||||
- Export received messages to Tapeti JSON file / Tapeti.Cmd command-line
|
||||
|
||||
|
||||
Nice-to-have
|
||||
------------
|
||||
- JSON syntax highlighting
|
65
PettingZoo/UI/BaseViewModel.cs
Normal file
@ -0,0 +1,65 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace PettingZoo.UI
|
||||
{
|
||||
public class BaseViewModel : INotifyPropertyChanged
|
||||
{
|
||||
private int commandsChangedDisabled;
|
||||
|
||||
public event PropertyChangedEventHandler? PropertyChanged;
|
||||
|
||||
protected virtual void RaisePropertyChanged([CallerMemberName] string? propertyName = null)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
|
||||
|
||||
protected bool SetField<T>(ref T field, T value, IEqualityComparer<T>? comparer = null, [CallerMemberName] string? propertyName = null,
|
||||
DelegateCommand[]? delegateCommandsChanged = null,
|
||||
string[]? otherPropertiesChanged = null)
|
||||
{
|
||||
if ((comparer ?? EqualityComparer<T>.Default).Equals(field, value))
|
||||
return false;
|
||||
|
||||
field = value;
|
||||
RaisePropertyChanged(propertyName);
|
||||
|
||||
if (otherPropertiesChanged != null)
|
||||
{
|
||||
foreach (var otherProperty in otherPropertiesChanged)
|
||||
RaisePropertyChanged(otherProperty);
|
||||
}
|
||||
|
||||
// ReSharper disable once InvertIf
|
||||
if (delegateCommandsChanged != null)
|
||||
{
|
||||
foreach (var delegateCommand in delegateCommandsChanged)
|
||||
delegateCommand.RaiseCanExecuteChanged();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
protected void DisableCommandsChanged(Action updateFields, params DelegateCommand[] delegateCommandsChangedAfter)
|
||||
{
|
||||
commandsChangedDisabled++;
|
||||
try
|
||||
{
|
||||
updateFields();
|
||||
}
|
||||
finally
|
||||
{
|
||||
commandsChangedDisabled--;
|
||||
if (commandsChangedDisabled == 0)
|
||||
{
|
||||
foreach (var delegateCommand in delegateCommandsChangedAfter)
|
||||
delegateCommand.RaiseCanExecuteChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
24
PettingZoo/UI/BindingProxy.cs
Normal file
@ -0,0 +1,24 @@
|
||||
using System.Windows;
|
||||
|
||||
namespace PettingZoo.UI
|
||||
{
|
||||
public class BindingProxy : Freezable
|
||||
{
|
||||
protected override Freezable CreateInstanceCore()
|
||||
{
|
||||
return new BindingProxy();
|
||||
}
|
||||
|
||||
|
||||
public object Data
|
||||
{
|
||||
get => GetValue(DataProperty);
|
||||
set => SetValue(DataProperty, value);
|
||||
}
|
||||
|
||||
|
||||
// Using a DependencyProperty as the backing store for Data. This enables animation, styling, binding, etc...
|
||||
public static readonly DependencyProperty DataProperty =
|
||||
DependencyProperty.Register("Data", typeof(object), typeof(BindingProxy), new UIPropertyMetadata(null));
|
||||
}
|
||||
}
|
25
PettingZoo/UI/Connection/ConnectionDisplayNameDialog.xaml
Normal file
@ -0,0 +1,25 @@
|
||||
<Window x:Class="PettingZoo.UI.Connection.ConnectionDisplayNameDialog"
|
||||
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"
|
||||
xmlns:local="clr-namespace:PettingZoo.UI.Connection"
|
||||
mc:Ignorable="d"
|
||||
Width="400"
|
||||
SizeToContent="Height"
|
||||
ResizeMode="NoResize"
|
||||
WindowStyle="ToolWindow"
|
||||
WindowStartupLocation="CenterOwner"
|
||||
Style="{StaticResource WindowStyle}"
|
||||
Title="{x:Static local:ConnectionDisplayNameStrings.WindowTitle}"
|
||||
FocusManager.FocusedElement="{Binding ElementName=DisplayNameTextBox}"
|
||||
d:DataContext="{d:DesignInstance local:ConnectionDisplayNameViewModel}">
|
||||
<StackPanel Margin="8">
|
||||
<TextBox Name="DisplayNameTextBox" Text="{Binding DisplayName, UpdateSourceTrigger=PropertyChanged}" />
|
||||
|
||||
<UniformGrid HorizontalAlignment="Right" Rows="1" Columns="2" Style="{StaticResource FooterPanel}">
|
||||
<Button IsDefault="True" Content="{x:Static local:ConnectionDisplayNameStrings.ButtonOK}" Style="{StaticResource FooterButton}" Command="{Binding OkCommand}"/>
|
||||
<Button IsCancel="True" Content="{x:Static local:ConnectionDisplayNameStrings.ButtonCancel}" Style="{StaticResource FooterButton}"/>
|
||||
</UniformGrid>
|
||||
</StackPanel>
|
||||
</Window>
|
49
PettingZoo/UI/Connection/ConnectionDisplayNameDialog.xaml.cs
Normal file
@ -0,0 +1,49 @@
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
|
||||
namespace PettingZoo.UI.Connection
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for ConnectionDisplayNameDialog.xaml
|
||||
/// </summary>
|
||||
public partial class ConnectionDisplayNameDialog
|
||||
{
|
||||
public static bool Execute(ref string displayName)
|
||||
{
|
||||
var viewModel = new ConnectionDisplayNameViewModel
|
||||
{
|
||||
DisplayName = displayName
|
||||
};
|
||||
|
||||
|
||||
var activeWindow = Application.Current.Windows
|
||||
.Cast<Window>()
|
||||
.FirstOrDefault(applicationWindow => applicationWindow.IsActive);
|
||||
|
||||
var window = new ConnectionDisplayNameDialog(viewModel)
|
||||
{
|
||||
Owner = activeWindow ?? Application.Current.MainWindow
|
||||
};
|
||||
|
||||
if (!window.ShowDialog().GetValueOrDefault())
|
||||
return false;
|
||||
|
||||
displayName = viewModel.DisplayName;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public ConnectionDisplayNameDialog(ConnectionDisplayNameViewModel viewModel)
|
||||
{
|
||||
viewModel.OkClick += (_, _) =>
|
||||
{
|
||||
DialogResult = true;
|
||||
};
|
||||
|
||||
DataContext = viewModel;
|
||||
InitializeComponent();
|
||||
|
||||
DisplayNameTextBox.CaretIndex = DisplayNameTextBox.Text.Length;
|
||||
}
|
||||
}
|
||||
}
|
90
PettingZoo/UI/Connection/ConnectionDisplayNameStrings.Designer.cs
generated
Normal file
@ -0,0 +1,90 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <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 PettingZoo.UI.Connection {
|
||||
using System;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
/// </summary>
|
||||
// This class was auto-generated by the StronglyTypedResourceBuilder
|
||||
// class via a tool like ResGen or Visual Studio.
|
||||
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||
// with the /str option, or rebuild your VS project.
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
public class ConnectionDisplayNameStrings {
|
||||
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
|
||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||
|
||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal ConnectionDisplayNameStrings() {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the cached ResourceManager instance used by this class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
public static global::System.Resources.ResourceManager ResourceManager {
|
||||
get {
|
||||
if (object.ReferenceEquals(resourceMan, null)) {
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("PettingZoo.UI.Connection.ConnectionDisplayNameStrings", typeof(ConnectionDisplayNameStrings).Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the current thread's CurrentUICulture property for all
|
||||
/// resource lookups using this strongly typed resource class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
public static global::System.Globalization.CultureInfo Culture {
|
||||
get {
|
||||
return resourceCulture;
|
||||
}
|
||||
set {
|
||||
resourceCulture = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Cancel.
|
||||
/// </summary>
|
||||
public static string ButtonCancel {
|
||||
get {
|
||||
return ResourceManager.GetString("ButtonCancel", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to OK.
|
||||
/// </summary>
|
||||
public static string ButtonOK {
|
||||
get {
|
||||
return ResourceManager.GetString("ButtonOK", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Profile name.
|
||||
/// </summary>
|
||||
public static string WindowTitle {
|
||||
get {
|
||||
return ResourceManager.GetString("WindowTitle", resourceCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
129
PettingZoo/UI/Connection/ConnectionDisplayNameStrings.resx
Normal file
@ -0,0 +1,129 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="ButtonCancel" xml:space="preserve">
|
||||
<value>Cancel</value>
|
||||
</data>
|
||||
<data name="ButtonOK" xml:space="preserve">
|
||||
<value>OK</value>
|
||||
</data>
|
||||
<data name="WindowTitle" xml:space="preserve">
|
||||
<value>Profile name</value>
|
||||
</data>
|
||||
</root>
|
41
PettingZoo/UI/Connection/ConnectionDisplayNameViewModel.cs
Normal file
@ -0,0 +1,41 @@
|
||||
using System;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace PettingZoo.UI.Connection
|
||||
{
|
||||
public class ConnectionDisplayNameViewModel : BaseViewModel
|
||||
{
|
||||
private string displayName = "";
|
||||
|
||||
private readonly DelegateCommand okCommand;
|
||||
|
||||
|
||||
public string DisplayName
|
||||
{
|
||||
get => displayName;
|
||||
set => SetField(ref displayName, value, delegateCommandsChanged: new [] { okCommand });
|
||||
}
|
||||
|
||||
public ICommand OkCommand => okCommand;
|
||||
|
||||
public event EventHandler? OkClick;
|
||||
|
||||
|
||||
public ConnectionDisplayNameViewModel()
|
||||
{
|
||||
okCommand = new DelegateCommand(OkExecute, OkCanExecute);
|
||||
}
|
||||
|
||||
|
||||
private void OkExecute()
|
||||
{
|
||||
OkClick?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
|
||||
private bool OkCanExecute()
|
||||
{
|
||||
return !string.IsNullOrWhiteSpace(DisplayName);
|
||||
}
|
||||
}
|
||||
}
|
321
PettingZoo/UI/Connection/ConnectionViewModel.cs
Normal file
@ -0,0 +1,321 @@
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Input;
|
||||
using PettingZoo.Core.Settings;
|
||||
|
||||
namespace PettingZoo.UI.Connection
|
||||
{
|
||||
public class ConnectionViewModel : BaseViewModel
|
||||
{
|
||||
private readonly IConnectionSettingsRepository connectionSettingsRepository;
|
||||
private readonly StoredConnectionSettings defaultSettings;
|
||||
private string host = null!;
|
||||
private string virtualHost = null!;
|
||||
private int port;
|
||||
private string username = null!;
|
||||
private string password = null!;
|
||||
|
||||
private bool subscribe;
|
||||
private string exchange = null!;
|
||||
private string routingKey = null!;
|
||||
|
||||
private bool storePassword;
|
||||
|
||||
private StoredConnectionSettings? selectedStoredConnection;
|
||||
|
||||
private readonly DelegateCommand okCommand;
|
||||
private readonly DelegateCommand saveCommand;
|
||||
private readonly DelegateCommand saveAsCommand;
|
||||
private readonly DelegateCommand deleteCommand;
|
||||
|
||||
private readonly DelegateCommand[] connectionChangedCommands;
|
||||
|
||||
|
||||
public string Host
|
||||
{
|
||||
get => host;
|
||||
set => SetField(ref host, value, delegateCommandsChanged: connectionChangedCommands);
|
||||
}
|
||||
|
||||
public string VirtualHost
|
||||
{
|
||||
get => virtualHost;
|
||||
set => SetField(ref virtualHost, value, delegateCommandsChanged: connectionChangedCommands);
|
||||
}
|
||||
|
||||
public int Port
|
||||
{
|
||||
get => port;
|
||||
set => SetField(ref port, value, delegateCommandsChanged: connectionChangedCommands);
|
||||
}
|
||||
|
||||
public string Username
|
||||
{
|
||||
get => username;
|
||||
set => SetField(ref username, value, delegateCommandsChanged: connectionChangedCommands);
|
||||
}
|
||||
|
||||
public string Password
|
||||
{
|
||||
get => password;
|
||||
set => SetField(ref password, value, delegateCommandsChanged: connectionChangedCommands);
|
||||
}
|
||||
|
||||
|
||||
public bool Subscribe
|
||||
{
|
||||
get => subscribe;
|
||||
set => SetField(ref subscribe, value, delegateCommandsChanged: connectionChangedCommands);
|
||||
}
|
||||
|
||||
public string Exchange
|
||||
{
|
||||
get => exchange;
|
||||
set
|
||||
{
|
||||
if (SetField(ref exchange, value, delegateCommandsChanged: connectionChangedCommands))
|
||||
AutoToggleSubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
public string RoutingKey
|
||||
{
|
||||
get => routingKey;
|
||||
set
|
||||
{
|
||||
if (SetField(ref routingKey, value, delegateCommandsChanged: connectionChangedCommands))
|
||||
AutoToggleSubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public bool StorePassword
|
||||
{
|
||||
get => storePassword;
|
||||
set => SetField(ref storePassword, value, delegateCommandsChanged: connectionChangedCommands);
|
||||
}
|
||||
|
||||
|
||||
public ObservableCollection<StoredConnectionSettings> StoredConnections { get; } = new();
|
||||
|
||||
public StoredConnectionSettings? SelectedStoredConnection
|
||||
{
|
||||
get => selectedStoredConnection;
|
||||
set
|
||||
{
|
||||
if (value == null)
|
||||
return;
|
||||
|
||||
if (!SetField(ref selectedStoredConnection, value, delegateCommandsChanged: new [] { deleteCommand }))
|
||||
return;
|
||||
|
||||
DisableCommandsChanged(
|
||||
() =>
|
||||
{
|
||||
Host = value.Host;
|
||||
VirtualHost = value.VirtualHost;
|
||||
Port = value.Port;
|
||||
Username = value.Username;
|
||||
Password = value.Password;
|
||||
StorePassword = value.StorePassword;
|
||||
|
||||
Exchange = value.Exchange;
|
||||
RoutingKey = value.RoutingKey;
|
||||
Subscribe = value.Subscribe;
|
||||
},
|
||||
connectionChangedCommands);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public ICommand OkCommand => okCommand;
|
||||
public ICommand SaveCommand => saveCommand;
|
||||
public ICommand SaveAsCommand => saveAsCommand;
|
||||
public ICommand DeleteCommand => deleteCommand;
|
||||
|
||||
public event EventHandler? OkClick;
|
||||
|
||||
|
||||
public ConnectionViewModel(IConnectionSettingsRepository connectionSettingsRepository, StoredConnectionSettings defaultSettings)
|
||||
{
|
||||
this.connectionSettingsRepository = connectionSettingsRepository;
|
||||
this.defaultSettings = defaultSettings;
|
||||
|
||||
okCommand = new DelegateCommand(OkExecute, OkCanExecute);
|
||||
saveCommand = new DelegateCommand(SaveExecute, SaveCanExecute);
|
||||
saveAsCommand = new DelegateCommand(SaveAsExecute, SaveAsCanExecute);
|
||||
deleteCommand = new DelegateCommand(DeleteExecute, DeleteCanExecute);
|
||||
|
||||
connectionChangedCommands = new[] { saveCommand, saveAsCommand, okCommand };
|
||||
}
|
||||
|
||||
|
||||
public async Task Initialize()
|
||||
{
|
||||
var defaultConnection = new StoredConnectionSettings(
|
||||
Guid.Empty,
|
||||
ConnectionWindowStrings.LastUsedDisplayName,
|
||||
defaultSettings.StorePassword,
|
||||
defaultSettings.Host,
|
||||
defaultSettings.VirtualHost,
|
||||
defaultSettings.Port,
|
||||
defaultSettings.Username,
|
||||
defaultSettings.Password,
|
||||
defaultSettings.Subscribe,
|
||||
defaultSettings.Exchange,
|
||||
defaultSettings.RoutingKey);
|
||||
|
||||
var isStored = false;
|
||||
|
||||
foreach (var storedConnectionSettings in await connectionSettingsRepository.GetStored())
|
||||
{
|
||||
if (!isStored && storedConnectionSettings.SameParameters(defaultConnection, false))
|
||||
{
|
||||
SelectedStoredConnection = storedConnectionSettings;
|
||||
isStored = true;
|
||||
}
|
||||
|
||||
StoredConnections.Add(storedConnectionSettings);
|
||||
}
|
||||
|
||||
if (isStored)
|
||||
{
|
||||
// The last used parameters match a stored connection, insert the "New connection" item with default parameters
|
||||
StoredConnections.Insert(0, new StoredConnectionSettings(Guid.Empty, ConnectionWindowStrings.LastUsedDisplayName, true, ConnectionSettings.Default));
|
||||
}
|
||||
else
|
||||
{
|
||||
// No match, use the passed parameters
|
||||
StoredConnections.Insert(0, defaultConnection);
|
||||
SelectedStoredConnection = defaultConnection;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public ConnectionSettings ToModel()
|
||||
{
|
||||
return new ConnectionSettings(Host, VirtualHost, Port, Username, Password, Subscribe, Exchange, RoutingKey);
|
||||
}
|
||||
|
||||
|
||||
private bool ValidConnection(bool requirePassword)
|
||||
{
|
||||
return !string.IsNullOrWhiteSpace(Host) &&
|
||||
!string.IsNullOrWhiteSpace(VirtualHost) &&
|
||||
Port > 0 &&
|
||||
!string.IsNullOrWhiteSpace(Username) &&
|
||||
(!requirePassword || !string.IsNullOrWhiteSpace(Password)) &&
|
||||
(!Subscribe || (
|
||||
!string.IsNullOrWhiteSpace(Exchange) &&
|
||||
!string.IsNullOrWhiteSpace(RoutingKey)
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
private void AutoToggleSubscribe()
|
||||
{
|
||||
Subscribe = !string.IsNullOrWhiteSpace(Exchange) && !string.IsNullOrWhiteSpace(RoutingKey);
|
||||
}
|
||||
|
||||
|
||||
private void OkExecute()
|
||||
{
|
||||
OkClick?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
|
||||
private bool OkCanExecute()
|
||||
{
|
||||
return ValidConnection(true);
|
||||
}
|
||||
|
||||
|
||||
private async void SaveExecute()
|
||||
{
|
||||
if (SelectedStoredConnection == null || SelectedStoredConnection.Id == Guid.Empty)
|
||||
return;
|
||||
|
||||
var selectedIndex = StoredConnections.IndexOf(SelectedStoredConnection);
|
||||
|
||||
var updatedStoredConnection = await connectionSettingsRepository.Update(SelectedStoredConnection.Id, SelectedStoredConnection.DisplayName, StorePassword, ToModel());
|
||||
|
||||
|
||||
StoredConnections[selectedIndex] = updatedStoredConnection;
|
||||
SelectedStoredConnection = updatedStoredConnection;
|
||||
}
|
||||
|
||||
|
||||
private bool SaveCanExecute()
|
||||
{
|
||||
return SelectedStoredConnection != null &&
|
||||
SelectedStoredConnection.Id != Guid.Empty &&
|
||||
ValidConnection(false) &&
|
||||
(
|
||||
!ToModel().SameParameters(SelectedStoredConnection, StorePassword) ||
|
||||
SelectedStoredConnection.StorePassword != StorePassword
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
private async void SaveAsExecute()
|
||||
{
|
||||
// TODO create and enforce unique name?
|
||||
var displayName = SelectedStoredConnection != null && SelectedStoredConnection.Id != Guid.Empty ? SelectedStoredConnection.DisplayName : "";
|
||||
|
||||
if (!ConnectionDisplayNameDialog.Execute(ref displayName))
|
||||
return;
|
||||
|
||||
var storedConnectionSettings = await connectionSettingsRepository.Add(displayName, StorePassword, ToModel());
|
||||
|
||||
StoredConnections.Add(storedConnectionSettings);
|
||||
SelectedStoredConnection = storedConnectionSettings;
|
||||
}
|
||||
|
||||
|
||||
private bool SaveAsCanExecute()
|
||||
{
|
||||
return ValidConnection(false);
|
||||
}
|
||||
|
||||
|
||||
private async void DeleteExecute()
|
||||
{
|
||||
if (SelectedStoredConnection == null || SelectedStoredConnection.Id == Guid.Empty)
|
||||
return;
|
||||
|
||||
var selectedIndex = StoredConnections.IndexOf(SelectedStoredConnection);
|
||||
|
||||
if (MessageBox.Show(
|
||||
string.Format(ConnectionWindowStrings.DeleteConfirm, SelectedStoredConnection.DisplayName),
|
||||
ConnectionWindowStrings.DeleteConfirmTitle,
|
||||
MessageBoxButton.YesNo,
|
||||
MessageBoxImage.Question) != MessageBoxResult.Yes)
|
||||
return;
|
||||
|
||||
await connectionSettingsRepository.Delete(SelectedStoredConnection.Id);
|
||||
|
||||
StoredConnections.Remove(SelectedStoredConnection);
|
||||
if (selectedIndex >= StoredConnections.Count)
|
||||
selectedIndex--;
|
||||
|
||||
SelectedStoredConnection = StoredConnections[selectedIndex];
|
||||
}
|
||||
|
||||
|
||||
private bool DeleteCanExecute()
|
||||
{
|
||||
return SelectedStoredConnection != null && SelectedStoredConnection.Id != Guid.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class DesignTimeConnectionViewModel : ConnectionViewModel
|
||||
{
|
||||
public DesignTimeConnectionViewModel() : base(null!, null!)
|
||||
{
|
||||
StoredConnections.Add(new StoredConnectionSettings(Guid.Empty, "Dummy", true, ConnectionSettings.Default));
|
||||
}
|
||||
}
|
||||
}
|
104
PettingZoo/UI/Connection/ConnectionWindow.xaml
Normal file
@ -0,0 +1,104 @@
|
||||
<Window x:Class="PettingZoo.UI.Connection.ConnectionWindow"
|
||||
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:ui="clr-namespace:PettingZoo.UI"
|
||||
xmlns:connection="clr-namespace:PettingZoo.UI.Connection"
|
||||
mc:Ignorable="d"
|
||||
d:DataContext="{d:DesignInstance connection:DesignTimeConnectionViewModel, IsDesignTimeCreatable = True}"
|
||||
Width="700"
|
||||
SizeToContent="Height"
|
||||
ResizeMode="NoResize"
|
||||
WindowStyle="ToolWindow"
|
||||
Style="{StaticResource WindowStyle}"
|
||||
Title="{x:Static connection:ConnectionWindowStrings.WindowTitle}"
|
||||
FocusManager.FocusedElement="{Binding ElementName=HostTextBox}"
|
||||
WindowStartupLocation="CenterOwner">
|
||||
<Grid Margin="8">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<UniformGrid Grid.Row="1" Grid.Column="0" HorizontalAlignment="Left" Rows="1" Columns="1" Style="{StaticResource FooterPanel}">
|
||||
<Button Content="{x:Static connection:ConnectionWindowStrings.ButtonDelete}" Style="{StaticResource FooterButtonLeft}" Command="{Binding DeleteCommand}"/>
|
||||
</UniformGrid>
|
||||
|
||||
<UniformGrid Grid.Row="1" Grid.Column="1" HorizontalAlignment="Left" Rows="1" Columns="3" Style="{StaticResource FooterPanel}">
|
||||
<Button Content="{x:Static connection:ConnectionWindowStrings.ButtonSave}" Style="{StaticResource FooterButtonLeft}" Command="{Binding SaveCommand}"/>
|
||||
<Button Content="{x:Static connection:ConnectionWindowStrings.ButtonSaveAs}" Style="{StaticResource FooterButtonLeft}" Command="{Binding SaveAsCommand}"/>
|
||||
</UniformGrid>
|
||||
|
||||
<UniformGrid Grid.Row="1" Grid.Column="2" HorizontalAlignment="Right" Rows="1" Columns="2" Style="{StaticResource FooterPanel}">
|
||||
<Button IsDefault="True" Content="{x:Static connection:ConnectionWindowStrings.ButtonOK}" Style="{StaticResource FooterButton}" Command="{Binding OkCommand}"/>
|
||||
<Button IsCancel="True" Content="{x:Static connection:ConnectionWindowStrings.ButtonCancel}" Style="{StaticResource FooterButton}"/>
|
||||
</UniformGrid>
|
||||
|
||||
<ListBox Grid.Row="0" Grid.Column="0" Margin="0 0 8 0" Width="250" ItemsSource="{Binding StoredConnections}" SelectedValue="{Binding SelectedStoredConnection}">
|
||||
<ListBox.ItemContainerStyle>
|
||||
<Style TargetType="ListBoxItem">
|
||||
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
|
||||
</Style>
|
||||
</ListBox.ItemContainerStyle>
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding DisplayName}">
|
||||
<TextBlock.InputBindings>
|
||||
<MouseBinding MouseAction="LeftDoubleClick" Command="{Binding DataContext.OkCommand, RelativeSource={RelativeSource AncestorType={x:Type ListBox}}}" />
|
||||
</TextBlock.InputBindings>
|
||||
</TextBlock>
|
||||
</DataTemplate>
|
||||
</ListBox.ItemTemplate>
|
||||
</ListBox>
|
||||
|
||||
<ui:GridLayout Style="{StaticResource Form}" Grid.Row="0" Grid.Column="1" Grid.ColumnSpan="2">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="24"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Label Grid.Column="0" Grid.Row="0" Content="{x:Static connection:ConnectionWindowStrings.LabelHost}"/>
|
||||
<TextBox Grid.Column="1" Grid.Row="0" Text="{Binding Host, UpdateSourceTrigger=PropertyChanged}" Name="HostTextBox" GotFocus="CaretToEnd" />
|
||||
|
||||
<Label Grid.Column="0" Grid.Row="1" Content="{x:Static connection:ConnectionWindowStrings.LabelPort}"/>
|
||||
<TextBox Grid.Column="1" Grid.Row="1" Text="{Binding Port, UpdateSourceTrigger=PropertyChanged}" Width="100" HorizontalAlignment="Left" PreviewTextInput="NumericPreviewTextInput" GotFocus="CaretToEnd" />
|
||||
|
||||
<Label Grid.Column="0" Grid.Row="2" Content="{x:Static connection:ConnectionWindowStrings.LabelVirtualHost}"/>
|
||||
<TextBox Grid.Column="1" Grid.Row="2" Text="{Binding VirtualHost, UpdateSourceTrigger=PropertyChanged}" GotFocus="CaretToEnd"/>
|
||||
|
||||
<Label Grid.Column="0" Grid.Row="3" Content="{x:Static connection:ConnectionWindowStrings.LabelUsername}"/>
|
||||
<TextBox Grid.Column="1" Grid.Row="3" Text="{Binding Username, UpdateSourceTrigger=PropertyChanged}" GotFocus="CaretToEnd"/>
|
||||
|
||||
<Label Grid.Column="0" Grid.Row="4" Content="{x:Static connection:ConnectionWindowStrings.LabelPassword}"/>
|
||||
<PasswordBox Grid.Column="1" Grid.Row="4" ui:PasswordBoxAssistant.BindPassword="true" ui:PasswordBoxAssistant.BoundPassword="{Binding Path=Password, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" GotFocus="CaretToEnd"/>
|
||||
<CheckBox Grid.Column="1" Grid.Row="5" Content="{x:Static connection:ConnectionWindowStrings.LabelStorePassword}" IsChecked="{Binding StorePassword}"/>
|
||||
|
||||
<CheckBox Grid.Column="1" Grid.Row="7" Content="{x:Static connection:ConnectionWindowStrings.LabelSubscribe}" IsChecked="{Binding Subscribe}"/>
|
||||
|
||||
<Label Grid.Column="0" Grid.Row="8" Content="{x:Static connection:ConnectionWindowStrings.LabelExchange}"/>
|
||||
<TextBox Grid.Column="1" Grid.Row="8" Text="{Binding Exchange, UpdateSourceTrigger=PropertyChanged}" GotFocus="CaretToEnd"/>
|
||||
|
||||
<Label Grid.Column="0" Grid.Row="9" Content="{x:Static connection:ConnectionWindowStrings.LabelRoutingKey}"/>
|
||||
<TextBox Grid.Column="1" Grid.Row="9" Text="{Binding RoutingKey, UpdateSourceTrigger=PropertyChanged}" GotFocus="CaretToEnd"/>
|
||||
</ui:GridLayout>
|
||||
</Grid>
|
||||
</Window>
|
||||
|
72
PettingZoo/UI/Connection/ConnectionWindow.xaml.cs
Normal file
@ -0,0 +1,72 @@
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
using PettingZoo.Core.Settings;
|
||||
|
||||
namespace PettingZoo.UI.Connection
|
||||
{
|
||||
public class WindowConnectionDialog : IConnectionDialog
|
||||
{
|
||||
private readonly IConnectionSettingsRepository connectionSettingsRepository;
|
||||
|
||||
public WindowConnectionDialog(IConnectionSettingsRepository connectionSettingsRepository)
|
||||
{
|
||||
this.connectionSettingsRepository = connectionSettingsRepository;
|
||||
}
|
||||
|
||||
|
||||
public async Task<ConnectionSettings?> Show()
|
||||
{
|
||||
var lastUsed = await connectionSettingsRepository.GetLastUsed();
|
||||
|
||||
var viewModel = new ConnectionViewModel(connectionSettingsRepository, lastUsed);
|
||||
await viewModel.Initialize();
|
||||
|
||||
var window = new ConnectionWindow(viewModel)
|
||||
{
|
||||
Owner = Application.Current.MainWindow
|
||||
};
|
||||
|
||||
viewModel.OkClick += (_, _) =>
|
||||
{
|
||||
window.DialogResult = true;
|
||||
};
|
||||
|
||||
|
||||
if (!window.ShowDialog().GetValueOrDefault())
|
||||
return null;
|
||||
|
||||
var newSettings = viewModel.ToModel();
|
||||
await connectionSettingsRepository.StoreLastUsed(viewModel.StorePassword, newSettings);
|
||||
|
||||
return newSettings;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public partial class ConnectionWindow
|
||||
{
|
||||
public ConnectionWindow(ConnectionViewModel viewModel)
|
||||
{
|
||||
DataContext = viewModel;
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
|
||||
private void NumericPreviewTextInput(object sender, TextCompositionEventArgs args)
|
||||
{
|
||||
if (!char.IsDigit(args.Text, args.Text.Length - 1))
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
|
||||
private void CaretToEnd(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (sender is not TextBox textBox)
|
||||
return;
|
||||
|
||||
textBox.CaretIndex = textBox.Text.Length;
|
||||
}
|
||||
}
|
||||
}
|
225
PettingZoo/UI/Connection/ConnectionWindowStrings.Designer.cs
generated
Normal file
@ -0,0 +1,225 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <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 PettingZoo.UI.Connection {
|
||||
using System;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
/// </summary>
|
||||
// This class was auto-generated by the StronglyTypedResourceBuilder
|
||||
// class via a tool like ResGen or Visual Studio.
|
||||
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||
// with the /str option, or rebuild your VS project.
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
public class ConnectionWindowStrings {
|
||||
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
|
||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||
|
||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal ConnectionWindowStrings() {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the cached ResourceManager instance used by this class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
public static global::System.Resources.ResourceManager ResourceManager {
|
||||
get {
|
||||
if (object.ReferenceEquals(resourceMan, null)) {
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("PettingZoo.UI.Connection.ConnectionWindowStrings", typeof(ConnectionWindowStrings).Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the current thread's CurrentUICulture property for all
|
||||
/// resource lookups using this strongly typed resource class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
public static global::System.Globalization.CultureInfo Culture {
|
||||
get {
|
||||
return resourceCulture;
|
||||
}
|
||||
set {
|
||||
resourceCulture = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Cancel.
|
||||
/// </summary>
|
||||
public static string ButtonCancel {
|
||||
get {
|
||||
return ResourceManager.GetString("ButtonCancel", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Delete.
|
||||
/// </summary>
|
||||
public static string ButtonDelete {
|
||||
get {
|
||||
return ResourceManager.GetString("ButtonDelete", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Connect.
|
||||
/// </summary>
|
||||
public static string ButtonOK {
|
||||
get {
|
||||
return ResourceManager.GetString("ButtonOK", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Save.
|
||||
/// </summary>
|
||||
public static string ButtonSave {
|
||||
get {
|
||||
return ResourceManager.GetString("ButtonSave", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Save as....
|
||||
/// </summary>
|
||||
public static string ButtonSaveAs {
|
||||
get {
|
||||
return ResourceManager.GetString("ButtonSaveAs", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Do you want to delete the connection settings "{0}"?.
|
||||
/// </summary>
|
||||
public static string DeleteConfirm {
|
||||
get {
|
||||
return ResourceManager.GetString("DeleteConfirm", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Delete connection.
|
||||
/// </summary>
|
||||
public static string DeleteConfirmTitle {
|
||||
get {
|
||||
return ResourceManager.GetString("DeleteConfirmTitle", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Exchange.
|
||||
/// </summary>
|
||||
public static string LabelExchange {
|
||||
get {
|
||||
return ResourceManager.GetString("LabelExchange", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Host.
|
||||
/// </summary>
|
||||
public static string LabelHost {
|
||||
get {
|
||||
return ResourceManager.GetString("LabelHost", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Password.
|
||||
/// </summary>
|
||||
public static string LabelPassword {
|
||||
get {
|
||||
return ResourceManager.GetString("LabelPassword", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Port.
|
||||
/// </summary>
|
||||
public static string LabelPort {
|
||||
get {
|
||||
return ResourceManager.GetString("LabelPort", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Routing key.
|
||||
/// </summary>
|
||||
public static string LabelRoutingKey {
|
||||
get {
|
||||
return ResourceManager.GetString("LabelRoutingKey", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Store password.
|
||||
/// </summary>
|
||||
public static string LabelStorePassword {
|
||||
get {
|
||||
return ResourceManager.GetString("LabelStorePassword", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Subscribe.
|
||||
/// </summary>
|
||||
public static string LabelSubscribe {
|
||||
get {
|
||||
return ResourceManager.GetString("LabelSubscribe", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Username.
|
||||
/// </summary>
|
||||
public static string LabelUsername {
|
||||
get {
|
||||
return ResourceManager.GetString("LabelUsername", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Virtual host.
|
||||
/// </summary>
|
||||
public static string LabelVirtualHost {
|
||||
get {
|
||||
return ResourceManager.GetString("LabelVirtualHost", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to <New connection>.
|
||||
/// </summary>
|
||||
public static string LastUsedDisplayName {
|
||||
get {
|
||||
return ResourceManager.GetString("LastUsedDisplayName", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Connection parameters.
|
||||
/// </summary>
|
||||
public static string WindowTitle {
|
||||
get {
|
||||
return ResourceManager.GetString("WindowTitle", resourceCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
174
PettingZoo/UI/Connection/ConnectionWindowStrings.resx
Normal file
@ -0,0 +1,174 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="ButtonCancel" xml:space="preserve">
|
||||
<value>Cancel</value>
|
||||
</data>
|
||||
<data name="ButtonDelete" xml:space="preserve">
|
||||
<value>Delete</value>
|
||||
</data>
|
||||
<data name="ButtonOK" xml:space="preserve">
|
||||
<value>Connect</value>
|
||||
</data>
|
||||
<data name="ButtonSave" xml:space="preserve">
|
||||
<value>Save</value>
|
||||
</data>
|
||||
<data name="ButtonSaveAs" xml:space="preserve">
|
||||
<value>Save as...</value>
|
||||
</data>
|
||||
<data name="DeleteConfirm" xml:space="preserve">
|
||||
<value>Do you want to delete the connection settings "{0}"?</value>
|
||||
</data>
|
||||
<data name="DeleteConfirmTitle" xml:space="preserve">
|
||||
<value>Delete connection</value>
|
||||
</data>
|
||||
<data name="LabelExchange" xml:space="preserve">
|
||||
<value>Exchange</value>
|
||||
</data>
|
||||
<data name="LabelHost" xml:space="preserve">
|
||||
<value>Host</value>
|
||||
</data>
|
||||
<data name="LabelPassword" xml:space="preserve">
|
||||
<value>Password</value>
|
||||
</data>
|
||||
<data name="LabelPort" xml:space="preserve">
|
||||
<value>Port</value>
|
||||
</data>
|
||||
<data name="LabelRoutingKey" xml:space="preserve">
|
||||
<value>Routing key</value>
|
||||
</data>
|
||||
<data name="LabelStorePassword" xml:space="preserve">
|
||||
<value>Store password</value>
|
||||
</data>
|
||||
<data name="LabelSubscribe" xml:space="preserve">
|
||||
<value>Subscribe</value>
|
||||
</data>
|
||||
<data name="LabelUsername" xml:space="preserve">
|
||||
<value>Username</value>
|
||||
</data>
|
||||
<data name="LabelVirtualHost" xml:space="preserve">
|
||||
<value>Virtual host</value>
|
||||
</data>
|
||||
<data name="LastUsedDisplayName" xml:space="preserve">
|
||||
<value><New connection></value>
|
||||
</data>
|
||||
<data name="WindowTitle" xml:space="preserve">
|
||||
<value>Connection parameters</value>
|
||||
</data>
|
||||
</root>
|
11
PettingZoo/UI/Connection/IConnectionDialog.cs
Normal file
@ -0,0 +1,11 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using PettingZoo.Core.Settings;
|
||||
|
||||
namespace PettingZoo.UI.Connection
|
||||
{
|
||||
public interface IConnectionDialog
|
||||
{
|
||||
Task<ConnectionSettings?> Show();
|
||||
}
|
||||
}
|
79
PettingZoo/UI/DelegateCommand.cs
Normal file
@ -0,0 +1,79 @@
|
||||
using System;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace PettingZoo.UI
|
||||
{
|
||||
public class DelegateCommand<T> : ICommand where T : class?
|
||||
{
|
||||
private readonly Func<T?, bool>? canExecute;
|
||||
private readonly Action<T?> execute;
|
||||
|
||||
public event EventHandler? CanExecuteChanged;
|
||||
|
||||
|
||||
public DelegateCommand(Action<T?> execute) : this(execute, null)
|
||||
{
|
||||
}
|
||||
|
||||
public DelegateCommand(Action<T?> execute, Func<T?, bool>? canExecute)
|
||||
{
|
||||
this.execute = execute;
|
||||
this.canExecute = canExecute;
|
||||
}
|
||||
|
||||
|
||||
public bool CanExecute(object? parameter)
|
||||
{
|
||||
return canExecute == null || canExecute((T?)parameter);
|
||||
}
|
||||
|
||||
|
||||
public void Execute(object? parameter)
|
||||
{
|
||||
execute((T?)parameter);
|
||||
}
|
||||
|
||||
|
||||
public void RaiseCanExecuteChanged()
|
||||
{
|
||||
CanExecuteChanged?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public class DelegateCommand : ICommand
|
||||
{
|
||||
private readonly Func<bool>? canExecute;
|
||||
private readonly Action execute;
|
||||
|
||||
public event EventHandler? CanExecuteChanged;
|
||||
|
||||
|
||||
public DelegateCommand(Action execute) : this(execute, null) { }
|
||||
|
||||
public DelegateCommand(Action execute, Func<bool>? canExecute)
|
||||
{
|
||||
this.execute = execute;
|
||||
this.canExecute = canExecute;
|
||||
}
|
||||
|
||||
|
||||
public bool CanExecute(object? parameter)
|
||||
{
|
||||
return canExecute == null || canExecute();
|
||||
}
|
||||
|
||||
|
||||
public void Execute(object? parameter)
|
||||
{
|
||||
execute();
|
||||
}
|
||||
|
||||
|
||||
public void RaiseCanExecuteChanged()
|
||||
{
|
||||
CanExecuteChanged?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
}
|
29
PettingZoo/UI/DependencyObjectExtensions.cs
Normal file
@ -0,0 +1,29 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Reactive.Disposables;
|
||||
using System.Reactive.Linq;
|
||||
using System.Windows;
|
||||
|
||||
namespace PettingZoo.UI
|
||||
{
|
||||
public static class DependencyObjectExtensions
|
||||
{
|
||||
public static IObservable<T> OnPropertyChanges<T>(this DependencyObject source, DependencyProperty property)
|
||||
{
|
||||
return Observable.Create<T>(o =>
|
||||
{
|
||||
var dpd = DependencyPropertyDescriptor.FromProperty(property, property.OwnerType);
|
||||
if (dpd == null)
|
||||
o.OnError(new InvalidOperationException("Can not register change handler for this dependency property."));
|
||||
|
||||
void Handler(object? sender, EventArgs e)
|
||||
{
|
||||
o.OnNext((T)source.GetValue(property));
|
||||
}
|
||||
|
||||
dpd?.AddValueChanged(source, Handler);
|
||||
return Disposable.Create(() => dpd?.RemoveValueChanged(source, Handler));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
19
PettingZoo/UI/EnumBooleanConverter.cs
Normal file
@ -0,0 +1,19 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Windows.Data;
|
||||
|
||||
namespace PettingZoo.UI
|
||||
{
|
||||
public class EnumBooleanConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
return value.Equals(parameter);
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
return ((bool)value) ? parameter : Binding.DoNothing;
|
||||
}
|
||||
}
|
||||
}
|
12
PettingZoo/UI/Example/ExamplePickerDialog.xaml
Normal file
@ -0,0 +1,12 @@
|
||||
<Window x:Class="PettingZoo.UI.Example.ExamplePickerDialog"
|
||||
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"
|
||||
xmlns:example="clr-namespace:PettingZoo.UI.Example"
|
||||
mc:Ignorable="d"
|
||||
Title="{x:Static example:ExamplePickerDialogStrings.WindowTitle}" Height="800" Width="600">
|
||||
<Grid>
|
||||
|
||||
</Grid>
|
||||
</Window>
|
15
PettingZoo/UI/Example/ExamplePickerDialog.xaml.cs
Normal file
@ -0,0 +1,15 @@
|
||||
using System.Windows;
|
||||
|
||||
namespace PettingZoo.UI.Example
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for ExamplePickerDialog.xaml
|
||||
/// </summary>
|
||||
public partial class ExamplePickerDialog : Window
|
||||
{
|
||||
public ExamplePickerDialog()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
72
PettingZoo/UI/Example/ExamplePickerDialogStrings.Designer.cs
generated
Normal file
@ -0,0 +1,72 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <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 PettingZoo.UI.Example {
|
||||
using System;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
/// </summary>
|
||||
// This class was auto-generated by the StronglyTypedResourceBuilder
|
||||
// class via a tool like ResGen or Visual Studio.
|
||||
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||
// with the /str option, or rebuild your VS project.
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
public class ExamplePickerDialogStrings {
|
||||
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
|
||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||
|
||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal ExamplePickerDialogStrings() {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the cached ResourceManager instance used by this class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
public static global::System.Resources.ResourceManager ResourceManager {
|
||||
get {
|
||||
if (object.ReferenceEquals(resourceMan, null)) {
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("PettingZoo.UI.Example.ExamplePickerDialogStrings", typeof(ExamplePickerDialogStrings).Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the current thread's CurrentUICulture property for all
|
||||
/// resource lookups using this strongly typed resource class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
public static global::System.Globalization.CultureInfo Culture {
|
||||
get {
|
||||
return resourceCulture;
|
||||
}
|
||||
set {
|
||||
resourceCulture = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Select example.
|
||||
/// </summary>
|
||||
public static string WindowTitle {
|
||||
get {
|
||||
return ResourceManager.GetString("WindowTitle", resourceCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
123
PettingZoo/UI/Example/ExamplePickerDialogStrings.resx
Normal file
@ -0,0 +1,123 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="WindowTitle" xml:space="preserve">
|
||||
<value>Select example</value>
|
||||
</data>
|
||||
</root>
|
6
PettingZoo/UI/Example/ExamplePickerDialogViewModel.cs
Normal file
@ -0,0 +1,6 @@
|
||||
namespace PettingZoo.UI.Example
|
||||
{
|
||||
public class ExamplePickerDialogViewModel
|
||||
{
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace PettingZoo.Infrastructure
|
||||
namespace PettingZoo.UI
|
||||
{
|
||||
// Source: http://daniel-albuschat.blogspot.nl/2011/07/gridlayout-for-wpf-escape-margin-hell.html
|
||||
|
||||
@ -32,7 +32,7 @@ namespace PettingZoo.Infrastructure
|
||||
|
||||
public Thickness ChildMargin
|
||||
{
|
||||
get { return (Thickness) GetValue(ChildMarginProperty); }
|
||||
get => (Thickness) GetValue(ChildMarginProperty);
|
||||
set
|
||||
{
|
||||
SetValue(ChildMarginProperty, value);
|
||||
@ -45,12 +45,13 @@ namespace PettingZoo.Infrastructure
|
||||
|
||||
public void UpdateChildMargins()
|
||||
{
|
||||
int maxColumn = 0;
|
||||
int maxRow = 0;
|
||||
var maxColumn = 0;
|
||||
var maxRow = 0;
|
||||
foreach (UIElement element in InternalChildren)
|
||||
{
|
||||
int row = GetRow(element);
|
||||
int column = GetColumn(element);
|
||||
var row = GetRow(element);
|
||||
var column = GetColumn(element);
|
||||
|
||||
if (row > maxRow)
|
||||
maxRow = row;
|
||||
if (column > maxColumn)
|
||||
@ -58,32 +59,31 @@ namespace PettingZoo.Infrastructure
|
||||
}
|
||||
foreach (UIElement element in InternalChildren)
|
||||
{
|
||||
var fe = element as FrameworkElement;
|
||||
if (null != fe)
|
||||
{
|
||||
int row = GetRow(fe);
|
||||
int column = GetColumn(fe);
|
||||
double factorLeft = 0.5;
|
||||
double factorTop = 0.5;
|
||||
double factorRight = 0.5;
|
||||
double factorBottom = 0.5;
|
||||
// Top row - no top margin
|
||||
if (row == 0)
|
||||
factorTop = 0;
|
||||
// Bottom row - no bottom margin
|
||||
if (row == maxRow)
|
||||
factorBottom = 0;
|
||||
// Leftmost column = no left margin
|
||||
if (column == 0)
|
||||
factorLeft = 0;
|
||||
// Rightmost column - no right margin
|
||||
if (column == maxColumn)
|
||||
factorRight = 0;
|
||||
fe.Margin = new Thickness(ChildMargin.Left*factorLeft,
|
||||
ChildMargin.Top*factorTop,
|
||||
ChildMargin.Right*factorRight,
|
||||
ChildMargin.Bottom*factorBottom);
|
||||
}
|
||||
if (element is not FrameworkElement fe)
|
||||
continue;
|
||||
|
||||
var row = GetRow(fe);
|
||||
var column = GetColumn(fe);
|
||||
var factorLeft = 0.5;
|
||||
var factorTop = 0.5;
|
||||
var factorRight = 0.5;
|
||||
var factorBottom = 0.5;
|
||||
// Top row - no top margin
|
||||
if (row == 0)
|
||||
factorTop = 0;
|
||||
// Bottom row - no bottom margin
|
||||
if (row == maxRow)
|
||||
factorBottom = 0;
|
||||
// Leftmost column = no left margin
|
||||
if (column == 0)
|
||||
factorLeft = 0;
|
||||
// Rightmost column - no right margin
|
||||
if (column == maxColumn)
|
||||
factorRight = 0;
|
||||
fe.Margin = new Thickness(ChildMargin.Left*factorLeft,
|
||||
ChildMargin.Top*factorTop,
|
||||
ChildMargin.Right*factorRight,
|
||||
ChildMargin.Bottom*factorBottom);
|
||||
}
|
||||
}
|
||||
|
@ -2,11 +2,11 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Specialized;
|
||||
using System.Windows;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace PettingZoo.Infrastructure
|
||||
namespace PettingZoo.UI
|
||||
{
|
||||
// Source: https://social.msdn.microsoft.com/Forums/vstudio/en-US/0f524459-b14e-4f9a-8264-267953418a2d/trivial-listboxlistview-autoscroll?forum=wpf
|
||||
//
|
||||
@ -29,7 +29,7 @@ namespace PettingZoo.Infrastructure
|
||||
|
||||
public static void SetAutoScroll(System.Windows.Controls.ListBox instance, bool value)
|
||||
{
|
||||
var oldHandler = (AutoScrollHandler) instance.GetValue(AutoScrollHandlerProperty);
|
||||
var oldHandler = (AutoScrollHandler?)instance.GetValue(AutoScrollHandlerProperty);
|
||||
if (oldHandler != null)
|
||||
{
|
||||
oldHandler.Dispose();
|
||||
@ -51,7 +51,7 @@ namespace PettingZoo.Infrastructure
|
||||
ItemsSourcePropertyChanged));
|
||||
|
||||
private readonly System.Windows.Controls.ListBox target;
|
||||
private ScrollViewer scrollViewer;
|
||||
private ScrollViewer? scrollViewer;
|
||||
|
||||
public AutoScrollHandler(System.Windows.Controls.ListBox target)
|
||||
{
|
||||
@ -74,8 +74,8 @@ namespace PettingZoo.Infrastructure
|
||||
|
||||
public IEnumerable ItemsSource
|
||||
{
|
||||
get { return (IEnumerable) GetValue(ItemsSourceProperty); }
|
||||
set { SetValue(ItemsSourceProperty, value); }
|
||||
get => (IEnumerable) GetValue(ItemsSourceProperty);
|
||||
set => SetValue(ItemsSourceProperty, value);
|
||||
}
|
||||
|
||||
|
||||
@ -87,16 +87,14 @@ namespace PettingZoo.Infrastructure
|
||||
|
||||
private void ItemsSourceChanged(IEnumerable oldValue, IEnumerable newValue)
|
||||
{
|
||||
var collection = oldValue as INotifyCollectionChanged;
|
||||
if (collection != null)
|
||||
collection.CollectionChanged -= Collection_CollectionChanged;
|
||||
if (oldValue is INotifyCollectionChanged oldCollection)
|
||||
oldCollection.CollectionChanged -= Collection_CollectionChanged;
|
||||
|
||||
collection = newValue as INotifyCollectionChanged;
|
||||
if (collection != null)
|
||||
collection.CollectionChanged += Collection_CollectionChanged;
|
||||
if (newValue is INotifyCollectionChanged newCollection)
|
||||
newCollection.CollectionChanged += Collection_CollectionChanged;
|
||||
}
|
||||
|
||||
private void Collection_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
|
||||
private void Collection_CollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
if (e.Action != NotifyCollectionChangedAction.Add || e.NewItems == null || e.NewItems.Count < 1)
|
||||
return;
|
||||
@ -109,24 +107,31 @@ namespace PettingZoo.Infrastructure
|
||||
return;
|
||||
}
|
||||
|
||||
if (Math.Abs(scrollViewer.VerticalOffset - scrollViewer.ScrollableHeight) < 1)
|
||||
target.ScrollIntoView(e.NewItems[e.NewItems.Count - 1]);
|
||||
if (e.NewItems.Count == 0)
|
||||
return;
|
||||
|
||||
// If not already at the bottom, keep the position stable
|
||||
if (Math.Abs(scrollViewer.VerticalOffset - scrollViewer.ScrollableHeight) > 0)
|
||||
return;
|
||||
|
||||
var item = e.NewItems[^1];
|
||||
if (item != null)
|
||||
target.ScrollIntoView(item);
|
||||
}
|
||||
|
||||
|
||||
private static ScrollViewer FindScrollViewer(DependencyObject parent)
|
||||
private static ScrollViewer? FindScrollViewer(DependencyObject parent)
|
||||
{
|
||||
var childCount = VisualTreeHelper.GetChildrenCount(parent);
|
||||
for (var childIndex = 0; childIndex < childCount; childIndex++)
|
||||
{
|
||||
var child = VisualTreeHelper.GetChild(parent, childIndex);
|
||||
var scrollViewer = (child as ScrollViewer);
|
||||
if (scrollViewer != null)
|
||||
if (child is ScrollViewer scrollViewer)
|
||||
return scrollViewer;
|
||||
|
||||
scrollViewer = FindScrollViewer(child);
|
||||
if (scrollViewer != null)
|
||||
return scrollViewer;
|
||||
var childScrollViewer = FindScrollViewer(child);
|
||||
if (childScrollViewer != null)
|
||||
return childScrollViewer;
|
||||
}
|
||||
|
||||
return null;
|
8
PettingZoo/UI/Main/ITabContainer.cs
Normal file
@ -0,0 +1,8 @@
|
||||
namespace PettingZoo.UI.Main
|
||||
{
|
||||
public interface ITabContainer
|
||||
{
|
||||
public double TabWidth { get; }
|
||||
public double TabHeight { get; }
|
||||
}
|
||||
}
|
124
PettingZoo/UI/Main/MainWindow.xaml
Normal file
@ -0,0 +1,124 @@
|
||||
<Window x:Class="PettingZoo.UI.Main.MainWindow"
|
||||
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:main="clr-namespace:PettingZoo.UI.Main"
|
||||
xmlns:tab="clr-namespace:PettingZoo.UI.Tab"
|
||||
xmlns:svgc="http://sharpvectors.codeplex.com/svgc/"
|
||||
xmlns:ui="clr-namespace:PettingZoo.UI"
|
||||
mc:Ignorable="d"
|
||||
d:DataContext="{d:DesignInstance main:DesignTimeMainWindowViewModel, IsDesignTimeCreatable=True}"
|
||||
Width="800"
|
||||
Height="800"
|
||||
ResizeMode="CanResizeWithGrip"
|
||||
Style="{StaticResource WindowStyle}"
|
||||
Title="{x:Static main:MainWindowStrings.WindowTitle}"
|
||||
Loaded="MainWindow_OnLoaded"
|
||||
Closed="MainWindow_OnClosed">
|
||||
<Window.InputBindings>
|
||||
<KeyBinding Modifiers="Control" Key="W" Command="{Binding CloseTabCommand}" />
|
||||
<KeyBinding Modifiers="Control" Key="U" Command="{Binding UndockTabCommand}" />
|
||||
</Window.InputBindings>
|
||||
<Window.Resources>
|
||||
<ui:BindingProxy x:Key="ContextMenuProxy" Data="{Binding}" />
|
||||
</Window.Resources>
|
||||
<DockPanel>
|
||||
<ToolBar DockPanel.Dock="Top" ToolBarTray.IsLocked="True" Loaded="Toolbar_Loaded">
|
||||
<Button Command="{Binding ConnectCommand}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Image Source="{svgc:SvgImage Source=/Images/Connect.svg, AppName=PettingZoo}" Width="16" Height="16" Style="{StaticResource ToolbarIcon}"/>
|
||||
<TextBlock Margin="3,0,0,0" Text="{x:Static main:MainWindowStrings.CommandConnect}" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
<Button Command="{Binding DisconnectCommand}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Image Source="{svgc:SvgImage Source=/Images/Disconnect.svg, AppName=PettingZoo}" Width="16" Height="16" Style="{StaticResource ToolbarIcon}"/>
|
||||
<TextBlock Margin="3,0,0,0" Text="{x:Static main:MainWindowStrings.CommandDisconnect}" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
<Separator Style="{StaticResource {x:Static ToolBar.SeparatorStyleKey}}" />
|
||||
<Button Command="{Binding SubscribeCommand}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Image Source="{svgc:SvgImage Source=/Images/Subscribe.svg, AppName=PettingZoo}" Width="16" Height="16" Style="{StaticResource ToolbarIcon}"/>
|
||||
<TextBlock Margin="3,0,0,0" Text="{x:Static main:MainWindowStrings.CommandSubscribe}" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
<Button Command="{Binding PublishCommand}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Image Source="{svgc:SvgImage Source=/Images/Publish.svg, AppName=PettingZoo}" Width="16" Height="16" Style="{StaticResource ToolbarIcon}"/>
|
||||
<TextBlock Margin="3,0,0,0" Text="{x:Static main:MainWindowStrings.CommandPublish}" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
<Button Command="{Binding UndockTabCommand}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Image Source="{svgc:SvgImage Source=/Images/Undock.svg, AppName=PettingZoo}" Width="16" Height="16" Style="{StaticResource ToolbarIcon}"/>
|
||||
<TextBlock Margin="3,0,0,0" Text="{x:Static main:MainWindowStrings.CommandUndock}" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
|
||||
<Separator Style="{StaticResource {x:Static ToolBar.SeparatorStyleKey}}" Visibility="{Binding ToolbarCommandsSeparatorVisibility}" />
|
||||
<ItemsControl ItemsSource="{Binding ToolbarCommands}">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<StackPanel Orientation="Horizontal" />
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Button Command="{Binding Command}" Style="{DynamicResource {x:Static ToolBar.ButtonStyleKey}}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Image Source="{Binding Icon}" Width="16" Height="16" Style="{StaticResource ToolbarIcon}"/>
|
||||
<TextBlock Margin="3,0,0,0" Text="{Binding Caption}" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</ToolBar>
|
||||
<StatusBar DockPanel.Dock="Bottom">
|
||||
<StatusBarItem>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Image Source="{svgc:SvgImage Source=/Images/Connecting.svg, AppName=PettingZoo}" Width="16" Height="16" Margin="4" Visibility="{Binding ConnectionStatusConnecting}" />
|
||||
<Image Source="{svgc:SvgImage Source=/Images/Ok.svg, AppName=PettingZoo}" Width="16" Height="16" Margin="4" Visibility="{Binding ConnectionStatusOk}" />
|
||||
<Image Source="{svgc:SvgImage Source=/Images/Error.svg, AppName=PettingZoo}" Width="16" Height="16" Margin="4" Visibility="{Binding ConnectionStatusError}" />
|
||||
<TextBlock Text="{Binding ConnectionStatus}" VerticalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
</StatusBarItem>
|
||||
</StatusBar>
|
||||
<Grid>
|
||||
<TabControl
|
||||
Name="SubscriberTabs"
|
||||
ItemsSource="{Binding Tabs}"
|
||||
SelectedValue="{Binding ActiveTab}">
|
||||
<TabControl.ItemContainerStyle>
|
||||
<Style TargetType="{x:Type TabItem}">
|
||||
<!-- ReSharper disable once Xaml.BindingWithContextNotResolved - valid property for ITab, not sure how to specify the DataContext here so just ignore the warning for now -->
|
||||
<Setter Property="Header" Value="{Binding Title}" />
|
||||
<Setter Property="AllowDrop" Value="True" />
|
||||
<Setter Property="ContextMenu">
|
||||
<Setter.Value>
|
||||
<ContextMenu>
|
||||
<!-- ReSharper disable Xaml.BindingWithContextNotResolved - binding is correct, just weird because of the required proxy -->
|
||||
<MenuItem Header="{x:Static main:MainWindowStrings.ContextMenuUndockTab}" Command="{Binding Data.UndockTabCommand, Source={StaticResource ContextMenuProxy}}" InputGestureText="Ctrl+U" />
|
||||
<MenuItem Header="{x:Static main:MainWindowStrings.ContextMenuCloseTab}" Command="{Binding Data.CloseTabCommand, Source={StaticResource ContextMenuProxy}}" InputGestureText="Ctrl+W" />
|
||||
<!-- ReSharper restore Xaml.BindingWithContextNotResolved -->
|
||||
</ContextMenu>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<EventSetter Event="PreviewMouseRightButtonDown" Handler="TabItem_PreviewRightMouseDown" />
|
||||
<EventSetter Event="PreviewMouseMove" Handler="TabItem_PreviewMouseMove" />
|
||||
<EventSetter Event="Drop" Handler="TabItem_Drop" />
|
||||
</Style>
|
||||
</TabControl.ItemContainerStyle>
|
||||
<TabControl.ContentTemplate>
|
||||
<DataTemplate DataType="{x:Type tab:ITab}">
|
||||
<ContentControl Content="{Binding Content}" />
|
||||
</DataTemplate>
|
||||
</TabControl.ContentTemplate>
|
||||
</TabControl>
|
||||
|
||||
<TextBlock Text="{x:Static main:MainWindowStrings.TabsEmptyText}" HorizontalAlignment="Center" VerticalAlignment="Center" Visibility="{Binding NoTabsVisibility}" />
|
||||
</Grid>
|
||||
</DockPanel>
|
||||
</Window>
|
147
PettingZoo/UI/Main/MainWindow.xaml.cs
Normal file
@ -0,0 +1,147 @@
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using PettingZoo.Core.Connection;
|
||||
using PettingZoo.UI.Connection;
|
||||
using PettingZoo.UI.Subscribe;
|
||||
using PettingZoo.UI.Tab;
|
||||
|
||||
namespace PettingZoo.UI.Main
|
||||
{
|
||||
#pragma warning disable CA1001 // MainWindow can't be IDisposable, handled instead in OnDispatcherShutDownStarted
|
||||
public partial class MainWindow : ITabContainer
|
||||
{
|
||||
private readonly MainWindowViewModel viewModel;
|
||||
|
||||
public bool WasMaximized;
|
||||
|
||||
|
||||
public MainWindow(IConnectionFactory connectionFactory, IConnectionDialog connectionDialog, ISubscribeDialog subscribeDialog)
|
||||
{
|
||||
WindowStartupLocation = WindowStartupLocation.CenterScreen;
|
||||
|
||||
viewModel = new MainWindowViewModel(connectionFactory, connectionDialog, subscribeDialog, this);
|
||||
DataContext = viewModel;
|
||||
InitializeComponent();
|
||||
|
||||
Dispatcher.ShutdownStarted += OnDispatcherShutDownStarted;
|
||||
|
||||
|
||||
// If the WindowState is Minimized, we can't tell if it was maximized before. To properly store
|
||||
// the last window position, keep track of it.
|
||||
this.OnPropertyChanges<WindowState>(WindowStateProperty)
|
||||
.Subscribe(newState =>
|
||||
{
|
||||
WasMaximized = newState switch
|
||||
{
|
||||
WindowState.Maximized => true,
|
||||
WindowState.Normal => false,
|
||||
_ => WasMaximized
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private async void OnDispatcherShutDownStarted(object? sender, EventArgs e)
|
||||
{
|
||||
if (DataContext is IAsyncDisposable disposable)
|
||||
await disposable.DisposeAsync();
|
||||
}
|
||||
|
||||
|
||||
private void MainWindow_OnLoaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
viewModel.ConnectCommand.Execute(null);
|
||||
}
|
||||
|
||||
|
||||
private void MainWindow_OnClosed(object? sender, EventArgs e)
|
||||
{
|
||||
var _ = Application.Current.Windows;
|
||||
}
|
||||
|
||||
|
||||
private void TabItem_PreviewRightMouseDown(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
var tabItem = GetParent<TabItem>(e.OriginalSource);
|
||||
if (tabItem == null)
|
||||
return;
|
||||
|
||||
var tabControl = GetParent<TabControl>(tabItem);
|
||||
if (tabControl == null)
|
||||
return;
|
||||
|
||||
tabControl.SelectedItem = tabItem.DataContext;
|
||||
}
|
||||
|
||||
|
||||
private void TabItem_PreviewMouseMove(object sender, MouseEventArgs e)
|
||||
{
|
||||
if (e.Source is not TabItem tabItem)
|
||||
return;
|
||||
|
||||
if (Mouse.PrimaryDevice.LeftButton == MouseButtonState.Pressed)
|
||||
DragDrop.DoDragDrop(tabItem, tabItem, DragDropEffects.All);
|
||||
}
|
||||
|
||||
|
||||
private void TabItem_Drop(object sender, DragEventArgs e)
|
||||
{
|
||||
var targetTab = GetParent<TabItem>(e.OriginalSource);
|
||||
if (targetTab == null)
|
||||
return;
|
||||
|
||||
var sourceTab = (TabItem?)e.Data.GetData(typeof(TabItem));
|
||||
if (sourceTab == null || sourceTab == targetTab)
|
||||
return;
|
||||
|
||||
var tabControl = GetParent<TabControl>(targetTab);
|
||||
if (tabControl?.ItemsSource is not ObservableCollection<ITab> dataCollection)
|
||||
return;
|
||||
|
||||
if (sourceTab.DataContext is not ITab sourceData || targetTab.DataContext is not ITab targetData)
|
||||
return;
|
||||
|
||||
var sourceIndex = dataCollection.IndexOf(sourceData);
|
||||
var targetIndex = dataCollection.IndexOf(targetData);
|
||||
|
||||
dataCollection.Move(sourceIndex, targetIndex);
|
||||
}
|
||||
|
||||
|
||||
private static T? GetParent<T>(object originalSource) where T : DependencyObject
|
||||
{
|
||||
var current = originalSource as DependencyObject;
|
||||
|
||||
while (current != null)
|
||||
{
|
||||
if (current is T targetType)
|
||||
return targetType;
|
||||
|
||||
current = VisualTreeHelper.GetParent(current);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public double TabWidth => SubscriberTabs.ActualWidth;
|
||||
public double TabHeight => SubscriberTabs.ActualHeight;
|
||||
|
||||
private void Toolbar_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// Hide arrow on the right side of the toolbar
|
||||
var toolBar = sender as ToolBar;
|
||||
|
||||
if (toolBar?.Template.FindName("OverflowGrid", toolBar) is FrameworkElement overflowGrid)
|
||||
overflowGrid.Visibility = Visibility.Collapsed;
|
||||
|
||||
if (toolBar?.Template.FindName("MainPanelBorder", toolBar) is FrameworkElement mainPanelBorder)
|
||||
mainPanelBorder.Margin = new Thickness(0);
|
||||
}
|
||||
}
|
||||
#pragma warning restore CA1001
|
||||
}
|
@ -8,7 +8,10 @@
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace PettingZoo.Properties {
|
||||
namespace PettingZoo.UI.Main {
|
||||
using System;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
/// </summary>
|
||||
@ -16,17 +19,17 @@ namespace PettingZoo.Properties {
|
||||
// class via a tool like ResGen or Visual Studio.
|
||||
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||
// with the /str option, or rebuild your VS project.
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
public class Resources {
|
||||
public class MainWindowStrings {
|
||||
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
|
||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||
|
||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal Resources() {
|
||||
internal MainWindowStrings() {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -36,7 +39,7 @@ namespace PettingZoo.Properties {
|
||||
public static global::System.Resources.ResourceManager ResourceManager {
|
||||
get {
|
||||
if (object.ReferenceEquals(resourceMan, null)) {
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("PettingZoo.Properties.Resources", typeof(Resources).Assembly);
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("PettingZoo.UI.Main.MainWindowStrings", typeof(MainWindowStrings).Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
@ -58,92 +61,65 @@ namespace PettingZoo.Properties {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Cancel.
|
||||
/// Looks up a localized string similar to Connect.
|
||||
/// </summary>
|
||||
public static string ButtonCancel {
|
||||
public static string CommandConnect {
|
||||
get {
|
||||
return ResourceManager.GetString("ButtonCancel", resourceCulture);
|
||||
return ResourceManager.GetString("CommandConnect", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to OK.
|
||||
/// Looks up a localized string similar to Disconnect.
|
||||
/// </summary>
|
||||
public static string ButtonOK {
|
||||
public static string CommandDisconnect {
|
||||
get {
|
||||
return ResourceManager.GetString("ButtonOK", resourceCulture);
|
||||
return ResourceManager.GetString("CommandDisconnect", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Exchange:.
|
||||
/// Looks up a localized string similar to New Publisher.
|
||||
/// </summary>
|
||||
public static string ConnectionExchange {
|
||||
public static string CommandPublish {
|
||||
get {
|
||||
return ResourceManager.GetString("ConnectionExchange", resourceCulture);
|
||||
return ResourceManager.GetString("CommandPublish", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Host:.
|
||||
/// Looks up a localized string similar to New Subscriber....
|
||||
/// </summary>
|
||||
public static string ConnectionHost {
|
||||
public static string CommandSubscribe {
|
||||
get {
|
||||
return ResourceManager.GetString("ConnectionHost", resourceCulture);
|
||||
return ResourceManager.GetString("CommandSubscribe", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Password:.
|
||||
/// Looks up a localized string similar to Undock tab.
|
||||
/// </summary>
|
||||
public static string ConnectionPassword {
|
||||
public static string CommandUndock {
|
||||
get {
|
||||
return ResourceManager.GetString("ConnectionPassword", resourceCulture);
|
||||
return ResourceManager.GetString("CommandUndock", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Port:.
|
||||
/// Looks up a localized string similar to Close tab.
|
||||
/// </summary>
|
||||
public static string ConnectionPort {
|
||||
public static string ContextMenuCloseTab {
|
||||
get {
|
||||
return ResourceManager.GetString("ConnectionPort", resourceCulture);
|
||||
return ResourceManager.GetString("ContextMenuCloseTab", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Routing key:.
|
||||
/// Looks up a localized string similar to Undock.
|
||||
/// </summary>
|
||||
public static string ConnectionRoutingKey {
|
||||
public static string ContextMenuUndockTab {
|
||||
get {
|
||||
return ResourceManager.GetString("ConnectionRoutingKey", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Username:.
|
||||
/// </summary>
|
||||
public static string ConnectionUsername {
|
||||
get {
|
||||
return ResourceManager.GetString("ConnectionUsername", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Virtual host:.
|
||||
/// </summary>
|
||||
public static string ConnectionVirtualHost {
|
||||
get {
|
||||
return ResourceManager.GetString("ConnectionVirtualHost", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Connection parameters.
|
||||
/// </summary>
|
||||
public static string ConnectionWindowTitle {
|
||||
get {
|
||||
return ResourceManager.GetString("ConnectionWindowTitle", resourceCulture);
|
||||
return ResourceManager.GetString("ContextMenuUndockTab", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
@ -165,51 +141,6 @@ namespace PettingZoo.Properties {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Petting Zoo - a RabbitMQ live message viewer.
|
||||
/// </summary>
|
||||
public static string MainWindowTitle {
|
||||
get {
|
||||
return ResourceManager.GetString("MainWindowTitle", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Body.
|
||||
/// </summary>
|
||||
public static string PanelTitleBody {
|
||||
get {
|
||||
return ResourceManager.GetString("PanelTitleBody", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Properties.
|
||||
/// </summary>
|
||||
public static string PanelTitleProperties {
|
||||
get {
|
||||
return ResourceManager.GetString("PanelTitleProperties", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Name.
|
||||
/// </summary>
|
||||
public static string PropertyName {
|
||||
get {
|
||||
return ResourceManager.GetString("PropertyName", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Value.
|
||||
/// </summary>
|
||||
public static string PropertyValue {
|
||||
get {
|
||||
return ResourceManager.GetString("PropertyValue", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Connected.
|
||||
/// </summary>
|
||||
@ -245,5 +176,23 @@ namespace PettingZoo.Properties {
|
||||
return ResourceManager.GetString("StatusError", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to No open tabs. Click on New Publisher or New Subscriber to open a new tab..
|
||||
/// </summary>
|
||||
public static string TabsEmptyText {
|
||||
get {
|
||||
return ResourceManager.GetString("TabsEmptyText", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Petting Zoo - a RabbitMQ live message viewer.
|
||||
/// </summary>
|
||||
public static string WindowTitle {
|
||||
get {
|
||||
return ResourceManager.GetString("WindowTitle", resourceCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -117,35 +117,26 @@
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="ButtonCancel" xml:space="preserve">
|
||||
<value>Cancel</value>
|
||||
<data name="CommandConnect" xml:space="preserve">
|
||||
<value>Connect</value>
|
||||
</data>
|
||||
<data name="ButtonOK" xml:space="preserve">
|
||||
<value>OK</value>
|
||||
<data name="CommandDisconnect" xml:space="preserve">
|
||||
<value>Disconnect</value>
|
||||
</data>
|
||||
<data name="ConnectionExchange" xml:space="preserve">
|
||||
<value>Exchange:</value>
|
||||
<data name="CommandPublish" xml:space="preserve">
|
||||
<value>New Publisher</value>
|
||||
</data>
|
||||
<data name="ConnectionHost" xml:space="preserve">
|
||||
<value>Host:</value>
|
||||
<data name="CommandSubscribe" xml:space="preserve">
|
||||
<value>New Subscriber...</value>
|
||||
</data>
|
||||
<data name="ConnectionPassword" xml:space="preserve">
|
||||
<value>Password:</value>
|
||||
<data name="CommandUndock" xml:space="preserve">
|
||||
<value>Undock tab</value>
|
||||
</data>
|
||||
<data name="ConnectionPort" xml:space="preserve">
|
||||
<value>Port:</value>
|
||||
<data name="ContextMenuCloseTab" xml:space="preserve">
|
||||
<value>Close tab</value>
|
||||
</data>
|
||||
<data name="ConnectionRoutingKey" xml:space="preserve">
|
||||
<value>Routing key:</value>
|
||||
</data>
|
||||
<data name="ConnectionUsername" xml:space="preserve">
|
||||
<value>Username:</value>
|
||||
</data>
|
||||
<data name="ConnectionVirtualHost" xml:space="preserve">
|
||||
<value>Virtual host:</value>
|
||||
</data>
|
||||
<data name="ConnectionWindowTitle" xml:space="preserve">
|
||||
<value>Connection parameters</value>
|
||||
<data name="ContextMenuUndockTab" xml:space="preserve">
|
||||
<value>Undock</value>
|
||||
</data>
|
||||
<data name="DeliveryModeNonPersistent" xml:space="preserve">
|
||||
<value>Non-persistent</value>
|
||||
@ -153,21 +144,6 @@
|
||||
<data name="DeliveryModePersistent" xml:space="preserve">
|
||||
<value>Persistent</value>
|
||||
</data>
|
||||
<data name="MainWindowTitle" xml:space="preserve">
|
||||
<value>Petting Zoo - a RabbitMQ live message viewer</value>
|
||||
</data>
|
||||
<data name="PanelTitleBody" xml:space="preserve">
|
||||
<value>Body</value>
|
||||
</data>
|
||||
<data name="PanelTitleProperties" xml:space="preserve">
|
||||
<value>Properties</value>
|
||||
</data>
|
||||
<data name="PropertyName" xml:space="preserve">
|
||||
<value>Name</value>
|
||||
</data>
|
||||
<data name="PropertyValue" xml:space="preserve">
|
||||
<value>Value</value>
|
||||
</data>
|
||||
<data name="StatusConnected" xml:space="preserve">
|
||||
<value>Connected</value>
|
||||
</data>
|
||||
@ -180,4 +156,10 @@
|
||||
<data name="StatusError" xml:space="preserve">
|
||||
<value>Error: {0}</value>
|
||||
</data>
|
||||
<data name="TabsEmptyText" xml:space="preserve">
|
||||
<value>No open tabs. Click on New Publisher or New Subscriber to open a new tab.</value>
|
||||
</data>
|
||||
<data name="WindowTitle" xml:space="preserve">
|
||||
<value>Petting Zoo - a RabbitMQ live message viewer</value>
|
||||
</data>
|
||||
</root>
|
327
PettingZoo/UI/Main/MainWindowViewModel.cs
Normal file
@ -0,0 +1,327 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Input;
|
||||
using PettingZoo.Core.Connection;
|
||||
using PettingZoo.UI.Connection;
|
||||
using PettingZoo.UI.Subscribe;
|
||||
using PettingZoo.UI.Tab;
|
||||
using PettingZoo.UI.Tab.Undocked;
|
||||
|
||||
namespace PettingZoo.UI.Main
|
||||
{
|
||||
public enum ConnectionStatusType
|
||||
{
|
||||
Connecting,
|
||||
Ok,
|
||||
Error
|
||||
}
|
||||
|
||||
|
||||
public class MainWindowViewModel : BaseViewModel, IAsyncDisposable, ITabHost
|
||||
{
|
||||
private readonly IConnectionFactory connectionFactory;
|
||||
private readonly IConnectionDialog connectionDialog;
|
||||
private readonly ISubscribeDialog subscribeDialog;
|
||||
private readonly ITabContainer tabContainer;
|
||||
private readonly ITabFactory tabFactory;
|
||||
|
||||
private SubscribeDialogParams? subscribeDialogParams;
|
||||
private IConnection? connection;
|
||||
private string connectionStatus;
|
||||
private ITab? activeTab;
|
||||
private readonly Dictionary<ITab, Window> undockedTabs = new();
|
||||
|
||||
private readonly DelegateCommand connectCommand;
|
||||
private readonly DelegateCommand disconnectCommand;
|
||||
private readonly DelegateCommand publishCommand;
|
||||
private readonly DelegateCommand subscribeCommand;
|
||||
private readonly DelegateCommand closeTabCommand;
|
||||
private readonly DelegateCommand undockTabCommand;
|
||||
|
||||
private ConnectionStatusType connectionStatusType;
|
||||
|
||||
|
||||
public string ConnectionStatus
|
||||
{
|
||||
get => connectionStatus;
|
||||
private set => SetField(ref connectionStatus, value);
|
||||
}
|
||||
|
||||
|
||||
public ConnectionStatusType ConnectionStatusType
|
||||
{
|
||||
get => connectionStatusType;
|
||||
set => SetField(ref connectionStatusType, value, otherPropertiesChanged: new [] { nameof(ConnectionStatusOk), nameof(ConnectionStatusError), nameof(ConnectionStatusConnecting) });
|
||||
}
|
||||
|
||||
public Visibility ConnectionStatusOk => ConnectionStatusType == ConnectionStatusType.Ok ? Visibility.Visible : Visibility.Collapsed;
|
||||
public Visibility ConnectionStatusError => ConnectionStatusType == ConnectionStatusType.Error ? Visibility.Visible : Visibility.Collapsed;
|
||||
public Visibility ConnectionStatusConnecting => ConnectionStatusType == ConnectionStatusType.Connecting ? Visibility.Visible : Visibility.Collapsed;
|
||||
|
||||
|
||||
public ObservableCollection<ITab> Tabs { get; }
|
||||
|
||||
public ITab? ActiveTab
|
||||
{
|
||||
get => activeTab;
|
||||
set
|
||||
{
|
||||
var currentTab = activeTab;
|
||||
|
||||
if (!SetField(ref activeTab, value, otherPropertiesChanged: new[] { nameof(ToolbarCommands), nameof(ToolbarCommandsSeparatorVisibility) }))
|
||||
return;
|
||||
|
||||
currentTab?.Deactivate();
|
||||
activeTab?.Activate();
|
||||
}
|
||||
}
|
||||
|
||||
public ICommand ConnectCommand => connectCommand;
|
||||
public ICommand DisconnectCommand => disconnectCommand;
|
||||
public ICommand PublishCommand => publishCommand;
|
||||
public ICommand SubscribeCommand => subscribeCommand;
|
||||
public ICommand CloseTabCommand => closeTabCommand;
|
||||
public ICommand UndockTabCommand => undockTabCommand;
|
||||
|
||||
public IEnumerable<TabToolbarCommand> ToolbarCommands => ActiveTab is ITabToolbarCommands tabToolbarCommands
|
||||
? tabToolbarCommands.ToolbarCommands
|
||||
: Enumerable.Empty<TabToolbarCommand>();
|
||||
|
||||
public Visibility ToolbarCommandsSeparatorVisibility =>
|
||||
ToolbarCommands.Any() ? Visibility.Visible : Visibility.Collapsed;
|
||||
|
||||
public Visibility NoTabsVisibility =>
|
||||
Tabs.Count > 0 ? Visibility.Collapsed : Visibility.Visible;
|
||||
|
||||
|
||||
public MainWindowViewModel(IConnectionFactory connectionFactory, IConnectionDialog connectionDialog,
|
||||
ISubscribeDialog subscribeDialog, ITabContainer tabContainer)
|
||||
{
|
||||
this.connectionFactory = connectionFactory;
|
||||
this.connectionDialog = connectionDialog;
|
||||
this.subscribeDialog = subscribeDialog;
|
||||
this.tabContainer = tabContainer;
|
||||
|
||||
connectionStatus = GetConnectionStatus(null);
|
||||
connectionStatusType = ConnectionStatusType.Error;
|
||||
|
||||
Tabs = new ObservableCollection<ITab>();
|
||||
connectCommand = new DelegateCommand(ConnectExecute);
|
||||
disconnectCommand = new DelegateCommand(DisconnectExecute, IsConnectedCanExecute);
|
||||
publishCommand = new DelegateCommand(PublishExecute, IsConnectedCanExecute);
|
||||
subscribeCommand = new DelegateCommand(SubscribeExecute, IsConnectedCanExecute);
|
||||
closeTabCommand = new DelegateCommand(CloseTabExecute, HasActiveTabCanExecute);
|
||||
undockTabCommand = new DelegateCommand(UndockTabExecute, HasActiveTabCanExecute);
|
||||
|
||||
tabFactory = new ViewTabFactory(this);
|
||||
}
|
||||
|
||||
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
GC.SuppressFinalize(this);
|
||||
|
||||
if (connection != null)
|
||||
await connection.DisposeAsync();
|
||||
}
|
||||
|
||||
|
||||
private async void ConnectExecute()
|
||||
{
|
||||
var connectionSettings = await connectionDialog.Show();
|
||||
if (connectionSettings == null)
|
||||
return;
|
||||
|
||||
if (connection != null)
|
||||
await connection.DisposeAsync();
|
||||
|
||||
connection = connectionFactory.CreateConnection(new ConnectionParams(
|
||||
connectionSettings.Host, connectionSettings.VirtualHost, connectionSettings.Port,
|
||||
connectionSettings.Username, connectionSettings.Password));
|
||||
connection.StatusChanged += ConnectionStatusChanged;
|
||||
|
||||
if (connectionSettings.Subscribe)
|
||||
{
|
||||
var subscriber = connection.Subscribe(connectionSettings.Exchange, connectionSettings.RoutingKey);
|
||||
AddTab(tabFactory.CreateSubscriberTab(connection, subscriber));
|
||||
}
|
||||
|
||||
ConnectionChanged();
|
||||
}
|
||||
|
||||
|
||||
private async void DisconnectExecute()
|
||||
{
|
||||
Tabs.Clear();
|
||||
|
||||
var capturedUndockedTabs = undockedTabs.ToList();
|
||||
undockedTabs.Clear();
|
||||
|
||||
foreach (var undockedTab in capturedUndockedTabs)
|
||||
undockedTab.Value.Close();
|
||||
|
||||
RaisePropertyChanged(nameof(NoTabsVisibility));
|
||||
undockTabCommand.RaiseCanExecuteChanged();
|
||||
|
||||
if (connection != null)
|
||||
{
|
||||
await connection.DisposeAsync();
|
||||
connection = null;
|
||||
}
|
||||
|
||||
ConnectionStatus = GetConnectionStatus(null);
|
||||
ConnectionStatusType = ConnectionStatusType.Error;
|
||||
ConnectionChanged();
|
||||
}
|
||||
|
||||
|
||||
private void SubscribeExecute()
|
||||
{
|
||||
if (connection == null)
|
||||
return;
|
||||
|
||||
var newParams = subscribeDialog.Show(subscribeDialogParams);
|
||||
if (newParams == null)
|
||||
return;
|
||||
|
||||
subscribeDialogParams = newParams;
|
||||
|
||||
var subscriber = connection.Subscribe(subscribeDialogParams.Exchange, subscribeDialogParams.RoutingKey);
|
||||
AddTab(tabFactory.CreateSubscriberTab(connection, subscriber));
|
||||
}
|
||||
|
||||
|
||||
private void PublishExecute()
|
||||
{
|
||||
if (connection == null)
|
||||
return;
|
||||
|
||||
AddTab(tabFactory.CreatePublisherTab(connection));
|
||||
}
|
||||
|
||||
|
||||
private bool IsConnectedCanExecute()
|
||||
{
|
||||
return connection != null;
|
||||
}
|
||||
|
||||
|
||||
private void CloseTabExecute()
|
||||
{
|
||||
RemoveActiveTab();
|
||||
}
|
||||
|
||||
|
||||
private void UndockTabExecute()
|
||||
{
|
||||
var tab = RemoveActiveTab();
|
||||
if (tab == null)
|
||||
return;
|
||||
|
||||
var tabHostWindow = UndockedTabHostWindow.Create(this, tab, tabContainer.TabWidth, tabContainer.TabHeight);
|
||||
undockedTabs.Add(tab, tabHostWindow);
|
||||
|
||||
tabHostWindow.Show();
|
||||
}
|
||||
|
||||
|
||||
private ITab? RemoveActiveTab()
|
||||
{
|
||||
if (ActiveTab == null)
|
||||
return null;
|
||||
|
||||
var activeTabIndex = Tabs.IndexOf(ActiveTab);
|
||||
if (activeTabIndex == -1)
|
||||
return null;
|
||||
|
||||
var tab = Tabs[activeTabIndex];
|
||||
Tabs.RemoveAt(activeTabIndex);
|
||||
|
||||
if (activeTabIndex == Tabs.Count)
|
||||
activeTabIndex--;
|
||||
|
||||
ActiveTab = activeTabIndex >= 0 ? Tabs[activeTabIndex] : null;
|
||||
closeTabCommand.RaiseCanExecuteChanged();
|
||||
undockTabCommand.RaiseCanExecuteChanged();
|
||||
RaisePropertyChanged(nameof(NoTabsVisibility));
|
||||
|
||||
return tab;
|
||||
}
|
||||
|
||||
|
||||
private bool HasActiveTabCanExecute()
|
||||
{
|
||||
return ActiveTab != null;
|
||||
}
|
||||
|
||||
|
||||
public void AddTab(ITab tab)
|
||||
{
|
||||
Tabs.Add(tab);
|
||||
ActiveTab = tab;
|
||||
|
||||
closeTabCommand.RaiseCanExecuteChanged();
|
||||
undockTabCommand.RaiseCanExecuteChanged();
|
||||
RaisePropertyChanged(nameof(NoTabsVisibility));
|
||||
}
|
||||
|
||||
|
||||
public void DockTab(ITab tab)
|
||||
{
|
||||
if (undockedTabs.Remove(tab, out var tabHostWindow))
|
||||
tabHostWindow.Close();
|
||||
|
||||
AddTab(tab);
|
||||
}
|
||||
|
||||
public void UndockedTabClosed(ITab tab)
|
||||
{
|
||||
undockedTabs.Remove(tab);
|
||||
}
|
||||
|
||||
|
||||
private void ConnectionChanged()
|
||||
{
|
||||
disconnectCommand.RaiseCanExecuteChanged();
|
||||
subscribeCommand.RaiseCanExecuteChanged();
|
||||
publishCommand.RaiseCanExecuteChanged();
|
||||
}
|
||||
|
||||
private void ConnectionStatusChanged(object? sender, StatusChangedEventArgs args)
|
||||
{
|
||||
ConnectionStatus = GetConnectionStatus(args);
|
||||
ConnectionStatusType = args.Status switch
|
||||
{
|
||||
Core.Connection.ConnectionStatus.Connected => ConnectionStatusType.Ok,
|
||||
Core.Connection.ConnectionStatus.Connecting => ConnectionStatusType.Connecting,
|
||||
_ => ConnectionStatusType.Error
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static string GetConnectionStatus(StatusChangedEventArgs? args)
|
||||
{
|
||||
return args?.Status switch
|
||||
{
|
||||
Core.Connection.ConnectionStatus.Connecting => string.Format(MainWindowStrings.StatusConnecting, args.Context),
|
||||
Core.Connection.ConnectionStatus.Connected => string.Format(MainWindowStrings.StatusConnected, args.Context),
|
||||
Core.Connection.ConnectionStatus.Error => string.Format(MainWindowStrings.StatusError, args.Context),
|
||||
Core.Connection.ConnectionStatus.Disconnected => MainWindowStrings.StatusDisconnected,
|
||||
_ => MainWindowStrings.StatusDisconnected
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class DesignTimeMainWindowViewModel : MainWindowViewModel
|
||||
{
|
||||
public DesignTimeMainWindowViewModel() : base(null!, null!, null!, null!)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace PettingZoo.Infrastructure
|
||||
namespace PettingZoo.UI
|
||||
{
|
||||
// Source: http://blog.functionalfun.net/2008/06/wpf-passwordbox-and-data-binding.html
|
||||
public static class PasswordBoxAssistant
|
||||
@ -35,9 +35,7 @@ namespace PettingZoo.Infrastructure
|
||||
var newPassword = (string) e.NewValue;
|
||||
|
||||
if (!GetUpdatingPassword(box))
|
||||
{
|
||||
box.Password = newPassword;
|
||||
}
|
||||
|
||||
box.PasswordChanged += HandlePasswordChanged;
|
||||
}
|
||||
@ -46,26 +44,19 @@ namespace PettingZoo.Infrastructure
|
||||
{
|
||||
// when the BindPassword attached property is set on a PasswordBox,
|
||||
// start listening to its PasswordChanged event
|
||||
|
||||
var box = dp as PasswordBox;
|
||||
|
||||
if (box == null)
|
||||
if (dp is not PasswordBox box)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var wasBound = (bool) (e.OldValue);
|
||||
var needToBind = (bool) (e.NewValue);
|
||||
var wasBound = (bool)e.OldValue;
|
||||
var needToBind = (bool)e.NewValue;
|
||||
|
||||
if (wasBound)
|
||||
{
|
||||
box.PasswordChanged -= HandlePasswordChanged;
|
||||
}
|
||||
|
||||
if (needToBind)
|
||||
{
|
||||
box.PasswordChanged += HandlePasswordChanged;
|
||||
}
|
||||
}
|
||||
|
||||
private static void HandlePasswordChanged(object sender, RoutedEventArgs e)
|
26
PettingZoo/UI/Subscribe/ISubscribeDialog.cs
Normal file
@ -0,0 +1,26 @@
|
||||
using System;
|
||||
|
||||
namespace PettingZoo.UI.Subscribe
|
||||
{
|
||||
public interface ISubscribeDialog
|
||||
{
|
||||
SubscribeDialogParams? Show(SubscribeDialogParams? defaultParams = null);
|
||||
}
|
||||
|
||||
|
||||
public class SubscribeDialogParams
|
||||
{
|
||||
public string Exchange { get; }
|
||||
public string RoutingKey { get; }
|
||||
|
||||
|
||||
public static SubscribeDialogParams Default { get; } = new("", "#");
|
||||
|
||||
|
||||
public SubscribeDialogParams(string exchange, string routingKey)
|
||||
{
|
||||
Exchange = exchange;
|
||||
RoutingKey = routingKey;
|
||||
}
|
||||
}
|
||||
}
|