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
|
.VS
|
||||||
## files generated by popular Visual Studio add-ons.
|
|
||||||
|
|
||||||
# User-specific files
|
|
||||||
*.suo
|
|
||||||
*.user
|
*.user
|
||||||
*.sln.docstates
|
|
||||||
|
|
||||||
# Build results
|
bin
|
||||||
[Dd]ebug/
|
obj
|
||||||
[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
|
|
@ -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 System.Text;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
namespace PettingZoo.Model
|
namespace PettingZoo.Core.Rendering
|
||||||
{
|
{
|
||||||
public class MessageBodyRenderer
|
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 }
|
{ "application/json", RenderJson }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
public static string Render(byte[] body, string contentType = "")
|
public static string Render(byte[] body, string? contentType)
|
||||||
{
|
{
|
||||||
Func<byte[], string> handler;
|
return (contentType != null) && ContentTypeHandlers.TryGetValue(contentType, out var handler)
|
||||||
|
? handler(body)
|
||||||
if (ContentTypeHandlers.TryGetValue(contentType, out handler))
|
: Encoding.UTF8.GetString(body);
|
||||||
return handler(body);
|
|
||||||
|
|
||||||
// ToDo hex output if required
|
// 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
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
# Visual Studio 2013
|
# Visual Studio Version 17
|
||||||
VisualStudioVersion = 12.0.40629.0
|
VisualStudioVersion = 17.0.31912.275
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
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
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
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}.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.ActiveCfg = Release|Any CPU
|
||||||
{24819D09-C747-4356-B686-D9DE9CAA6F59}.Release|Any CPU.Build.0 = 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
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
|
SolutionGuid = {07CE270E-9E57-49CD-8D5C-79C7B7A98517}
|
||||||
|
EndGlobalSection
|
||||||
EndGlobal
|
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">
|
<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: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/=MQ/@EntryIndexedValue">MQ</s:String>
|
||||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=WPF/@EntryIndexedValue">WPF</s:String></wpf:ResourceDictionary>
|
<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"
|
<Application xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
x:Class="PettingZoo.App"
|
x:Class="PettingZoo.App"
|
||||||
Startup="ApplicationStartup">
|
ShutdownMode="OnMainWindowClose"
|
||||||
|
DispatcherUnhandledException="App_OnDispatcherUnhandledException">
|
||||||
<Application.Resources>
|
<Application.Resources>
|
||||||
<ResourceDictionary>
|
<ResourceDictionary>
|
||||||
<ResourceDictionary.MergedDictionaries>
|
<ResourceDictionary.MergedDictionaries>
|
||||||
<ResourceDictionary Source="Style.xaml"/>
|
<ResourceDictionary Source="Style.xaml"/>
|
||||||
<ResourceDictionary Source="Icons.xaml"/>
|
|
||||||
</ResourceDictionary.MergedDictionaries>
|
</ResourceDictionary.MergedDictionaries>
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
</Application.Resources>
|
</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"?>
|
<?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">
|
<!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"
|
<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">
|
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"/>
|
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>
|
|
||||||
<g>
|
|
||||||
</g>
|
|
||||||
</svg>
|
</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"?>
|
<?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">
|
<!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"
|
<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">
|
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"/>
|
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>
|
|
||||||
<g>
|
|
||||||
</g>
|
|
||||||
</svg>
|
</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"?>
|
<?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">
|
<!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"
|
<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">
|
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"/>
|
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>
|
|
||||||
<g>
|
|
||||||
</g>
|
|
||||||
</svg>
|
</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"
|
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:infrastructure="clr-namespace:PettingZoo.Infrastructure">
|
xmlns:ui="clr-namespace:PettingZoo.UI">
|
||||||
<!-- Global styling -->
|
<!-- Global styling -->
|
||||||
<Style x:Key="WindowStyle" TargetType="{x:Type Window}">
|
<Style x:Key="WindowStyle" TargetType="{x:Type Window}">
|
||||||
<Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
|
<Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
|
||||||
@ -10,6 +10,11 @@
|
|||||||
<Setter Property="Padding" Value="8,4"/>
|
<Setter Property="Padding" Value="8,4"/>
|
||||||
</Style>
|
</Style>
|
||||||
|
|
||||||
|
<Style TargetType="{x:Type TextBox}">
|
||||||
|
<Setter Property="Padding" Value="3" />
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
|
||||||
<!-- Explicit styling -->
|
<!-- Explicit styling -->
|
||||||
<Style x:Key="SidePanel" TargetType="{x:Type Border}">
|
<Style x:Key="SidePanel" TargetType="{x:Type Border}">
|
||||||
<Setter Property="BorderThickness" Value="1"/>
|
<Setter Property="BorderThickness" Value="1"/>
|
||||||
@ -29,8 +34,12 @@
|
|||||||
<Setter Property="Margin" Value="8,0,0,0" />
|
<Setter Property="Margin" Value="8,0,0,0" />
|
||||||
</Style>
|
</Style>
|
||||||
|
|
||||||
|
<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 infrastructure:GridLayout}">
|
|
||||||
|
<Style x:Key="Form" TargetType="{x:Type ui:GridLayout}">
|
||||||
<Setter Property="ChildMargin" Value="4"/>
|
<Setter Property="ChildMargin" Value="4"/>
|
||||||
</Style>
|
</Style>
|
||||||
|
|
||||||
@ -59,6 +68,25 @@
|
|||||||
</Style.Triggers>
|
</Style.Triggers>
|
||||||
</Style>
|
</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>
|
</Style>
|
||||||
</ResourceDictionary>
|
</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;
|
||||||
using System.Windows.Controls;
|
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
|
// 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
|
public Thickness ChildMargin
|
||||||
{
|
{
|
||||||
get { return (Thickness) GetValue(ChildMarginProperty); }
|
get => (Thickness) GetValue(ChildMarginProperty);
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
SetValue(ChildMarginProperty, value);
|
SetValue(ChildMarginProperty, value);
|
||||||
@ -45,12 +45,13 @@ namespace PettingZoo.Infrastructure
|
|||||||
|
|
||||||
public void UpdateChildMargins()
|
public void UpdateChildMargins()
|
||||||
{
|
{
|
||||||
int maxColumn = 0;
|
var maxColumn = 0;
|
||||||
int maxRow = 0;
|
var maxRow = 0;
|
||||||
foreach (UIElement element in InternalChildren)
|
foreach (UIElement element in InternalChildren)
|
||||||
{
|
{
|
||||||
int row = GetRow(element);
|
var row = GetRow(element);
|
||||||
int column = GetColumn(element);
|
var column = GetColumn(element);
|
||||||
|
|
||||||
if (row > maxRow)
|
if (row > maxRow)
|
||||||
maxRow = row;
|
maxRow = row;
|
||||||
if (column > maxColumn)
|
if (column > maxColumn)
|
||||||
@ -58,32 +59,31 @@ namespace PettingZoo.Infrastructure
|
|||||||
}
|
}
|
||||||
foreach (UIElement element in InternalChildren)
|
foreach (UIElement element in InternalChildren)
|
||||||
{
|
{
|
||||||
var fe = element as FrameworkElement;
|
if (element is not FrameworkElement fe)
|
||||||
if (null != fe)
|
continue;
|
||||||
{
|
|
||||||
int row = GetRow(fe);
|
var row = GetRow(fe);
|
||||||
int column = GetColumn(fe);
|
var column = GetColumn(fe);
|
||||||
double factorLeft = 0.5;
|
var factorLeft = 0.5;
|
||||||
double factorTop = 0.5;
|
var factorTop = 0.5;
|
||||||
double factorRight = 0.5;
|
var factorRight = 0.5;
|
||||||
double factorBottom = 0.5;
|
var factorBottom = 0.5;
|
||||||
// Top row - no top margin
|
// Top row - no top margin
|
||||||
if (row == 0)
|
if (row == 0)
|
||||||
factorTop = 0;
|
factorTop = 0;
|
||||||
// Bottom row - no bottom margin
|
// Bottom row - no bottom margin
|
||||||
if (row == maxRow)
|
if (row == maxRow)
|
||||||
factorBottom = 0;
|
factorBottom = 0;
|
||||||
// Leftmost column = no left margin
|
// Leftmost column = no left margin
|
||||||
if (column == 0)
|
if (column == 0)
|
||||||
factorLeft = 0;
|
factorLeft = 0;
|
||||||
// Rightmost column - no right margin
|
// Rightmost column - no right margin
|
||||||
if (column == maxColumn)
|
if (column == maxColumn)
|
||||||
factorRight = 0;
|
factorRight = 0;
|
||||||
fe.Margin = new Thickness(ChildMargin.Left*factorLeft,
|
fe.Margin = new Thickness(ChildMargin.Left*factorLeft,
|
||||||
ChildMargin.Top*factorTop,
|
ChildMargin.Top*factorTop,
|
||||||
ChildMargin.Right*factorRight,
|
ChildMargin.Right*factorRight,
|
||||||
ChildMargin.Bottom*factorBottom);
|
ChildMargin.Bottom*factorBottom);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2,11 +2,11 @@
|
|||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Specialized;
|
using System.Collections.Specialized;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Data;
|
|
||||||
using System.Windows.Controls;
|
using System.Windows.Controls;
|
||||||
|
using System.Windows.Data;
|
||||||
using System.Windows.Media;
|
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
|
// 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)
|
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)
|
if (oldHandler != null)
|
||||||
{
|
{
|
||||||
oldHandler.Dispose();
|
oldHandler.Dispose();
|
||||||
@ -51,7 +51,7 @@ namespace PettingZoo.Infrastructure
|
|||||||
ItemsSourcePropertyChanged));
|
ItemsSourcePropertyChanged));
|
||||||
|
|
||||||
private readonly System.Windows.Controls.ListBox target;
|
private readonly System.Windows.Controls.ListBox target;
|
||||||
private ScrollViewer scrollViewer;
|
private ScrollViewer? scrollViewer;
|
||||||
|
|
||||||
public AutoScrollHandler(System.Windows.Controls.ListBox target)
|
public AutoScrollHandler(System.Windows.Controls.ListBox target)
|
||||||
{
|
{
|
||||||
@ -74,8 +74,8 @@ namespace PettingZoo.Infrastructure
|
|||||||
|
|
||||||
public IEnumerable ItemsSource
|
public IEnumerable ItemsSource
|
||||||
{
|
{
|
||||||
get { return (IEnumerable) GetValue(ItemsSourceProperty); }
|
get => (IEnumerable) GetValue(ItemsSourceProperty);
|
||||||
set { SetValue(ItemsSourceProperty, value); }
|
set => SetValue(ItemsSourceProperty, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -87,16 +87,14 @@ namespace PettingZoo.Infrastructure
|
|||||||
|
|
||||||
private void ItemsSourceChanged(IEnumerable oldValue, IEnumerable newValue)
|
private void ItemsSourceChanged(IEnumerable oldValue, IEnumerable newValue)
|
||||||
{
|
{
|
||||||
var collection = oldValue as INotifyCollectionChanged;
|
if (oldValue is INotifyCollectionChanged oldCollection)
|
||||||
if (collection != null)
|
oldCollection.CollectionChanged -= Collection_CollectionChanged;
|
||||||
collection.CollectionChanged -= Collection_CollectionChanged;
|
|
||||||
|
|
||||||
collection = newValue as INotifyCollectionChanged;
|
if (newValue is INotifyCollectionChanged newCollection)
|
||||||
if (collection != null)
|
newCollection.CollectionChanged += Collection_CollectionChanged;
|
||||||
collection.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)
|
if (e.Action != NotifyCollectionChangedAction.Add || e.NewItems == null || e.NewItems.Count < 1)
|
||||||
return;
|
return;
|
||||||
@ -109,24 +107,31 @@ namespace PettingZoo.Infrastructure
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Math.Abs(scrollViewer.VerticalOffset - scrollViewer.ScrollableHeight) < 1)
|
if (e.NewItems.Count == 0)
|
||||||
target.ScrollIntoView(e.NewItems[e.NewItems.Count - 1]);
|
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);
|
var childCount = VisualTreeHelper.GetChildrenCount(parent);
|
||||||
for (var childIndex = 0; childIndex < childCount; childIndex++)
|
for (var childIndex = 0; childIndex < childCount; childIndex++)
|
||||||
{
|
{
|
||||||
var child = VisualTreeHelper.GetChild(parent, childIndex);
|
var child = VisualTreeHelper.GetChild(parent, childIndex);
|
||||||
var scrollViewer = (child as ScrollViewer);
|
if (child is ScrollViewer scrollViewer)
|
||||||
if (scrollViewer != null)
|
|
||||||
return scrollViewer;
|
return scrollViewer;
|
||||||
|
|
||||||
scrollViewer = FindScrollViewer(child);
|
var childScrollViewer = FindScrollViewer(child);
|
||||||
if (scrollViewer != null)
|
if (childScrollViewer != null)
|
||||||
return scrollViewer;
|
return childScrollViewer;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
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>
|
// </auto-generated>
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
namespace PettingZoo.Properties {
|
namespace PettingZoo.UI.Main {
|
||||||
|
using System;
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -16,17 +19,17 @@ namespace PettingZoo.Properties {
|
|||||||
// class via a tool like ResGen or Visual Studio.
|
// class via a tool like ResGen or Visual Studio.
|
||||||
// To add or remove a member, edit your .ResX file then rerun ResGen
|
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||||
// with the /str option, or rebuild your VS project.
|
// 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.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||||
public class Resources {
|
public class MainWindowStrings {
|
||||||
|
|
||||||
private static global::System.Resources.ResourceManager resourceMan;
|
private static global::System.Resources.ResourceManager resourceMan;
|
||||||
|
|
||||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||||
|
|
||||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||||
internal Resources() {
|
internal MainWindowStrings() {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -36,7 +39,7 @@ namespace PettingZoo.Properties {
|
|||||||
public static global::System.Resources.ResourceManager ResourceManager {
|
public static global::System.Resources.ResourceManager ResourceManager {
|
||||||
get {
|
get {
|
||||||
if (object.ReferenceEquals(resourceMan, null)) {
|
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;
|
resourceMan = temp;
|
||||||
}
|
}
|
||||||
return resourceMan;
|
return resourceMan;
|
||||||
@ -58,92 +61,65 @@ namespace PettingZoo.Properties {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to Cancel.
|
/// Looks up a localized string similar to Connect.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string ButtonCancel {
|
public static string CommandConnect {
|
||||||
get {
|
get {
|
||||||
return ResourceManager.GetString("ButtonCancel", resourceCulture);
|
return ResourceManager.GetString("CommandConnect", resourceCulture);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to OK.
|
/// Looks up a localized string similar to Disconnect.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string ButtonOK {
|
public static string CommandDisconnect {
|
||||||
get {
|
get {
|
||||||
return ResourceManager.GetString("ButtonOK", resourceCulture);
|
return ResourceManager.GetString("CommandDisconnect", resourceCulture);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to Exchange:.
|
/// Looks up a localized string similar to New Publisher.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string ConnectionExchange {
|
public static string CommandPublish {
|
||||||
get {
|
get {
|
||||||
return ResourceManager.GetString("ConnectionExchange", resourceCulture);
|
return ResourceManager.GetString("CommandPublish", resourceCulture);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to Host:.
|
/// Looks up a localized string similar to New Subscriber....
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string ConnectionHost {
|
public static string CommandSubscribe {
|
||||||
get {
|
get {
|
||||||
return ResourceManager.GetString("ConnectionHost", resourceCulture);
|
return ResourceManager.GetString("CommandSubscribe", resourceCulture);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to Password:.
|
/// Looks up a localized string similar to Undock tab.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string ConnectionPassword {
|
public static string CommandUndock {
|
||||||
get {
|
get {
|
||||||
return ResourceManager.GetString("ConnectionPassword", resourceCulture);
|
return ResourceManager.GetString("CommandUndock", resourceCulture);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to Port:.
|
/// Looks up a localized string similar to Close tab.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string ConnectionPort {
|
public static string ContextMenuCloseTab {
|
||||||
get {
|
get {
|
||||||
return ResourceManager.GetString("ConnectionPort", resourceCulture);
|
return ResourceManager.GetString("ContextMenuCloseTab", resourceCulture);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to Routing key:.
|
/// Looks up a localized string similar to Undock.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string ConnectionRoutingKey {
|
public static string ContextMenuUndockTab {
|
||||||
get {
|
get {
|
||||||
return ResourceManager.GetString("ConnectionRoutingKey", resourceCulture);
|
return ResourceManager.GetString("ContextMenuUndockTab", 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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -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>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to Connected.
|
/// Looks up a localized string similar to Connected.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -245,5 +176,23 @@ namespace PettingZoo.Properties {
|
|||||||
return ResourceManager.GetString("StatusError", resourceCulture);
|
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">
|
<resheader name="writer">
|
||||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
</resheader>
|
</resheader>
|
||||||
<data name="ButtonCancel" xml:space="preserve">
|
<data name="CommandConnect" xml:space="preserve">
|
||||||
<value>Cancel</value>
|
<value>Connect</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ButtonOK" xml:space="preserve">
|
<data name="CommandDisconnect" xml:space="preserve">
|
||||||
<value>OK</value>
|
<value>Disconnect</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ConnectionExchange" xml:space="preserve">
|
<data name="CommandPublish" xml:space="preserve">
|
||||||
<value>Exchange:</value>
|
<value>New Publisher</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ConnectionHost" xml:space="preserve">
|
<data name="CommandSubscribe" xml:space="preserve">
|
||||||
<value>Host:</value>
|
<value>New Subscriber...</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ConnectionPassword" xml:space="preserve">
|
<data name="CommandUndock" xml:space="preserve">
|
||||||
<value>Password:</value>
|
<value>Undock tab</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ConnectionPort" xml:space="preserve">
|
<data name="ContextMenuCloseTab" xml:space="preserve">
|
||||||
<value>Port:</value>
|
<value>Close tab</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ConnectionRoutingKey" xml:space="preserve">
|
<data name="ContextMenuUndockTab" xml:space="preserve">
|
||||||
<value>Routing key:</value>
|
<value>Undock</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>
|
</data>
|
||||||
<data name="DeliveryModeNonPersistent" xml:space="preserve">
|
<data name="DeliveryModeNonPersistent" xml:space="preserve">
|
||||||
<value>Non-persistent</value>
|
<value>Non-persistent</value>
|
||||||
@ -153,21 +144,6 @@
|
|||||||
<data name="DeliveryModePersistent" xml:space="preserve">
|
<data name="DeliveryModePersistent" xml:space="preserve">
|
||||||
<value>Persistent</value>
|
<value>Persistent</value>
|
||||||
</data>
|
</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">
|
<data name="StatusConnected" xml:space="preserve">
|
||||||
<value>Connected</value>
|
<value>Connected</value>
|
||||||
</data>
|
</data>
|
||||||
@ -180,4 +156,10 @@
|
|||||||
<data name="StatusError" xml:space="preserve">
|
<data name="StatusError" xml:space="preserve">
|
||||||
<value>Error: {0}</value>
|
<value>Error: {0}</value>
|
||||||
</data>
|
</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>
|
</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;
|
||||||
using System.Windows.Controls;
|
using System.Windows.Controls;
|
||||||
|
|
||||||
namespace PettingZoo.Infrastructure
|
namespace PettingZoo.UI
|
||||||
{
|
{
|
||||||
// Source: http://blog.functionalfun.net/2008/06/wpf-passwordbox-and-data-binding.html
|
// Source: http://blog.functionalfun.net/2008/06/wpf-passwordbox-and-data-binding.html
|
||||||
public static class PasswordBoxAssistant
|
public static class PasswordBoxAssistant
|
||||||
@ -35,9 +35,7 @@ namespace PettingZoo.Infrastructure
|
|||||||
var newPassword = (string) e.NewValue;
|
var newPassword = (string) e.NewValue;
|
||||||
|
|
||||||
if (!GetUpdatingPassword(box))
|
if (!GetUpdatingPassword(box))
|
||||||
{
|
|
||||||
box.Password = newPassword;
|
box.Password = newPassword;
|
||||||
}
|
|
||||||
|
|
||||||
box.PasswordChanged += HandlePasswordChanged;
|
box.PasswordChanged += HandlePasswordChanged;
|
||||||
}
|
}
|
||||||
@ -46,26 +44,19 @@ namespace PettingZoo.Infrastructure
|
|||||||
{
|
{
|
||||||
// when the BindPassword attached property is set on a PasswordBox,
|
// when the BindPassword attached property is set on a PasswordBox,
|
||||||
// start listening to its PasswordChanged event
|
// start listening to its PasswordChanged event
|
||||||
|
if (dp is not PasswordBox box)
|
||||||
var box = dp as PasswordBox;
|
|
||||||
|
|
||||||
if (box == null)
|
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var wasBound = (bool) (e.OldValue);
|
var wasBound = (bool)e.OldValue;
|
||||||
var needToBind = (bool) (e.NewValue);
|
var needToBind = (bool)e.NewValue;
|
||||||
|
|
||||||
if (wasBound)
|
if (wasBound)
|
||||||
{
|
|
||||||
box.PasswordChanged -= HandlePasswordChanged;
|
box.PasswordChanged -= HandlePasswordChanged;
|
||||||
}
|
|
||||||
|
|
||||||
if (needToBind)
|
if (needToBind)
|
||||||
{
|
|
||||||
box.PasswordChanged += HandlePasswordChanged;
|
box.PasswordChanged += HandlePasswordChanged;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void HandlePasswordChanged(object sender, RoutedEventArgs e)
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|