Started major refactoring
- Upgrade to .NET 5 - Split functionality into libraries - Tabbed interface for multiple subscribers - Placeholder for publishing tab
11
.editorconfig
Normal file
@ -0,0 +1,11 @@
|
||||
[*.cs]
|
||||
|
||||
# IDE0011: Add braces
|
||||
csharp_prefer_braces = when_multiline
|
||||
csharp_style_var_for_built_in_types=true:silent
|
||||
csharp_style_var_when_type_is_apparent=true:silent
|
||||
csharp_style_var_elsewhere=true:silent
|
||||
|
||||
dotnet_diagnostic.IDE0055.severity = none
|
||||
|
||||
dotnet_diagnostic.IDE0130.severity = none
|
190
.gitignore
vendored
@ -1,189 +1,5 @@
|
||||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
|
||||
# User-specific files
|
||||
*.suo
|
||||
.VS
|
||||
*.user
|
||||
*.sln.docstates
|
||||
|
||||
# Build results
|
||||
[Dd]ebug/
|
||||
[Dd]ebugPublic/
|
||||
[Rr]elease/
|
||||
x64/
|
||||
build/
|
||||
bld/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
|
||||
# Roslyn cache directories
|
||||
*.ide/
|
||||
|
||||
# MSTest test Results
|
||||
[Tt]est[Rr]esult*/
|
||||
[Bb]uild[Ll]og.*
|
||||
|
||||
#NUNIT
|
||||
*.VisualState.xml
|
||||
TestResult.xml
|
||||
|
||||
# Build Results of an ATL Project
|
||||
[Dd]ebugPS/
|
||||
[Rr]eleasePS/
|
||||
dlldata.c
|
||||
|
||||
*_i.c
|
||||
*_p.c
|
||||
*_i.h
|
||||
*.ilk
|
||||
*.meta
|
||||
*.obj
|
||||
*.pch
|
||||
*.pdb
|
||||
*.pgc
|
||||
*.pgd
|
||||
*.rsp
|
||||
*.sbr
|
||||
*.tlb
|
||||
*.tli
|
||||
*.tlh
|
||||
*.tmp
|
||||
*.tmp_proj
|
||||
*.log
|
||||
*.vspscc
|
||||
*.vssscc
|
||||
.builds
|
||||
*.pidb
|
||||
*.svclog
|
||||
*.scc
|
||||
|
||||
# Chutzpah Test files
|
||||
_Chutzpah*
|
||||
|
||||
# Visual C++ cache files
|
||||
ipch/
|
||||
*.aps
|
||||
*.ncb
|
||||
*.opensdf
|
||||
*.sdf
|
||||
*.cachefile
|
||||
|
||||
# Visual Studio profiler
|
||||
*.psess
|
||||
*.vsp
|
||||
*.vspx
|
||||
|
||||
# TFS 2012 Local Workspace
|
||||
$tf/
|
||||
|
||||
# Guidance Automation Toolkit
|
||||
*.gpState
|
||||
|
||||
# ReSharper is a .NET coding add-in
|
||||
_ReSharper*/
|
||||
*.[Rr]e[Ss]harper
|
||||
*.DotSettings.user
|
||||
|
||||
# JustCode is a .NET coding addin-in
|
||||
.JustCode
|
||||
|
||||
# TeamCity is a build add-in
|
||||
_TeamCity*
|
||||
|
||||
# DotCover is a Code Coverage Tool
|
||||
*.dotCover
|
||||
|
||||
# NCrunch
|
||||
_NCrunch_*
|
||||
.*crunch*.local.xml
|
||||
|
||||
# MightyMoose
|
||||
*.mm.*
|
||||
AutoTest.Net/
|
||||
|
||||
# Web workbench (sass)
|
||||
.sass-cache/
|
||||
|
||||
# Installshield output folder
|
||||
[Ee]xpress/
|
||||
|
||||
# DocProject is a documentation generator add-in
|
||||
DocProject/buildhelp/
|
||||
DocProject/Help/*.HxT
|
||||
DocProject/Help/*.HxC
|
||||
DocProject/Help/*.hhc
|
||||
DocProject/Help/*.hhk
|
||||
DocProject/Help/*.hhp
|
||||
DocProject/Help/Html2
|
||||
DocProject/Help/html
|
||||
|
||||
# Click-Once directory
|
||||
publish/
|
||||
|
||||
# Publish Web Output
|
||||
*.[Pp]ublish.xml
|
||||
*.azurePubxml
|
||||
## TODO: Comment the next line if you want to checkin your
|
||||
## web deploy settings but do note that will include unencrypted
|
||||
## passwords
|
||||
#*.pubxml
|
||||
|
||||
# NuGet Packages Directory
|
||||
packages/*
|
||||
## TODO: If the tool you use requires repositories.config
|
||||
## uncomment the next line
|
||||
#!packages/repositories.config
|
||||
|
||||
# Enable "build/" folder in the NuGet Packages folder since
|
||||
# NuGet packages use it for MSBuild targets.
|
||||
# This line needs to be after the ignore of the build folder
|
||||
# (and the packages folder if the line above has been uncommented)
|
||||
!packages/build/
|
||||
|
||||
# Windows Azure Build Output
|
||||
csx/
|
||||
*.build.csdef
|
||||
|
||||
# Windows Store app package directory
|
||||
AppPackages/
|
||||
|
||||
# Others
|
||||
sql/
|
||||
*.Cache
|
||||
ClientBin/
|
||||
[Ss]tyle[Cc]op.*
|
||||
~$*
|
||||
*~
|
||||
*.dbmdl
|
||||
*.dbproj.schemaview
|
||||
*.pfx
|
||||
*.publishsettings
|
||||
node_modules/
|
||||
|
||||
# RIA/Silverlight projects
|
||||
Generated_Code/
|
||||
|
||||
# Backup & report files from converting an old project file
|
||||
# to a newer Visual Studio version. Backup files are not needed,
|
||||
# because we have git ;-)
|
||||
_UpgradeReport_Files/
|
||||
Backup*/
|
||||
UpgradeLog*.XML
|
||||
UpgradeLog*.htm
|
||||
|
||||
# SQL Server files
|
||||
*.mdf
|
||||
*.ldf
|
||||
|
||||
# Business Intelligence projects
|
||||
*.rdl.data
|
||||
*.bim.layout
|
||||
*.bim_*.settings
|
||||
|
||||
# Microsoft Fakes
|
||||
FakesAssemblies/
|
||||
|
||||
# LightSwitch generated files
|
||||
GeneratedArtifacts/
|
||||
_Pvt_Extensions/
|
||||
ModelManifest.xml
|
||||
bin
|
||||
obj
|
@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<configuration>
|
||||
<startup>
|
||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
|
||||
</startup>
|
||||
</configuration>
|
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);
|
||||
}
|
||||
}
|
||||
}
|
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;
|
||||
}
|
||||
}
|
||||
}
|
36
PettingZoo.Core/Connection/IConnection.cs
Normal file
@ -0,0 +1,36 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace PettingZoo.Core.Connection
|
||||
{
|
||||
public interface IConnection : IAsyncDisposable
|
||||
{
|
||||
event EventHandler<StatusChangedEventArgs> StatusChanged;
|
||||
|
||||
ISubscriber Subscribe(string exchange, string routingKey);
|
||||
Task Publish(MessageInfo 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);
|
||||
}
|
||||
}
|
26
PettingZoo.Core/Connection/ISubscriber.cs
Normal file
@ -0,0 +1,26 @@
|
||||
using System;
|
||||
|
||||
namespace PettingZoo.Core.Connection
|
||||
{
|
||||
public interface ISubscriber : IAsyncDisposable
|
||||
{
|
||||
string Exchange {get; }
|
||||
string RoutingKey { get; }
|
||||
|
||||
event EventHandler<MessageReceivedEventArgs>? MessageReceived;
|
||||
|
||||
void Start();
|
||||
}
|
||||
|
||||
|
||||
public class MessageReceivedEventArgs : EventArgs
|
||||
{
|
||||
public MessageInfo MessageInfo { get; }
|
||||
|
||||
|
||||
public MessageReceivedEventArgs(MessageInfo messageInfo)
|
||||
{
|
||||
MessageInfo = messageInfo;
|
||||
}
|
||||
}
|
||||
}
|
23
PettingZoo.Core/Connection/MessageInfo.cs
Normal file
@ -0,0 +1,23 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace PettingZoo.Core.Connection
|
||||
{
|
||||
public class MessageInfo
|
||||
{
|
||||
public DateTime Timestamp { get; }
|
||||
public string Exchange { get; }
|
||||
public string RoutingKey { get; }
|
||||
public byte[] Body { get; }
|
||||
public IDictionary<string, string> Properties { get; }
|
||||
|
||||
public MessageInfo(string exchange, string routingKey, byte[] body, IDictionary<string, string> properties, DateTime timestamp)
|
||||
{
|
||||
Exchange = exchange;
|
||||
RoutingKey = routingKey;
|
||||
Body = body;
|
||||
Properties = properties;
|
||||
Timestamp = timestamp;
|
||||
}
|
||||
}
|
||||
}
|
12
PettingZoo.Core/PettingZoo.Core.csproj
Normal file
@ -0,0 +1,12 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
@ -3,11 +3,11 @@ using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace PettingZoo.Model
|
||||
namespace PettingZoo.Core.Rendering
|
||||
{
|
||||
public class MessageBodyRenderer
|
||||
{
|
||||
public static Dictionary<string, Func<byte[], string>> ContentTypeHandlers = new Dictionary<string, Func<byte[], string>>
|
||||
public static Dictionary<string, Func<byte[], string>> ContentTypeHandlers = new()
|
||||
{
|
||||
{ "application/json", RenderJson }
|
||||
};
|
||||
@ -15,13 +15,11 @@ namespace PettingZoo.Model
|
||||
|
||||
public static string Render(byte[] body, string contentType = "")
|
||||
{
|
||||
Func<byte[], string> handler;
|
||||
|
||||
if (ContentTypeHandlers.TryGetValue(contentType, out handler))
|
||||
return handler(body);
|
||||
return ContentTypeHandlers.TryGetValue(contentType, out var handler)
|
||||
? handler(body)
|
||||
: Encoding.UTF8.GetString(body);
|
||||
|
||||
// ToDo hex output if required
|
||||
return Encoding.UTF8.GetString(body);
|
||||
}
|
||||
|
||||
|
17
PettingZoo.RabbitMQ/PettingZoo.RabbitMQ.csproj
Normal file
@ -0,0 +1,17 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
</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>
|
142
PettingZoo.RabbitMQ/RabbitMQClientConnection.cs
Normal file
@ -0,0 +1,142 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public ISubscriber Subscribe(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(MessageInfo 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);
|
||||
}
|
||||
}
|
||||
}
|
138
PettingZoo.RabbitMQ/RabbitMQClientPropertiesConverter.cs
Normal file
@ -0,0 +1,138 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
using RabbitMQ.Client;
|
||||
|
||||
namespace PettingZoo.RabbitMQ
|
||||
{
|
||||
public static class RabbitMQClientPropertiesConverter
|
||||
{
|
||||
public static IDictionary<string, string> Convert(IBasicProperties basicProperties)
|
||||
{
|
||||
var properties = new Dictionary<string, string>();
|
||||
|
||||
if (basicProperties.IsDeliveryModePresent())
|
||||
properties.Add(RabbitMQProperties.DeliveryMode, basicProperties.DeliveryMode.ToString(CultureInfo.InvariantCulture));
|
||||
|
||||
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);
|
||||
|
||||
// ReSharper disable once InvertIf
|
||||
if (basicProperties.Headers != null)
|
||||
{
|
||||
foreach (var (key, value) in basicProperties.Headers)
|
||||
properties.Add(key, Encoding.UTF8.GetString((byte[]) value));
|
||||
}
|
||||
|
||||
return properties;
|
||||
}
|
||||
|
||||
|
||||
public static IBasicProperties Convert(IDictionary<string, string> properties, IBasicProperties targetProperties)
|
||||
{
|
||||
foreach (var (key, value) in properties)
|
||||
{
|
||||
switch (key)
|
||||
{
|
||||
case RabbitMQProperties.DeliveryMode:
|
||||
if (byte.TryParse(value, out var deliveryMode))
|
||||
targetProperties.DeliveryMode = deliveryMode;
|
||||
|
||||
break;
|
||||
|
||||
case RabbitMQProperties.ContentType:
|
||||
targetProperties.ContentType = value;
|
||||
break;
|
||||
|
||||
case RabbitMQProperties.ContentEncoding:
|
||||
targetProperties.ContentEncoding = value;
|
||||
break;
|
||||
|
||||
case RabbitMQProperties.Priority:
|
||||
if (byte.TryParse(value, out var priority))
|
||||
targetProperties.Priority = priority;
|
||||
|
||||
break;
|
||||
|
||||
case RabbitMQProperties.CorrelationId:
|
||||
targetProperties.CorrelationId = value;
|
||||
break;
|
||||
|
||||
case RabbitMQProperties.ReplyTo:
|
||||
targetProperties.ReplyTo = value;
|
||||
break;
|
||||
|
||||
case RabbitMQProperties.Expiration:
|
||||
targetProperties.Expiration = value;
|
||||
break;
|
||||
|
||||
case RabbitMQProperties.MessageId:
|
||||
targetProperties.MessageId = value;
|
||||
break;
|
||||
|
||||
case RabbitMQProperties.Timestamp:
|
||||
if (long.TryParse(value, out var timestamp))
|
||||
targetProperties.Timestamp = new AmqpTimestamp(timestamp);
|
||||
|
||||
break;
|
||||
|
||||
case RabbitMQProperties.Type:
|
||||
targetProperties.Type = value;
|
||||
break;
|
||||
|
||||
case RabbitMQProperties.UserId:
|
||||
targetProperties.UserId = value;
|
||||
break;
|
||||
|
||||
case RabbitMQProperties.AppId:
|
||||
targetProperties.AppId = value;
|
||||
break;
|
||||
|
||||
case RabbitMQProperties.ClusterId:
|
||||
targetProperties.ClusterId = value;
|
||||
break;
|
||||
|
||||
default:
|
||||
targetProperties.Headers ??= new Dictionary<string, object>();
|
||||
targetProperties.Headers.Add(key, Encoding.UTF8.GetBytes(value));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return targetProperties;
|
||||
}
|
||||
}
|
||||
}
|
79
PettingZoo.RabbitMQ/RabbitMQClientSubscriber.cs
Normal file
@ -0,0 +1,79 @@
|
||||
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 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()
|
||||
{
|
||||
if (model != null && consumerTag != null && model.IsOpen)
|
||||
model.BasicCancelNoWait(consumerTag);
|
||||
|
||||
return default;
|
||||
}
|
||||
|
||||
|
||||
public void Start()
|
||||
{
|
||||
started = true;
|
||||
if (model == null)
|
||||
return;
|
||||
|
||||
var queueName = model.QueueDeclare().QueueName;
|
||||
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 MessageInfo(
|
||||
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
|
||||
)
|
||||
));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
namespace PettingZoo.Model
|
||||
namespace PettingZoo.RabbitMQ
|
||||
{
|
||||
static class RabbitMQProperties
|
||||
public static class RabbitMQProperties
|
||||
{
|
||||
public const string ContentType = "content-type";
|
||||
public const string ContentEncoding = "content-encoding";
|
14
PettingZoo.RabbitMQ/RabbitMQPropertiesExtensions.cs
Normal file
@ -0,0 +1,14 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace PettingZoo.RabbitMQ
|
||||
{
|
||||
public static class RabbitMQPropertiesExtensions
|
||||
{
|
||||
public static string ContentType(this IDictionary<string, string> properties)
|
||||
{
|
||||
return properties.TryGetValue(RabbitMQProperties.ContentType, out var value)
|
||||
? value
|
||||
: "";
|
||||
}
|
||||
}
|
||||
}
|
@ -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,18 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 2013
|
||||
VisualStudioVersion = 12.0.40629.0
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.31911.196
|
||||
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("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PettingZoo.Core", "PettingZoo.Core\PettingZoo.Core.csproj", "{AD20CA14-6272-4C50-819D-F9FE6A963DB1}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PettingZoo.RabbitMQ", "PettingZoo.RabbitMQ\PettingZoo.RabbitMQ.csproj", "{220149F3-A8D6-44ED-B3B6-DFE506EB018A}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
@ -15,8 +24,19 @@ Global
|
||||
{24819D09-C747-4356-B686-D9DE9CAA6F59}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{24819D09-C747-4356-B686-D9DE9CAA6F59}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{24819D09-C747-4356-B686-D9DE9CAA6F59}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{AD20CA14-6272-4C50-819D-F9FE6A963DB1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{AD20CA14-6272-4C50-819D-F9FE6A963DB1}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{AD20CA14-6272-4C50-819D-F9FE6A963DB1}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{AD20CA14-6272-4C50-819D-F9FE6A963DB1}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{220149F3-A8D6-44ED-B3B6-DFE506EB018A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{220149F3-A8D6-44ED-B3B6-DFE506EB018A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{220149F3-A8D6-44ED-B3B6-DFE506EB018A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{220149F3-A8D6-44ED-B3B6-DFE506EB018A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {07CE270E-9E57-49CD-8D5C-79C7B7A98517}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
@ -1,12 +1,12 @@
|
||||
<Application xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
x:Class="PettingZoo.App"
|
||||
Startup="ApplicationStartup">
|
||||
ShutdownMode="OnMainWindowClose"
|
||||
DispatcherUnhandledException="App_OnDispatcherUnhandledException">
|
||||
<Application.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="Style.xaml"/>
|
||||
<ResourceDictionary Source="Icons.xaml"/>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
||||
</Application.Resources>
|
13
PettingZoo/App.xaml.cs
Normal file
@ -0,0 +1,13 @@
|
||||
using System.Windows;
|
||||
using System.Windows.Threading;
|
||||
|
||||
namespace PettingZoo
|
||||
{
|
||||
public partial class App
|
||||
{
|
||||
private void App_OnDispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
|
||||
{
|
||||
_ = MessageBox.Show($"Unhandled exception: {e.Exception.Message}", "Petting Zoo - Exception", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
}
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.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 |
63
PettingZoo/Images/Publish.svg
Normal file
@ -0,0 +1,63 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Generator: Adobe Illustrator 18.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 58.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>
|
||||
<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>
|
After Width: | Height: | Size: 2.5 KiB |
49
PettingZoo/Images/PublishSend.svg
Normal file
@ -0,0 +1,49 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Generator: Adobe Illustrator 18.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 58 58" style="enable-background:new 0 0 58 58;" xml:space="preserve">
|
||||
<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>
|
||||
<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>
|
After Width: | Height: | Size: 1.2 KiB |
63
PettingZoo/Images/Subscribe.svg
Normal file
@ -0,0 +1,63 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Generator: Adobe Illustrator 18.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 58.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>
|
||||
<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>
|
After Width: | Height: | Size: 2.1 KiB |
112
PettingZoo/PettingZoo.csproj
Normal file
@ -0,0 +1,112 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>net5.0-windows</TargetFramework>
|
||||
<UseWPF>true</UseWPF>
|
||||
<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\PublishSend.svg" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Resource Include="Images\Clear.svg" />
|
||||
<Resource Include="Images\Connect.svg" />
|
||||
<Resource Include="Images\Disconnect.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.6" />
|
||||
<PackageReference Include="SimpleInjector" Version="5.3.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\PettingZoo.Core\PettingZoo.Core.csproj" />
|
||||
<ProjectReference Include="..\PettingZoo.RabbitMQ\PettingZoo.RabbitMQ.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Update="UI\Connection\ConnectionWindowStrings.Designer.cs">
|
||||
<DependentUpon>ConnectionWindowStrings.resx</DependentUpon>
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
</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\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>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Update="UI\Connection\ConnectionWindowStrings.resx">
|
||||
<LastGenOutput>ConnectionWindowStrings.Designer.cs</LastGenOutput>
|
||||
<Generator>PublicResXFileCodeGenerator</Generator>
|
||||
</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\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>
|
||||
</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\Subscriber\SubscriberView.xaml">
|
||||
<XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB |
@ -5,16 +5,22 @@ using System.Reflection;
|
||||
using System.Windows;
|
||||
using System.Windows.Markup;
|
||||
using Newtonsoft.Json;
|
||||
using PettingZoo.Model;
|
||||
using PettingZoo.View;
|
||||
using PettingZoo.ViewModel;
|
||||
using PettingZoo.Core.Connection;
|
||||
using PettingZoo.RabbitMQ;
|
||||
using PettingZoo.Settings;
|
||||
using PettingZoo.UI.Connection;
|
||||
using PettingZoo.UI.Main;
|
||||
using PettingZoo.UI.Subscribe;
|
||||
using PettingZoo.UI.Tab;
|
||||
using PettingZoo.UI.Tab.Subscriber;
|
||||
using SimpleInjector;
|
||||
|
||||
namespace PettingZoo
|
||||
{
|
||||
public partial class App
|
||||
public static class Program
|
||||
{
|
||||
public void ApplicationStartup(object sender, StartupEventArgs e)
|
||||
[STAThread]
|
||||
public static void Main()
|
||||
{
|
||||
// WPF defaults to US for date formatting in bindings, this fixes it
|
||||
FrameworkElement.LanguageProperty.OverrideMetadata(typeof(FrameworkElement), new FrameworkPropertyMetadata(
|
||||
@ -28,28 +34,50 @@ namespace PettingZoo
|
||||
private static Container Bootstrap()
|
||||
{
|
||||
var container = new Container();
|
||||
|
||||
// See comments in RunApplication
|
||||
container.Options.EnableAutoVerification = false;
|
||||
|
||||
container.RegisterSingleton(() => new UserSettings(new AppDataSettingsSerializer("Settings.json")));
|
||||
|
||||
container.Register<IConnectionFactory, RabbitMQClientConnectionFactory>();
|
||||
container.Register<IConnectionInfoBuilder, WindowConnectionInfoBuilder>();
|
||||
container.Register<IConnectionDialog, WindowConnectionDialog>();
|
||||
container.Register<ISubscribeDialog, WindowSubscribeDialog>();
|
||||
container.Register<ITabFactory, ViewTabFactory>();
|
||||
|
||||
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)
|
||||
private static void RunApplication(Container container)
|
||||
{
|
||||
var mainWindow = container.GetInstance<MainWindow>();
|
||||
mainWindow.Closed += (sender, args) => container.Dispose();
|
||||
try
|
||||
{
|
||||
var app = new App();
|
||||
app.InitializeComponent();
|
||||
|
||||
mainWindow.Show();
|
||||
#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
|
||||
|
||||
var mainWindow = container.GetInstance<MainWindow>();
|
||||
_ = app.Run(mainWindow);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// TODO Log the exception and exit
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -64,8 +92,8 @@ namespace PettingZoo
|
||||
var companyName = GetProductInfo<AssemblyCompanyAttribute>().Company;
|
||||
var productName = GetProductInfo<AssemblyProductAttribute>().Product;
|
||||
|
||||
path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
|
||||
companyName, productName);
|
||||
path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
|
||||
companyName, productName);
|
||||
fullPath = Path.Combine(path, filename);
|
||||
}
|
||||
|
||||
@ -79,7 +107,7 @@ namespace PettingZoo
|
||||
|
||||
public void Write(UserSettings settings)
|
||||
{
|
||||
Directory.CreateDirectory(path);
|
||||
_ = Directory.CreateDirectory(path);
|
||||
File.WriteAllText(fullPath, JsonConvert.SerializeObject(settings, Formatting.Indented));
|
||||
}
|
||||
|
||||
@ -87,11 +115,10 @@ namespace PettingZoo
|
||||
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];
|
||||
return attributes.Length == 0
|
||||
? throw new Exception("Missing product information in assembly")
|
||||
: (T) attributes[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
namespace PettingZoo.Model
|
||||
namespace PettingZoo.Settings
|
||||
{
|
||||
public interface IUserSettingsSerializer
|
||||
{
|
||||
@ -15,6 +15,7 @@
|
||||
public string LastUsername { get; set; }
|
||||
public string LastPassword { get; set; }
|
||||
|
||||
//public bool LastSubscribe { get; set; }
|
||||
public string LastExchange { get; set; }
|
||||
public string LastRoutingKey { get; set; }
|
||||
|
||||
@ -27,7 +28,7 @@
|
||||
LastUsername = "guest";
|
||||
LastPassword = "guest";
|
||||
|
||||
LastExchange = "amqp";
|
||||
LastExchange = "";
|
||||
LastRoutingKey = "#";
|
||||
}
|
||||
}
|
||||
@ -35,7 +36,7 @@
|
||||
|
||||
public class UserSettings
|
||||
{
|
||||
public ConnectionWindowSettings ConnectionWindow { get; private set; }
|
||||
public ConnectionWindowSettings ConnectionWindow { get; }
|
||||
|
||||
|
||||
private readonly IUserSettingsSerializer serializer;
|
@ -1,6 +1,6 @@
|
||||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:infrastructure="clr-namespace:PettingZoo.Infrastructure">
|
||||
xmlns:ui="clr-namespace:PettingZoo.UI">
|
||||
<!-- Global styling -->
|
||||
<Style x:Key="WindowStyle" TargetType="{x:Type Window}">
|
||||
<Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
|
||||
@ -30,7 +30,7 @@
|
||||
</Style>
|
||||
|
||||
|
||||
<Style x:Key="Form" TargetType="{x:Type infrastructure:GridLayout}">
|
||||
<Style x:Key="Form" TargetType="{x:Type ui:GridLayout}">
|
||||
<Setter Property="ChildMargin" Value="4"/>
|
||||
</Style>
|
||||
|
||||
@ -59,6 +59,8 @@
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
|
||||
<Style x:Key="Routingkey" TargetType="{x:Type TextBlock}">
|
||||
<Style x:Key="TypeSelection" TargetType="{x:Type FrameworkElement}">
|
||||
<Setter Property="Margin" Value="0 0 8 0" />
|
||||
<Setter Property="VerticalAlignment" Value="Center" />
|
||||
</Style>
|
||||
</ResourceDictionary>
|
41
PettingZoo/UI/BaseViewModel.cs
Normal file
@ -0,0 +1,41 @@
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace PettingZoo.UI
|
||||
{
|
||||
public class BaseViewModel : INotifyPropertyChanged
|
||||
{
|
||||
public event PropertyChangedEventHandler? PropertyChanged;
|
||||
|
||||
protected virtual void RaisePropertyChanged([CallerMemberName] string? propertyName = null)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
|
||||
|
||||
protected virtual void RaiseOtherPropertyChanged(string propertyName)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
|
||||
|
||||
protected bool SetField<T>(ref T field, T value, IEqualityComparer<T>? comparer = null, [CallerMemberName] string? propertyName = null,
|
||||
params string[]? otherPropertiesChanged)
|
||||
{
|
||||
if ((comparer ?? EqualityComparer<T>.Default).Equals(field, value))
|
||||
return false;
|
||||
|
||||
field = value;
|
||||
RaisePropertyChanged(propertyName);
|
||||
|
||||
if (otherPropertiesChanged == null)
|
||||
return true;
|
||||
|
||||
foreach (var otherProperty in otherPropertiesChanged)
|
||||
RaisePropertyChanged(otherProperty);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
117
PettingZoo/UI/Connection/ConnectionViewModel.cs
Normal file
@ -0,0 +1,117 @@
|
||||
using System;
|
||||
using System.Windows.Input;
|
||||
|
||||
// TODO validate input
|
||||
|
||||
namespace PettingZoo.UI.Connection
|
||||
{
|
||||
public class ConnectionViewModel : BaseViewModel
|
||||
{
|
||||
private string host;
|
||||
private string virtualHost;
|
||||
private int port;
|
||||
private string username;
|
||||
private string password;
|
||||
|
||||
private bool subscribe;
|
||||
private string exchange;
|
||||
private string routingKey;
|
||||
|
||||
|
||||
public string Host
|
||||
{
|
||||
get => host;
|
||||
set => SetField(ref host, value);
|
||||
}
|
||||
|
||||
public string VirtualHost
|
||||
{
|
||||
get => virtualHost;
|
||||
set => SetField(ref virtualHost, value);
|
||||
}
|
||||
|
||||
public int Port
|
||||
{
|
||||
get => port;
|
||||
set => SetField(ref port, value);
|
||||
}
|
||||
|
||||
public string Username
|
||||
{
|
||||
get => username;
|
||||
set => SetField(ref username, value);
|
||||
}
|
||||
|
||||
public string Password
|
||||
{
|
||||
get => password;
|
||||
set => SetField(ref password, value);
|
||||
}
|
||||
|
||||
|
||||
public bool Subscribe
|
||||
{
|
||||
get => subscribe;
|
||||
set => SetField(ref subscribe, value);
|
||||
}
|
||||
|
||||
public string Exchange
|
||||
{
|
||||
get => exchange;
|
||||
set => SetField(ref exchange, value);
|
||||
}
|
||||
|
||||
public string RoutingKey
|
||||
{
|
||||
get => routingKey;
|
||||
set => SetField(ref routingKey, value);
|
||||
}
|
||||
|
||||
|
||||
public ICommand OkCommand { get; }
|
||||
|
||||
public event EventHandler? OkClick;
|
||||
|
||||
|
||||
public ConnectionViewModel(ConnectionDialogParams model)
|
||||
{
|
||||
OkCommand = new DelegateCommand(OkExecute, OkCanExecute);
|
||||
|
||||
host = model.Host;
|
||||
virtualHost = model.VirtualHost;
|
||||
port = model.Port;
|
||||
username = model.Username;
|
||||
password = model.Password;
|
||||
|
||||
subscribe = model.Subscribe;
|
||||
exchange = model.Exchange;
|
||||
routingKey = model.RoutingKey;
|
||||
}
|
||||
|
||||
|
||||
public ConnectionDialogParams ToModel()
|
||||
{
|
||||
return new(Host, VirtualHost, Port, Username, Password, Subscribe, Exchange, RoutingKey);
|
||||
}
|
||||
|
||||
|
||||
private void OkExecute()
|
||||
{
|
||||
OkClick?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
|
||||
private static bool OkCanExecute()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class DesignTimeConnectionViewModel : ConnectionViewModel
|
||||
{
|
||||
public DesignTimeConnectionViewModel() : base(ConnectionDialogParams.Default)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -1,27 +1,26 @@
|
||||
<Window x:Class="PettingZoo.View.ConnectionWindow"
|
||||
<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:res="clr-namespace:PettingZoo.Properties"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:viewModel="clr-namespace:PettingZoo.ViewModel"
|
||||
xmlns:infrastructure="clr-namespace:PettingZoo.Infrastructure"
|
||||
xmlns:ui="clr-namespace:PettingZoo.UI"
|
||||
xmlns:connection="clr-namespace:PettingZoo.UI.Connection"
|
||||
mc:Ignorable="d"
|
||||
d:DataContext="{d:DesignInstance viewModel:ConnectionViewModel}"
|
||||
d:DataContext="{d:DesignInstance connection:DesignTimeConnectionViewModel, IsDesignTimeCreatable = True}"
|
||||
Width="500"
|
||||
SizeToContent="Height"
|
||||
ResizeMode="NoResize"
|
||||
WindowStyle="ToolWindow"
|
||||
Style="{StaticResource WindowStyle}"
|
||||
Title="{x:Static res:Resources.ConnectionWindowTitle}"
|
||||
Title="{x:Static connection:ConnectionWindowStrings.WindowTitle}"
|
||||
FocusManager.FocusedElement="{Binding ElementName=HostTextBox}">
|
||||
<DockPanel Margin="8">
|
||||
<UniformGrid DockPanel.Dock="Bottom" HorizontalAlignment="Right" Rows="1" Columns="2" Style="{StaticResource FooterPanel}">
|
||||
<Button IsDefault="True" Content="{x:Static res:Resources.ButtonOK}" Style="{StaticResource FooterButton}" Command="{Binding OkCommand}"/>
|
||||
<Button IsCancel="True" Content="{x:Static res:Resources.ButtonCancel}" Style="{StaticResource FooterButton}"/>
|
||||
<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>
|
||||
|
||||
<infrastructure:GridLayout Style="{StaticResource Form}">
|
||||
<ui:GridLayout Style="{StaticResource Form}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
@ -35,28 +34,31 @@
|
||||
<RowDefinition Height="8"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Label Grid.Column="0" Grid.Row="0" Content="{x:Static res:Resources.ConnectionHost}"/>
|
||||
<Label Grid.Column="0" Grid.Row="0" Content="{x:Static connection:ConnectionWindowStrings.LabelHost}"/>
|
||||
<TextBox Grid.Column="1" Grid.Row="0" Text="{Binding Host}" Name="HostTextBox" />
|
||||
|
||||
<Label Grid.Column="0" Grid.Row="1" Content="{x:Static res:Resources.ConnectionPort}"/>
|
||||
<Label Grid.Column="0" Grid.Row="1" Content="{x:Static connection:ConnectionWindowStrings.LabelPort}"/>
|
||||
<TextBox Grid.Column="1" Grid.Row="1" Text="{Binding Port}" Width="100" HorizontalAlignment="Left" PreviewTextInput="NumericPreviewTextInput" />
|
||||
|
||||
<Label Grid.Column="0" Grid.Row="2" Content="{x:Static res:Resources.ConnectionVirtualHost}"/>
|
||||
|
||||
<Label Grid.Column="0" Grid.Row="2" Content="{x:Static connection:ConnectionWindowStrings.LabelVirtualHost}"/>
|
||||
<TextBox Grid.Column="1" Grid.Row="2" Text="{Binding VirtualHost}"/>
|
||||
|
||||
<Label Grid.Column="0" Grid.Row="3" Content="{x:Static res:Resources.ConnectionUsername}"/>
|
||||
|
||||
<Label Grid.Column="0" Grid.Row="3" Content="{x:Static connection:ConnectionWindowStrings.LabelUsername}"/>
|
||||
<TextBox Grid.Column="1" Grid.Row="3" Text="{Binding Username}"/>
|
||||
|
||||
<Label Grid.Column="0" Grid.Row="4" Content="{x:Static res:Resources.ConnectionPassword}"/>
|
||||
<PasswordBox Grid.Column="1" Grid.Row="4" infrastructure:PasswordBoxAssistant.BindPassword="true" infrastructure:PasswordBoxAssistant.BoundPassword="{Binding Path=Password, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
|
||||
<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}"/>
|
||||
|
||||
<Label Grid.Column="0" Grid.Row="6" Content="{x:Static res:Resources.ConnectionExchange}"/>
|
||||
<TextBox Grid.Column="1" Grid.Row="6" Text="{Binding Exchange}"/>
|
||||
<CheckBox Grid.Column="1" Grid.Row="6" Content="{x:Static connection:ConnectionWindowStrings.LabelSubscribe}" IsChecked="{Binding Subscribe}"/>
|
||||
|
||||
<Label Grid.Column="0" Grid.Row="7" Content="{x:Static res:Resources.ConnectionRoutingKey}"/>
|
||||
<TextBox Grid.Column="1" Grid.Row="7" Text="{Binding RoutingKey}"/>
|
||||
</infrastructure:GridLayout>
|
||||
<Label Grid.Column="0" Grid.Row="7" Content="{x:Static connection:ConnectionWindowStrings.LabelExchange}"/>
|
||||
<TextBox Grid.Column="1" Grid.Row="7" Text="{Binding Exchange}"/>
|
||||
|
||||
<Label Grid.Column="0" Grid.Row="8" Content="{x:Static connection:ConnectionWindowStrings.LabelRoutingKey}"/>
|
||||
<TextBox Grid.Column="1" Grid.Row="8" Text="{Binding RoutingKey}"/>
|
||||
</ui:GridLayout>
|
||||
</DockPanel>
|
||||
</Window>
|
||||
|
45
PettingZoo/UI/Connection/ConnectionWindow.xaml.cs
Normal file
@ -0,0 +1,45 @@
|
||||
using System.Windows;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace PettingZoo.UI.Connection
|
||||
{
|
||||
public class WindowConnectionDialog : IConnectionDialog
|
||||
{
|
||||
public ConnectionDialogParams? Show(ConnectionDialogParams? defaultParams = null)
|
||||
{
|
||||
var viewModel = new ConnectionViewModel(defaultParams ?? ConnectionDialogParams.Default);
|
||||
var window = new ConnectionWindow(viewModel)
|
||||
{
|
||||
Owner = Application.Current.MainWindow
|
||||
};
|
||||
|
||||
viewModel.OkClick += (_, _) =>
|
||||
{
|
||||
window.DialogResult = true;
|
||||
};
|
||||
|
||||
return window.ShowDialog().GetValueOrDefault()
|
||||
? viewModel.ToModel()
|
||||
: null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public partial class ConnectionWindow
|
||||
{
|
||||
public ConnectionWindow(ConnectionViewModel viewModel)
|
||||
{
|
||||
WindowStartupLocation = WindowStartupLocation.CenterOwner;
|
||||
|
||||
InitializeComponent();
|
||||
DataContext = viewModel;
|
||||
}
|
||||
|
||||
|
||||
private void NumericPreviewTextInput(object sender, TextCompositionEventArgs args)
|
||||
{
|
||||
if (!char.IsDigit(args.Text, args.Text.Length - 1))
|
||||
args.Handled = true;
|
||||
}
|
||||
}
|
||||
}
|
162
PettingZoo/UI/Connection/ConnectionWindowStrings.Designer.cs
generated
Normal file
@ -0,0 +1,162 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <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", "16.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 OK.
|
||||
/// </summary>
|
||||
public static string ButtonOK {
|
||||
get {
|
||||
return ResourceManager.GetString("ButtonOK", 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 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 Connection parameters.
|
||||
/// </summary>
|
||||
public static string WindowTitle {
|
||||
get {
|
||||
return ResourceManager.GetString("WindowTitle", resourceCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
153
PettingZoo/UI/Connection/ConnectionWindowStrings.resx
Normal file
@ -0,0 +1,153 @@
|
||||
<?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="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="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="WindowTitle" xml:space="preserve">
|
||||
<value>Connection parameters</value>
|
||||
</data>
|
||||
</root>
|
43
PettingZoo/UI/Connection/IConnectionDialog.cs
Normal file
@ -0,0 +1,43 @@
|
||||
using System;
|
||||
|
||||
namespace PettingZoo.UI.Connection
|
||||
{
|
||||
public interface IConnectionDialog
|
||||
{
|
||||
ConnectionDialogParams? Show(ConnectionDialogParams? defaultParams = null);
|
||||
}
|
||||
|
||||
|
||||
public class ConnectionDialogParams
|
||||
{
|
||||
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 ConnectionDialogParams Default { get; } = new("localhost", "/", 5672, "guest", "guest", false, "", "#");
|
||||
|
||||
|
||||
public ConnectionDialogParams(string host, string virtualHost, int port, string username, string password, bool subscribe, string exchange, string routingKey)
|
||||
{
|
||||
if (subscribe && (string.IsNullOrEmpty(exchange) || string.IsNullOrEmpty(routingKey)))
|
||||
throw new ArgumentException(@"Exchange and RoutingKey must be provided when Subscribe is true", nameof(subscribe));
|
||||
|
||||
Host = host;
|
||||
VirtualHost = virtualHost;
|
||||
Port = port;
|
||||
Username = username;
|
||||
Password = password;
|
||||
|
||||
Subscribe = subscribe;
|
||||
Exchange = exchange;
|
||||
RoutingKey = routingKey;
|
||||
}
|
||||
}
|
||||
}
|
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);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace PettingZoo.Infrastructure
|
||||
namespace PettingZoo.UI
|
||||
{
|
||||
// Source: http://daniel-albuschat.blogspot.nl/2011/07/gridlayout-for-wpf-escape-margin-hell.html
|
||||
|
||||
@ -32,7 +32,7 @@ namespace PettingZoo.Infrastructure
|
||||
|
||||
public Thickness ChildMargin
|
||||
{
|
||||
get { return (Thickness) GetValue(ChildMarginProperty); }
|
||||
get => (Thickness) GetValue(ChildMarginProperty);
|
||||
set
|
||||
{
|
||||
SetValue(ChildMarginProperty, value);
|
||||
@ -45,12 +45,13 @@ namespace PettingZoo.Infrastructure
|
||||
|
||||
public void UpdateChildMargins()
|
||||
{
|
||||
int maxColumn = 0;
|
||||
int maxRow = 0;
|
||||
var maxColumn = 0;
|
||||
var maxRow = 0;
|
||||
foreach (UIElement element in InternalChildren)
|
||||
{
|
||||
int row = GetRow(element);
|
||||
int column = GetColumn(element);
|
||||
var row = GetRow(element);
|
||||
var column = GetColumn(element);
|
||||
|
||||
if (row > maxRow)
|
||||
maxRow = row;
|
||||
if (column > maxColumn)
|
||||
@ -58,32 +59,31 @@ namespace PettingZoo.Infrastructure
|
||||
}
|
||||
foreach (UIElement element in InternalChildren)
|
||||
{
|
||||
var fe = element as FrameworkElement;
|
||||
if (null != fe)
|
||||
{
|
||||
int row = GetRow(fe);
|
||||
int column = GetColumn(fe);
|
||||
double factorLeft = 0.5;
|
||||
double factorTop = 0.5;
|
||||
double factorRight = 0.5;
|
||||
double factorBottom = 0.5;
|
||||
// Top row - no top margin
|
||||
if (row == 0)
|
||||
factorTop = 0;
|
||||
// Bottom row - no bottom margin
|
||||
if (row == maxRow)
|
||||
factorBottom = 0;
|
||||
// Leftmost column = no left margin
|
||||
if (column == 0)
|
||||
factorLeft = 0;
|
||||
// Rightmost column - no right margin
|
||||
if (column == maxColumn)
|
||||
factorRight = 0;
|
||||
fe.Margin = new Thickness(ChildMargin.Left*factorLeft,
|
||||
ChildMargin.Top*factorTop,
|
||||
ChildMargin.Right*factorRight,
|
||||
ChildMargin.Bottom*factorBottom);
|
||||
}
|
||||
if (element is not FrameworkElement fe)
|
||||
continue;
|
||||
|
||||
var row = GetRow(fe);
|
||||
var column = GetColumn(fe);
|
||||
var factorLeft = 0.5;
|
||||
var factorTop = 0.5;
|
||||
var factorRight = 0.5;
|
||||
var factorBottom = 0.5;
|
||||
// Top row - no top margin
|
||||
if (row == 0)
|
||||
factorTop = 0;
|
||||
// Bottom row - no bottom margin
|
||||
if (row == maxRow)
|
||||
factorBottom = 0;
|
||||
// Leftmost column = no left margin
|
||||
if (column == 0)
|
||||
factorLeft = 0;
|
||||
// Rightmost column - no right margin
|
||||
if (column == maxColumn)
|
||||
factorRight = 0;
|
||||
fe.Margin = new Thickness(ChildMargin.Left*factorLeft,
|
||||
ChildMargin.Top*factorTop,
|
||||
ChildMargin.Right*factorRight,
|
||||
ChildMargin.Bottom*factorBottom);
|
||||
}
|
||||
}
|
||||
|
@ -2,11 +2,11 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Specialized;
|
||||
using System.Windows;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace PettingZoo.Infrastructure
|
||||
namespace PettingZoo.UI
|
||||
{
|
||||
// Source: https://social.msdn.microsoft.com/Forums/vstudio/en-US/0f524459-b14e-4f9a-8264-267953418a2d/trivial-listboxlistview-autoscroll?forum=wpf
|
||||
//
|
||||
@ -29,7 +29,7 @@ namespace PettingZoo.Infrastructure
|
||||
|
||||
public static void SetAutoScroll(System.Windows.Controls.ListBox instance, bool value)
|
||||
{
|
||||
var oldHandler = (AutoScrollHandler) instance.GetValue(AutoScrollHandlerProperty);
|
||||
var oldHandler = (AutoScrollHandler?)instance.GetValue(AutoScrollHandlerProperty);
|
||||
if (oldHandler != null)
|
||||
{
|
||||
oldHandler.Dispose();
|
||||
@ -51,7 +51,7 @@ namespace PettingZoo.Infrastructure
|
||||
ItemsSourcePropertyChanged));
|
||||
|
||||
private readonly System.Windows.Controls.ListBox target;
|
||||
private ScrollViewer scrollViewer;
|
||||
private ScrollViewer? scrollViewer;
|
||||
|
||||
public AutoScrollHandler(System.Windows.Controls.ListBox target)
|
||||
{
|
||||
@ -74,8 +74,8 @@ namespace PettingZoo.Infrastructure
|
||||
|
||||
public IEnumerable ItemsSource
|
||||
{
|
||||
get { return (IEnumerable) GetValue(ItemsSourceProperty); }
|
||||
set { SetValue(ItemsSourceProperty, value); }
|
||||
get => (IEnumerable) GetValue(ItemsSourceProperty);
|
||||
set => SetValue(ItemsSourceProperty, value);
|
||||
}
|
||||
|
||||
|
||||
@ -87,16 +87,14 @@ namespace PettingZoo.Infrastructure
|
||||
|
||||
private void ItemsSourceChanged(IEnumerable oldValue, IEnumerable newValue)
|
||||
{
|
||||
var collection = oldValue as INotifyCollectionChanged;
|
||||
if (collection != null)
|
||||
collection.CollectionChanged -= Collection_CollectionChanged;
|
||||
if (oldValue is INotifyCollectionChanged oldCollection)
|
||||
oldCollection.CollectionChanged -= Collection_CollectionChanged;
|
||||
|
||||
collection = newValue as INotifyCollectionChanged;
|
||||
if (collection != null)
|
||||
collection.CollectionChanged += Collection_CollectionChanged;
|
||||
if (newValue is INotifyCollectionChanged newCollection)
|
||||
newCollection.CollectionChanged += Collection_CollectionChanged;
|
||||
}
|
||||
|
||||
private void Collection_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
|
||||
private void Collection_CollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
if (e.Action != NotifyCollectionChangedAction.Add || e.NewItems == null || e.NewItems.Count < 1)
|
||||
return;
|
||||
@ -109,24 +107,31 @@ namespace PettingZoo.Infrastructure
|
||||
return;
|
||||
}
|
||||
|
||||
if (Math.Abs(scrollViewer.VerticalOffset - scrollViewer.ScrollableHeight) < 1)
|
||||
target.ScrollIntoView(e.NewItems[e.NewItems.Count - 1]);
|
||||
if (e.NewItems.Count == 0)
|
||||
return;
|
||||
|
||||
// If not already at the bottom, keep the position stable
|
||||
if (Math.Abs(scrollViewer.VerticalOffset - scrollViewer.ScrollableHeight) > 0)
|
||||
return;
|
||||
|
||||
var item = e.NewItems[^1];
|
||||
if (item != null)
|
||||
target.ScrollIntoView(item);
|
||||
}
|
||||
|
||||
|
||||
private static ScrollViewer FindScrollViewer(DependencyObject parent)
|
||||
private static ScrollViewer? FindScrollViewer(DependencyObject parent)
|
||||
{
|
||||
var childCount = VisualTreeHelper.GetChildrenCount(parent);
|
||||
for (var childIndex = 0; childIndex < childCount; childIndex++)
|
||||
{
|
||||
var child = VisualTreeHelper.GetChild(parent, childIndex);
|
||||
var scrollViewer = (child as ScrollViewer);
|
||||
if (scrollViewer != null)
|
||||
if (child is ScrollViewer scrollViewer)
|
||||
return scrollViewer;
|
||||
|
||||
scrollViewer = FindScrollViewer(child);
|
||||
if (scrollViewer != null)
|
||||
return scrollViewer;
|
||||
var childScrollViewer = FindScrollViewer(child);
|
||||
if (childScrollViewer != null)
|
||||
return childScrollViewer;
|
||||
}
|
||||
|
||||
return null;
|
97
PettingZoo/UI/Main/MainWindow.xaml
Normal file
@ -0,0 +1,97 @@
|
||||
<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/"
|
||||
mc:Ignorable="d"
|
||||
d:DataContext="{d:DesignInstance main:DesignTimeMainWindowViewModel, IsDesignTimeCreatable=True}"
|
||||
Width="800"
|
||||
Height="600"
|
||||
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}" />
|
||||
</Window.InputBindings>
|
||||
<DockPanel>
|
||||
<ToolBar DockPanel.Dock="Top" ToolBarTray.IsLocked="True">
|
||||
<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>
|
||||
|
||||
<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>
|
||||
<TextBlock Text="{Binding ConnectionStatus}"/>
|
||||
</StatusBarItem>
|
||||
</StatusBar>
|
||||
<TabControl
|
||||
Name="SubscriberTabs"
|
||||
ItemsSource="{Binding Tabs}"
|
||||
SelectedValue="{Binding ActiveTab}">
|
||||
<TabControl.ItemContainerStyle>
|
||||
<Style TargetType="{x:Type TabItem}">
|
||||
<Setter Property="Header" Value="{Binding Title}" />
|
||||
<Setter Property="ContextMenu">
|
||||
<Setter.Value>
|
||||
<ContextMenu>
|
||||
<MenuItem Header="{x:Static main:MainWindowStrings.ContextMenuCloseTab}" Command="{Binding CloseTabCommand}" InputGestureText="Ctrl+W" />
|
||||
</ContextMenu>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</TabControl.ItemContainerStyle>
|
||||
<TabControl.ContentTemplate>
|
||||
<DataTemplate DataType="{x:Type tab:ITab}">
|
||||
<ContentControl Content="{Binding Content}" />
|
||||
</DataTemplate>
|
||||
|
||||
</TabControl.ContentTemplate>
|
||||
</TabControl>
|
||||
</DockPanel>
|
||||
</Window>
|
50
PettingZoo/UI/Main/MainWindow.xaml.cs
Normal file
@ -0,0 +1,50 @@
|
||||
using System;
|
||||
using System.Windows;
|
||||
using PettingZoo.Core.Connection;
|
||||
using PettingZoo.UI.Connection;
|
||||
using PettingZoo.UI.Subscribe;
|
||||
using PettingZoo.UI.Tab;
|
||||
|
||||
namespace PettingZoo.UI.Main
|
||||
{
|
||||
// TODO support undocking tabs (and redocking afterwards)
|
||||
// TODO allow tab reordering
|
||||
|
||||
#pragma warning disable CA1001 // MainWindow can't be IDisposable, handled instead in OnDispatcherShutDownStarted
|
||||
public partial class MainWindow
|
||||
{
|
||||
private readonly MainWindowViewModel viewModel;
|
||||
|
||||
|
||||
public MainWindow(IConnectionFactory connectionFactory, IConnectionDialog connectionDialog, ISubscribeDialog subscribeDialog, ITabFactory tabFactory)
|
||||
{
|
||||
WindowStartupLocation = WindowStartupLocation.CenterScreen;
|
||||
|
||||
InitializeComponent();
|
||||
viewModel = new MainWindowViewModel(connectionFactory, connectionDialog, subscribeDialog, tabFactory);
|
||||
DataContext = viewModel;
|
||||
|
||||
Dispatcher.ShutdownStarted += OnDispatcherShutDownStarted;
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
#pragma warning restore CA1001
|
||||
}
|
@ -8,7 +8,10 @@
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace PettingZoo.Properties {
|
||||
namespace PettingZoo.UI.Main {
|
||||
using System;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
/// </summary>
|
||||
@ -16,17 +19,17 @@ namespace PettingZoo.Properties {
|
||||
// class via a tool like ResGen or Visual Studio.
|
||||
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||
// with the /str option, or rebuild your VS project.
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
public class Resources {
|
||||
public class MainWindowStrings {
|
||||
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
|
||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||
|
||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal Resources() {
|
||||
internal MainWindowStrings() {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -36,7 +39,7 @@ namespace PettingZoo.Properties {
|
||||
public static global::System.Resources.ResourceManager ResourceManager {
|
||||
get {
|
||||
if (object.ReferenceEquals(resourceMan, null)) {
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("PettingZoo.Properties.Resources", typeof(Resources).Assembly);
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("PettingZoo.UI.Main.MainWindowStrings", typeof(MainWindowStrings).Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
@ -58,92 +61,47 @@ namespace PettingZoo.Properties {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Cancel.
|
||||
/// Looks up a localized string similar to Connect.
|
||||
/// </summary>
|
||||
public static string ButtonCancel {
|
||||
public static string CommandConnect {
|
||||
get {
|
||||
return ResourceManager.GetString("ButtonCancel", resourceCulture);
|
||||
return ResourceManager.GetString("CommandConnect", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to OK.
|
||||
/// Looks up a localized string similar to Disconnect.
|
||||
/// </summary>
|
||||
public static string ButtonOK {
|
||||
public static string CommandDisconnect {
|
||||
get {
|
||||
return ResourceManager.GetString("ButtonOK", resourceCulture);
|
||||
return ResourceManager.GetString("CommandDisconnect", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Exchange:.
|
||||
/// Looks up a localized string similar to New Publisher.
|
||||
/// </summary>
|
||||
public static string ConnectionExchange {
|
||||
public static string CommandPublish {
|
||||
get {
|
||||
return ResourceManager.GetString("ConnectionExchange", resourceCulture);
|
||||
return ResourceManager.GetString("CommandPublish", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Host:.
|
||||
/// Looks up a localized string similar to New Subscriber....
|
||||
/// </summary>
|
||||
public static string ConnectionHost {
|
||||
public static string CommandSubscribe {
|
||||
get {
|
||||
return ResourceManager.GetString("ConnectionHost", resourceCulture);
|
||||
return ResourceManager.GetString("CommandSubscribe", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Password:.
|
||||
/// Looks up a localized string similar to Close tab.
|
||||
/// </summary>
|
||||
public static string ConnectionPassword {
|
||||
public static string ContextMenuCloseTab {
|
||||
get {
|
||||
return ResourceManager.GetString("ConnectionPassword", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Port:.
|
||||
/// </summary>
|
||||
public static string ConnectionPort {
|
||||
get {
|
||||
return ResourceManager.GetString("ConnectionPort", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Routing key:.
|
||||
/// </summary>
|
||||
public static string ConnectionRoutingKey {
|
||||
get {
|
||||
return ResourceManager.GetString("ConnectionRoutingKey", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Username:.
|
||||
/// </summary>
|
||||
public static string ConnectionUsername {
|
||||
get {
|
||||
return ResourceManager.GetString("ConnectionUsername", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Virtual host:.
|
||||
/// </summary>
|
||||
public static string ConnectionVirtualHost {
|
||||
get {
|
||||
return ResourceManager.GetString("ConnectionVirtualHost", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Connection parameters.
|
||||
/// </summary>
|
||||
public static string ConnectionWindowTitle {
|
||||
get {
|
||||
return ResourceManager.GetString("ConnectionWindowTitle", resourceCulture);
|
||||
return ResourceManager.GetString("ContextMenuCloseTab", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
@ -165,51 +123,6 @@ namespace PettingZoo.Properties {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Petting Zoo - a RabbitMQ live message viewer.
|
||||
/// </summary>
|
||||
public static string MainWindowTitle {
|
||||
get {
|
||||
return ResourceManager.GetString("MainWindowTitle", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Body.
|
||||
/// </summary>
|
||||
public static string PanelTitleBody {
|
||||
get {
|
||||
return ResourceManager.GetString("PanelTitleBody", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Properties.
|
||||
/// </summary>
|
||||
public static string PanelTitleProperties {
|
||||
get {
|
||||
return ResourceManager.GetString("PanelTitleProperties", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Name.
|
||||
/// </summary>
|
||||
public static string PropertyName {
|
||||
get {
|
||||
return ResourceManager.GetString("PropertyName", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Value.
|
||||
/// </summary>
|
||||
public static string PropertyValue {
|
||||
get {
|
||||
return ResourceManager.GetString("PropertyValue", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Connected.
|
||||
/// </summary>
|
||||
@ -245,5 +158,14 @@ namespace PettingZoo.Properties {
|
||||
return ResourceManager.GetString("StatusError", 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,20 @@
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="ButtonCancel" xml:space="preserve">
|
||||
<value>Cancel</value>
|
||||
<data name="CommandConnect" xml:space="preserve">
|
||||
<value>Connect</value>
|
||||
</data>
|
||||
<data name="ButtonOK" xml:space="preserve">
|
||||
<value>OK</value>
|
||||
<data name="CommandDisconnect" xml:space="preserve">
|
||||
<value>Disconnect</value>
|
||||
</data>
|
||||
<data name="ConnectionExchange" xml:space="preserve">
|
||||
<value>Exchange:</value>
|
||||
<data name="CommandPublish" xml:space="preserve">
|
||||
<value>New Publisher</value>
|
||||
</data>
|
||||
<data name="ConnectionHost" xml:space="preserve">
|
||||
<value>Host:</value>
|
||||
<data name="CommandSubscribe" xml:space="preserve">
|
||||
<value>New Subscriber...</value>
|
||||
</data>
|
||||
<data name="ConnectionPassword" xml:space="preserve">
|
||||
<value>Password:</value>
|
||||
</data>
|
||||
<data name="ConnectionPort" xml:space="preserve">
|
||||
<value>Port:</value>
|
||||
</data>
|
||||
<data name="ConnectionRoutingKey" xml:space="preserve">
|
||||
<value>Routing key:</value>
|
||||
</data>
|
||||
<data name="ConnectionUsername" xml:space="preserve">
|
||||
<value>Username:</value>
|
||||
</data>
|
||||
<data name="ConnectionVirtualHost" xml:space="preserve">
|
||||
<value>Virtual host:</value>
|
||||
</data>
|
||||
<data name="ConnectionWindowTitle" xml:space="preserve">
|
||||
<value>Connection parameters</value>
|
||||
<data name="ContextMenuCloseTab" xml:space="preserve">
|
||||
<value>Close tab</value>
|
||||
</data>
|
||||
<data name="DeliveryModeNonPersistent" xml:space="preserve">
|
||||
<value>Non-persistent</value>
|
||||
@ -153,21 +138,6 @@
|
||||
<data name="DeliveryModePersistent" xml:space="preserve">
|
||||
<value>Persistent</value>
|
||||
</data>
|
||||
<data name="MainWindowTitle" xml:space="preserve">
|
||||
<value>Petting Zoo - a RabbitMQ live message viewer</value>
|
||||
</data>
|
||||
<data name="PanelTitleBody" xml:space="preserve">
|
||||
<value>Body</value>
|
||||
</data>
|
||||
<data name="PanelTitleProperties" xml:space="preserve">
|
||||
<value>Properties</value>
|
||||
</data>
|
||||
<data name="PropertyName" xml:space="preserve">
|
||||
<value>Name</value>
|
||||
</data>
|
||||
<data name="PropertyValue" xml:space="preserve">
|
||||
<value>Value</value>
|
||||
</data>
|
||||
<data name="StatusConnected" xml:space="preserve">
|
||||
<value>Connected</value>
|
||||
</data>
|
||||
@ -180,4 +150,7 @@
|
||||
<data name="StatusError" xml:space="preserve">
|
||||
<value>Error: {0}</value>
|
||||
</data>
|
||||
<data name="WindowTitle" xml:space="preserve">
|
||||
<value>Petting Zoo - a RabbitMQ live message viewer</value>
|
||||
</data>
|
||||
</root>
|
237
PettingZoo/UI/Main/MainWindowViewModel.cs
Normal file
@ -0,0 +1,237 @@
|
||||
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;
|
||||
|
||||
namespace PettingZoo.UI.Main
|
||||
{
|
||||
public class MainWindowViewModel : BaseViewModel, IAsyncDisposable
|
||||
{
|
||||
private readonly IConnectionFactory connectionFactory;
|
||||
private readonly IConnectionDialog connectionDialog;
|
||||
private readonly ISubscribeDialog subscribeDialog;
|
||||
private readonly ITabFactory tabFactory;
|
||||
|
||||
private ConnectionDialogParams? connectionDialogParams;
|
||||
private SubscribeDialogParams? subscribeDialogParams;
|
||||
private IConnection? connection;
|
||||
private string connectionStatus;
|
||||
private ITab? activeTab;
|
||||
|
||||
private readonly DelegateCommand connectCommand;
|
||||
private readonly DelegateCommand disconnectCommand;
|
||||
private readonly DelegateCommand publishCommand;
|
||||
private readonly DelegateCommand subscribeCommand;
|
||||
private readonly DelegateCommand closeTabCommand;
|
||||
|
||||
|
||||
public string ConnectionStatus
|
||||
{
|
||||
get => connectionStatus;
|
||||
private set => SetField(ref connectionStatus, value);
|
||||
}
|
||||
|
||||
|
||||
public ObservableCollection<ITab> Tabs { get; }
|
||||
|
||||
public ITab? ActiveTab
|
||||
{
|
||||
get => activeTab;
|
||||
set => SetField(ref activeTab, value, otherPropertiesChanged: new []
|
||||
{
|
||||
nameof(ToolbarCommands),
|
||||
nameof(ToolbarCommandsSeparatorVisibility)
|
||||
});
|
||||
}
|
||||
|
||||
public ICommand ConnectCommand => connectCommand;
|
||||
public ICommand DisconnectCommand => disconnectCommand;
|
||||
public ICommand PublishCommand => publishCommand;
|
||||
public ICommand SubscribeCommand => subscribeCommand;
|
||||
public ICommand CloseTabCommand => closeTabCommand;
|
||||
|
||||
public IEnumerable<TabToolbarCommand> ToolbarCommands => ActiveTab is ITabToolbarCommands tabToolbarCommands
|
||||
? tabToolbarCommands.ToolbarCommands
|
||||
: Enumerable.Empty<TabToolbarCommand>();
|
||||
|
||||
public Visibility ToolbarCommandsSeparatorVisibility =>
|
||||
ToolbarCommands.Any() ? Visibility.Visible : Visibility.Collapsed;
|
||||
|
||||
|
||||
public MainWindowViewModel(IConnectionFactory connectionFactory, IConnectionDialog connectionDialog,
|
||||
ISubscribeDialog subscribeDialog, ITabFactory tabFactory)
|
||||
{
|
||||
this.connectionFactory = connectionFactory;
|
||||
this.connectionDialog = connectionDialog;
|
||||
this.subscribeDialog = subscribeDialog;
|
||||
this.tabFactory = tabFactory;
|
||||
|
||||
connectionStatus = GetConnectionStatus(null);
|
||||
|
||||
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, CloseTabCanExecute);
|
||||
}
|
||||
|
||||
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
if (connection != null)
|
||||
await connection.DisposeAsync();
|
||||
}
|
||||
|
||||
|
||||
private async void ConnectExecute()
|
||||
{
|
||||
//var newParams = connectionDialog.Show(connectionDialogParams);
|
||||
var newParams = new ConnectionDialogParams("localhost", "/", 5672, "guest", "guest", true, "lef", "#");
|
||||
if (newParams == null)
|
||||
return;
|
||||
|
||||
if (connection != null)
|
||||
await connection.DisposeAsync();
|
||||
|
||||
connectionDialogParams = newParams;
|
||||
connection = connectionFactory.CreateConnection(new ConnectionParams(
|
||||
connectionDialogParams.Host, connectionDialogParams.VirtualHost, connectionDialogParams.Port,
|
||||
connectionDialogParams.Username, connectionDialogParams.Password));
|
||||
connection.StatusChanged += ConnectionStatusChanged;
|
||||
|
||||
if (connectionDialogParams.Subscribe)
|
||||
{
|
||||
var subscriber = connection.Subscribe(connectionDialogParams.Exchange, connectionDialogParams.RoutingKey);
|
||||
AddTab(tabFactory.CreateSubscriberTab(CloseTabCommand, subscriber));
|
||||
|
||||
}
|
||||
|
||||
ConnectionChanged();
|
||||
}
|
||||
|
||||
|
||||
private async void DisconnectExecute()
|
||||
{
|
||||
Tabs.Clear();
|
||||
|
||||
if (connection != null)
|
||||
{
|
||||
await connection.DisposeAsync();
|
||||
connection = null;
|
||||
}
|
||||
|
||||
connectionDialogParams = null;
|
||||
ConnectionStatus = GetConnectionStatus(null);
|
||||
|
||||
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(CloseTabCommand, subscriber));
|
||||
}
|
||||
|
||||
|
||||
private void PublishExecute()
|
||||
{
|
||||
if (connection == null)
|
||||
return;
|
||||
|
||||
AddTab(tabFactory.CreatePublisherTab(CloseTabCommand, connection));
|
||||
}
|
||||
|
||||
|
||||
private bool IsConnectedCanExecute()
|
||||
{
|
||||
return connection != null;
|
||||
}
|
||||
|
||||
|
||||
private void CloseTabExecute()
|
||||
{
|
||||
if (ActiveTab == null)
|
||||
return;
|
||||
|
||||
var activeTabIndex = Tabs.IndexOf(ActiveTab);
|
||||
if (activeTabIndex == -1)
|
||||
return;
|
||||
|
||||
Tabs.RemoveAt(activeTabIndex);
|
||||
|
||||
if (activeTabIndex == Tabs.Count)
|
||||
activeTabIndex--;
|
||||
|
||||
ActiveTab = activeTabIndex >= 0 ? Tabs[activeTabIndex] : null;
|
||||
closeTabCommand.RaiseCanExecuteChanged();
|
||||
}
|
||||
|
||||
|
||||
private bool CloseTabCanExecute()
|
||||
{
|
||||
return ActiveTab != null;
|
||||
}
|
||||
|
||||
|
||||
private void AddTab(ITab tab)
|
||||
{
|
||||
Tabs.Add(tab);
|
||||
ActiveTab = tab;
|
||||
|
||||
closeTabCommand.RaiseCanExecuteChanged();
|
||||
}
|
||||
|
||||
|
||||
private void ConnectionChanged()
|
||||
{
|
||||
disconnectCommand.RaiseCanExecuteChanged();
|
||||
subscribeCommand.RaiseCanExecuteChanged();
|
||||
publishCommand.RaiseCanExecuteChanged();
|
||||
}
|
||||
|
||||
private void ConnectionStatusChanged(object? sender, StatusChangedEventArgs args)
|
||||
{
|
||||
ConnectionStatus = GetConnectionStatus(args);
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static string GetConnectionStatus(StatusChangedEventArgs? args)
|
||||
{
|
||||
return args?.Status switch
|
||||
{
|
||||
Core.Connection.ConnectionStatus.Connecting => string.Format(MainWindowStrings.StatusConnecting, args.Context),
|
||||
Core.Connection.ConnectionStatus.Connected => string.Format(MainWindowStrings.StatusConnected, args.Context),
|
||||
Core.Connection.ConnectionStatus.Error => string.Format(MainWindowStrings.StatusError, args.Context),
|
||||
Core.Connection.ConnectionStatus.Disconnected => MainWindowStrings.StatusDisconnected,
|
||||
_ => MainWindowStrings.StatusDisconnected
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class DesignTimeMainWindowViewModel : MainWindowViewModel
|
||||
{
|
||||
public DesignTimeMainWindowViewModel() : base(null!, null!, null!, null!)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace PettingZoo.Infrastructure
|
||||
namespace PettingZoo.UI
|
||||
{
|
||||
// Source: http://blog.functionalfun.net/2008/06/wpf-passwordbox-and-data-binding.html
|
||||
public static class PasswordBoxAssistant
|
||||
@ -35,9 +35,7 @@ namespace PettingZoo.Infrastructure
|
||||
var newPassword = (string) e.NewValue;
|
||||
|
||||
if (!GetUpdatingPassword(box))
|
||||
{
|
||||
box.Password = newPassword;
|
||||
}
|
||||
|
||||
box.PasswordChanged += HandlePasswordChanged;
|
||||
}
|
||||
@ -46,26 +44,19 @@ namespace PettingZoo.Infrastructure
|
||||
{
|
||||
// when the BindPassword attached property is set on a PasswordBox,
|
||||
// start listening to its PasswordChanged event
|
||||
|
||||
var box = dp as PasswordBox;
|
||||
|
||||
if (box == null)
|
||||
if (dp is not PasswordBox box)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var wasBound = (bool) (e.OldValue);
|
||||
var needToBind = (bool) (e.NewValue);
|
||||
var wasBound = (bool)e.OldValue;
|
||||
var needToBind = (bool)e.NewValue;
|
||||
|
||||
if (wasBound)
|
||||
{
|
||||
box.PasswordChanged -= HandlePasswordChanged;
|
||||
}
|
||||
|
||||
if (needToBind)
|
||||
{
|
||||
box.PasswordChanged += HandlePasswordChanged;
|
||||
}
|
||||
}
|
||||
|
||||
private static void HandlePasswordChanged(object sender, RoutedEventArgs e)
|
26
PettingZoo/UI/Subscribe/ISubscribeDialog.cs
Normal file
@ -0,0 +1,26 @@
|
||||
using System;
|
||||
|
||||
namespace PettingZoo.UI.Subscribe
|
||||
{
|
||||
public interface ISubscribeDialog
|
||||
{
|
||||
SubscribeDialogParams? Show(SubscribeDialogParams? defaultParams = null);
|
||||
}
|
||||
|
||||
|
||||
public class SubscribeDialogParams
|
||||
{
|
||||
public string Exchange { get; }
|
||||
public string RoutingKey { get; }
|
||||
|
||||
|
||||
public static SubscribeDialogParams Default { get; } = new("", "#");
|
||||
|
||||
|
||||
public SubscribeDialogParams(string exchange, string routingKey)
|
||||
{
|
||||
Exchange = exchange;
|
||||
RoutingKey = routingKey;
|
||||
}
|
||||
}
|
||||
}
|
66
PettingZoo/UI/Subscribe/SubscribeViewModel.cs
Normal file
@ -0,0 +1,66 @@
|
||||
using System;
|
||||
using System.Windows.Input;
|
||||
|
||||
// TODO validate input
|
||||
|
||||
namespace PettingZoo.UI.Subscribe
|
||||
{
|
||||
public class SubscribeViewModel : BaseViewModel
|
||||
{
|
||||
private string exchange;
|
||||
private string routingKey;
|
||||
|
||||
|
||||
public string Exchange
|
||||
{
|
||||
get => exchange;
|
||||
set => SetField(ref exchange, value);
|
||||
}
|
||||
|
||||
public string RoutingKey
|
||||
{
|
||||
get => routingKey;
|
||||
set => SetField(ref routingKey, value);
|
||||
}
|
||||
|
||||
|
||||
public ICommand OkCommand { get; }
|
||||
|
||||
public event EventHandler? OkClick;
|
||||
|
||||
|
||||
public SubscribeViewModel(SubscribeDialogParams subscribeParams)
|
||||
{
|
||||
OkCommand = new DelegateCommand(OkExecute, OkCanExecute);
|
||||
|
||||
exchange = subscribeParams.Exchange;
|
||||
routingKey = subscribeParams.RoutingKey;
|
||||
}
|
||||
|
||||
|
||||
public SubscribeDialogParams ToModel()
|
||||
{
|
||||
return new(Exchange, RoutingKey);
|
||||
}
|
||||
|
||||
|
||||
private void OkExecute()
|
||||
{
|
||||
OkClick?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
|
||||
private static bool OkCanExecute()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class DesignTimeSubscribeViewModel : SubscribeViewModel
|
||||
{
|
||||
public DesignTimeSubscribeViewModel() : base(SubscribeDialogParams.Default)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
41
PettingZoo/UI/Subscribe/SubscribeWindow.xaml
Normal file
@ -0,0 +1,41 @@
|
||||
<Window x:Class="PettingZoo.UI.Subscribe.SubscribeWindow"
|
||||
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"
|
||||
xmlns:subscribe="clr-namespace:PettingZoo.UI.Subscribe"
|
||||
mc:Ignorable="d"
|
||||
d:DataContext="{d:DesignInstance connection:DesignTimeConnectionViewModel, IsDesignTimeCreatable = True}"
|
||||
Width="500"
|
||||
SizeToContent="Height"
|
||||
ResizeMode="NoResize"
|
||||
WindowStyle="ToolWindow"
|
||||
Style="{StaticResource WindowStyle}"
|
||||
Title="{x:Static subscribe:SubscribeWindowStrings.WindowTitle}"
|
||||
FocusManager.FocusedElement="{Binding ElementName=ExchangeTextBox}">
|
||||
<DockPanel Margin="8">
|
||||
<UniformGrid DockPanel.Dock="Bottom" HorizontalAlignment="Right" Rows="1" Columns="2" Style="{StaticResource FooterPanel}">
|
||||
<Button IsDefault="True" Content="{x:Static subscribe:SubscribeWindowStrings.ButtonOK}" Style="{StaticResource FooterButton}" Command="{Binding OkCommand}"/>
|
||||
<Button IsCancel="True" Content="{x:Static subscribe:SubscribeWindowStrings.ButtonCancel}" Style="{StaticResource FooterButton}"/>
|
||||
</UniformGrid>
|
||||
|
||||
<ui:GridLayout Style="{StaticResource Form}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Label Grid.Column="0" Grid.Row="0" Content="{x:Static subscribe:SubscribeWindowStrings.LabelExchange}"/>
|
||||
<TextBox Grid.Column="1" Grid.Row="0" Text="{Binding Exchange}" Name="ExchangeTextBox"/>
|
||||
|
||||
<Label Grid.Column="0" Grid.Row="1" Content="{x:Static subscribe:SubscribeWindowStrings.LabelRoutingKey}"/>
|
||||
<TextBox Grid.Column="1" Grid.Row="1" Text="{Binding RoutingKey}"/>
|
||||
</ui:GridLayout>
|
||||
</DockPanel>
|
||||
</Window>
|
||||
|
37
PettingZoo/UI/Subscribe/SubscribeWindow.xaml.cs
Normal file
@ -0,0 +1,37 @@
|
||||
using System.Windows;
|
||||
|
||||
namespace PettingZoo.UI.Subscribe
|
||||
{
|
||||
public class WindowSubscribeDialog : ISubscribeDialog
|
||||
{
|
||||
public SubscribeDialogParams? Show(SubscribeDialogParams? defaultParams = null)
|
||||
{
|
||||
var viewModel = new SubscribeViewModel(defaultParams ?? SubscribeDialogParams.Default);
|
||||
var window = new SubscribeWindow(viewModel)
|
||||
{
|
||||
Owner = Application.Current.MainWindow
|
||||
};
|
||||
|
||||
viewModel.OkClick += (_, _) =>
|
||||
{
|
||||
window.DialogResult = true;
|
||||
};
|
||||
|
||||
return window.ShowDialog().GetValueOrDefault()
|
||||
? viewModel.ToModel()
|
||||
: null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public partial class SubscribeWindow
|
||||
{
|
||||
public SubscribeWindow(SubscribeViewModel viewModel)
|
||||
{
|
||||
WindowStartupLocation = WindowStartupLocation.CenterOwner;
|
||||
|
||||
InitializeComponent();
|
||||
DataContext = viewModel;
|
||||
}
|
||||
}
|
||||
}
|
108
PettingZoo/UI/Subscribe/SubscribeWindowStrings.Designer.cs
generated
Normal file
@ -0,0 +1,108 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <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.Subscribe {
|
||||
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", "16.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
public class SubscribeWindowStrings {
|
||||
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
|
||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||
|
||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal SubscribeWindowStrings() {
|
||||
}
|
||||
|
||||
/// <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.Subscribe.SubscribeWindowStrings", typeof(SubscribeWindowStrings).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 Exchange:.
|
||||
/// </summary>
|
||||
public static string LabelExchange {
|
||||
get {
|
||||
return ResourceManager.GetString("LabelExchange", 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 Subscribe parameters.
|
||||
/// </summary>
|
||||
public static string WindowTitle {
|
||||
get {
|
||||
return ResourceManager.GetString("WindowTitle", resourceCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
135
PettingZoo/UI/Subscribe/SubscribeWindowStrings.resx
Normal file
@ -0,0 +1,135 @@
|
||||
<?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="LabelExchange" xml:space="preserve">
|
||||
<value>Exchange:</value>
|
||||
</data>
|
||||
<data name="LabelRoutingKey" xml:space="preserve">
|
||||
<value>Routing key:</value>
|
||||
</data>
|
||||
<data name="WindowTitle" xml:space="preserve">
|
||||
<value>Subscribe parameters</value>
|
||||
</data>
|
||||
</root>
|
37
PettingZoo/UI/SvgIconHelper.cs
Normal file
@ -0,0 +1,37 @@
|
||||
using System;
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
using SharpVectors.Dom.Svg;
|
||||
using SharpVectors.Renderers.Utils;
|
||||
using SharpVectors.Renderers.Wpf;
|
||||
|
||||
namespace PettingZoo.UI
|
||||
{
|
||||
public static class SvgIconHelper
|
||||
{
|
||||
public static ImageSource LoadFromResource(string resourceName)
|
||||
{
|
||||
var streamInfo = Application.GetResourceStream(new Uri($"/PettingZoo;component{resourceName}", UriKind.Relative));
|
||||
if (streamInfo == null)
|
||||
throw new ArgumentException($"Resource '{resourceName}' not found");
|
||||
|
||||
// Basically the code used in FileSvgConverter, but that only supports outputting to a file not returning the Drawing
|
||||
var wpfDrawingSettings = new WpfDrawingSettings
|
||||
{
|
||||
IncludeRuntime = true,
|
||||
TextAsGeometry = true
|
||||
};
|
||||
var wpfRenderer = new WpfDrawingRenderer(wpfDrawingSettings);
|
||||
var wpfWindow = new WpfSvgWindow(0, 0, wpfRenderer);
|
||||
|
||||
wpfWindow.LoadDocument(streamInfo.Stream, wpfDrawingSettings);
|
||||
wpfRenderer.InvalidRect = SvgRectF.Empty;
|
||||
wpfRenderer.Render(wpfWindow.Document);
|
||||
|
||||
if (wpfRenderer.Drawing == null)
|
||||
throw new ArgumentException($"Resource '{resourceName}' is not a valid SVG");
|
||||
|
||||
return new DrawingImage(wpfRenderer.Drawing);
|
||||
}
|
||||
}
|
||||
}
|
37
PettingZoo/UI/Tab/ITab.cs
Normal file
@ -0,0 +1,37 @@
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace PettingZoo.UI.Tab
|
||||
{
|
||||
public interface ITabToolbarCommands : INotifyPropertyChanged
|
||||
{
|
||||
IEnumerable<TabToolbarCommand> ToolbarCommands { get; }
|
||||
}
|
||||
|
||||
|
||||
public interface ITab : ITabToolbarCommands
|
||||
{
|
||||
string Title { get; }
|
||||
ContentControl Content { get; }
|
||||
ICommand CloseTabCommand { get; }
|
||||
}
|
||||
|
||||
|
||||
public readonly struct TabToolbarCommand
|
||||
{
|
||||
public ICommand Command { get; }
|
||||
public string Caption { get; }
|
||||
public ImageSource Icon { get; }
|
||||
|
||||
|
||||
public TabToolbarCommand(ICommand command, string caption, ImageSource icon)
|
||||
{
|
||||
Command = command;
|
||||
Caption = caption;
|
||||
Icon = icon;
|
||||
}
|
||||
}
|
||||
}
|
15
PettingZoo/UI/Tab/ITabFactory.cs
Normal file
@ -0,0 +1,15 @@
|
||||
using System.Windows.Input;
|
||||
using PettingZoo.Core.Connection;
|
||||
|
||||
namespace PettingZoo.UI.Tab
|
||||
{
|
||||
// Passing the closeTabCommand is necessary because I haven't figured out how to bind the main window's
|
||||
// context menu items for the tab to the main window's datacontext yet. RelativeSource doesn't seem to work
|
||||
// because the popup is it's own window. Refactor if a better solution is found.
|
||||
|
||||
public interface ITabFactory
|
||||
{
|
||||
ITab CreateSubscriberTab(ICommand closeTabCommand, ISubscriber subscriber);
|
||||
ITab CreatePublisherTab(ICommand closeTabCommand, IConnection connection);
|
||||
}
|
||||
}
|
21
PettingZoo/UI/Tab/Publisher/PublisherView.xaml
Normal file
@ -0,0 +1,21 @@
|
||||
<UserControl x:Class="PettingZoo.UI.Tab.Publisher.PublisherView"
|
||||
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:res="clr-namespace:PettingZoo.UI.Tab.Publisher"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="450"
|
||||
d:DesignWidth="800"
|
||||
d:DataContext="{d:DesignInstance res:DesignTimePublisherViewModel, IsDesignTimeCreatable=True}"
|
||||
Background="White">
|
||||
<StackPanel Margin="4">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Text="{x:Static res:PublisherViewStrings.LabelMessageType}" Style="{StaticResource TypeSelection}" />
|
||||
<RadioButton Content="{x:Static res:PublisherViewStrings.OptionMessageTypeRaw}" Style="{StaticResource TypeSelection}" IsChecked="{Binding MessageTypeRaw}" />
|
||||
<RadioButton Content="{x:Static res:PublisherViewStrings.OptionMessageTypeTapeti}" Style="{StaticResource TypeSelection}" IsChecked="{Binding MessageTypeTapeti}" />
|
||||
</StackPanel>
|
||||
|
||||
<TextBlock>TODO: implement publish forms</TextBlock>
|
||||
</StackPanel>
|
||||
</UserControl>
|
19
PettingZoo/UI/Tab/Publisher/PublisherView.xaml.cs
Normal file
@ -0,0 +1,19 @@
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace PettingZoo.UI.Tab.Publisher
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for PublisherView.xaml
|
||||
/// </summary>
|
||||
public partial class PublisherView
|
||||
{
|
||||
public PublisherView(PublisherViewModel viewModel)
|
||||
{
|
||||
InitializeComponent();
|
||||
DataContext = viewModel;
|
||||
|
||||
if (!System.ComponentModel.DesignerProperties.GetIsInDesignMode(this))
|
||||
Background = Brushes.Transparent;
|
||||
}
|
||||
}
|
||||
}
|
90
PettingZoo/UI/Tab/Publisher/PublisherViewModel.cs
Normal file
@ -0,0 +1,90 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Windows.Input;
|
||||
using PettingZoo.Core.Connection;
|
||||
|
||||
namespace PettingZoo.UI.Tab.Publisher
|
||||
{
|
||||
public enum MessageType
|
||||
{
|
||||
Raw,
|
||||
Tapeti
|
||||
}
|
||||
|
||||
|
||||
public class PublisherViewModel : BaseViewModel, ITabToolbarCommands
|
||||
{
|
||||
private readonly IConnection connection;
|
||||
|
||||
private MessageType messageType;
|
||||
|
||||
private readonly DelegateCommand publishCommand;
|
||||
private readonly TabToolbarCommand[] toolbarCommands;
|
||||
|
||||
|
||||
public MessageType MessageType
|
||||
{
|
||||
get => messageType;
|
||||
set => SetField(ref messageType, value,
|
||||
otherPropertiesChanged: new[]
|
||||
{
|
||||
nameof(MessageTypeRaw),
|
||||
nameof(MessageTypeTapeti)
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
public bool MessageTypeRaw
|
||||
{
|
||||
get => MessageType == MessageType.Raw;
|
||||
set { if (value) MessageType = MessageType.Raw; }
|
||||
}
|
||||
|
||||
public bool MessageTypeTapeti
|
||||
{
|
||||
get => MessageType == MessageType.Tapeti;
|
||||
set { if (value) MessageType = MessageType.Tapeti; }
|
||||
}
|
||||
|
||||
|
||||
public ICommand PublishCommand => publishCommand;
|
||||
|
||||
|
||||
// TODO make more dynamic, include entered routing key for example
|
||||
public string Title => "Publish";
|
||||
public IEnumerable<TabToolbarCommand> ToolbarCommands => toolbarCommands;
|
||||
|
||||
|
||||
public PublisherViewModel(IConnection connection)
|
||||
{
|
||||
this.connection = connection;
|
||||
|
||||
publishCommand = new DelegateCommand(PublishExecute, PublishCanExecute);
|
||||
|
||||
toolbarCommands = new[]
|
||||
{
|
||||
new TabToolbarCommand(PublishCommand, PublisherViewStrings.CommandPublish, SvgIconHelper.LoadFromResource("/Images/PublishSend.svg"))
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
private void PublishExecute()
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
|
||||
private bool PublishCanExecute()
|
||||
{
|
||||
// TODO validate input
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class DesignTimePublisherViewModel : PublisherViewModel
|
||||
{
|
||||
public DesignTimePublisherViewModel() : base(null!)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
99
PettingZoo/UI/Tab/Publisher/PublisherViewStrings.Designer.cs
generated
Normal file
@ -0,0 +1,99 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <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.Tab.Publisher {
|
||||
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", "16.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
public class PublisherViewStrings {
|
||||
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
|
||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||
|
||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal PublisherViewStrings() {
|
||||
}
|
||||
|
||||
/// <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.Tab.Publisher.PublisherViewStrings", typeof(PublisherViewStrings).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 Publish.
|
||||
/// </summary>
|
||||
public static string CommandPublish {
|
||||
get {
|
||||
return ResourceManager.GetString("CommandPublish", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Message type: .
|
||||
/// </summary>
|
||||
public static string LabelMessageType {
|
||||
get {
|
||||
return ResourceManager.GetString("LabelMessageType", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Raw.
|
||||
/// </summary>
|
||||
public static string OptionMessageTypeRaw {
|
||||
get {
|
||||
return ResourceManager.GetString("OptionMessageTypeRaw", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Tapeti.
|
||||
/// </summary>
|
||||
public static string OptionMessageTypeTapeti {
|
||||
get {
|
||||
return ResourceManager.GetString("OptionMessageTypeTapeti", resourceCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
132
PettingZoo/UI/Tab/Publisher/PublisherViewStrings.resx
Normal file
@ -0,0 +1,132 @@
|
||||
<?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="CommandPublish" xml:space="preserve">
|
||||
<value>Publish</value>
|
||||
</data>
|
||||
<data name="LabelMessageType" xml:space="preserve">
|
||||
<value>Message type: </value>
|
||||
</data>
|
||||
<data name="OptionMessageTypeRaw" xml:space="preserve">
|
||||
<value>Raw</value>
|
||||
</data>
|
||||
<data name="OptionMessageTypeTapeti" xml:space="preserve">
|
||||
<value>Tapeti</value>
|
||||
</data>
|
||||
</root>
|
69
PettingZoo/UI/Tab/Subscriber/SubscriberView.xaml
Normal file
@ -0,0 +1,69 @@
|
||||
<UserControl x:Class="PettingZoo.UI.Tab.Subscriber.SubscriberView"
|
||||
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:res="clr-namespace:PettingZoo.UI.Tab.Subscriber"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="450"
|
||||
d:DesignWidth="800"
|
||||
d:DataContext="{d:DesignInstance res:DesignTimeSubscriberViewModel, IsDesignTimeCreatable=True}"
|
||||
Background="White">
|
||||
<Grid Margin="4">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<ListBox Grid.Column="0" Grid.Row="0"
|
||||
HorizontalAlignment="Stretch"
|
||||
ItemsSource="{Binding Messages}"
|
||||
SelectedItem="{Binding Path=SelectedMessage, Mode=TwoWay}"
|
||||
ui:ListBox.AutoScroll="True">
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" MinWidth="150"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Grid.Column="0" Grid.Row="0" Text="{Binding Timestamp, StringFormat=g}" Style="{StaticResource Timestamp}"></TextBlock>
|
||||
<TextBlock Grid.Column="1" Grid.Row="0" Text="{Binding RoutingKey}" Style="{StaticResource Routingkey}"></TextBlock>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ListBox.ItemTemplate>
|
||||
</ListBox>
|
||||
<GridSplitter Width="5" Grid.Column="1" Grid.Row="0" HorizontalAlignment="Stretch"/>
|
||||
<Grid Grid.Column="2" Grid.Row="0">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="200"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Border Grid.Column="0" Grid.Row="0" Style="{StaticResource SidePanel}">
|
||||
<DockPanel>
|
||||
<Label DockPanel.Dock="Top" Style="{StaticResource HeaderLabel}" Content="{x:Static res:SubscriberViewStrings.PanelTitleBody}"/>
|
||||
<TextBox
|
||||
Text="{Binding SelectedMessageBody, Mode=OneWay}"
|
||||
BorderThickness="0"
|
||||
IsReadOnly="True"
|
||||
HorizontalScrollBarVisibility="Auto"
|
||||
VerticalScrollBarVisibility="Auto"/>
|
||||
</DockPanel>
|
||||
</Border>
|
||||
<GridSplitter HorizontalAlignment="Stretch" Grid.Column="0" Grid.Row="1" Height="5" ResizeDirection="Rows"/>
|
||||
<Border Grid.Column="0" Grid.Row="2" Style="{StaticResource SidePanel}">
|
||||
<DockPanel>
|
||||
<Label DockPanel.Dock="Top" Style="{StaticResource HeaderLabel}" Content="{x:Static res:SubscriberViewStrings.PanelTitleProperties}"/>
|
||||
<DataGrid ItemsSource="{Binding SelectedMessageProperties}" AutoGenerateColumns="False" IsReadOnly="True" Style="{StaticResource Properties}">
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn Binding="{Binding Key}" Header="{x:Static res:SubscriberViewStrings.PropertyName}" Width="100"/>
|
||||
<DataGridTextColumn Binding="{Binding Value}" Header="{x:Static res:SubscriberViewStrings.PropertyValue}" Width="*"/>
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
</DockPanel>
|
||||
</Border>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</UserControl>
|
21
PettingZoo/UI/Tab/Subscriber/SubscriberView.xaml.cs
Normal file
@ -0,0 +1,21 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace PettingZoo.UI.Tab.Subscriber
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for SubscriberView.xaml
|
||||
/// </summary>
|
||||
public partial class SubscriberView
|
||||
{
|
||||
public SubscriberView(SubscriberViewModel viewModel)
|
||||
{
|
||||
InitializeComponent();
|
||||
DataContext = viewModel;
|
||||
|
||||
|
||||
if (!System.ComponentModel.DesignerProperties.GetIsInDesignMode(this))
|
||||
Background = Brushes.Transparent;
|
||||
}
|
||||
}
|
||||
}
|
131
PettingZoo/UI/Tab/Subscriber/SubscriberViewModel.cs
Normal file
@ -0,0 +1,131 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using PettingZoo.Core.Connection;
|
||||
using PettingZoo.Core.Rendering;
|
||||
using PettingZoo.RabbitMQ;
|
||||
|
||||
// TODO update title with unread message count if tab is not active
|
||||
|
||||
namespace PettingZoo.UI.Tab.Subscriber
|
||||
{
|
||||
public class SubscriberViewModel : BaseViewModel, ITabToolbarCommands
|
||||
{
|
||||
private readonly ISubscriber subscriber;
|
||||
private readonly TaskScheduler uiScheduler;
|
||||
private MessageInfo? selectedMessage;
|
||||
private readonly DelegateCommand clearCommand;
|
||||
private readonly TabToolbarCommand[] toolbarCommands;
|
||||
|
||||
|
||||
public ICommand ClearCommand => clearCommand;
|
||||
|
||||
public ObservableCollection<MessageInfo> Messages { get; }
|
||||
|
||||
public MessageInfo? SelectedMessage
|
||||
{
|
||||
get => selectedMessage;
|
||||
set
|
||||
{
|
||||
if (value == selectedMessage)
|
||||
return;
|
||||
|
||||
selectedMessage = value;
|
||||
RaisePropertyChanged();
|
||||
RaiseOtherPropertyChanged(nameof(SelectedMessageBody));
|
||||
RaiseOtherPropertyChanged(nameof(SelectedMessageProperties));
|
||||
}
|
||||
}
|
||||
|
||||
public string SelectedMessageBody =>
|
||||
SelectedMessage != null
|
||||
? MessageBodyRenderer.Render(SelectedMessage.Body, SelectedMessage.Properties.ContentType())
|
||||
: "";
|
||||
|
||||
public IDictionary<string, string>? SelectedMessageProperties => SelectedMessage?.Properties;
|
||||
|
||||
public string Title => $"{subscriber.Exchange} - {subscriber.RoutingKey}";
|
||||
public IEnumerable<TabToolbarCommand> ToolbarCommands => toolbarCommands;
|
||||
|
||||
|
||||
public SubscriberViewModel(ISubscriber subscriber)
|
||||
{
|
||||
this.subscriber = subscriber;
|
||||
|
||||
uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
|
||||
|
||||
Messages = new ObservableCollection<MessageInfo>();
|
||||
clearCommand = new DelegateCommand(ClearExecute, ClearCanExecute);
|
||||
|
||||
toolbarCommands = new[]
|
||||
{
|
||||
new TabToolbarCommand(ClearCommand, SubscriberViewStrings.CommandClear, SvgIconHelper.LoadFromResource("/Images/Clear.svg"))
|
||||
};
|
||||
|
||||
subscriber.MessageReceived += SubscriberMessageReceived;
|
||||
subscriber.Start();
|
||||
}
|
||||
|
||||
|
||||
private void ClearExecute()
|
||||
{
|
||||
Messages.Clear();
|
||||
clearCommand.RaiseCanExecuteChanged();
|
||||
}
|
||||
|
||||
|
||||
private bool ClearCanExecute()
|
||||
{
|
||||
return Messages.Count > 0;
|
||||
}
|
||||
|
||||
|
||||
private void SubscriberMessageReceived(object? sender, MessageReceivedEventArgs args)
|
||||
{
|
||||
RunFromUiScheduler(() =>
|
||||
{
|
||||
Messages.Add(args.MessageInfo);
|
||||
clearCommand.RaiseCanExecuteChanged();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private void RunFromUiScheduler(Action action)
|
||||
{
|
||||
_ = Task.Factory.StartNew(action, CancellationToken.None, TaskCreationOptions.None, uiScheduler);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
public class DesignTimeSubscriberViewModel : SubscriberViewModel
|
||||
{
|
||||
public DesignTimeSubscriberViewModel() : base(new DesignTimeSubscriber())
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
private class DesignTimeSubscriber : ISubscriber
|
||||
{
|
||||
public ValueTask DisposeAsync()
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
|
||||
public string Exchange { get; } = "dummy";
|
||||
public string RoutingKey { get; } = "dummy";
|
||||
|
||||
#pragma warning disable CS0067
|
||||
public event EventHandler<MessageReceivedEventArgs>? MessageReceived;
|
||||
#pragma warning restore CS0067
|
||||
|
||||
public void Start()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
126
PettingZoo/UI/Tab/Subscriber/SubscriberViewStrings.Designer.cs
generated
Normal file
@ -0,0 +1,126 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <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.Tab.Subscriber {
|
||||
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", "16.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
public class SubscriberViewStrings {
|
||||
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
|
||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||
|
||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal SubscriberViewStrings() {
|
||||
}
|
||||
|
||||
/// <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.Tab.Subscriber.SubscriberViewStrings", typeof(SubscriberViewStrings).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 Clear.
|
||||
/// </summary>
|
||||
public static string CommandClear {
|
||||
get {
|
||||
return ResourceManager.GetString("CommandClear", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Non-persistent.
|
||||
/// </summary>
|
||||
public static string DeliveryModeNonPersistent {
|
||||
get {
|
||||
return ResourceManager.GetString("DeliveryModeNonPersistent", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Persistent.
|
||||
/// </summary>
|
||||
public static string DeliveryModePersistent {
|
||||
get {
|
||||
return ResourceManager.GetString("DeliveryModePersistent", 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
141
PettingZoo/UI/Tab/Subscriber/SubscriberViewStrings.resx
Normal file
@ -0,0 +1,141 @@
|
||||
<?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="CommandClear" xml:space="preserve">
|
||||
<value>Clear</value>
|
||||
</data>
|
||||
<data name="DeliveryModeNonPersistent" xml:space="preserve">
|
||||
<value>Non-persistent</value>
|
||||
</data>
|
||||
<data name="DeliveryModePersistent" xml:space="preserve">
|
||||
<value>Persistent</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>
|
||||
</root>
|
51
PettingZoo/UI/Tab/ViewTab.cs
Normal file
@ -0,0 +1,51 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace PettingZoo.UI.Tab
|
||||
{
|
||||
public class ViewTab<TView, TViewModel> : ITab where TView : ContentControl where TViewModel : INotifyPropertyChanged
|
||||
{
|
||||
public string Title => getTitle(viewModel);
|
||||
public ContentControl Content { get; }
|
||||
public ICommand CloseTabCommand { get; }
|
||||
|
||||
public IEnumerable<TabToolbarCommand> ToolbarCommands => viewModel is ITabToolbarCommands tabToolbarCommands
|
||||
? tabToolbarCommands.ToolbarCommands
|
||||
: Enumerable.Empty<TabToolbarCommand>();
|
||||
|
||||
public event PropertyChangedEventHandler? PropertyChanged;
|
||||
|
||||
|
||||
private readonly TViewModel viewModel;
|
||||
private readonly Func<TViewModel, string> getTitle;
|
||||
|
||||
|
||||
public ViewTab(ICommand closeTabCommand, TView view, TViewModel viewModel, Expression<Func<TViewModel, string>> title)
|
||||
{
|
||||
if (title.Body is not MemberExpression titleMemberExpression)
|
||||
throw new ArgumentException(@"Invalid expression type, expected viewModel => viewModel.TitlePropertyName", nameof(title));
|
||||
|
||||
var titlePropertyName = titleMemberExpression.Member.Name;
|
||||
|
||||
CloseTabCommand = closeTabCommand;
|
||||
this.viewModel = viewModel;
|
||||
getTitle = title.Compile();
|
||||
Content = view;
|
||||
|
||||
|
||||
viewModel.PropertyChanged += (_, args) =>
|
||||
{
|
||||
if (args.PropertyName == titlePropertyName)
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Title)));
|
||||
|
||||
else if (args.PropertyName == nameof(ToolbarCommands))
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ToolbarCommands)));
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
31
PettingZoo/UI/Tab/ViewTabFactory.cs
Normal file
@ -0,0 +1,31 @@
|
||||
using System.Windows.Input;
|
||||
using PettingZoo.Core.Connection;
|
||||
using PettingZoo.UI.Tab.Publisher;
|
||||
using PettingZoo.UI.Tab.Subscriber;
|
||||
|
||||
namespace PettingZoo.UI.Tab
|
||||
{
|
||||
public class ViewTabFactory : ITabFactory
|
||||
{
|
||||
public ITab CreateSubscriberTab(ICommand closeTabCommand, ISubscriber subscriber)
|
||||
{
|
||||
var viewModel = new SubscriberViewModel(subscriber);
|
||||
return new ViewTab<SubscriberView, SubscriberViewModel>(
|
||||
closeTabCommand,
|
||||
new SubscriberView(viewModel),
|
||||
viewModel,
|
||||
vm => vm.Title);
|
||||
}
|
||||
|
||||
|
||||
public ITab CreatePublisherTab(ICommand closeTabCommand, IConnection connection)
|
||||
{
|
||||
var viewModel = new PublisherViewModel(connection);
|
||||
return new ViewTab<PublisherView, PublisherViewModel>(
|
||||
closeTabCommand,
|
||||
new PublisherView(viewModel),
|
||||
viewModel,
|
||||
vm => vm.Title);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,54 +0,0 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Windows;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("Petting Zoo")]
|
||||
[assembly: AssemblyDescription("Petting Zoo - a RabbitMQ message viewer")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("X²Software")]
|
||||
[assembly: AssemblyProduct("Petting Zoo")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2016")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
//In order to begin building localizable applications, set
|
||||
//<UICulture>CultureYouAreCodingWith</UICulture> in your .csproj file
|
||||
//inside a <PropertyGroup>. For example, if you are using US english
|
||||
//in your source files, set the <UICulture> to en-US. Then uncomment
|
||||
//the NeutralResourceLanguage attribute below. Update the "en-US" in
|
||||
//the line below to match the UICulture setting in the project file.
|
||||
|
||||
//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
|
||||
|
||||
|
||||
[assembly: ThemeInfo(
|
||||
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
|
||||
//(used if a resource is not found in the page,
|
||||
// or application resource dictionaries)
|
||||
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
|
||||
//(used if a resource is not found in the page,
|
||||
// app, or any theme specific resource dictionaries)
|
||||
)]
|
||||
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
[assembly: GuidAttribute("1739c968-ca2a-4293-b487-bfa193a7caf3")]
|
@ -1,72 +0,0 @@
|
||||
using System.Windows;
|
||||
using System.Windows.Input;
|
||||
using AutoMapper;
|
||||
using PettingZoo.Model;
|
||||
using PettingZoo.ViewModel;
|
||||
|
||||
namespace PettingZoo.View
|
||||
{
|
||||
public class WindowConnectionInfoBuilder : IConnectionInfoBuilder
|
||||
{
|
||||
private readonly UserSettings userSettings;
|
||||
|
||||
private static readonly IMapper ConnectionInfoMapper = new MapperConfiguration(cfg =>
|
||||
{
|
||||
cfg.RecognizeDestinationPrefixes("Last");
|
||||
cfg.RecognizePrefixes("Last");
|
||||
|
||||
cfg.CreateMap<ConnectionInfo, ConnectionWindowSettings>().ReverseMap();
|
||||
}).CreateMapper();
|
||||
|
||||
|
||||
public WindowConnectionInfoBuilder(UserSettings userSettings)
|
||||
{
|
||||
this.userSettings = userSettings;
|
||||
}
|
||||
|
||||
|
||||
public ConnectionInfo Build()
|
||||
{
|
||||
var connectionInfo = ConnectionInfoMapper.Map<ConnectionInfo>(userSettings.ConnectionWindow);
|
||||
var viewModel = new ConnectionViewModel(connectionInfo);
|
||||
|
||||
var dialog = new ConnectionWindow(viewModel)
|
||||
{
|
||||
Owner = Application.Current.MainWindow
|
||||
};
|
||||
|
||||
viewModel.CloseWindow += (sender, args) =>
|
||||
{
|
||||
dialog.DialogResult = true;
|
||||
};
|
||||
|
||||
connectionInfo = dialog.ShowDialog().GetValueOrDefault() ? viewModel.ToModel() : null;
|
||||
if (connectionInfo != null)
|
||||
{
|
||||
ConnectionInfoMapper.Map(connectionInfo, userSettings.ConnectionWindow);
|
||||
userSettings.Save();
|
||||
}
|
||||
|
||||
return connectionInfo;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public partial class ConnectionWindow
|
||||
{
|
||||
public ConnectionWindow(ConnectionViewModel viewModel)
|
||||
{
|
||||
WindowStartupLocation = WindowStartupLocation.CenterOwner;
|
||||
|
||||
InitializeComponent();
|
||||
DataContext = viewModel;
|
||||
}
|
||||
|
||||
|
||||
private void NumericPreviewTextInput(object sender, TextCompositionEventArgs args)
|
||||
{
|
||||
if (!char.IsDigit(args.Text, args.Text.Length - 1))
|
||||
args.Handled = true;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,100 +0,0 @@
|
||||
<Window x:Class="PettingZoo.View.MainWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:res="clr-namespace:PettingZoo.Properties"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:viewModel="clr-namespace:PettingZoo.ViewModel"
|
||||
xmlns:infrastructure="clr-namespace:PettingZoo.Infrastructure"
|
||||
mc:Ignorable="d"
|
||||
d:DataContext="{d:DesignInstance viewModel:MainViewModel}"
|
||||
Width="800"
|
||||
Height="600"
|
||||
ResizeMode="CanResizeWithGrip"
|
||||
Style="{StaticResource WindowStyle}"
|
||||
Title="{x:Static res:Resources.MainWindowTitle}">
|
||||
<DockPanel>
|
||||
<ToolBar DockPanel.Dock="Top" ToolBarTray.IsLocked="True">
|
||||
<Button Command="{Binding ConnectCommand}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Image Source="{StaticResource ConnectIcon}" Width="16" Height="16" Style="{StaticResource ToolbarIcon}"/>
|
||||
<TextBlock Margin="3,0,0,0">Connect</TextBlock>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
<Button Command="{Binding DisconnectCommand}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Image Source="{StaticResource DisconnectIcon}" Width="16" Height="16" Style="{StaticResource ToolbarIcon}"/>
|
||||
<TextBlock Margin="3,0,0,0">Disconnect</TextBlock>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
<Separator Style="{StaticResource {x:Static ToolBar.SeparatorStyleKey}}" />
|
||||
<Button Command="{Binding ClearCommand}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Image Source="{StaticResource ClearIcon}" Width="16" Height="16" Style="{StaticResource ToolbarIcon}"/>
|
||||
<TextBlock Margin="3,0,0,0">Clear messages</TextBlock>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
</ToolBar>
|
||||
<StatusBar DockPanel.Dock="Bottom">
|
||||
<StatusBarItem>
|
||||
<TextBlock Text="{Binding ConnectionStatus}"/>
|
||||
</StatusBarItem>
|
||||
</StatusBar>
|
||||
<Grid Margin="4">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<ListBox Grid.Column="0" Grid.Row="0"
|
||||
HorizontalAlignment="Stretch"
|
||||
ItemsSource="{Binding Messages}"
|
||||
SelectedItem="{Binding Path=SelectedMessage, Mode=TwoWay}"
|
||||
infrastructure:ListBox.AutoScroll="True">
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" MinWidth="150"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Grid.Column="0" Grid.Row="0" Text="{Binding Timestamp, StringFormat=g}" Style="{StaticResource Timestamp}"></TextBlock>
|
||||
<TextBlock Grid.Column="1" Grid.Row="0" Text="{Binding RoutingKey}" Style="{StaticResource Routingkey}"></TextBlock>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ListBox.ItemTemplate>
|
||||
</ListBox>
|
||||
<GridSplitter Width="5" Grid.Column="1" Grid.Row="0" HorizontalAlignment="Stretch"/>
|
||||
<Grid Grid.Column="2" Grid.Row="0">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="200"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Border Grid.Column="0" Grid.Row="0" Style="{StaticResource SidePanel}">
|
||||
<DockPanel>
|
||||
<Label DockPanel.Dock="Top" Style="{StaticResource HeaderLabel}" Content="{x:Static res:Resources.PanelTitleBody}"/>
|
||||
<TextBox
|
||||
Text="{Binding SelectedMessageBody, Mode=OneWay}"
|
||||
BorderThickness="0"
|
||||
IsReadOnly="True"
|
||||
HorizontalScrollBarVisibility="Auto"
|
||||
VerticalScrollBarVisibility="Auto"/>
|
||||
</DockPanel>
|
||||
</Border>
|
||||
<GridSplitter HorizontalAlignment="Stretch" Grid.Column="0" Grid.Row="1" Height="5" ResizeDirection="Rows"/>
|
||||
<Border Grid.Column="0" Grid.Row="2" Style="{StaticResource SidePanel}">
|
||||
<DockPanel>
|
||||
<Label DockPanel.Dock="Top" Style="{StaticResource HeaderLabel}" Content="{x:Static res:Resources.PanelTitleProperties}"/>
|
||||
<DataGrid ItemsSource="{Binding SelectedMessageProperties}" AutoGenerateColumns="False" IsReadOnly="True" Style="{StaticResource Properties}">
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn Binding="{Binding Key}" Header="{x:Static res:Resources.PropertyName}" Width="100"/>
|
||||
<DataGridTextColumn Binding="{Binding Value}" Header="{x:Static res:Resources.PropertyValue}" Width="*"/>
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
</DockPanel>
|
||||
</Border>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</DockPanel>
|
||||
</Window>
|
@ -1,27 +0,0 @@
|
||||
using System;
|
||||
using System.Windows;
|
||||
using PettingZoo.ViewModel;
|
||||
|
||||
namespace PettingZoo.View
|
||||
{
|
||||
public partial class MainWindow
|
||||
{
|
||||
public MainWindow(MainViewModel viewModel)
|
||||
{
|
||||
WindowStartupLocation = WindowStartupLocation.CenterScreen;
|
||||
|
||||
InitializeComponent();
|
||||
DataContext = viewModel;
|
||||
|
||||
Dispatcher.ShutdownStarted += OnDispatcherShutDownStarted;
|
||||
}
|
||||
|
||||
|
||||
private void OnDispatcherShutDownStarted(object sender, EventArgs e)
|
||||
{
|
||||
var disposable = DataContext as IDisposable;
|
||||
if (!ReferenceEquals(null, disposable))
|
||||
disposable.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,64 +0,0 @@
|
||||
using System;
|
||||
using System.Windows.Input;
|
||||
using AutoMapper;
|
||||
using PettingZoo.Infrastructure;
|
||||
using PettingZoo.Model;
|
||||
|
||||
namespace PettingZoo.ViewModel
|
||||
{
|
||||
public class ConnectionViewModel : BaseViewModel
|
||||
{
|
||||
private static readonly IMapper ModelMapper = new MapperConfiguration(cfg =>
|
||||
cfg.CreateMap<ConnectionInfo, ConnectionViewModel>().ReverseMap()
|
||||
).CreateMapper();
|
||||
|
||||
|
||||
private readonly DelegateCommand okCommand;
|
||||
|
||||
|
||||
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; }
|
||||
|
||||
|
||||
public ICommand OkCommand { get { return okCommand; } }
|
||||
|
||||
public event EventHandler CloseWindow;
|
||||
|
||||
|
||||
public ConnectionViewModel()
|
||||
{
|
||||
okCommand = new DelegateCommand(OkExecute, OkCanExecute);
|
||||
}
|
||||
|
||||
|
||||
public ConnectionViewModel(ConnectionInfo model) : this()
|
||||
{
|
||||
ModelMapper.Map(model, this);
|
||||
}
|
||||
|
||||
|
||||
public ConnectionInfo ToModel()
|
||||
{
|
||||
return ModelMapper.Map<ConnectionInfo>(this);
|
||||
}
|
||||
|
||||
|
||||
private void OkExecute()
|
||||
{
|
||||
if (CloseWindow != null)
|
||||
CloseWindow(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
|
||||
private bool OkCanExecute()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,210 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using PettingZoo.Infrastructure;
|
||||
using PettingZoo.Model;
|
||||
using PettingZoo.Properties;
|
||||
|
||||
namespace PettingZoo.ViewModel
|
||||
{
|
||||
public class MainViewModel : BaseViewModel, IDisposable
|
||||
{
|
||||
private readonly TaskScheduler uiScheduler;
|
||||
private readonly IConnectionInfoBuilder connectionInfoBuilder;
|
||||
private readonly IConnectionFactory connectionFactory;
|
||||
|
||||
private ConnectionInfo connectionInfo;
|
||||
private IConnection connection;
|
||||
private string connectionStatus;
|
||||
private readonly ObservableCollection<MessageInfo> messages;
|
||||
private MessageInfo selectedMessage;
|
||||
|
||||
private readonly DelegateCommand connectCommand;
|
||||
private readonly DelegateCommand disconnectCommand;
|
||||
private readonly DelegateCommand clearCommand;
|
||||
|
||||
|
||||
public ConnectionInfo ConnectionInfo {
|
||||
get { return connectionInfo; }
|
||||
private set
|
||||
{
|
||||
if (value == connectionInfo)
|
||||
return;
|
||||
|
||||
connectionInfo = value;
|
||||
RaisePropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public string ConnectionStatus
|
||||
{
|
||||
get { return connectionStatus; }
|
||||
private set
|
||||
{
|
||||
if (value == connectionStatus)
|
||||
return;
|
||||
|
||||
connectionStatus = value;
|
||||
RaisePropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public ObservableCollection<MessageInfo> Messages { get { return messages; } }
|
||||
|
||||
public MessageInfo SelectedMessage
|
||||
{
|
||||
get { return selectedMessage; }
|
||||
set
|
||||
{
|
||||
if (value == selectedMessage)
|
||||
return;
|
||||
|
||||
selectedMessage = value;
|
||||
RaisePropertyChanged();
|
||||
RaiseOtherPropertyChanged("SelectedMessageBody");
|
||||
RaiseOtherPropertyChanged("SelectedMessageProperties");
|
||||
}
|
||||
}
|
||||
|
||||
public string SelectedMessageBody
|
||||
{
|
||||
get
|
||||
{
|
||||
return SelectedMessage != null
|
||||
? MessageBodyRenderer.Render(SelectedMessage.Body, SelectedMessage.ContentType)
|
||||
: "";
|
||||
}
|
||||
}
|
||||
|
||||
public Dictionary<string, string> SelectedMessageProperties
|
||||
{
|
||||
get { return SelectedMessage != null ? SelectedMessage.Properties : null; }
|
||||
}
|
||||
|
||||
public ICommand ConnectCommand { get { return connectCommand; } }
|
||||
public ICommand DisconnectCommand { get { return disconnectCommand; } }
|
||||
public ICommand ClearCommand { get { return clearCommand; } }
|
||||
|
||||
|
||||
public MainViewModel(IConnectionInfoBuilder connectionInfoBuilder, IConnectionFactory connectionFactory)
|
||||
{
|
||||
uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
|
||||
|
||||
this.connectionInfoBuilder = connectionInfoBuilder;
|
||||
this.connectionFactory = connectionFactory;
|
||||
|
||||
connectionStatus = GetConnectionStatus(null);
|
||||
messages = new ObservableCollection<MessageInfo>();
|
||||
|
||||
connectCommand = new DelegateCommand(ConnectExecute);
|
||||
disconnectCommand = new DelegateCommand(DisconnectExecute, DisconnectCanExecute);
|
||||
clearCommand = new DelegateCommand(ClearExecute, ClearCanExecute);
|
||||
}
|
||||
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (connection != null)
|
||||
{
|
||||
connection.Dispose();
|
||||
connection = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void ConnectExecute()
|
||||
{
|
||||
var newInfo = connectionInfoBuilder.Build();
|
||||
if (newInfo == null)
|
||||
return;
|
||||
|
||||
if (connection != null)
|
||||
connection.Dispose();
|
||||
|
||||
ConnectionInfo = newInfo;
|
||||
connection = connectionFactory.CreateConnection(connectionInfo);
|
||||
connection.MessageReceived += ConnectionMessageReceived;
|
||||
connection.StatusChanged += ConnectionStatusChanged;
|
||||
|
||||
disconnectCommand.RaiseCanExecuteChanged();
|
||||
}
|
||||
|
||||
|
||||
private void DisconnectExecute()
|
||||
{
|
||||
if (connection != null)
|
||||
{
|
||||
connection.Dispose();
|
||||
connection = null;
|
||||
}
|
||||
|
||||
ConnectionInfo = null;
|
||||
ConnectionStatus = GetConnectionStatus(null);
|
||||
|
||||
disconnectCommand.RaiseCanExecuteChanged();
|
||||
}
|
||||
|
||||
|
||||
private bool DisconnectCanExecute()
|
||||
{
|
||||
return connection != null;
|
||||
}
|
||||
|
||||
|
||||
private void ClearExecute()
|
||||
{
|
||||
messages.Clear();
|
||||
clearCommand.RaiseCanExecuteChanged();
|
||||
}
|
||||
|
||||
|
||||
private bool ClearCanExecute()
|
||||
{
|
||||
return messages.Count > 0;
|
||||
}
|
||||
|
||||
|
||||
private void ConnectionStatusChanged(object sender, StatusChangedEventArgs args)
|
||||
{
|
||||
ConnectionStatus = GetConnectionStatus(args);
|
||||
}
|
||||
|
||||
|
||||
private void ConnectionMessageReceived(object sender, MessageReceivedEventArgs args)
|
||||
{
|
||||
RunFromUiScheduler(() =>
|
||||
{
|
||||
messages.Add(args.MessageInfo);
|
||||
clearCommand.RaiseCanExecuteChanged();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private string GetConnectionStatus(StatusChangedEventArgs args)
|
||||
{
|
||||
if (args != null)
|
||||
switch (args.Status)
|
||||
{
|
||||
case Model.ConnectionStatus.Connecting:
|
||||
return String.Format(Resources.StatusConnecting, args.Context);
|
||||
|
||||
case Model.ConnectionStatus.Connected:
|
||||
return String.Format(Resources.StatusConnected, args.Context);
|
||||
|
||||
case Model.ConnectionStatus.Error:
|
||||
return String.Format(Resources.StatusError, args.Context);
|
||||
}
|
||||
|
||||
return Resources.StatusDisconnected;
|
||||
}
|
||||
|
||||
|
||||
private void RunFromUiScheduler(Action action)
|
||||
{
|
||||
Task.Factory.StartNew(action, CancellationToken.None, TaskCreationOptions.None, uiScheduler);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="AutoMapper" version="4.2.1" targetFramework="net45" />
|
||||
<package id="Newtonsoft.Json" version="8.0.3" targetFramework="net45" />
|
||||
<package id="RabbitMQ.Client" version="3.6.2" targetFramework="net45" />
|
||||
<package id="SimpleInjector" version="3.1.5" targetFramework="net45" />
|
||||
</packages>
|