1
0
mirror of synced 2024-11-23 21:13:50 +00:00

Merge branch 'release/1.4'

This commit is contained in:
Mark van Renswoude 2024-11-04 10:34:39 +01:00
commit 948f2ec118
77 changed files with 2183 additions and 767 deletions

View File

@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.14.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,138 @@
using System.Text;
using System.Text.Json;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace PettingZoo.Benchmark
{
/*
Small JSON
| Method | Mean | Error | StdDev | Gen 0 | Allocated |
|----------------- |----------:|----------:|----------:|-------:|----------:|
| TestJsonConvert | 13.226 us | 0.1808 us | 0.1603 us | 3.6316 | 15 KB |
| TestJTokenParse | 12.360 us | 0.2453 us | 0.5010 us | 3.6011 | 15 KB |
| TestReaderWriter | 6.398 us | 0.1260 us | 0.1294 us | 2.0294 | 8 KB |
| TestJsonDocument | 4.400 us | 0.0758 us | 0.0902 us | 2.1019 | 9 KB |
Large JSON (25 MB)
| Method | Mean | Error | StdDev | Gen 0 | Gen 1 | Gen 2 | Allocated |
|----------------- |-----------:|---------:|---------:|-----------:|-----------:|----------:|----------:|
| TestJsonConvert | 1,331.6 ms | 20.36 ms | 19.05 ms | 57000.0000 | 21000.0000 | 3000.0000 | 390 MB |
| TestJTokenParse | 1,411.0 ms | 27.28 ms | 24.18 ms | 62000.0000 | 23000.0000 | 4000.0000 | 415 MB |
| TestReaderWriter | 298.6 ms | 5.89 ms | 9.34 ms | 25000.0000 | 8000.0000 | 2000.0000 | 199 MB |
| TestJsonDocument | 278.5 ms | 5.29 ms | 6.30 ms | - | - | - | 246 MB |
*/
[MemoryDiagnoser]
public class JsonPrettyPrint
{
// Small Json file, which is likely to be typical for most RabbitMQ messages.
private const string Json = "{\"glossary\":{\"title\":\"example glossary\",\"GlossDiv\":{\"title\":\"S\",\"GlossList\":{\"GlossEntry\":{\"ID\":\"SGML\",\"SortAs\":\"SGML\",\"GlossTerm\":\"Standard Generalized Markup Language\",\"Acronym\":\"SGML\",\"Abbrev\":\"ISO 8879:1986\",\"GlossDef\":{\"para\":\"A meta-markup language, used to create markup languages such as DocBook.\",\"GlossSeeAlso\":[\"GML\",\"XML\"]},\"GlossSee\":\"markup\"}}}}}";
// To test with a large file instead, specify the file name here.
// For example, I've benchmarked it with this 25 MB JSON file: https://raw.githubusercontent.com/json-iterator/test-data/master/large-file.json
//private const string JsonFilename = "";
private const string JsonFilename = "D:\\Temp\\large-file.json";
private readonly string testJson;
public JsonPrettyPrint()
{
testJson = string.IsNullOrEmpty(JsonFilename)
? Json
: File.ReadAllText(JsonFilename);
}
[Benchmark]
public string TestJsonConvert()
{
var obj = JsonConvert.DeserializeObject(testJson);
return JsonConvert.SerializeObject(obj, Formatting.Indented);
}
[Benchmark]
public string TestJTokenParse()
{
var obj = JToken.Parse(testJson);
return obj.ToString(Formatting.Indented);
}
[Benchmark]
public string TestReaderWriter()
{
using var stringReader = new StringReader(testJson);
using var jsonTextReader = new JsonTextReader(stringReader);
using var stringWriter = new StringWriter();
using var jsonWriter = new JsonTextWriter(stringWriter);
jsonWriter.Formatting = Formatting.Indented;
while (jsonTextReader.Read())
jsonWriter.WriteToken(jsonTextReader);
jsonWriter.Flush();
return stringWriter.ToString();
}
[Benchmark]
public string TestJsonDocument()
{
var doc = JsonDocument.Parse(testJson);
using var stream = new MemoryStream();
var writer = new Utf8JsonWriter(stream, new JsonWriterOptions { Indented = true });
doc.WriteTo(writer);
writer.Flush();
return Encoding.UTF8.GetString(stream.ToArray());
}
}
public class Program
{
public static void Main()
{
BenchmarkRunner.Run<JsonPrettyPrint>();
// To prove they all provide the correct output
/*
var prettyPrint = new JsonPrettyPrint();
Console.WriteLine("JsonConvert");
Console.WriteLine("-----------");
Console.WriteLine(prettyPrint.TestJsonConvert());
Console.WriteLine("JToken");
Console.WriteLine("------");
Console.WriteLine(prettyPrint.TestJTokenParse());
Console.WriteLine("ReaderWriter");
Console.WriteLine("------------");
Console.WriteLine(prettyPrint.TestReaderWriter());
Console.WriteLine("JsonDocument");
Console.WriteLine("------------");
Console.WriteLine(prettyPrint.TestJsonDocument());
*/
}
}
}

View File

@ -17,5 +17,11 @@
Username = username; Username = username;
Password = password; Password = password;
} }
public override string ToString()
{
return $"{Host}:{Port}{VirtualHost}";
}
} }
} }

View File

@ -0,0 +1,100 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.Threading.Tasks;
namespace PettingZoo.Core.Connection
{
public class DynamicConnection : IConnection
{
public Guid ConnectionId => currentConnection?.ConnectionId ?? Guid.Empty;
public ConnectionParams? ConnectionParams { get; private set; }
public ConnectionStatus Status { get; private set; } = ConnectionStatus.Disconnected;
public event EventHandler<StatusChangedEventArgs>? StatusChanged;
private IConnection? currentConnection;
public async ValueTask DisposeAsync()
{
if (currentConnection != null)
await currentConnection.DisposeAsync();
GC.SuppressFinalize(this);
}
public void Connect()
{
CheckConnection();
currentConnection.Connect();
}
public async ValueTask Disconnect()
{
if (currentConnection == null)
return;
var disconnectedConnectionId = currentConnection.ConnectionId;
await currentConnection.DisposeAsync();
currentConnection = null;
ConnectionStatusChanged(this, new StatusChangedEventArgs(disconnectedConnectionId, ConnectionStatus.Disconnected));
}
public void SetConnection(IConnection connection)
{
if (currentConnection != null)
{
currentConnection.StatusChanged -= ConnectionStatusChanged;
ConnectionStatusChanged(this, new StatusChangedEventArgs(currentConnection.ConnectionId, ConnectionStatus.Disconnected));
}
currentConnection = connection;
// Assume we get the new connection before Connect is called, thus before the status changes
if (currentConnection != null)
currentConnection.StatusChanged += ConnectionStatusChanged;
}
public ISubscriber Subscribe(string exchange, string routingKey)
{
CheckConnection();
return currentConnection.Subscribe(exchange, routingKey);
}
public ISubscriber Subscribe()
{
CheckConnection();
return currentConnection.Subscribe();
}
public Task Publish(PublishMessageInfo messageInfo)
{
CheckConnection();
return currentConnection.Publish(messageInfo);
}
private void ConnectionStatusChanged(object? sender, StatusChangedEventArgs e)
{
ConnectionParams = e.ConnectionParams;
Status = e.Status;
StatusChanged?.Invoke(sender, e);
}
[MemberNotNull(nameof(currentConnection))]
private void CheckConnection()
{
if (currentConnection == null)
throw new InvalidOperationException("No current connection");
}
}
}

View File

@ -5,8 +5,15 @@ namespace PettingZoo.Core.Connection
{ {
public interface IConnection : IAsyncDisposable public interface IConnection : IAsyncDisposable
{ {
Guid ConnectionId { get; }
ConnectionParams? ConnectionParams { get; }
ConnectionStatus Status { get; }
event EventHandler<StatusChangedEventArgs> StatusChanged; event EventHandler<StatusChangedEventArgs> StatusChanged;
void Connect();
ISubscriber Subscribe(string exchange, string routingKey); ISubscriber Subscribe(string exchange, string routingKey);
ISubscriber Subscribe(); ISubscriber Subscribe();
@ -25,14 +32,18 @@ namespace PettingZoo.Core.Connection
public class StatusChangedEventArgs : EventArgs public class StatusChangedEventArgs : EventArgs
{ {
public Guid ConnectionId { get; }
public ConnectionStatus Status { get; } public ConnectionStatus Status { get; }
public string? Context { get; } public ConnectionParams? ConnectionParams { get; }
public Exception? Exception { get; }
public StatusChangedEventArgs(ConnectionStatus status, string? context) public StatusChangedEventArgs(Guid connectionId, ConnectionStatus status, ConnectionParams? connectionParams = null, Exception? exception = null)
{ {
ConnectionId = connectionId;
Status = status; Status = status;
Context = context; ConnectionParams = connectionParams;
Exception = exception;
} }
} }
} }

View File

@ -0,0 +1,201 @@
using System;
using System.Collections.Generic;
using System.Linq;
using PettingZoo.Core.Connection;
namespace PettingZoo.Core.ExportImport.Publisher
{
public enum PublisherMessageType
{
Raw,
Tapeti
}
public class PublisherMessage : IEquatable<PublisherMessage>
{
public PublisherMessageType MessageType { get; init; }
public bool SendToExchange { get; init; }
public string? Exchange { get; init; }
public string? RoutingKey { get; init; }
public string? Queue { get; init; }
public bool ReplyToNewSubscriber { get; init; }
public string? ReplyTo { get; init; }
public RawPublisherMessage? RawPublisherMessage { get; init; }
public TapetiPublisherMessage? TapetiPublisherMessage { get; init; }
public bool Equals(PublisherMessage? other)
{
if (other == null) return false;
if (ReferenceEquals(this, other)) return true;
return MessageType == other.MessageType &&
SendToExchange == other.SendToExchange &&
Exchange == other.Exchange &&
RoutingKey == other.RoutingKey &&
Queue == other.Queue &&
ReplyToNewSubscriber == other.ReplyToNewSubscriber &&
ReplyTo == other.ReplyTo &&
Equals(RawPublisherMessage, other.RawPublisherMessage) &&
Equals(TapetiPublisherMessage, other.TapetiPublisherMessage);
}
public override bool Equals(object? obj)
{
if (obj == null) return false;
if (ReferenceEquals(this, obj)) return true;
return obj is PublisherMessage publisherMessage && Equals(publisherMessage);
}
public override int GetHashCode()
{
var hashCode = new HashCode();
hashCode.Add((int)MessageType);
hashCode.Add(SendToExchange);
hashCode.Add(Exchange);
hashCode.Add(RoutingKey);
hashCode.Add(Queue);
hashCode.Add(ReplyToNewSubscriber);
hashCode.Add(ReplyTo);
hashCode.Add(RawPublisherMessage);
hashCode.Add(TapetiPublisherMessage);
return hashCode.ToHashCode();
}
}
public class RawPublisherMessage : IEquatable<RawPublisherMessage>
{
public MessageDeliveryMode DeliveryMode { get; init; }
public string? ContentType { get; init; }
public string? CorrelationId { get; init; }
public string? AppId { get; init; }
public string? ContentEncoding { get; init; }
public string? Expiration { get; init; }
public string? MessageId { get; init; }
public string? Priority { get; init; }
public string? Timestamp { get; init; }
public string? TypeProperty { get; init; }
public string? UserId { get; init; }
public string? Payload { get; init; }
public bool EnableMacros { get; init; }
public Dictionary<string, string>? Headers { get; init; }
public bool Equals(RawPublisherMessage? other)
{
if (other == null) return false;
if (ReferenceEquals(this, other)) return true;
return DeliveryMode == other.DeliveryMode &&
ContentType == other.ContentType &&
CorrelationId == other.CorrelationId &&
AppId == other.AppId &&
ContentEncoding == other.ContentEncoding &&
Expiration == other.Expiration &&
MessageId == other.MessageId &&
Priority == other.Priority &&
Timestamp == other.Timestamp &&
TypeProperty == other.TypeProperty &&
UserId == other.UserId &&
Payload == other.Payload &&
EnableMacros == other.EnableMacros &&
HeadersEquals(other.Headers);
}
private bool HeadersEquals(Dictionary<string, string>? other)
{
if (other == null)
return Headers == null || Headers.Count == 0;
if (Headers == null)
return other.Count == 0;
return other.OrderBy(h => h.Key).SequenceEqual(Headers.OrderBy(h => h.Key));
}
public override bool Equals(object? obj)
{
if (obj == null) return false;
if (ReferenceEquals(this, obj)) return true;
return obj is RawPublisherMessage rawPublisherMessage && Equals(rawPublisherMessage);
}
public override int GetHashCode()
{
var hashCode = new HashCode();
hashCode.Add((int)DeliveryMode);
hashCode.Add(ContentType);
hashCode.Add(CorrelationId);
hashCode.Add(AppId);
hashCode.Add(ContentEncoding);
hashCode.Add(Expiration);
hashCode.Add(MessageId);
hashCode.Add(Priority);
hashCode.Add(Timestamp);
hashCode.Add(TypeProperty);
hashCode.Add(UserId);
hashCode.Add(Payload);
hashCode.Add(EnableMacros);
if (Headers != null)
foreach (var (key, value) in Headers)
{
hashCode.Add(key);
hashCode.Add(value);
}
return hashCode.ToHashCode();
}
}
public class TapetiPublisherMessage : IEquatable<TapetiPublisherMessage>
{
public string? CorrelationId { get; init; }
public string? Payload { get; init; }
public bool EnableMacros { get; init; }
public string? ClassName { get; init; }
public string? AssemblyName { get; init; }
public bool Equals(TapetiPublisherMessage? other)
{
if (other == null) return false;
if (ReferenceEquals(this, other)) return true;
return CorrelationId == other.CorrelationId &&
Payload == other.Payload &&
EnableMacros == other.EnableMacros &&
ClassName == other.ClassName &&
AssemblyName == other.AssemblyName;
}
public override bool Equals(object? obj)
{
if (obj == null) return false;
if (ReferenceEquals(this, obj)) return true;
return obj is TapetiPublisherMessage tapetiPublisherMessage && Equals(tapetiPublisherMessage);
}
public override int GetHashCode()
{
return HashCode.Combine(CorrelationId, Payload, EnableMacros, ClassName, AssemblyName);
}
}
}

View File

@ -1,6 +1,6 @@
using System; using System;
namespace PettingZoo.Core.ExportImport namespace PettingZoo.Core.ExportImport.Subscriber
{ {
public abstract class BaseProgressDecorator public abstract class BaseProgressDecorator
{ {

View File

@ -1,7 +1,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
namespace PettingZoo.Core.ExportImport namespace PettingZoo.Core.ExportImport.Subscriber
{ {
public class ExportImportFormatProvider : IExportImportFormatProvider public class ExportImportFormatProvider : IExportImportFormatProvider
{ {

View File

@ -4,7 +4,7 @@ using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using PettingZoo.Core.Connection; using PettingZoo.Core.Connection;
namespace PettingZoo.Core.ExportImport namespace PettingZoo.Core.ExportImport.Subscriber
{ {
public interface IExportImportFormat public interface IExportImportFormat
{ {

View File

@ -1,6 +1,6 @@
using System.Collections.Generic; using System.Collections.Generic;
namespace PettingZoo.Core.ExportImport namespace PettingZoo.Core.ExportImport.Subscriber
{ {
public interface IExportImportFormatProvider public interface IExportImportFormatProvider
{ {

View File

@ -3,7 +3,7 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using PettingZoo.Core.Connection; using PettingZoo.Core.Connection;
namespace PettingZoo.Core.ExportImport namespace PettingZoo.Core.ExportImport.Subscriber
{ {
public class ImportSubscriber : ISubscriber public class ImportSubscriber : ISubscriber
{ {

View File

@ -2,7 +2,7 @@
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
namespace PettingZoo.Core.ExportImport namespace PettingZoo.Core.ExportImport.Subscriber
{ {
public class ListEnumerableProgressDecorator<T> : BaseProgressDecorator, IEnumerable<T> public class ListEnumerableProgressDecorator<T> : BaseProgressDecorator, IEnumerable<T>
{ {

View File

@ -3,7 +3,7 @@ using System.IO;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace PettingZoo.Core.ExportImport namespace PettingZoo.Core.ExportImport.Subscriber
{ {
public class StreamProgressDecorator : BaseProgressDecorator public class StreamProgressDecorator : BaseProgressDecorator
{ {

View File

@ -7,8 +7,8 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Serilog" Version="2.10.0" /> <PackageReference Include="Serilog" Version="4.1.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Text; using System.Text;
using Newtonsoft.Json; using Newtonsoft.Json;
@ -26,8 +27,18 @@ namespace PettingZoo.Core.Rendering
var bodyText = Encoding.UTF8.GetString(body); var bodyText = Encoding.UTF8.GetString(body);
try try
{ {
var obj = JsonConvert.DeserializeObject(bodyText); using var stringReader = new StringReader(bodyText);
return JsonConvert.SerializeObject(obj, Formatting.Indented); using var jsonTextReader = new JsonTextReader(stringReader);
using var stringWriter = new StringWriter();
using var jsonWriter = new JsonTextWriter(stringWriter);
jsonWriter.Formatting = Formatting.Indented;
while (jsonTextReader.Read())
jsonWriter.WriteToken(jsonTextReader);
jsonWriter.Flush();
return stringWriter.ToString();
} }
catch catch
{ {

View File

@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using PettingZoo.Core.ExportImport.Publisher;
namespace PettingZoo.Core.Settings
{
public interface IPublisherMessagesRepository
{
// For now read everything into memory, you need quite a few and/or huge messsages before that becomes an issue
Task<IEnumerable<StoredPublisherMessage>> GetStored();
Task<StoredPublisherMessage> Add(string displayName, PublisherMessage message);
Task<StoredPublisherMessage> Update(Guid id, string displayName, PublisherMessage message);
Task Delete(Guid id);
}
public class StoredPublisherMessage
{
public Guid Id { get; }
public string DisplayName { get; }
public PublisherMessage Message { get; }
public StoredPublisherMessage(Guid id, string displayName, PublisherMessage message)
{
Id = id;
DisplayName = displayName;
Message = message;
}
}
}

View File

@ -7,9 +7,9 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="RabbitMQ.Client" Version="6.2.2" /> <PackageReference Include="RabbitMQ.Client" Version="6.5.0" />
<PackageReference Include="Serilog" Version="2.10.0" /> <PackageReference Include="Serilog" Version="4.1.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -3,51 +3,56 @@ using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using PettingZoo.Core.Connection; using PettingZoo.Core.Connection;
using RabbitMQ.Client; using RabbitMQ.Client;
using IConnection = RabbitMQ.Client.IConnection;
namespace PettingZoo.RabbitMQ namespace PettingZoo.RabbitMQ
{ {
public class RabbitMQClientConnection : Core.Connection.IConnection public class RabbitMQClientConnection : Core.Connection.IConnection
{ {
public Guid ConnectionId { get; } = Guid.NewGuid();
public ConnectionParams? ConnectionParams { get; }
public ConnectionStatus Status { get; set; }
public event EventHandler<StatusChangedEventArgs>? StatusChanged;
private const int ConnectRetryDelay = 5000; private const int ConnectRetryDelay = 5000;
private readonly CancellationTokenSource connectionTaskToken = new(); private readonly CancellationTokenSource connectionTaskToken = new();
private readonly Task connectionTask; private Task? connectionTask;
private readonly object connectionLock = new(); private readonly object connectionLock = new();
private global::RabbitMQ.Client.IConnection? connection; private IConnection? connection;
private IModel? model;
public event EventHandler<StatusChangedEventArgs>? StatusChanged;
public RabbitMQClientConnection(ConnectionParams connectionParams) public RabbitMQClientConnection(ConnectionParams connectionParams)
{ {
connectionTask = Task.Factory.StartNew(() => TryConnection(connectionParams, connectionTaskToken.Token), CancellationToken.None); ConnectionParams = connectionParams;
} }
public async ValueTask DisposeAsync() public async ValueTask DisposeAsync()
{ {
GC.SuppressFinalize(this);
if (connectionTask == null)
return;
connectionTaskToken.Cancel(); connectionTaskToken.Cancel();
if (!connectionTask.IsCompleted) if (!connectionTask.IsCompleted)
await connectionTask; await connectionTask;
lock (connectionLock) lock (connectionLock)
{ {
if (model != null)
{
model.Dispose();
model = null;
}
if (connection != null) if (connection != null)
{ {
connection.Dispose(); connection.Dispose();
connection = null; connection = null;
} }
} }
}
GC.SuppressFinalize(this);
public void Connect()
{
connectionTask = Task.Factory.StartNew(() => TryConnection(ConnectionParams!, connectionTaskToken.Token), CancellationToken.None);
} }
@ -67,6 +72,7 @@ namespace PettingZoo.RabbitMQ
{ {
lock (connectionLock) lock (connectionLock)
{ {
var model = connection?.CreateModel();
var subscriber = new RabbitMQClientSubscriber(model, exchange, routingKey); var subscriber = new RabbitMQClientSubscriber(model, exchange, routingKey);
if (model != null) if (model != null)
return subscriber; return subscriber;
@ -79,10 +85,10 @@ namespace PettingZoo.RabbitMQ
lock (connectionLock) lock (connectionLock)
{ {
if (model == null) if (connection == null)
return; return;
subscriber.Connected(model); subscriber.Connected(connection.CreateModel());
} }
StatusChanged -= ConnectSubscriber; StatusChanged -= ConnectSubscriber;
@ -97,12 +103,30 @@ namespace PettingZoo.RabbitMQ
public Task Publish(PublishMessageInfo messageInfo) public Task Publish(PublishMessageInfo messageInfo)
{ {
if (model == null) IConnection? lockedConnection;
lock (connectionLock)
{
lockedConnection = connection;
}
if (lockedConnection == null)
throw new InvalidOperationException("Not connected"); throw new InvalidOperationException("Not connected");
using (var model = lockedConnection.CreateModel())
{
try
{
model.BasicPublish(messageInfo.Exchange, messageInfo.RoutingKey, false, model.BasicPublish(messageInfo.Exchange, messageInfo.RoutingKey, false,
RabbitMQClientPropertiesConverter.Convert(messageInfo.Properties, model.CreateBasicProperties()), RabbitMQClientPropertiesConverter.Convert(messageInfo.Properties,
model.CreateBasicProperties()),
messageInfo.Body); messageInfo.Body);
}
finally
{
model.Close();
}
}
return Task.CompletedTask; return Task.CompletedTask;
} }
@ -119,22 +143,22 @@ namespace PettingZoo.RabbitMQ
Password = connectionParams.Password Password = connectionParams.Password
}; };
var statusContext = $"{connectionParams.Host}:{connectionParams.Port}{connectionParams.VirtualHost}";
while (!cancellationToken.IsCancellationRequested) while (!cancellationToken.IsCancellationRequested)
{ {
DoStatusChanged(ConnectionStatus.Connecting, statusContext); DoStatusChanged(ConnectionStatus.Connecting);
try try
{
lock (connectionLock)
{ {
connection = factory.CreateConnection(); connection = factory.CreateConnection();
model = connection.CreateModel(); }
DoStatusChanged(ConnectionStatus.Connected, statusContext); DoStatusChanged(ConnectionStatus.Connected);
break; break;
} }
catch (Exception e) catch (Exception e)
{ {
DoStatusChanged(ConnectionStatus.Error, e.Message); DoStatusChanged(ConnectionStatus.Error, e);
try try
{ {
@ -148,9 +172,10 @@ namespace PettingZoo.RabbitMQ
} }
private void DoStatusChanged(ConnectionStatus status, string? context = null) private void DoStatusChanged(ConnectionStatus status, Exception? exception = null)
{ {
StatusChanged?.Invoke(this, new StatusChangedEventArgs(status, context)); Status = status;
StatusChanged?.Invoke(this, new StatusChangedEventArgs(ConnectionId, status, ConnectionParams, exception));
} }
} }
} }

View File

@ -0,0 +1,80 @@
using PettingZoo.Core.ExportImport.Publisher;
using PettingZoo.Core.Settings;
namespace PettingZoo.Settings.LiteDB
{
public class LiteDBPublisherMessagesRepository : BaseLiteDBRepository, IPublisherMessagesRepository
{
private const string CollectionMessages = "messages";
public LiteDBPublisherMessagesRepository() : base(@"publisherMessages")
{
}
public async Task<IEnumerable<StoredPublisherMessage>> GetStored()
{
using var database = GetDatabase();
var collection = database.GetCollection<PublisherMessageRecord>(CollectionMessages);
return (await collection.FindAllAsync())
.Select(r => new StoredPublisherMessage(r.Id, r.DisplayName, r.Message))
.ToArray();
}
public async Task<StoredPublisherMessage> Add(string displayName, PublisherMessage message)
{
using var database = GetDatabase();
var collection = database.GetCollection<PublisherMessageRecord>(CollectionMessages);
var id = Guid.NewGuid();
await collection.InsertAsync(PublisherMessageRecord.FromPublisherMessage(id, displayName, message));
return new StoredPublisherMessage(id, displayName, message);
}
public async Task<StoredPublisherMessage> Update(Guid id, string displayName, PublisherMessage message)
{
using var database = GetDatabase();
var collection = database.GetCollection<PublisherMessageRecord>(CollectionMessages);
await collection.UpdateAsync(PublisherMessageRecord.FromPublisherMessage(id, displayName, message));
return new StoredPublisherMessage(id, displayName, message);
}
public async Task Delete(Guid id)
{
using var database = GetDatabase();
var collection = database.GetCollection<PublisherMessageRecord>(CollectionMessages);
await collection.DeleteAsync(id);
}
// ReSharper disable MemberCanBePrivate.Local - for LiteDB
// ReSharper disable PropertyCanBeMadeInitOnly.Local
private class PublisherMessageRecord
{
public Guid Id { get; set; }
public string DisplayName { get; set; } = null!;
public PublisherMessage Message { get; set; } = null!;
public static PublisherMessageRecord FromPublisherMessage(Guid id, string displayName, PublisherMessage message)
{
return new PublisherMessageRecord
{
Id = id,
DisplayName = displayName,
Message = message
};
}
}
// ReSharper restore PropertyCanBeMadeInitOnly.Local
// ReSharper restore MemberCanBePrivate.Local
}
}

View File

@ -7,9 +7,9 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="LiteDB" Version="5.0.11" /> <PackageReference Include="LiteDB" Version="5.0.21" />
<PackageReference Include="LiteDB.Async" Version="0.0.11" /> <PackageReference Include="LiteDB.Async" Version="0.1.8" />
<PackageReference Include="Serilog" Version="2.10.0" /> <PackageReference Include="Serilog" Version="4.1.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -1,8 +1,8 @@
using System.Collections.Generic; using System.Collections.Generic;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using PettingZoo.Core.ExportImport; using PettingZoo.Core.ExportImport.Subscriber;
namespace PettingZoo.Tapeti.Export namespace PettingZoo.Tapeti.ExportImport
{ {
public abstract class BaseTapetiCmdExportImportFormat : IExportImportFormat public abstract class BaseTapetiCmdExportImportFormat : IExportImportFormat
{ {

View File

@ -8,10 +8,9 @@ using System.Threading.Tasks;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using PettingZoo.Core.Connection; using PettingZoo.Core.Connection;
using PettingZoo.Core.ExportImport; using PettingZoo.Core.ExportImport.Subscriber;
namespace PettingZoo.Tapeti.ExportImport
namespace PettingZoo.Tapeti.Export
{ {
public class TapetiCmdExportFormat : BaseTapetiCmdExportImportFormat, IExportFormat public class TapetiCmdExportFormat : BaseTapetiCmdExportImportFormat, IExportFormat
{ {

View File

@ -8,7 +8,7 @@
// </auto-generated> // </auto-generated>
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
namespace PettingZoo.Tapeti.Export { namespace PettingZoo.Tapeti.ExportImport {
using System; using System;
@ -39,7 +39,7 @@ namespace PettingZoo.Tapeti.Export {
internal static global::System.Resources.ResourceManager ResourceManager { internal static global::System.Resources.ResourceManager ResourceManager {
get { get {
if (object.ReferenceEquals(resourceMan, null)) { if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("PettingZoo.Tapeti.Export.TapetiCmdImportExportStrings", typeof(TapetiCmdImportExportStrings).Assembly); global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("PettingZoo.Tapeti.ExportImport.TapetiCmdImportExportStrings", typeof(TapetiCmdImportExportStrings).Assembly);
resourceMan = temp; resourceMan = temp;
} }
return resourceMan; return resourceMan;

View File

@ -7,9 +7,9 @@ using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Newtonsoft.Json; using Newtonsoft.Json;
using PettingZoo.Core.Connection; using PettingZoo.Core.Connection;
using PettingZoo.Core.ExportImport; using PettingZoo.Core.ExportImport.Subscriber;
namespace PettingZoo.Tapeti.Export namespace PettingZoo.Tapeti.ExportImport
{ {
public class TapetiCmdImportFormat : BaseTapetiCmdExportImportFormat, IImportFormat public class TapetiCmdImportFormat : BaseTapetiCmdExportImportFormat, IImportFormat
{ {

View File

@ -8,18 +8,19 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="NuGet.PackageManagement" Version="6.0.0" /> <PackageReference Include="NuGet.PackageManagement" Version="6.11.1" />
<PackageReference Include="NuGet.Packaging" Version="6.0.0" /> <PackageReference Include="NuGet.Packaging" Version="6.11.1" />
<PackageReference Include="NuGet.Protocol" Version="6.0.0" /> <PackageReference Include="NuGet.Protocol" Version="6.11.1" />
<PackageReference Include="Serilog" Version="2.10.0" /> <PackageReference Include="RabbitMQ.Client" Version="6.5.0" />
<PackageReference Include="SharpVectors" Version="1.7.7" /> <PackageReference Include="Serilog" Version="4.1.0" />
<PackageReference Include="SharpVectors" Version="1.8.4.2" />
<PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" /> <PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />
<PackageReference Include="System.Reactive" Version="5.0.0" /> <PackageReference Include="System.Reactive" Version="6.0.1" />
<PackageReference Include="System.Reflection.MetadataLoadContext" Version="6.0.0" /> <PackageReference Include="System.Reflection.MetadataLoadContext" Version="8.0.1" />
<PackageReference Include="Tapeti" Version="2.8.2" /> <PackageReference Include="Tapeti" Version="3.1.4" />
<PackageReference Include="Tapeti.Annotations" Version="3.0.0" /> <PackageReference Include="Tapeti.Annotations" Version="3.1.0" />
<PackageReference Include="Tapeti.DataAnnotations.Extensions" Version="3.0.0" /> <PackageReference Include="Tapeti.DataAnnotations.Extensions" Version="3.2.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@ -33,7 +34,7 @@
<AutoGen>True</AutoGen> <AutoGen>True</AutoGen>
<DependentUpon>AssemblyParserStrings.resx</DependentUpon> <DependentUpon>AssemblyParserStrings.resx</DependentUpon>
</Compile> </Compile>
<Compile Update="Export\TapetiCmdImportExportStrings.Designer.cs"> <Compile Update="ExportImport\TapetiCmdImportExportStrings.Designer.cs">
<DesignTime>True</DesignTime> <DesignTime>True</DesignTime>
<AutoGen>True</AutoGen> <AutoGen>True</AutoGen>
<DependentUpon>TapetiCmdImportExportStrings.resx</DependentUpon> <DependentUpon>TapetiCmdImportExportStrings.resx</DependentUpon>
@ -60,7 +61,7 @@
<Generator>ResXFileCodeGenerator</Generator> <Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>AssemblyParserStrings.Designer.cs</LastGenOutput> <LastGenOutput>AssemblyParserStrings.Designer.cs</LastGenOutput>
</EmbeddedResource> </EmbeddedResource>
<EmbeddedResource Update="Export\TapetiCmdImportExportStrings.resx"> <EmbeddedResource Update="ExportImport\TapetiCmdImportExportStrings.resx">
<Generator>ResXFileCodeGenerator</Generator> <Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>TapetiCmdImportExportStrings.Designer.cs</LastGenOutput> <LastGenOutput>TapetiCmdImportExportStrings.Designer.cs</LastGenOutput>
</EmbeddedResource> </EmbeddedResource>

View File

@ -1,325 +0,0 @@
<Project>
<PropertyGroup>
<AssemblyName>PettingZoo.Tapeti</AssemblyName>
<IntermediateOutputPath>obj\Debug\</IntermediateOutputPath>
<BaseIntermediateOutputPath>obj\</BaseIntermediateOutputPath>
<MSBuildProjectExtensionsPath>P:\Development\PettingZoo\PettingZoo.Tapeti\obj\</MSBuildProjectExtensionsPath>
<_TargetAssemblyProjectName>PettingZoo.Tapeti</_TargetAssemblyProjectName>
</PropertyGroup>
<Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk" />
<PropertyGroup>
<TargetFramework>net6.0-windows</TargetFramework>
<Version>0.1</Version>
<UseWpf>true</UseWpf>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="NuGet.PackageManagement" Version="6.0.0" />
<PackageReference Include="NuGet.Packaging" Version="6.0.0" />
<PackageReference Include="NuGet.Protocol" Version="6.0.0" />
<PackageReference Include="Serilog" Version="2.10.0" />
<PackageReference Include="SharpVectors" Version="1.7.7" />
<PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />
<PackageReference Include="System.Reactive" Version="5.0.0" />
<PackageReference Include="System.Reflection.MetadataLoadContext" Version="6.0.0" />
<PackageReference Include="Tapeti.Annotations" Version="3.0.0" />
<PackageReference Include="Tapeti.DataAnnotations.Extensions" Version="3.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\PettingZoo.Core\PettingZoo.Core.csproj" />
<ProjectReference Include="..\PettingZoo.WPF\PettingZoo.WPF.csproj" />
</ItemGroup>
<ItemGroup>
<Compile Update="AssemblyParser\AssemblyParserStrings.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>AssemblyParserStrings.resx</DependentUpon>
</Compile>
<Compile Update="Export\TapetiCmdImportExportStrings.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>TapetiCmdImportExportStrings.resx</DependentUpon>
</Compile>
<Compile Update="TapetiClassLibraryExampleGeneratorStrings.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>TapetiClassLibraryExampleGeneratorStrings.resx</DependentUpon>
</Compile>
<Compile Update="UI\ClassSelection\ClassSelectionStrings.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>ClassSelectionStrings.resx</DependentUpon>
</Compile>
<Compile Update="UI\PackageSelection\PackageSelectionStrings.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>PackageSelectionStrings.resx</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="AssemblyParser\AssemblyParserStrings.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>AssemblyParserStrings.Designer.cs</LastGenOutput>
</EmbeddedResource>
<EmbeddedResource Update="Export\TapetiCmdImportExportStrings.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>TapetiCmdImportExportStrings.Designer.cs</LastGenOutput>
</EmbeddedResource>
<EmbeddedResource Update="TapetiClassLibraryExampleGeneratorStrings.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>TapetiClassLibraryExampleGeneratorStrings.Designer.cs</LastGenOutput>
</EmbeddedResource>
<EmbeddedResource Update="UI\ClassSelection\ClassSelectionStrings.resx">
<Generator>PublicResXFileCodeGenerator</Generator>
<LastGenOutput>ClassSelectionStrings.Designer.cs</LastGenOutput>
</EmbeddedResource>
<EmbeddedResource Update="UI\PackageSelection\PackageSelectionStrings.resx">
<Generator>PublicResXFileCodeGenerator</Generator>
<LastGenOutput>PackageSelectionStrings.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\6.0.0\ref\net6.0\Accessibility.dll" />
<ReferencePath Include="C:\Users\PsychoMark\.nuget\packages\avalonedit\6.1.3.50\lib\net6.0-windows7.0\ICSharpCode.AvalonEdit.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\Microsoft.CSharp.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\Microsoft.VisualBasic.Core.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\6.0.0\ref\net6.0\Microsoft.VisualBasic.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\6.0.0\ref\net6.0\Microsoft.VisualBasic.Forms.dll" />
<ReferencePath Include="C:\Users\PsychoMark\.nuget\packages\microsoft.web.xdt\3.0.0\lib\netstandard2.0\Microsoft.Web.XmlTransform.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\Microsoft.Win32.Primitives.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\6.0.0\ref\net6.0\Microsoft.Win32.Registry.AccessControl.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\Microsoft.Win32.Registry.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\6.0.0\ref\net6.0\Microsoft.Win32.SystemEvents.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\mscorlib.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\netstandard.dll" />
<ReferencePath Include="C:\Users\PsychoMark\.nuget\packages\newtonsoft.json\13.0.1\lib\netstandard2.0\Newtonsoft.Json.dll" />
<ReferencePath Include="C:\Users\PsychoMark\.nuget\packages\nuget.commands\6.0.0\lib\net5.0\NuGet.Commands.dll" />
<ReferencePath Include="C:\Users\PsychoMark\.nuget\packages\nuget.common\6.0.0\lib\netstandard2.0\NuGet.Common.dll" />
<ReferencePath Include="C:\Users\PsychoMark\.nuget\packages\nuget.configuration\6.0.0\lib\netstandard2.0\NuGet.Configuration.dll" />
<ReferencePath Include="C:\Users\PsychoMark\.nuget\packages\nuget.credentials\6.0.0\lib\net5.0\NuGet.Credentials.dll" />
<ReferencePath Include="C:\Users\PsychoMark\.nuget\packages\nuget.dependencyresolver.core\6.0.0\lib\net5.0\NuGet.DependencyResolver.Core.dll" />
<ReferencePath Include="C:\Users\PsychoMark\.nuget\packages\nuget.frameworks\6.0.0\lib\netstandard2.0\NuGet.Frameworks.dll" />
<ReferencePath Include="C:\Users\PsychoMark\.nuget\packages\nuget.librarymodel\6.0.0\lib\netstandard2.0\NuGet.LibraryModel.dll" />
<ReferencePath Include="C:\Users\PsychoMark\.nuget\packages\nuget.packagemanagement\6.0.0\lib\netstandard2.0\NuGet.PackageManagement.dll" />
<ReferencePath Include="C:\Users\PsychoMark\.nuget\packages\nuget.packaging\6.0.0\lib\net5.0\NuGet.Packaging.dll" />
<ReferencePath Include="C:\Users\PsychoMark\.nuget\packages\nuget.projectmodel\6.0.0\lib\net5.0\NuGet.ProjectModel.dll" />
<ReferencePath Include="C:\Users\PsychoMark\.nuget\packages\nuget.protocol\6.0.0\lib\net5.0\NuGet.Protocol.dll" />
<ReferencePath Include="C:\Users\PsychoMark\.nuget\packages\nuget.resolver\6.0.0\lib\net5.0\NuGet.Resolver.dll" />
<ReferencePath Include="C:\Users\PsychoMark\.nuget\packages\nuget.versioning\6.0.0\lib\netstandard2.0\NuGet.Versioning.dll" />
<ReferencePath Include="P:\Development\PettingZoo\PettingZoo.Core\bin\Debug\net6.0\PettingZoo.Core.dll" />
<ReferencePath Include="P:\Development\PettingZoo\PettingZoo.WPF\bin\Debug\net6.0-windows\PettingZoo.WPF.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\6.0.0\ref\net6.0\PresentationCore.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\6.0.0\ref\net6.0\PresentationFramework.Aero.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\6.0.0\ref\net6.0\PresentationFramework.Aero2.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\6.0.0\ref\net6.0\PresentationFramework.AeroLite.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\6.0.0\ref\net6.0\PresentationFramework.Classic.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\6.0.0\ref\net6.0\PresentationFramework.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\6.0.0\ref\net6.0\PresentationFramework.Luna.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\6.0.0\ref\net6.0\PresentationFramework.Royale.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\6.0.0\ref\net6.0\PresentationUI.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\6.0.0\ref\net6.0\ReachFramework.dll" />
<ReferencePath Include="C:\Users\PsychoMark\.nuget\packages\serilog\2.10.0\lib\netstandard2.1\Serilog.dll" />
<ReferencePath Include="C:\Users\PsychoMark\.nuget\packages\sharpvectors\1.7.7\lib\net5.0-windows\SharpVectors.Converters.Wpf.dll" />
<ReferencePath Include="C:\Users\PsychoMark\.nuget\packages\sharpvectors\1.7.7\lib\net5.0-windows\SharpVectors.Core.dll" />
<ReferencePath Include="C:\Users\PsychoMark\.nuget\packages\sharpvectors\1.7.7\lib\net5.0-windows\SharpVectors.Css.dll" />
<ReferencePath Include="C:\Users\PsychoMark\.nuget\packages\sharpvectors\1.7.7\lib\net5.0-windows\SharpVectors.Dom.dll" />
<ReferencePath Include="C:\Users\PsychoMark\.nuget\packages\sharpvectors\1.7.7\lib\net5.0-windows\SharpVectors.Model.dll" />
<ReferencePath Include="C:\Users\PsychoMark\.nuget\packages\sharpvectors\1.7.7\lib\net5.0-windows\SharpVectors.Rendering.Gdi.dll" />
<ReferencePath Include="C:\Users\PsychoMark\.nuget\packages\sharpvectors\1.7.7\lib\net5.0-windows\SharpVectors.Rendering.Wpf.dll" />
<ReferencePath Include="C:\Users\PsychoMark\.nuget\packages\sharpvectors\1.7.7\lib\net5.0-windows\SharpVectors.Runtime.Wpf.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.AppContext.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Buffers.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\6.0.0\ref\net6.0\System.CodeDom.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Collections.Concurrent.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Collections.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Collections.Immutable.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Collections.NonGeneric.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Collections.Specialized.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.ComponentModel.Annotations.dll" />
<ReferencePath Include="C:\Users\PsychoMark\.nuget\packages\system.componentmodel.composition\4.5.0\ref\netstandard2.0\System.ComponentModel.Composition.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.ComponentModel.DataAnnotations.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.ComponentModel.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.ComponentModel.EventBasedAsync.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.ComponentModel.Primitives.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.ComponentModel.TypeConverter.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\6.0.0\ref\net6.0\System.Configuration.ConfigurationManager.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Configuration.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Console.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Core.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Data.Common.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Data.DataSetExtensions.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Data.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\6.0.0\ref\net6.0\System.Design.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Diagnostics.Contracts.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Diagnostics.Debug.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Diagnostics.DiagnosticSource.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\6.0.0\ref\net6.0\System.Diagnostics.EventLog.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Diagnostics.FileVersionInfo.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\6.0.0\ref\net6.0\System.Diagnostics.PerformanceCounter.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Diagnostics.Process.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Diagnostics.StackTrace.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Diagnostics.TextWriterTraceListener.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Diagnostics.Tools.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Diagnostics.TraceSource.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Diagnostics.Tracing.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\6.0.0\ref\net6.0\System.DirectoryServices.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\6.0.0\ref\net6.0\System.Drawing.Common.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\6.0.0\ref\net6.0\System.Drawing.Design.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\6.0.0\ref\net6.0\System.Drawing.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Drawing.Primitives.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Dynamic.Runtime.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Formats.Asn1.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Globalization.Calendars.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Globalization.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Globalization.Extensions.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.IO.Compression.Brotli.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.IO.Compression.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.IO.Compression.FileSystem.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.IO.Compression.ZipFile.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.IO.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.IO.FileSystem.AccessControl.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.IO.FileSystem.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.IO.FileSystem.DriveInfo.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.IO.FileSystem.Primitives.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.IO.FileSystem.Watcher.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.IO.IsolatedStorage.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.IO.MemoryMappedFiles.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\6.0.0\ref\net6.0\System.IO.Packaging.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.IO.Pipes.AccessControl.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.IO.Pipes.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.IO.UnmanagedMemoryStream.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Linq.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Linq.Expressions.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Linq.Parallel.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Linq.Queryable.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Memory.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Net.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Net.Http.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Net.Http.Json.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Net.HttpListener.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Net.Mail.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Net.NameResolution.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Net.NetworkInformation.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Net.Ping.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Net.Primitives.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Net.Requests.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Net.Security.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Net.ServicePoint.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Net.Sockets.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Net.WebClient.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Net.WebHeaderCollection.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Net.WebProxy.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Net.WebSockets.Client.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Net.WebSockets.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Numerics.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Numerics.Vectors.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.ObjectModel.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\6.0.0\ref\net6.0\System.Printing.dll" />
<ReferencePath Include="C:\Users\PsychoMark\.nuget\packages\system.reactive\5.0.0\lib\net5.0\System.Reactive.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Reflection.DispatchProxy.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Reflection.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Reflection.Emit.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Reflection.Emit.ILGeneration.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Reflection.Emit.Lightweight.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Reflection.Extensions.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Reflection.Metadata.dll" />
<ReferencePath Include="C:\Users\PsychoMark\.nuget\packages\system.reflection.metadataloadcontext\6.0.0\lib\net6.0\System.Reflection.MetadataLoadContext.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Reflection.Primitives.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Reflection.TypeExtensions.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\6.0.0\ref\net6.0\System.Resources.Extensions.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Resources.Reader.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Resources.ResourceManager.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Resources.Writer.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Runtime.CompilerServices.Unsafe.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Runtime.CompilerServices.VisualC.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Runtime.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Runtime.Extensions.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Runtime.Handles.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Runtime.InteropServices.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Runtime.InteropServices.RuntimeInformation.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Runtime.Intrinsics.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Runtime.Loader.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Runtime.Numerics.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Runtime.Serialization.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Runtime.Serialization.Formatters.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Runtime.Serialization.Json.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Runtime.Serialization.Primitives.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Runtime.Serialization.Xml.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Security.AccessControl.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Security.Claims.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Security.Cryptography.Algorithms.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Security.Cryptography.Cng.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Security.Cryptography.Csp.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Security.Cryptography.Encoding.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Security.Cryptography.OpenSsl.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\6.0.0\ref\net6.0\System.Security.Cryptography.Pkcs.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Security.Cryptography.Primitives.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\6.0.0\ref\net6.0\System.Security.Cryptography.ProtectedData.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Security.Cryptography.X509Certificates.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\6.0.0\ref\net6.0\System.Security.Cryptography.Xml.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Security.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\6.0.0\ref\net6.0\System.Security.Permissions.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Security.Principal.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Security.Principal.Windows.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Security.SecureString.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.ServiceModel.Web.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.ServiceProcess.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Text.Encoding.CodePages.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Text.Encoding.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Text.Encoding.Extensions.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Text.Encodings.Web.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Text.Json.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Text.RegularExpressions.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\6.0.0\ref\net6.0\System.Threading.AccessControl.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Threading.Channels.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Threading.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Threading.Overlapped.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Threading.Tasks.Dataflow.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Threading.Tasks.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Threading.Tasks.Extensions.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Threading.Tasks.Parallel.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Threading.Thread.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Threading.ThreadPool.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Threading.Timer.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Transactions.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Transactions.Local.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.ValueTuple.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Web.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Web.HttpUtility.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\6.0.0\ref\net6.0\System.Windows.Controls.Ribbon.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Windows.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\6.0.0\ref\net6.0\System.Windows.Extensions.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\6.0.0\ref\net6.0\System.Windows.Forms.Design.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\6.0.0\ref\net6.0\System.Windows.Forms.Design.Editors.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\6.0.0\ref\net6.0\System.Windows.Forms.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\6.0.0\ref\net6.0\System.Windows.Forms.Primitives.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\6.0.0\ref\net6.0\System.Windows.Input.Manipulations.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\6.0.0\ref\net6.0\System.Windows.Presentation.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\6.0.0\ref\net6.0\System.Xaml.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Xml.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Xml.Linq.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Xml.ReaderWriter.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Xml.Serialization.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Xml.XDocument.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Xml.XmlDocument.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Xml.XmlSerializer.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Xml.XPath.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Xml.XPath.XDocument.dll" />
<ReferencePath Include="C:\Users\PsychoMark\.nuget\packages\tapeti.annotations\3.0.0\lib\netstandard2.0\Tapeti.Annotations.dll" />
<ReferencePath Include="C:\Users\PsychoMark\.nuget\packages\tapeti.dataannotations.extensions\3.0.0\lib\netstandard2.0\Tapeti.DataAnnotations.Extensions.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\6.0.0\ref\net6.0\UIAutomationClient.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\6.0.0\ref\net6.0\UIAutomationClientSideProviders.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\6.0.0\ref\net6.0\UIAutomationProvider.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\6.0.0\ref\net6.0\UIAutomationTypes.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\6.0.0\ref\net6.0\WindowsBase.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\6.0.0\ref\net6.0\WindowsFormsIntegration.dll" />
</ItemGroup>
<ItemGroup>
<Compile Include="P:\Development\PettingZoo\PettingZoo.Tapeti\obj\Debug\net6.0-windows\UI\ClassSelection\ClassSelectionWindow.g.cs" />
<Compile Include="P:\Development\PettingZoo\PettingZoo.Tapeti\obj\Debug\net6.0-windows\UI\PackageSelection\PackageSelectionWindow.g.cs" />
<Compile Include="P:\Development\PettingZoo\PettingZoo.Tapeti\obj\Debug\net6.0-windows\GeneratedInternalTypeHelper.g.cs" />
</ItemGroup>
<Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" />
</Project>

View File

@ -7,11 +7,11 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="FluentAssertions" Version="6.2.0" /> <PackageReference Include="FluentAssertions" Version="6.12.1" />
<PackageReference Include="FluentAssertions.Json" Version="6.0.0" /> <PackageReference Include="FluentAssertions.Json" Version="6.1.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
<PackageReference Include="xunit" Version="2.4.1" /> <PackageReference Include="xunit" Version="2.9.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3"> <PackageReference Include="xunit.runner.visualstudio" Version="2.8.2">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>

View File

@ -0,0 +1,26 @@
using System.Windows;
using System.Windows.Controls;
namespace PettingZoo.WPF.Controls
{
public class AutoOverflowToolBar : ToolBar
{
public AutoOverflowToolBar()
{
Loaded += (_, _) =>
{
// Hide overflow arrow on the right side of the toolbar when not required
if (Template.FindName("OverflowButton", this) is not FrameworkElement overflowButton)
return;
if (!overflowButton.IsEnabled)
overflowButton.Visibility = Visibility.Hidden;
overflowButton.IsEnabledChanged += (_, _) =>
{
overflowButton.Visibility = overflowButton.IsEnabled ? Visibility.Visible : Visibility.Hidden;
};
};
}
}
}

View File

@ -7,7 +7,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="AvalonEdit" Version="6.1.3.50" /> <PackageReference Include="AvalonEdit" Version="6.3.0.90" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -0,0 +1,20 @@
using System;
using System.Globalization;
using System.Windows.Data;
namespace PettingZoo.WPF.ValueConverters
{
public class SameReferenceConverter : IMultiValueConverter
{
public object Convert(object?[] values, Type targetType, object parameter, CultureInfo culture)
{
return ReferenceEquals(values[0], values[1]);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
}

View File

@ -20,7 +20,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PettingZoo.Settings.LiteDB"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PettingZoo.WPF", "PettingZoo.WPF\PettingZoo.WPF.csproj", "{E6617B69-2AC4-4056-B801-DD32E2374B71}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PettingZoo.WPF", "PettingZoo.WPF\PettingZoo.WPF.csproj", "{E6617B69-2AC4-4056-B801-DD32E2374B71}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PettingZoo.Test", "PettingZoo.Test\PettingZoo.Test.csproj", "{3DD7F8D5-2CEE-414D-AC9C-9F395568B79F}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PettingZoo.Test", "PettingZoo.Test\PettingZoo.Test.csproj", "{3DD7F8D5-2CEE-414D-AC9C-9F395568B79F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PettingZoo.Benchmark", "PettingZoo.Benchmark\PettingZoo.Benchmark.csproj", "{C25BC83A-D302-46D2-97F6-5F888B824E2D}"
EndProject EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -56,6 +58,10 @@ Global
{3DD7F8D5-2CEE-414D-AC9C-9F395568B79F}.Debug|Any CPU.Build.0 = Debug|Any CPU {3DD7F8D5-2CEE-414D-AC9C-9F395568B79F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3DD7F8D5-2CEE-414D-AC9C-9F395568B79F}.Release|Any CPU.ActiveCfg = Release|Any CPU {3DD7F8D5-2CEE-414D-AC9C-9F395568B79F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3DD7F8D5-2CEE-414D-AC9C-9F395568B79F}.Release|Any CPU.Build.0 = Release|Any CPU {3DD7F8D5-2CEE-414D-AC9C-9F395568B79F}.Release|Any CPU.Build.0 = Release|Any CPU
{C25BC83A-D302-46D2-97F6-5F888B824E2D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C25BC83A-D302-46D2-97F6-5F888B824E2D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C25BC83A-D302-46D2-97F6-5F888B824E2D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C25BC83A-D302-46D2-97F6-5F888B824E2D}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 58 58" style="enable-background:new 0 0 58 58;" xml:space="preserve">
<g>
<g>
<polygon style="fill:#EFEBDE;" points="46.5,14 32.5,0 1.5,0 1.5,58 46.5,58 "/>
<g>
<path style="fill:#D5D0BB;" d="M11.5,23h25c0.552,0,1-0.447,1-1s-0.448-1-1-1h-25c-0.552,0-1,0.447-1,1S10.948,23,11.5,23z"/>
<path style="fill:#D5D0BB;" d="M11.5,15h10c0.552,0,1-0.447,1-1s-0.448-1-1-1h-10c-0.552,0-1,0.447-1,1S10.948,15,11.5,15z"/>
<path style="fill:#D5D0BB;" d="M36.5,29h-25c-0.552,0-1,0.447-1,1s0.448,1,1,1h25c0.552,0,1-0.447,1-1S37.052,29,36.5,29z"/>
<path style="fill:#D5D0BB;" d="M36.5,37h-25c-0.552,0-1,0.447-1,1s0.448,1,1,1h25c0.552,0,1-0.447,1-1S37.052,37,36.5,37z"/>
<path style="fill:#D5D0BB;" d="M36.5,45h-25c-0.552,0-1,0.447-1,1s0.448,1,1,1h25c0.552,0,1-0.447,1-1S37.052,45,36.5,45z"/>
</g>
<polygon style="fill:#D5D0BB;" points="32.5,0 32.5,14 46.5,14 "/>
</g>
<g>
<circle style="fill:#F29C21;" cx="44.5" cy="46" r="12"/>
<path style="fill:#FFFFFF;" d="M50.5,47h-12c-0.552,0-1-0.448-1-1s0.448-1,1-1h12c0.552,0,1,0.448,1,1S51.052,47,50.5,47z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -1,32 +1,53 @@
<?xml version="1.0" encoding="iso-8859-1"?> <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <svg
<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" version="1.1"
viewBox="0 0 58.707 58.707" style="enable-background:new 0 0 58.707 58.707;" xml:space="preserve"> viewBox="0 0 25.2705 25.269251"
<g> xml:space="preserve"
<g> xmlns="http://www.w3.org/2000/svg"
<polygon style="fill:#EFEBDE;" points="46.072,14 32.072,0 1.072,0 1.072,58 46.072,58 "/> xmlns:svg="http://www.w3.org/2000/svg">
<g> <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"/> id="g18"
<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"/> transform="translate(-32.36475,-33.43775)">
<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"/> <g
<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"/> id="g16">
<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"/> <polygon
</g> style="fill:#eddcc7"
<polygon style="fill:#D5D0BB;" points="32.072,0 32.072,14 46.072,14 "/> points="34.205,56.511 38.852,51.865 36.201,49.214 36.194,49.222 "
</g> id="polygon2" />
<g> <path
<polygon style="fill:#EDDCC7;" points="36.201,49.214 36.194,49.222 34.205,56.511 38.852,51.865 "/> style="fill:#d75a4a"
<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 d="m 55.451,35.266 -1.247,-1.247 c -0.775,-0.775 -2.032,-0.775 -2.807,0 l -3.582,3.581 2.651,2.651 z"
L55.451,35.266z"/> id="path4" />
<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"/> <rect
<polygon style="fill:#D6C4B1;" points="41.85,54.879 41.858,54.871 38.852,51.865 34.205,56.511 34.072,57 "/> x="41.459"
<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 y="36.521"
L53.472,43.257z"/> transform="matrix(0.7071,0.7071,-0.7071,0.7071,44.3228,-17.5395)"
style="fill:#f29c21"
width="3.7490001"
height="16.424"
id="rect6" />
<polygon
style="fill:#d6c4b1"
points="38.852,51.865 34.205,56.511 34.072,57 41.85,54.879 41.858,54.871 "
id="polygon8" />
<path
style="fill:#a34740"
d="m 53.472,43.257 3.582,-3.582 c 0.775,-0.775 0.775,-2.032 0,-2.807 l -1.602,-1.602 -4.985,4.985 z"
id="path10" />
<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"/> <rect
<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 x="44.035999"
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"/> y="39.348999"
transform="matrix(-0.7071,-0.7071,0.7071,-0.7071,45.1717,113.8333)"
style="fill:#e18c25"
width="4.2509999"
height="16.424"
id="rect12" />
<path
style="fill:#5e5e5e"
d="m 33.365,58.707 c -0.256,0 -0.512,-0.098 -0.707,-0.293 -0.391,-0.391 -0.391,-1.023 0,-1.414 l 2.207,-2.207 c 0.391,-0.391 1.023,-0.391 1.414,0 0.391,0.391 0.391,1.023 0,1.414 l -2.207,2.207 c -0.195,0.195 -0.451,0.293 -0.707,0.293 z"
id="path14" />
</g> </g>
</g> </g>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 58 58" style="enable-background:new 0 0 58 58;" xml:space="preserve">
<g>
<g>
<polygon style="fill:#EFEBDE;" points="46.5,14 32.5,0 1.5,0 1.5,58 46.5,58 "/>
<g>
<path style="fill:#D5D0BB;" d="M11.5,23h25c0.552,0,1-0.447,1-1s-0.448-1-1-1h-25c-0.552,0-1,0.447-1,1S10.948,23,11.5,23z"/>
<path style="fill:#D5D0BB;" d="M11.5,15h10c0.552,0,1-0.447,1-1s-0.448-1-1-1h-10c-0.552,0-1,0.447-1,1S10.948,15,11.5,15z"/>
<path style="fill:#D5D0BB;" d="M36.5,29h-25c-0.552,0-1,0.447-1,1s0.448,1,1,1h25c0.552,0,1-0.447,1-1S37.052,29,36.5,29z"/>
<path style="fill:#D5D0BB;" d="M36.5,37h-25c-0.552,0-1,0.447-1,1s0.448,1,1,1h25c0.552,0,1-0.447,1-1S37.052,37,36.5,37z"/>
<path style="fill:#D5D0BB;" d="M36.5,45h-25c-0.552,0-1,0.447-1,1s0.448,1,1,1h25c0.552,0,1-0.447,1-1S37.052,45,36.5,45z"/>
</g>
<polygon style="fill:#D5D0BB;" points="32.5,0 32.5,14 46.5,14 "/>
</g>
<g>
<circle style="fill:#26B999;" cx="44.5" cy="46" r="12"/>
<path style="fill:#FFFFFF;" d="M51.071,40.179c-0.455-0.316-1.077-0.204-1.392,0.25l-5.596,8.04l-3.949-3.242
c-0.426-0.351-1.057-0.288-1.407,0.139c-0.351,0.427-0.289,1.057,0.139,1.407l4.786,3.929c0.18,0.147,0.404,0.227,0.634,0.227
c0.045,0,0.091-0.003,0.137-0.009c0.276-0.039,0.524-0.19,0.684-0.419l6.214-8.929C51.636,41.118,51.524,40.495,51.071,40.179z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 58 58" style="enable-background:new 0 0 58 58;" xml:space="preserve">
<g>
<g>
<polygon style="fill:#EFEBDE;" points="47.222,14 33.222,0 2.222,0 2.222,58 47.222,58 "/>
<g>
<path style="fill:#D5D0BB;" d="M12.222,23h25c0.552,0,1-0.447,1-1s-0.448-1-1-1h-25c-0.552,0-1,0.447-1,1S11.669,23,12.222,23z"
/>
<path style="fill:#D5D0BB;" d="M12.222,15h10c0.552,0,1-0.447,1-1s-0.448-1-1-1h-10c-0.552,0-1,0.447-1,1S11.669,15,12.222,15z"
/>
<path style="fill:#D5D0BB;" d="M37.222,29h-25c-0.552,0-1,0.447-1,1s0.448,1,1,1h25c0.552,0,1-0.447,1-1S37.774,29,37.222,29z"/>
<path style="fill:#D5D0BB;" d="M37.222,37h-25c-0.552,0-1,0.447-1,1s0.448,1,1,1h25c0.552,0,1-0.447,1-1S37.774,37,37.222,37z"/>
<path style="fill:#D5D0BB;" d="M37.222,45h-25c-0.552,0-1,0.447-1,1s0.448,1,1,1h25c0.552,0,1-0.447,1-1S37.774,45,37.222,45z"/>
</g>
<polygon style="fill:#D5D0BB;" points="33.222,0 33.222,14 47.222,14 "/>
</g>
<g>
<path style="fill:#3D324C;" d="M55.485,55.293l-6.797-6.797l6.483-3.241l-17.637-6.498l6.499,17.637l3.241-6.484l6.797,6.797
C54.267,56.902,54.522,57,54.778,57s0.512-0.098,0.707-0.293C55.876,56.316,55.876,55.684,55.485,55.293z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -1,32 +1,29 @@
<?xml version="1.0" encoding="iso-8859-1"?> <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <svg
<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" version="1.1"
viewBox="0 0 58.195 58.195" style="enable-background:new 0 0 58.195 58.195;" xml:space="preserve"> viewBox="0 0 28.39 18.476"
<g> xml:space="preserve"
<g> xmlns="http://www.w3.org/2000/svg"
<polygon style="fill:#EFEBDE;" points="45,14.097 31,0.097 0,0.097 0,58.097 45,58.097 "/> xmlns:svg="http://www.w3.org/2000/svg">
<g> <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 id="g10"
"/> transform="translate(-29.806,-39.133)">
<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 <g
"/> id="g8">
<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:#ffffff"
<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 d="m 57,48.289 -0.107,0.163 c -7.121,10.876 -18.773,10.876 -25.893,0 v 0 l 0.107,-0.163 c 7.12,-10.877 18.772,-10.877 25.893,0 z"
"/> id="path2" />
<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 <circle
"/> style="fill:#556080"
</g> cx="43.764"
<polygon style="fill:#D5D0BB;" points="31,0.097 31,14.097 45,14.097 "/> cy="46.007"
</g> r="5.9089999"
<g> id="circle4" />
<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 <path
C38.227,37.412,49.879,37.412,57,48.289L57,48.289z"/> style="fill:#8697cb"
<circle style="fill:#556080;" cx="43.764" cy="46.007" r="5.909"/> d="m 43.947,57.609 c -5.254,0 -10.148,-3.058 -13.783,-8.609 l -0.358,-0.547 0.465,-0.711 c 3.635,-5.552 8.53,-8.609 13.784,-8.609 5.253,0 10.148,3.057 13.783,8.609 l 0.358,0.547 -0.465,0.711 c -3.636,5.552 -8.531,8.609 -13.784,8.609 z m -11.744,-9.161 c 3.206,4.624 7.356,7.161 11.744,7.161 4.436,0 8.63,-2.594 11.85,-7.317 -3.206,-4.624 -7.356,-7.161 -11.743,-7.161 -4.437,10e-4 -8.631,2.594 -11.851,7.317 z"
<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 id="path6" />
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>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -20,6 +20,7 @@
<ItemGroup> <ItemGroup>
<None Remove="Images\Busy.svg" /> <None Remove="Images\Busy.svg" />
<None Remove="Images\Delete.svg" />
<None Remove="Images\Dock.svg" /> <None Remove="Images\Dock.svg" />
<None Remove="Images\Error.svg" /> <None Remove="Images\Error.svg" />
<None Remove="Images\Example.svg" /> <None Remove="Images\Example.svg" />
@ -29,6 +30,8 @@
<None Remove="Images\Ok.svg" /> <None Remove="Images\Ok.svg" />
<None Remove="Images\PublishSend.svg" /> <None Remove="Images\PublishSend.svg" />
<None Remove="Images\RabbitMQ.svg" /> <None Remove="Images\RabbitMQ.svg" />
<None Remove="Images\Save.svg" />
<None Remove="Images\SaveAs.svg" />
<None Remove="Images\Tapeti.png" /> <None Remove="Images\Tapeti.png" />
<None Remove="Images\Undock.svg" /> <None Remove="Images\Undock.svg" />
</ItemGroup> </ItemGroup>
@ -36,6 +39,7 @@
<ItemGroup> <ItemGroup>
<Resource Include="Images\Clear.svg" /> <Resource Include="Images\Clear.svg" />
<Resource Include="Images\Connect.svg" /> <Resource Include="Images\Connect.svg" />
<Resource Include="Images\Delete.svg" />
<Resource Include="Images\Disconnect.svg" /> <Resource Include="Images\Disconnect.svg" />
<Resource Include="Images\Dock.svg" /> <Resource Include="Images\Dock.svg" />
<Resource Include="Images\Error.svg" /> <Resource Include="Images\Error.svg" />
@ -47,17 +51,19 @@
<Resource Include="Images\Publish.svg" /> <Resource Include="Images\Publish.svg" />
<Resource Include="Images\PublishSend.svg" /> <Resource Include="Images\PublishSend.svg" />
<Resource Include="Images\RabbitMQ.svg" /> <Resource Include="Images\RabbitMQ.svg" />
<Resource Include="Images\Save.svg" />
<Resource Include="Images\SaveAs.svg" />
<Resource Include="Images\Subscribe.svg" /> <Resource Include="Images\Subscribe.svg" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="AvalonEdit" Version="6.1.3.50" /> <PackageReference Include="AvalonEdit" Version="6.3.0.90" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Serilog" Version="2.10.0" /> <PackageReference Include="Serilog" Version="4.1.0" />
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" /> <PackageReference Include="Serilog.Sinks.File" Version="6.0.0" />
<PackageReference Include="SharpVectors" Version="1.7.7" /> <PackageReference Include="SharpVectors" Version="1.8.4.2" />
<PackageReference Include="SimpleInjector" Version="5.3.2" /> <PackageReference Include="SimpleInjector" Version="5.5.0" />
<PackageReference Include="System.Reactive" Version="5.0.0" /> <PackageReference Include="System.Reactive" Version="6.0.1" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@ -75,10 +81,10 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Update="UI\Connection\ConnectionDisplayNameStrings.Designer.cs"> <Compile Update="UI\InputDialogStrings.Designer.cs">
<DesignTime>True</DesignTime> <DesignTime>True</DesignTime>
<AutoGen>True</AutoGen> <AutoGen>True</AutoGen>
<DependentUpon>ConnectionDisplayNameStrings.resx</DependentUpon> <DependentUpon>InputDialogStrings.resx</DependentUpon>
</Compile> </Compile>
<Compile Update="UI\Connection\ConnectionWindowStrings.Designer.cs"> <Compile Update="UI\Connection\ConnectionWindowStrings.Designer.cs">
<DependentUpon>ConnectionWindowStrings.resx</DependentUpon> <DependentUpon>ConnectionWindowStrings.resx</DependentUpon>
@ -100,6 +106,11 @@
<AutoGen>True</AutoGen> <AutoGen>True</AutoGen>
<DependentUpon>PayloadEditorStrings.resx</DependentUpon> <DependentUpon>PayloadEditorStrings.resx</DependentUpon>
</Compile> </Compile>
<Compile Update="UI\Tab\Publisher\StoredPublisherMessagesStrings.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>StoredPublisherMessagesStrings.resx</DependentUpon>
</Compile>
<Compile Update="UI\Tab\Publisher\TapetiPublisherViewStrings.Designer.cs"> <Compile Update="UI\Tab\Publisher\TapetiPublisherViewStrings.Designer.cs">
<DependentUpon>TapetiPublisherViewStrings.resx</DependentUpon> <DependentUpon>TapetiPublisherViewStrings.resx</DependentUpon>
<DesignTime>True</DesignTime> <DesignTime>True</DesignTime>
@ -131,9 +142,9 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<EmbeddedResource Update="UI\Connection\ConnectionDisplayNameStrings.resx"> <EmbeddedResource Update="UI\InputDialogStrings.resx">
<Generator>PublicResXFileCodeGenerator</Generator> <Generator>PublicResXFileCodeGenerator</Generator>
<LastGenOutput>ConnectionDisplayNameStrings.Designer.cs</LastGenOutput> <LastGenOutput>InputDialogStrings.Designer.cs</LastGenOutput>
</EmbeddedResource> </EmbeddedResource>
<EmbeddedResource Update="UI\Connection\ConnectionWindowStrings.resx"> <EmbeddedResource Update="UI\Connection\ConnectionWindowStrings.resx">
<LastGenOutput>ConnectionWindowStrings.Designer.cs</LastGenOutput> <LastGenOutput>ConnectionWindowStrings.Designer.cs</LastGenOutput>
@ -151,6 +162,10 @@
<Generator>PublicResXFileCodeGenerator</Generator> <Generator>PublicResXFileCodeGenerator</Generator>
<LastGenOutput>PayloadEditorStrings.Designer.cs</LastGenOutput> <LastGenOutput>PayloadEditorStrings.Designer.cs</LastGenOutput>
</EmbeddedResource> </EmbeddedResource>
<EmbeddedResource Update="UI\Tab\Publisher\StoredPublisherMessagesStrings.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>StoredPublisherMessagesStrings.Designer.cs</LastGenOutput>
</EmbeddedResource>
<EmbeddedResource Update="UI\Tab\Publisher\TapetiPublisherViewStrings.resx"> <EmbeddedResource Update="UI\Tab\Publisher\TapetiPublisherViewStrings.resx">
<LastGenOutput>TapetiPublisherViewStrings.Designer.cs</LastGenOutput> <LastGenOutput>TapetiPublisherViewStrings.Designer.cs</LastGenOutput>
<Generator>PublicResXFileCodeGenerator</Generator> <Generator>PublicResXFileCodeGenerator</Generator>

View File

@ -4,18 +4,19 @@ using System.IO;
using System.Windows; using System.Windows;
using System.Windows.Markup; using System.Windows.Markup;
using PettingZoo.Core.Connection; using PettingZoo.Core.Connection;
using PettingZoo.Core.ExportImport; using PettingZoo.Core.ExportImport.Subscriber;
using PettingZoo.Core.Generator; using PettingZoo.Core.Generator;
using PettingZoo.Core.Macros; using PettingZoo.Core.Macros;
using PettingZoo.Core.Settings; using PettingZoo.Core.Settings;
using PettingZoo.RabbitMQ; using PettingZoo.RabbitMQ;
using PettingZoo.Settings.LiteDB; using PettingZoo.Settings.LiteDB;
using PettingZoo.Tapeti; using PettingZoo.Tapeti;
using PettingZoo.Tapeti.Export; using PettingZoo.Tapeti.ExportImport;
using PettingZoo.UI.Connection; using PettingZoo.UI.Connection;
using PettingZoo.UI.Main; using PettingZoo.UI.Main;
using PettingZoo.UI.Subscribe; using PettingZoo.UI.Subscribe;
using PettingZoo.UI.Tab; using PettingZoo.UI.Tab;
using PettingZoo.UI.Tab.Publisher;
using Serilog; using Serilog;
using SimpleInjector; using SimpleInjector;
@ -82,10 +83,12 @@ namespace PettingZoo
container.Register<ISubscribeDialog, WindowSubscribeDialog>(); container.Register<ISubscribeDialog, WindowSubscribeDialog>();
container.Register<IConnectionSettingsRepository, LiteDBConnectionSettingsRepository>(); container.Register<IConnectionSettingsRepository, LiteDBConnectionSettingsRepository>();
container.Register<IUISettingsRepository, LiteDBUISettingsRepository>(); container.Register<IUISettingsRepository, LiteDBUISettingsRepository>();
container.RegisterSingleton<IPublisherMessagesRepository, LiteDBPublisherMessagesRepository>();
container.Register<IExampleGenerator, TapetiClassLibraryExampleGenerator>(); container.Register<IExampleGenerator, TapetiClassLibraryExampleGenerator>();
container.RegisterSingleton<ITabHostProvider, TabHostProvider>(); container.RegisterSingleton<ITabHostProvider, TabHostProvider>();
container.Register<ITabFactory, ViewTabFactory>(); container.Register<ITabFactory, ViewTabFactory>();
container.RegisterSingleton<IPayloadMacroProcessor, PayloadMacroProcessor>(); container.RegisterSingleton<IPayloadMacroProcessor, PayloadMacroProcessor>();
container.RegisterSingleton<StoredPublisherMessagesViewModel>();
container.RegisterInstance<IExportImportFormatProvider>(new ExportImportFormatProvider( container.RegisterInstance<IExportImportFormatProvider>(new ExportImportFormatProvider(
new TapetiCmdExportFormat(), new TapetiCmdExportFormat(),

View File

@ -8,5 +8,4 @@ Should-have
Nice-to-have Nice-to-have
------------ ------------
- Save / load publisher messages (either as templates or to disk)
- Tapeti: fetch NuGet dependencies to improve the chances of succesfully loading the assembly, instead of the current "extraAssembliesPaths" workaround - Tapeti: fetch NuGet dependencies to improve the chances of succesfully loading the assembly, instead of the current "extraAssembliesPaths" workaround

View File

@ -1,25 +0,0 @@
<Window x:Class="PettingZoo.UI.Connection.ConnectionDisplayNameDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:PettingZoo.UI.Connection"
mc:Ignorable="d"
Width="400"
SizeToContent="Height"
ResizeMode="NoResize"
WindowStyle="ToolWindow"
WindowStartupLocation="CenterOwner"
Style="{StaticResource WindowStyle}"
Title="{x:Static local:ConnectionDisplayNameStrings.WindowTitle}"
FocusManager.FocusedElement="{Binding ElementName=DisplayNameTextBox}"
d:DataContext="{d:DesignInstance local:ConnectionDisplayNameViewModel}">
<StackPanel Margin="8">
<TextBox Name="DisplayNameTextBox" Text="{Binding DisplayName, UpdateSourceTrigger=PropertyChanged}" />
<UniformGrid HorizontalAlignment="Right" Rows="1" Columns="2" Style="{StaticResource FooterPanel}">
<Button IsDefault="True" Content="{x:Static local:ConnectionDisplayNameStrings.ButtonOK}" Style="{StaticResource FooterButton}" Command="{Binding OkCommand}"/>
<Button IsCancel="True" Content="{x:Static local:ConnectionDisplayNameStrings.ButtonCancel}" Style="{StaticResource FooterButton}"/>
</UniformGrid>
</StackPanel>
</Window>

View File

@ -262,7 +262,7 @@ namespace PettingZoo.UI.Connection
// TODO create and enforce unique name? // TODO create and enforce unique name?
var displayName = SelectedStoredConnection != null && SelectedStoredConnection.Id != Guid.Empty ? SelectedStoredConnection.DisplayName : ""; var displayName = SelectedStoredConnection != null && SelectedStoredConnection.Id != Guid.Empty ? SelectedStoredConnection.DisplayName : "";
if (!ConnectionDisplayNameDialog.Execute(ref displayName)) if (!InputDialog.Execute(ref displayName, ConnectionWindowStrings.ProfileNameDialogTitle))
return; return;
var storedConnectionSettings = await connectionSettingsRepository.Add(displayName, StorePassword, ToModel()); var storedConnectionSettings = await connectionSettingsRepository.Add(displayName, StorePassword, ToModel());

View File

@ -213,6 +213,15 @@ namespace PettingZoo.UI.Connection {
} }
} }
/// <summary>
/// Looks up a localized string similar to Profile name.
/// </summary>
public static string ProfileNameDialogTitle {
get {
return ResourceManager.GetString("ProfileNameDialogTitle", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Connection parameters. /// Looks up a localized string similar to Connection parameters.
/// </summary> /// </summary>

View File

@ -112,10 +112,10 @@
<value>2.0</value> <value>2.0</value>
</resheader> </resheader>
<resheader name="reader"> <resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader> </resheader>
<resheader name="writer"> <resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader> </resheader>
<data name="ButtonCancel" xml:space="preserve"> <data name="ButtonCancel" xml:space="preserve">
<value>Cancel</value> <value>Cancel</value>
@ -168,6 +168,9 @@
<data name="LastUsedDisplayName" xml:space="preserve"> <data name="LastUsedDisplayName" xml:space="preserve">
<value>&lt;New connection&gt;</value> <value>&lt;New connection&gt;</value>
</data> </data>
<data name="ProfileNameDialogTitle" xml:space="preserve">
<value>Profile name</value>
</data>
<data name="WindowTitle" xml:space="preserve"> <data name="WindowTitle" xml:space="preserve">
<value>Connection parameters</value> <value>Connection parameters</value>
</data> </data>

View File

@ -0,0 +1,25 @@
<Window x:Class="PettingZoo.UI.InputDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:PettingZoo.UI"
mc:Ignorable="d"
Width="400"
SizeToContent="Height"
ResizeMode="NoResize"
WindowStyle="ToolWindow"
WindowStartupLocation="CenterOwner"
Style="{StaticResource WindowStyle}"
Title="{Binding Title}"
FocusManager.FocusedElement="{Binding ElementName=ValueTextBox}"
d:DataContext="{d:DesignInstance local:InputDialogViewModel}">
<StackPanel Margin="8">
<TextBox Name="ValueTextBox" Text="{Binding Value, UpdateSourceTrigger=PropertyChanged}" />
<UniformGrid HorizontalAlignment="Right" Rows="1" Columns="2" Style="{StaticResource FooterPanel}">
<Button IsDefault="True" Content="{x:Static local:InputDialogStrings.ButtonOK}" Style="{StaticResource FooterButton}" Command="{Binding OkCommand}"/>
<Button IsCancel="True" Content="{x:Static local:InputDialogStrings.ButtonCancel}" Style="{StaticResource FooterButton}"/>
</UniformGrid>
</StackPanel>
</Window>

View File

@ -1,18 +1,19 @@
using System.Linq; using System.Linq;
using System.Windows; using System.Windows;
namespace PettingZoo.UI.Connection namespace PettingZoo.UI
{ {
/// <summary> /// <summary>
/// Interaction logic for ConnectionDisplayNameDialog.xaml /// Interaction logic for InputDialog.xaml
/// </summary> /// </summary>
public partial class ConnectionDisplayNameDialog public partial class InputDialog
{ {
public static bool Execute(ref string displayName) public static bool Execute(ref string value, string title)
{ {
var viewModel = new ConnectionDisplayNameViewModel var viewModel = new InputDialogViewModel
{ {
DisplayName = displayName Value = value,
Title = title
}; };
@ -20,7 +21,7 @@ namespace PettingZoo.UI.Connection
.Cast<Window>() .Cast<Window>()
.FirstOrDefault(applicationWindow => applicationWindow.IsActive); .FirstOrDefault(applicationWindow => applicationWindow.IsActive);
var window = new ConnectionDisplayNameDialog(viewModel) var window = new InputDialog(viewModel)
{ {
Owner = activeWindow ?? Application.Current.MainWindow Owner = activeWindow ?? Application.Current.MainWindow
}; };
@ -28,12 +29,12 @@ namespace PettingZoo.UI.Connection
if (!window.ShowDialog().GetValueOrDefault()) if (!window.ShowDialog().GetValueOrDefault())
return false; return false;
displayName = viewModel.DisplayName; value = viewModel.Value;
return true; return true;
} }
public ConnectionDisplayNameDialog(ConnectionDisplayNameViewModel viewModel) public InputDialog(InputDialogViewModel viewModel)
{ {
viewModel.OkClick += (_, _) => viewModel.OkClick += (_, _) =>
{ {
@ -43,7 +44,7 @@ namespace PettingZoo.UI.Connection
DataContext = viewModel; DataContext = viewModel;
InitializeComponent(); InitializeComponent();
DisplayNameTextBox.CaretIndex = DisplayNameTextBox.Text.Length; ValueTextBox.CaretIndex = ValueTextBox.Text.Length;
} }
} }
} }

View File

@ -8,7 +8,7 @@
// </auto-generated> // </auto-generated>
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
namespace PettingZoo.UI.Connection { namespace PettingZoo.UI {
using System; using System;
@ -22,14 +22,14 @@ namespace PettingZoo.UI.Connection {
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
public class ConnectionDisplayNameStrings { public class InputDialogStrings {
private static global::System.Resources.ResourceManager resourceMan; private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture; private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal ConnectionDisplayNameStrings() { internal InputDialogStrings() {
} }
/// <summary> /// <summary>
@ -39,7 +39,7 @@ namespace PettingZoo.UI.Connection {
public static global::System.Resources.ResourceManager ResourceManager { public static global::System.Resources.ResourceManager ResourceManager {
get { get {
if (object.ReferenceEquals(resourceMan, null)) { if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("PettingZoo.UI.Connection.ConnectionDisplayNameStrings", typeof(ConnectionDisplayNameStrings).Assembly); global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("PettingZoo.UI.InputDialogStrings", typeof(InputDialogStrings).Assembly);
resourceMan = temp; resourceMan = temp;
} }
return resourceMan; return resourceMan;
@ -77,14 +77,5 @@ namespace PettingZoo.UI.Connection {
return ResourceManager.GetString("ButtonOK", resourceCulture); return ResourceManager.GetString("ButtonOK", resourceCulture);
} }
} }
/// <summary>
/// Looks up a localized string similar to Profile name.
/// </summary>
public static string WindowTitle {
get {
return ResourceManager.GetString("WindowTitle", resourceCulture);
}
}
} }
} }

View File

@ -112,10 +112,10 @@
<value>2.0</value> <value>2.0</value>
</resheader> </resheader>
<resheader name="reader"> <resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader> </resheader>
<resheader name="writer"> <resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader> </resheader>
<data name="ButtonCancel" xml:space="preserve"> <data name="ButtonCancel" xml:space="preserve">
<value>Cancel</value> <value>Cancel</value>
@ -123,7 +123,4 @@
<data name="ButtonOK" xml:space="preserve"> <data name="ButtonOK" xml:space="preserve">
<value>OK</value> <value>OK</value>
</data> </data>
<data name="WindowTitle" xml:space="preserve">
<value>Profile name</value>
</data>
</root> </root>

View File

@ -2,27 +2,36 @@
using System.Windows.Input; using System.Windows.Input;
using PettingZoo.WPF.ViewModel; using PettingZoo.WPF.ViewModel;
namespace PettingZoo.UI.Connection namespace PettingZoo.UI
{ {
public class ConnectionDisplayNameViewModel : BaseViewModel public class InputDialogViewModel : BaseViewModel
{ {
private string displayName = ""; private string title = "";
private string value = "";
private readonly DelegateCommand okCommand; private readonly DelegateCommand okCommand;
public string DisplayName public string Title
{ {
get => displayName; get => title;
set => SetField(ref displayName, value, delegateCommandsChanged: new [] { okCommand }); set => SetField(ref title, value);
} }
public string Value
{
get => value;
set => SetField(ref this.value, value, delegateCommandsChanged: new [] { okCommand });
}
public ICommand OkCommand => okCommand; public ICommand OkCommand => okCommand;
public event EventHandler? OkClick; public event EventHandler? OkClick;
public ConnectionDisplayNameViewModel() public InputDialogViewModel()
{ {
okCommand = new DelegateCommand(OkExecute, OkCanExecute); okCommand = new DelegateCommand(OkExecute, OkCanExecute);
} }
@ -36,7 +45,7 @@ namespace PettingZoo.UI.Connection
private bool OkCanExecute() private bool OkCanExecute()
{ {
return !string.IsNullOrWhiteSpace(DisplayName); return !string.IsNullOrWhiteSpace(Value);
} }
} }
} }

View File

@ -7,6 +7,7 @@
xmlns:tab="clr-namespace:PettingZoo.UI.Tab" xmlns:tab="clr-namespace:PettingZoo.UI.Tab"
xmlns:svgc="http://sharpvectors.codeplex.com/svgc/" xmlns:svgc="http://sharpvectors.codeplex.com/svgc/"
xmlns:ui="clr-namespace:PettingZoo.UI" xmlns:ui="clr-namespace:PettingZoo.UI"
xmlns:controls="clr-namespace:PettingZoo.WPF.Controls;assembly=PettingZoo.WPF"
mc:Ignorable="d" mc:Ignorable="d"
d:DataContext="{d:DesignInstance main:DesignTimeMainWindowViewModel, IsDesignTimeCreatable=True}" d:DataContext="{d:DesignInstance main:DesignTimeMainWindowViewModel, IsDesignTimeCreatable=True}"
Width="800" Width="800"
@ -24,7 +25,7 @@
<ui:BindingProxy x:Key="ContextMenuProxy" Data="{Binding}" /> <ui:BindingProxy x:Key="ContextMenuProxy" Data="{Binding}" />
</Window.Resources> </Window.Resources>
<DockPanel> <DockPanel>
<ToolBar DockPanel.Dock="Top" ToolBarTray.IsLocked="True" Loaded="Toolbar_Loaded"> <controls:AutoOverflowToolBar DockPanel.Dock="Top" ToolBarTray.IsLocked="True">
<Button Command="{Binding ConnectCommand}"> <Button Command="{Binding ConnectCommand}">
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<Image Source="{svgc:SvgImage Source=/Images/Connect.svg, AppName=PettingZoo}" Width="16" Height="16" Style="{StaticResource ToolbarIcon}"/> <Image Source="{svgc:SvgImage Source=/Images/Connect.svg, AppName=PettingZoo}" Width="16" Height="16" Style="{StaticResource ToolbarIcon}"/>
@ -82,7 +83,7 @@
</DataTemplate> </DataTemplate>
</ItemsControl.ItemTemplate> </ItemsControl.ItemTemplate>
</ItemsControl> </ItemsControl>
</ToolBar> </controls:AutoOverflowToolBar>
<StatusBar DockPanel.Dock="Bottom"> <StatusBar DockPanel.Dock="Bottom">
<StatusBarItem> <StatusBarItem>
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">

View File

@ -5,7 +5,7 @@ using System.Windows.Controls;
using System.Windows.Input; using System.Windows.Input;
using System.Windows.Media; using System.Windows.Media;
using PettingZoo.Core.Connection; using PettingZoo.Core.Connection;
using PettingZoo.Core.ExportImport; using PettingZoo.Core.ExportImport.Subscriber;
using PettingZoo.UI.Connection; using PettingZoo.UI.Connection;
using PettingZoo.UI.Subscribe; using PettingZoo.UI.Subscribe;
using PettingZoo.UI.Tab; using PettingZoo.UI.Tab;
@ -139,18 +139,6 @@ namespace PettingZoo.UI.Main
public double TabWidth => SubscriberTabs.ActualWidth; public double TabWidth => SubscriberTabs.ActualWidth;
public double TabHeight => SubscriberTabs.ActualHeight; public double TabHeight => SubscriberTabs.ActualHeight;
private void Toolbar_Loaded(object sender, RoutedEventArgs e)
{
// Hide arrow on the right side of the toolbar
var toolBar = sender as ToolBar;
if (toolBar?.Template.FindName("OverflowGrid", toolBar) is FrameworkElement overflowGrid)
overflowGrid.Visibility = Visibility.Collapsed;
if (toolBar?.Template.FindName("MainPanelBorder", toolBar) is FrameworkElement mainPanelBorder)
mainPanelBorder.Margin = new Thickness(0);
}
} }
#pragma warning restore CA1001 #pragma warning restore CA1001
} }

View File

@ -160,7 +160,7 @@ namespace PettingZoo.UI.Main {
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to Connected. /// Looks up a localized string similar to Connected to {0}.
/// </summary> /// </summary>
public static string StatusConnected { public static string StatusConnected {
get { get {

View File

@ -151,7 +151,7 @@
<value>Importing messages...</value> <value>Importing messages...</value>
</data> </data>
<data name="StatusConnected" xml:space="preserve"> <data name="StatusConnected" xml:space="preserve">
<value>Connected</value> <value>Connected to {0}</value>
</data> </data>
<data name="StatusConnecting" xml:space="preserve"> <data name="StatusConnecting" xml:space="preserve">
<value>Connecting to {0}...</value> <value>Connecting to {0}...</value>

View File

@ -9,7 +9,7 @@ using System.Windows;
using System.Windows.Forms; using System.Windows.Forms;
using System.Windows.Input; using System.Windows.Input;
using PettingZoo.Core.Connection; using PettingZoo.Core.Connection;
using PettingZoo.Core.ExportImport; using PettingZoo.Core.ExportImport.Subscriber;
using PettingZoo.UI.Connection; using PettingZoo.UI.Connection;
using PettingZoo.UI.Subscribe; using PettingZoo.UI.Subscribe;
using PettingZoo.UI.Tab; using PettingZoo.UI.Tab;
@ -43,7 +43,7 @@ namespace PettingZoo.UI.Main
private readonly IExportImportFormatProvider exportImportFormatProvider; private readonly IExportImportFormatProvider exportImportFormatProvider;
private SubscribeDialogParams? subscribeDialogParams; private SubscribeDialogParams? subscribeDialogParams;
private IConnection? connection; private readonly DynamicConnection connection = new();
private string connectionStatus; private string connectionStatus;
private ITab? activeTab; private ITab? activeTab;
private readonly Dictionary<ITab, Window> undockedTabs = new(); private readonly Dictionary<ITab, Window> undockedTabs = new();
@ -141,14 +141,14 @@ namespace PettingZoo.UI.Main
closeTabCommand = new DelegateCommand(CloseTabExecute, HasActiveTabCanExecute); closeTabCommand = new DelegateCommand(CloseTabExecute, HasActiveTabCanExecute);
undockTabCommand = new DelegateCommand(UndockTabExecute, HasActiveTabCanExecute); undockTabCommand = new DelegateCommand(UndockTabExecute, HasActiveTabCanExecute);
importCommand = new DelegateCommand(ImportExecute); importCommand = new DelegateCommand(ImportExecute);
connection.StatusChanged += ConnectionStatusChanged;
} }
public async ValueTask DisposeAsync() public async ValueTask DisposeAsync()
{ {
GC.SuppressFinalize(this); GC.SuppressFinalize(this);
if (connection != null)
await connection.DisposeAsync(); await connection.DisposeAsync();
} }
@ -159,13 +159,9 @@ namespace PettingZoo.UI.Main
if (connectionSettings == null) if (connectionSettings == null)
return; return;
if (connection != null) connection.SetConnection(connectionFactory.CreateConnection(new ConnectionParams(
await connection.DisposeAsync();
connection = connectionFactory.CreateConnection(new ConnectionParams(
connectionSettings.Host, connectionSettings.VirtualHost, connectionSettings.Port, connectionSettings.Host, connectionSettings.VirtualHost, connectionSettings.Port,
connectionSettings.Username, connectionSettings.Password)); connectionSettings.Username, connectionSettings.Password)));
connection.StatusChanged += ConnectionStatusChanged;
if (connectionSettings.Subscribe) if (connectionSettings.Subscribe)
{ {
@ -173,38 +169,20 @@ namespace PettingZoo.UI.Main
tabFactory.CreateSubscriberTab(connection, subscriber); tabFactory.CreateSubscriberTab(connection, subscriber);
} }
connection.Connect();
ConnectionChanged(); ConnectionChanged();
} }
private async void DisconnectExecute() private async void DisconnectExecute()
{ {
Tabs.Clear(); await connection.Disconnect();
var capturedUndockedTabs = undockedTabs.ToList();
undockedTabs.Clear();
foreach (var undockedTab in capturedUndockedTabs)
undockedTab.Value.Close();
RaisePropertyChanged(nameof(NoTabsVisibility));
undockTabCommand.RaiseCanExecuteChanged();
if (connection != null)
{
await connection.DisposeAsync();
connection = null;
}
ConnectionStatus = GetConnectionStatus(null);
ConnectionStatusType = ConnectionStatusType.Error;
ConnectionChanged();
} }
private void SubscribeExecute() private void SubscribeExecute()
{ {
if (connection == null) if (connection.Status != Core.Connection.ConnectionStatus.Connected)
return; return;
var newParams = subscribeDialog.Show(subscribeDialogParams); var newParams = subscribeDialog.Show(subscribeDialogParams);
@ -220,7 +198,7 @@ namespace PettingZoo.UI.Main
private void PublishExecute() private void PublishExecute()
{ {
if (connection == null) if (connection.Status != Core.Connection.ConnectionStatus.Connected)
return; return;
tabFactory.CreatePublisherTab(connection); tabFactory.CreatePublisherTab(connection);
@ -229,7 +207,7 @@ namespace PettingZoo.UI.Main
private bool IsConnectedCanExecute() private bool IsConnectedCanExecute()
{ {
return connection != null; return connection.Status == Core.Connection.ConnectionStatus.Connected;
} }
@ -419,6 +397,8 @@ namespace PettingZoo.UI.Main
Core.Connection.ConnectionStatus.Connecting => ConnectionStatusType.Connecting, Core.Connection.ConnectionStatus.Connecting => ConnectionStatusType.Connecting,
_ => ConnectionStatusType.Error _ => ConnectionStatusType.Error
}; };
Application.Current.Dispatcher.BeginInvoke(ConnectionChanged);
} }
@ -427,9 +407,9 @@ namespace PettingZoo.UI.Main
{ {
return args?.Status switch return args?.Status switch
{ {
Core.Connection.ConnectionStatus.Connecting => string.Format(MainWindowStrings.StatusConnecting, args.Context), Core.Connection.ConnectionStatus.Connecting => string.Format(MainWindowStrings.StatusConnecting, args.ConnectionParams),
Core.Connection.ConnectionStatus.Connected => string.Format(MainWindowStrings.StatusConnected, args.Context), Core.Connection.ConnectionStatus.Connected => string.Format(MainWindowStrings.StatusConnected, args.ConnectionParams),
Core.Connection.ConnectionStatus.Error => string.Format(MainWindowStrings.StatusError, args.Context), Core.Connection.ConnectionStatus.Error => string.Format(MainWindowStrings.StatusError, args.Exception?.Message),
Core.Connection.ConnectionStatus.Disconnected => MainWindowStrings.StatusDisconnected, Core.Connection.ConnectionStatus.Disconnected => MainWindowStrings.StatusDisconnected,
_ => MainWindowStrings.StatusDisconnected _ => MainWindowStrings.StatusDisconnected
}; };

View File

@ -4,7 +4,7 @@ namespace PettingZoo.UI.Tab
{ {
public interface ITabFactory public interface ITabFactory
{ {
void CreateSubscriberTab(IConnection? connection, ISubscriber subscriber); void CreateSubscriberTab(IConnection connection, ISubscriber subscriber);
string CreateReplySubscriberTab(IConnection connection); string CreateReplySubscriberTab(IConnection connection);
void CreatePublisherTab(IConnection connection, ReceivedMessageInfo? fromReceivedMessage = null); void CreatePublisherTab(IConnection connection, ReceivedMessageInfo? fromReceivedMessage = null);
} }

View File

@ -123,16 +123,15 @@ namespace PettingZoo.UI.Tab.Publisher
} }
private IPayloadMacroProcessor? macroProcessor;
public IPayloadMacroProcessor? MacroProcessor public IPayloadMacroProcessor? MacroProcessor
{ {
get => macroProcessor; get => viewModel.MacroProcessor;
set set
{ {
if (value == macroProcessor) if (value == viewModel.MacroProcessor)
return; return;
macroProcessor = value; viewModel.MacroProcessor = value;
UpdateMacroContextMenu(); UpdateMacroContextMenu();
} }
} }
@ -266,10 +265,10 @@ namespace PettingZoo.UI.Tab.Publisher
{ {
ContextMenuInsertMacro.Items.Clear(); ContextMenuInsertMacro.Items.Clear();
if (macroProcessor == null) if (MacroProcessor == null)
return; return;
foreach (var macro in macroProcessor.Macros) foreach (var macro in MacroProcessor.Macros)
{ {
var macroMenuItem = new MenuItem var macroMenuItem = new MenuItem
{ {

View File

@ -2,10 +2,10 @@
using System.ComponentModel; using System.ComponentModel;
using System.Reactive.Linq; using System.Reactive.Linq;
using System.Windows; using System.Windows;
using System.Windows.Input;
using ICSharpCode.AvalonEdit.Highlighting; using ICSharpCode.AvalonEdit.Highlighting;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using PettingZoo.Core.Macros;
using PettingZoo.Core.Validation; using PettingZoo.Core.Validation;
using PettingZoo.WPF.ViewModel; using PettingZoo.WPF.ViewModel;
@ -159,6 +159,7 @@ namespace PettingZoo.UI.Tab.Publisher
public IPayloadValidator? Validator { get; set; } public IPayloadValidator? Validator { get; set; }
public IPayloadMacroProcessor? MacroProcessor { get; set; }
public PayloadEditorViewModel() public PayloadEditorViewModel()
@ -166,7 +167,7 @@ namespace PettingZoo.UI.Tab.Publisher
var observable = Observable.FromEventPattern<PropertyChangedEventHandler, PropertyChangedEventArgs>( var observable = Observable.FromEventPattern<PropertyChangedEventHandler, PropertyChangedEventArgs>(
h => PropertyChanged += h, h => PropertyChanged += h,
h => PropertyChanged -= h) h => PropertyChanged -= h)
.Where(e => e.EventArgs.PropertyName == nameof(Payload)); .Where(e => e.EventArgs.PropertyName is nameof(Payload) or nameof(EnableMacros));
observable observable
.Subscribe(_ => ValidatingPayload()); .Subscribe(_ => ValidatingPayload());
@ -204,14 +205,18 @@ namespace PettingZoo.UI.Tab.Publisher
{ {
if (!string.IsNullOrEmpty(Payload)) if (!string.IsNullOrEmpty(Payload))
{ {
var validatePayload = EnableMacros && MacroProcessor != null
? MacroProcessor.Apply(Payload)
: Payload;
if (Validator != null && Validator.CanValidate()) if (Validator != null && Validator.CanValidate())
{ {
Validator.Validate(payload); Validator.Validate(validatePayload);
ValidationInfo = new ValidationInfo(ValidationStatus.Ok); ValidationInfo = new ValidationInfo(ValidationStatus.Ok);
} }
else else
{ {
JToken.Parse(Payload); JToken.Parse(validatePayload);
ValidationInfo = new ValidationInfo(ValidationStatus.OkSyntax); ValidationInfo = new ValidationInfo(ValidationStatus.OkSyntax);
} }
} }

View File

@ -6,12 +6,20 @@
xmlns:res="clr-namespace:PettingZoo.UI.Tab.Publisher" xmlns:res="clr-namespace:PettingZoo.UI.Tab.Publisher"
xmlns:controls="clr-namespace:PettingZoo.WPF.Controls;assembly=PettingZoo.WPF" xmlns:controls="clr-namespace:PettingZoo.WPF.Controls;assembly=PettingZoo.WPF"
xmlns:svgc="http://sharpvectors.codeplex.com/svgc/" xmlns:svgc="http://sharpvectors.codeplex.com/svgc/"
xmlns:valueConverters="clr-namespace:PettingZoo.WPF.ValueConverters;assembly=PettingZoo.WPF"
mc:Ignorable="d" mc:Ignorable="d"
d:DesignHeight="450" d:DesignHeight="1200"
d:DesignWidth="800" d:DesignWidth="800"
d:DataContext="{d:DesignInstance res:DesignTimePublisherViewModel, IsDesignTimeCreatable=True}" d:DataContext="{d:DesignInstance res:DesignTimePublisherViewModel, IsDesignTimeCreatable=True}"
Background="White"> Background="White">
<ScrollViewer VerticalScrollBarVisibility="Auto"> <Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="350" />
</Grid.ColumnDefinitions>
<ScrollViewer Grid.Column="0" VerticalScrollBarVisibility="Auto">
<controls:GridLayout Style="{StaticResource Form}" Margin="4" Grid.IsSharedSizeScope="True"> <controls:GridLayout Style="{StaticResource Form}" Margin="4" Grid.IsSharedSizeScope="True">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/>
@ -26,6 +34,7 @@
<RowDefinition Height="16" /> <RowDefinition Height="16" />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="Label" /> <ColumnDefinition Width="Auto" SharedSizeGroup="Label" />
@ -73,6 +82,92 @@
<ContentControl Grid.Row="10" Grid.Column="0" Grid.ColumnSpan="2" Margin="0 8 0 0" Content="{Binding MessageTypeControl}" /> <ContentControl Grid.Row="10" Grid.Column="0" Grid.ColumnSpan="2" Margin="0 8 0 0" Content="{Binding MessageTypeControl}" />
<Button Grid.Row="11" Grid.Column="1" Command="{Binding PublishCommand}" Content="{x:Static res:PublisherViewStrings.CommandPublish}" HorizontalAlignment="Left" /> <Button Grid.Row="11" Grid.Column="1" Command="{Binding PublishCommand}" Content="{x:Static res:PublisherViewStrings.CommandPublish}" HorizontalAlignment="Left" />
<TextBlock Grid.Row="12" Grid.Column="1" Text="{x:Static res:PublisherViewStrings.Published}" Visibility="{Binding PublishedVisibility}" />
</controls:GridLayout> </controls:GridLayout>
</ScrollViewer> </ScrollViewer>
<GridSplitter Width="5" Grid.Column="1" Grid.Row="0" HorizontalAlignment="Stretch"/>
<Grid Grid.Column="2" Grid.Row="0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Label Grid.Row="0" Style="{StaticResource HeaderLabel}" Content="{x:Static res:PublisherViewStrings.PanelTitleMessages}"/>
<controls:AutoOverflowToolBar Grid.Row="1" ToolBarTray.IsLocked="True" Margin="0,0,0,4">
<!-- TODO load button in addition to double-click. I don't like hidden-only functionality -->
<Button Command="{Binding SaveCommand}">
<StackPanel Orientation="Horizontal">
<Image Source="{svgc:SvgImage Source=/Images/Save.svg, AppName=PettingZoo}" Width="16" Height="16" Style="{StaticResource ToolbarIcon}"/>
<TextBlock Margin="3,0,0,0" Text="{x:Static res:PublisherViewStrings.ToolbarSave}" />
</StackPanel>
</Button>
<Button Command="{Binding SaveAsCommand}">
<StackPanel Orientation="Horizontal">
<Image Source="{svgc:SvgImage Source=/Images/SaveAs.svg, AppName=PettingZoo}" Width="16" Height="16" Style="{StaticResource ToolbarIcon}"/>
<TextBlock Margin="3,0,0,0" Text="{x:Static res:PublisherViewStrings.ToolbarSaveAs}" />
</StackPanel>
</Button>
<Separator Style="{StaticResource {x:Static ToolBar.SeparatorStyleKey}}" />
<Button Command="{Binding DeleteCommand}">
<StackPanel Orientation="Horizontal">
<Image Source="{svgc:SvgImage Source=/Images/Delete.svg, AppName=PettingZoo}" Width="16" Height="16" Style="{StaticResource ToolbarIcon}"/>
<TextBlock Margin="3,0,0,0" Text="{x:Static res:PublisherViewStrings.ToolbarDelete}" />
</StackPanel>
</Button>
<Separator Style="{StaticResource {x:Static ToolBar.SeparatorStyleKey}}" />
<Button Command="{Binding ExportCommand}">
<StackPanel Orientation="Horizontal">
<Image Source="{svgc:SvgImage Source=/Images/Export.svg, AppName=PettingZoo}" Width="16" Height="16" Style="{StaticResource ToolbarIcon}"/>
<TextBlock Margin="3,0,0,0" Text="{x:Static res:PublisherViewStrings.ToolbarExport}" />
</StackPanel>
</Button>
<Button Command="{Binding ImportCommand}">
<StackPanel Orientation="Horizontal">
<Image Source="{svgc:SvgImage Source=/Images/Import.svg, AppName=PettingZoo}" Width="16" Height="16" Style="{StaticResource ToolbarIcon}"/>
<TextBlock Margin="3,0,0,0" Text="{x:Static res:PublisherViewStrings.ToolbarImport}" />
</StackPanel>
</Button>
<!-- TODO export / import -->
</controls:AutoOverflowToolBar>
<ListBox Grid.Row="2" ItemsSource="{Binding StoredMessages}" SelectedValue="{Binding SelectedStoredMessage}">
<ListBox.Resources>
<valueConverters:SameReferenceConverter x:Key="SameReferenceConverter" />
</ListBox.Resources>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding DisplayName}">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Style.Triggers>
<DataTrigger Value="True">
<DataTrigger.Binding>
<MultiBinding Converter="{StaticResource SameReferenceConverter}">
<Binding RelativeSource="{RelativeSource AncestorType={x:Type ListBoxItem}}" Path="DataContext" />
<Binding RelativeSource="{RelativeSource AncestorType={x:Type ListBox}}" Path="DataContext.ActiveStoredMessage" />
</MultiBinding>
</DataTrigger.Binding>
<Setter Property="FontWeight" Value="Bold" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
<TextBlock.InputBindings>
<MouseBinding MouseAction="LeftDoubleClick" Command="{Binding DataContext.LoadStoredMessageCommand, RelativeSource={RelativeSource AncestorType={x:Type ListBox}}}" />
</TextBlock.InputBindings>
</TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Grid>
</UserControl> </UserControl>

View File

@ -1,27 +1,33 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Forms;
using System.Windows.Input; using System.Windows.Input;
using System.Windows.Threading;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using PettingZoo.Core.Connection; using PettingZoo.Core.Connection;
using PettingZoo.Core.ExportImport.Publisher;
using PettingZoo.Core.Generator; using PettingZoo.Core.Generator;
using PettingZoo.Core.Macros; using PettingZoo.Core.Macros;
using PettingZoo.Core.Settings;
using PettingZoo.WPF.ViewModel; using PettingZoo.WPF.ViewModel;
using Application = System.Windows.Application;
using UserControl = System.Windows.Controls.UserControl;
namespace PettingZoo.UI.Tab.Publisher namespace PettingZoo.UI.Tab.Publisher
{ {
public enum MessageType public class PublisherViewModel : BaseViewModel, IDisposable, ITabToolbarCommands, ITabHostWindowNotify, IPublishDestination
{
Raw,
Tapeti
}
public class PublisherViewModel : BaseViewModel, ITabToolbarCommands, ITabHostWindowNotify, IPublishDestination
{ {
private readonly IConnection connection; private readonly IConnection connection;
private readonly IExampleGenerator exampleGenerator; private readonly IExampleGenerator exampleGenerator;
private readonly IPayloadMacroProcessor payloadMacroProcessor; private readonly IPayloadMacroProcessor payloadMacroProcessor;
private readonly StoredPublisherMessagesViewModel storedPublisherMessagesViewModel;
private readonly ITabFactory tabFactory; private readonly ITabFactory tabFactory;
private bool sendToExchange = true; private bool sendToExchange = true;
@ -29,18 +35,32 @@ namespace PettingZoo.UI.Tab.Publisher
private string routingKey = ""; private string routingKey = "";
private string queue = ""; private string queue = "";
private string replyTo = ""; private string replyTo = "";
private bool replyToSpecified = true; private bool replyToNewSubscriber;
private MessageType messageType; private StoredPublisherMessage? selectedStoredMessage;
private StoredPublisherMessage? activeStoredMessage;
private PublisherMessageType messageType;
private UserControl? messageTypeControl; private UserControl? messageTypeControl;
private ICommand? messageTypePublishCommand; private ICommand? messageTypePublishCommand;
private RawPublisherViewModel? rawPublisherViewModel;
private UserControl? rawPublisherView; private UserControl? rawPublisherView;
private TapetiPublisherViewModel? tapetiPublisherViewModel;
private UserControl? tapetiPublisherView; private UserControl? tapetiPublisherView;
private readonly DelegateCommand publishCommand; private readonly DelegateCommand publishCommand;
private readonly DelegateCommand saveCommand;
private readonly DelegateCommand saveAsCommand;
private readonly DelegateCommand deleteCommand;
private readonly DelegateCommand loadStoredMessageCommand;
private readonly DelegateCommand exportCommand;
private readonly DelegateCommand importCommand;
private readonly TabToolbarCommand[] toolbarCommands; private readonly TabToolbarCommand[] toolbarCommands;
private Window? tabHostWindow; private Window? tabHostWindow;
private bool disableCheckCanSave;
public bool SendToExchange public bool SendToExchange
@ -48,7 +68,8 @@ namespace PettingZoo.UI.Tab.Publisher
get => sendToExchange; get => sendToExchange;
set => SetField(ref sendToExchange, value, set => SetField(ref sendToExchange, value,
delegateCommandsChanged: new[] { publishCommand }, delegateCommandsChanged: new[] { publishCommand },
otherPropertiesChanged: new[] { nameof(SendToQueue), nameof(ExchangeVisibility), nameof(QueueVisibility), nameof(Title) }); otherPropertiesChanged: new[]
{ nameof(SendToQueue), nameof(ExchangeVisibility), nameof(QueueVisibility), nameof(Title) });
} }
@ -69,14 +90,16 @@ namespace PettingZoo.UI.Tab.Publisher
public string RoutingKey public string RoutingKey
{ {
get => routingKey; get => routingKey;
set => SetField(ref routingKey, value, delegateCommandsChanged: new[] { publishCommand }, otherPropertiesChanged: new[] { nameof(Title) }); set => SetField(ref routingKey, value, delegateCommandsChanged: new[] { publishCommand },
otherPropertiesChanged: new[] { nameof(Title) });
} }
public string Queue public string Queue
{ {
get => queue; get => queue;
set => SetField(ref queue, value, delegateCommandsChanged: new[] { publishCommand }, otherPropertiesChanged: new[] { nameof(Title) }); set => SetField(ref queue, value, delegateCommandsChanged: new[] { publishCommand },
otherPropertiesChanged: new[] { nameof(Title) });
} }
@ -89,15 +112,16 @@ namespace PettingZoo.UI.Tab.Publisher
public bool ReplyToSpecified public bool ReplyToSpecified
{ {
get => replyToSpecified; get => !ReplyToNewSubscriber;
set => SetField(ref replyToSpecified, value, otherPropertiesChanged: new[] { nameof(ReplyToNewSubscriber) }); set => ReplyToNewSubscriber = !value;
} }
public bool ReplyToNewSubscriber public bool ReplyToNewSubscriber
{ {
get => !ReplyToSpecified; get => replyToNewSubscriber;
set => ReplyToSpecified = !value; set => SetField(ref replyToNewSubscriber, value,
otherPropertiesChanged: new[] { nameof(ReplyToSpecified) });
} }
@ -105,7 +129,7 @@ namespace PettingZoo.UI.Tab.Publisher
public virtual Visibility QueueVisibility => SendToQueue ? Visibility.Visible : Visibility.Collapsed; public virtual Visibility QueueVisibility => SendToQueue ? Visibility.Visible : Visibility.Collapsed;
public MessageType MessageType public PublisherMessageType MessageType
{ {
get => messageType; get => messageType;
set set
@ -124,14 +148,20 @@ namespace PettingZoo.UI.Tab.Publisher
public bool MessageTypeRaw public bool MessageTypeRaw
{ {
get => MessageType == MessageType.Raw; get => MessageType == PublisherMessageType.Raw;
set { if (value) MessageType = MessageType.Raw; } set
{
if (value) MessageType = PublisherMessageType.Raw;
}
} }
public bool MessageTypeTapeti public bool MessageTypeTapeti
{ {
get => MessageType == MessageType.Tapeti; get => MessageType == PublisherMessageType.Tapeti;
set { if (value) MessageType = MessageType.Tapeti; } set
{
if (value) MessageType = PublisherMessageType.Tapeti;
}
} }
@ -142,12 +172,51 @@ namespace PettingZoo.UI.Tab.Publisher
} }
public ObservableCollectionEx<StoredPublisherMessage> StoredMessages =>
storedPublisherMessagesViewModel.StoredMessages;
public StoredPublisherMessage? SelectedStoredMessage
{
get => selectedStoredMessage;
set => SetField(ref selectedStoredMessage, value, delegateCommandsChanged: new[] { loadStoredMessageCommand, deleteCommand, exportCommand });
}
public StoredPublisherMessage? ActiveStoredMessage
{
get => activeStoredMessage;
set => SetField(ref activeStoredMessage, value);
}
public ICommand PublishCommand => publishCommand; public ICommand PublishCommand => publishCommand;
public ICommand SaveCommand => saveCommand;
public ICommand SaveAsCommand => saveAsCommand;
public ICommand DeleteCommand => deleteCommand;
public ICommand LoadStoredMessageCommand => loadStoredMessageCommand;
public ICommand ExportCommand => exportCommand;
public ICommand ImportCommand => importCommand;
private readonly DispatcherTimer publishedVisibilityTimer = new()
{
Interval = TimeSpan.FromSeconds(1)
};
private Visibility publishedVisibility = Visibility.Hidden;
public Visibility PublishedVisibility
{
get => publishedVisibility;
set => SetField(ref publishedVisibility, value);
}
public string Title => SendToQueue public string Title => SendToQueue
? string.IsNullOrWhiteSpace(Queue) ? PublisherViewStrings.TabTitleEmpty : string.Format(PublisherViewStrings.TabTitle, Queue) ? string.IsNullOrWhiteSpace(Queue) ? PublisherViewStrings.TabTitleEmpty :
: string.IsNullOrWhiteSpace(RoutingKey) ? PublisherViewStrings.TabTitleEmpty : string.Format(PublisherViewStrings.TabTitle, RoutingKey); string.Format(PublisherViewStrings.TabTitle, Queue)
: string.IsNullOrWhiteSpace(RoutingKey)
? PublisherViewStrings.TabTitleEmpty
: string.Format(PublisherViewStrings.TabTitle, RoutingKey);
public IEnumerable<TabToolbarCommand> ToolbarCommands => toolbarCommands; public IEnumerable<TabToolbarCommand> ToolbarCommands => toolbarCommands;
@ -157,35 +226,83 @@ namespace PettingZoo.UI.Tab.Publisher
string IPublishDestination.RoutingKey => SendToExchange ? RoutingKey : Queue; string IPublishDestination.RoutingKey => SendToExchange ? RoutingKey : Queue;
public PublisherViewModel(ITabFactory tabFactory, IConnection connection, IExampleGenerator exampleGenerator, IPayloadMacroProcessor payloadMacroProcessor, ReceivedMessageInfo? fromReceivedMessage = null) public PublisherViewModel(ITabFactory tabFactory, IConnection connection, IExampleGenerator exampleGenerator,
IPayloadMacroProcessor payloadMacroProcessor,
StoredPublisherMessagesViewModel storedPublisherMessagesViewModel,
ReceivedMessageInfo? fromReceivedMessage = null)
{ {
this.connection = connection; this.connection = connection;
this.exampleGenerator = exampleGenerator; this.exampleGenerator = exampleGenerator;
this.payloadMacroProcessor = payloadMacroProcessor; this.payloadMacroProcessor = payloadMacroProcessor;
this.storedPublisherMessagesViewModel = storedPublisherMessagesViewModel;
this.tabFactory = tabFactory; this.tabFactory = tabFactory;
publishCommand = new DelegateCommand(PublishExecute, PublishCanExecute); publishCommand = new DelegateCommand(PublishExecute, PublishCanExecute);
saveCommand = new DelegateCommand(SaveExecute, SaveCanExecute);
saveAsCommand = new DelegateCommand(SaveAsExecute);
deleteCommand = new DelegateCommand(DeleteExecute, SelectedMessageCanExecute);
loadStoredMessageCommand = new DelegateCommand(LoadStoredMessageExecute, SelectedMessageCanExecute);
exportCommand = new DelegateCommand(ExportExecute, SelectedMessageCanExecute);
importCommand = new DelegateCommand(ImportExecute);
toolbarCommands = new[] toolbarCommands = new[]
{ {
new TabToolbarCommand(PublishCommand, PublisherViewStrings.CommandPublish, SvgIconHelper.LoadFromResource("/Images/PublishSend.svg")) new TabToolbarCommand(PublishCommand, PublisherViewStrings.CommandPublish,
SvgIconHelper.LoadFromResource("/Images/PublishSend.svg"))
}; };
if (fromReceivedMessage != null) if (fromReceivedMessage != null)
SetMessageTypeControl(fromReceivedMessage); SetMessageTypeControl(fromReceivedMessage);
else else
SetMessageTypeControl(MessageType.Raw); SetMessageTypeControl(PublisherMessageType.Raw);
PropertyChanged += (_, _) => { saveCommand.RaiseCanExecuteChanged(); };
// ReSharper disable once ConditionIsAlwaysTrueOrFalse - null in design time
if (connection != null)
connection.StatusChanged += ConnectionStatusChanged;
publishedVisibilityTimer.Tick += (_, _) =>
{
PublishedVisibility = Visibility.Hidden;
publishedVisibilityTimer.Stop();
};
}
public void Dispose()
{
connection.StatusChanged -= ConnectionStatusChanged;
GC.SuppressFinalize(this);
}
private void ConnectionStatusChanged(object? sender, StatusChangedEventArgs e)
{
Application.Current.Dispatcher.BeginInvoke(() =>
{
publishCommand.RaiseCanExecuteChanged();
});
} }
private void PublishExecute() private void PublishExecute()
{ {
messageTypePublishCommand?.Execute(null); messageTypePublishCommand?.Execute(null);
PublishedVisibility = Visibility.Visible;
publishedVisibilityTimer.Stop();
publishedVisibilityTimer.Start();
} }
private bool PublishCanExecute() private bool PublishCanExecute()
{ {
if (connection.Status != ConnectionStatus.Connected)
return false;
if (SendToExchange) if (SendToExchange)
{ {
if (string.IsNullOrWhiteSpace(Exchange) || string.IsNullOrWhiteSpace(RoutingKey)) if (string.IsNullOrWhiteSpace(Exchange) || string.IsNullOrWhiteSpace(RoutingKey))
@ -201,13 +318,11 @@ namespace PettingZoo.UI.Tab.Publisher
} }
private void SetMessageTypeControl(MessageType value) private void SetMessageTypeControl(PublisherMessageType value)
{ {
switch (value) switch (value)
{ {
case MessageType.Raw: case PublisherMessageType.Raw:
RawPublisherViewModel rawPublisherViewModel;
if (rawPublisherView == null) if (rawPublisherView == null)
{ {
rawPublisherViewModel = new RawPublisherViewModel(connection, this, payloadMacroProcessor); rawPublisherViewModel = new RawPublisherViewModel(connection, this, payloadMacroProcessor);
@ -216,19 +331,22 @@ namespace PettingZoo.UI.Tab.Publisher
publishCommand.RaiseCanExecuteChanged(); publishCommand.RaiseCanExecuteChanged();
}; };
rawPublisherView ??= new RawPublisherView(rawPublisherViewModel); // This is becoming a bit messy, find a cleaner way...
// TODO monitor header changes as well, instead of only the collection
rawPublisherViewModel.PropertyChanged += (_, _) => { saveCommand.RaiseCanExecuteChanged(); };
rawPublisherViewModel.Headers.CollectionChanged += (_, _) => { saveCommand.RaiseCanExecuteChanged(); };
rawPublisherView = new RawPublisherView(rawPublisherViewModel);
} }
else else
rawPublisherViewModel = (RawPublisherViewModel)rawPublisherView.DataContext; Debug.Assert(rawPublisherViewModel != null);
MessageTypeControl = rawPublisherView; MessageTypeControl = rawPublisherView;
messageTypePublishCommand = rawPublisherViewModel.PublishCommand; messageTypePublishCommand = rawPublisherViewModel.PublishCommand;
break; break;
case MessageType.Tapeti: case PublisherMessageType.Tapeti:
TapetiPublisherViewModel tapetiPublisherViewModel;
if (tapetiPublisherView == null) if (tapetiPublisherView == null)
{ {
tapetiPublisherViewModel = new TapetiPublisherViewModel(connection, this, exampleGenerator, payloadMacroProcessor); tapetiPublisherViewModel = new TapetiPublisherViewModel(connection, this, exampleGenerator, payloadMacroProcessor);
@ -237,13 +355,15 @@ namespace PettingZoo.UI.Tab.Publisher
publishCommand.RaiseCanExecuteChanged(); publishCommand.RaiseCanExecuteChanged();
}; };
tapetiPublisherView ??= new TapetiPublisherView(tapetiPublisherViewModel); tapetiPublisherViewModel.PropertyChanged += (_, _) => { saveCommand.RaiseCanExecuteChanged(); };
tapetiPublisherView = new TapetiPublisherView(tapetiPublisherViewModel);
if (tabHostWindow != null) if (tabHostWindow != null)
tapetiPublisherViewModel.HostWindowChanged(tabHostWindow); tapetiPublisherViewModel.HostWindowChanged(tabHostWindow);
} }
else else
tapetiPublisherViewModel = (TapetiPublisherViewModel)tapetiPublisherView.DataContext; Debug.Assert(tapetiPublisherViewModel != null);
MessageTypeControl = tapetiPublisherView; MessageTypeControl = tapetiPublisherView;
@ -266,17 +386,17 @@ namespace PettingZoo.UI.Tab.Publisher
if (TapetiPublisherViewModel.IsTapetiMessage(fromReceivedMessage)) if (TapetiPublisherViewModel.IsTapetiMessage(fromReceivedMessage))
{ {
var tapetiPublisherViewModel = new TapetiPublisherViewModel(connection, this, exampleGenerator, payloadMacroProcessor, fromReceivedMessage); tapetiPublisherViewModel = new TapetiPublisherViewModel(connection, this, exampleGenerator, payloadMacroProcessor, fromReceivedMessage);
tapetiPublisherView = new TapetiPublisherView(tapetiPublisherViewModel); tapetiPublisherView = new TapetiPublisherView(tapetiPublisherViewModel);
MessageType = MessageType.Tapeti; MessageType = PublisherMessageType.Tapeti;
} }
else else
{ {
var rawPublisherViewModel = new RawPublisherViewModel(connection, this, payloadMacroProcessor, fromReceivedMessage); rawPublisherViewModel = new RawPublisherViewModel(connection, this, payloadMacroProcessor, fromReceivedMessage);
rawPublisherView = new RawPublisherView(rawPublisherViewModel); rawPublisherView = new RawPublisherView(rawPublisherViewModel);
MessageType = MessageType.Raw; MessageType = PublisherMessageType.Raw;
} }
} }
@ -304,16 +424,234 @@ namespace PettingZoo.UI.Tab.Publisher
(tapetiPublisherView?.DataContext as TapetiPublisherViewModel)?.HostWindowChanged(hostWindow); (tapetiPublisherView?.DataContext as TapetiPublisherViewModel)?.HostWindowChanged(hostWindow);
} }
private bool SaveCanExecute()
{
if (disableCheckCanSave)
return false;
return ActiveStoredMessage != null && !GetPublisherMessage().Equals(ActiveStoredMessage.Message);
}
private void SaveExecute()
{
if (ActiveStoredMessage == null)
return;
storedPublisherMessagesViewModel.Save(ActiveStoredMessage, GetPublisherMessage(), message =>
{
ActiveStoredMessage = message;
SelectedStoredMessage = message;
saveCommand.RaiseCanExecuteChanged();
});
}
private void SaveAsExecute()
{
storedPublisherMessagesViewModel.SaveAs(GetPublisherMessage(), ActiveStoredMessage?.DisplayName, message =>
{
ActiveStoredMessage = message;
SelectedStoredMessage = message;
});
}
private void DeleteExecute()
{
if (SelectedStoredMessage == null)
return;
var message = SelectedStoredMessage;
storedPublisherMessagesViewModel.Delete(message, () =>
{
if (SelectedStoredMessage == message)
SelectedStoredMessage = null;
if (ActiveStoredMessage == message)
ActiveStoredMessage = null;
});
}
private bool SelectedMessageCanExecute()
{
return SelectedStoredMessage != null;
}
private void LoadStoredMessageExecute()
{
if (SelectedStoredMessage == null)
return;
var message = SelectedStoredMessage.Message;
disableCheckCanSave = true;
try
{
MessageType = message.MessageType;
SendToExchange = message.SendToExchange;
Exchange = message.Exchange ?? "";
RoutingKey = message.RoutingKey ?? "";
Queue = message.Queue ?? "";
ReplyToNewSubscriber = message.ReplyToNewSubscriber;
ReplyTo = message.ReplyTo ?? "";
// ReSharper disable once SwitchStatementHandlesSomeKnownEnumValuesWithDefault
switch (message.MessageType)
{
case PublisherMessageType.Raw:
if (message.RawPublisherMessage != null)
rawPublisherViewModel?.LoadPublisherMessage(message.RawPublisherMessage);
break;
case PublisherMessageType.Tapeti:
if (message.TapetiPublisherMessage != null)
tapetiPublisherViewModel?.LoadPublisherMessage(message.TapetiPublisherMessage);
break;
}
ActiveStoredMessage = SelectedStoredMessage;
}
finally
{
disableCheckCanSave = false;
saveCommand.RaiseCanExecuteChanged();
}
}
private static readonly JsonSerializerSettings ExportImportSettings = new()
{
Converters = new List<JsonConverter> { new StringEnumConverter() }
};
private void ExportExecute()
{
if (SelectedStoredMessage == null)
return;
var invalidChars = Regex.Escape(new string(Path.GetInvalidFileNameChars()));
var invalidRegStr = string.Format(@"([{0}]*\.+$)|([{0}]+)", invalidChars);
var suggestedFilename = Regex.Replace(SelectedStoredMessage.DisplayName, invalidRegStr, "_");
var dialog = new SaveFileDialog
{
Filter = PublisherViewStrings.StoredMessagesExportImportFilter,
FileName = suggestedFilename
};
if (dialog.ShowDialog() != DialogResult.OK)
return;
File.WriteAllText(dialog.FileName, JsonConvert.SerializeObject(SelectedStoredMessage.Message, ExportImportSettings), Encoding.UTF8);
}
private void ImportExecute()
{
var dialog = new OpenFileDialog
{
Filter = PublisherViewStrings.StoredMessagesExportImportFilter
};
if (dialog.ShowDialog() != DialogResult.OK)
return;
var fileContents = File.ReadAllText(dialog.FileName, Encoding.UTF8);
var message = JsonConvert.DeserializeObject<PublisherMessage>(fileContents, ExportImportSettings);
if (message == null)
return;
var displayName = dialog.FileName.EndsWith(".pubmsg.json")
? Path.GetFileName(dialog.FileName)[..^".pubmsg.json".Length]
: Path.GetFileNameWithoutExtension(dialog.FileName);
storedPublisherMessagesViewModel.SaveAs(message, displayName, storedMessage =>
{
SelectedStoredMessage = storedMessage;
});
}
private PublisherMessage GetPublisherMessage()
{
return new PublisherMessage
{
MessageType = MessageType,
SendToExchange = SendToExchange,
Exchange = Exchange,
RoutingKey = RoutingKey,
Queue = Queue,
ReplyToNewSubscriber = ReplyToNewSubscriber,
ReplyTo = ReplyTo,
RawPublisherMessage = MessageType == PublisherMessageType.Raw
? rawPublisherViewModel?.GetPublisherMessage()
: null,
TapetiPublisherMessage = MessageType == PublisherMessageType.Tapeti
? tapetiPublisherViewModel?.GetPublisherMessage()
: null
};
}
} }
public class DesignTimePublisherViewModel : PublisherViewModel public class DesignTimePublisherViewModel : PublisherViewModel
{ {
public DesignTimePublisherViewModel() : base(null!, null!, null!, null!) public DesignTimePublisherViewModel() : base(null!, null!, null!, null!, new StoredPublisherMessagesViewModel(new DesignTimePublisherMessagesRepository()))
{ {
StoredMessages.CollectionChanged += (_, _) =>
{
if (StoredMessages.Count < 2)
return;
SelectedStoredMessage = StoredMessages[0];
ActiveStoredMessage = StoredMessages[1];
};
PublishedVisibility = Visibility.Visible;
} }
public override Visibility ExchangeVisibility => Visibility.Visible; public override Visibility ExchangeVisibility => Visibility.Visible;
public override Visibility QueueVisibility => Visibility.Visible; public override Visibility QueueVisibility => Visibility.Visible;
private class DesignTimePublisherMessagesRepository : IPublisherMessagesRepository
{
public Task<IEnumerable<StoredPublisherMessage>> GetStored()
{
return Task.FromResult(new StoredPublisherMessage[]
{
new(new Guid("16fdf930-2e4c-48f4-ae21-68dac9ca62e6"), "Design-time message 1", new PublisherMessage()),
new(new Guid("01d2671b-4426-4c1c-bcbc-61689d14796e"), "Design-time message 2", new PublisherMessage())
} as IEnumerable<StoredPublisherMessage>);
}
public Task<StoredPublisherMessage> Add(string displayName, PublisherMessage message)
{
throw new NotSupportedException();
}
public Task<StoredPublisherMessage> Update(Guid id, string displayName, PublisherMessage message)
{
throw new NotSupportedException();
}
public Task Delete(Guid id)
{
throw new NotSupportedException();
}
}
} }
} }

View File

@ -168,6 +168,24 @@ namespace PettingZoo.UI.Tab.Publisher {
} }
} }
/// <summary>
/// Looks up a localized string similar to Saved messages.
/// </summary>
public static string PanelTitleMessages {
get {
return ResourceManager.GetString("PanelTitleMessages", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Message published.
/// </summary>
public static string Published {
get {
return ResourceManager.GetString("Published", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Re: . /// Looks up a localized string similar to Re: .
/// </summary> /// </summary>
@ -177,6 +195,15 @@ namespace PettingZoo.UI.Tab.Publisher {
} }
} }
/// <summary>
/// Looks up a localized string similar to PettingZoo message (*.pubmsg.json)|*.pubmsg.json.
/// </summary>
public static string StoredMessagesExportImportFilter {
get {
return ResourceManager.GetString("StoredMessagesExportImportFilter", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Publish: {0}. /// Looks up a localized string similar to Publish: {0}.
/// </summary> /// </summary>
@ -194,5 +221,50 @@ namespace PettingZoo.UI.Tab.Publisher {
return ResourceManager.GetString("TabTitleEmpty", resourceCulture); return ResourceManager.GetString("TabTitleEmpty", resourceCulture);
} }
} }
/// <summary>
/// Looks up a localized string similar to Delete.
/// </summary>
public static string ToolbarDelete {
get {
return ResourceManager.GetString("ToolbarDelete", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Export....
/// </summary>
public static string ToolbarExport {
get {
return ResourceManager.GetString("ToolbarExport", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Import....
/// </summary>
public static string ToolbarImport {
get {
return ResourceManager.GetString("ToolbarImport", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Save.
/// </summary>
public static string ToolbarSave {
get {
return ResourceManager.GetString("ToolbarSave", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Save as....
/// </summary>
public static string ToolbarSaveAs {
get {
return ResourceManager.GetString("ToolbarSaveAs", resourceCulture);
}
}
} }
} }

View File

@ -153,13 +153,37 @@
<data name="OptionMessageTypeTapeti" xml:space="preserve"> <data name="OptionMessageTypeTapeti" xml:space="preserve">
<value>Tapeti message</value> <value>Tapeti message</value>
</data> </data>
<data name="PanelTitleMessages" xml:space="preserve">
<value>Saved messages</value>
</data>
<data name="Published" xml:space="preserve">
<value>Message published</value>
</data>
<data name="ReplyToCorrelationIdPrefix" xml:space="preserve"> <data name="ReplyToCorrelationIdPrefix" xml:space="preserve">
<value>Re: </value> <value>Re: </value>
</data> </data>
<data name="StoredMessagesExportImportFilter" xml:space="preserve">
<value>PettingZoo message (*.pubmsg.json)|*.pubmsg.json</value>
</data>
<data name="TabTitle" xml:space="preserve"> <data name="TabTitle" xml:space="preserve">
<value>Publish: {0}</value> <value>Publish: {0}</value>
</data> </data>
<data name="TabTitleEmpty" xml:space="preserve"> <data name="TabTitleEmpty" xml:space="preserve">
<value>Publish</value> <value>Publish</value>
</data> </data>
<data name="ToolbarDelete" xml:space="preserve">
<value>Delete</value>
</data>
<data name="ToolbarExport" xml:space="preserve">
<value>Export...</value>
</data>
<data name="ToolbarImport" xml:space="preserve">
<value>Import...</value>
</data>
<data name="ToolbarSave" xml:space="preserve">
<value>Save</value>
</data>
<data name="ToolbarSaveAs" xml:space="preserve">
<value>Save as...</value>
</data>
</root> </root>

View File

@ -3,7 +3,6 @@ using System.Linq;
using System.Windows; using System.Windows;
using System.Windows.Input; using System.Windows.Input;
using System.Windows.Threading; using System.Windows.Threading;
using PettingZoo.Core.Macros;
namespace PettingZoo.UI.Tab.Publisher namespace PettingZoo.UI.Tab.Publisher
{ {

View File

@ -1,5 +1,4 @@
using System; using System;
using System.Collections.ObjectModel;
using System.ComponentModel; using System.ComponentModel;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Linq; using System.Linq;
@ -7,6 +6,7 @@ using System.Text;
using System.Windows; using System.Windows;
using System.Windows.Input; using System.Windows.Input;
using PettingZoo.Core.Connection; using PettingZoo.Core.Connection;
using PettingZoo.Core.ExportImport.Publisher;
using PettingZoo.Core.Macros; using PettingZoo.Core.Macros;
using PettingZoo.WPF.ViewModel; using PettingZoo.WPF.ViewModel;
@ -37,10 +37,17 @@ namespace PettingZoo.UI.Tab.Publisher
public MessageDeliveryMode DeliveryMode
{
get => deliveryMode;
set => SetField(ref deliveryMode, value, otherPropertiesChanged: new[] { nameof(DeliveryModeIndex) });
}
public int DeliveryModeIndex public int DeliveryModeIndex
{ {
get => deliveryMode == MessageDeliveryMode.Persistent ? 1 : 0; get => DeliveryMode == MessageDeliveryMode.Persistent ? 1 : 0;
set => SetField(ref deliveryMode, value == 1 ? MessageDeliveryMode.Persistent : MessageDeliveryMode.NonPersistent); set => DeliveryMode = value == 1 ? MessageDeliveryMode.Persistent : MessageDeliveryMode.NonPersistent;
} }
@ -127,7 +134,7 @@ namespace PettingZoo.UI.Tab.Publisher
} }
public ObservableCollection<Header> Headers { get; } = new(); public ObservableCollectionEx<Header> Headers { get; } = new();
public ICommand PublishCommand => publishCommand; public ICommand PublishCommand => publishCommand;
@ -194,6 +201,60 @@ namespace PettingZoo.UI.Tab.Publisher
} }
public RawPublisherMessage GetPublisherMessage()
{
return new RawPublisherMessage
{
DeliveryMode = DeliveryMode,
ContentType = ContentType,
CorrelationId = CorrelationId,
AppId = AppId,
ContentEncoding = ContentEncoding,
Expiration = Expiration,
MessageId = MessageId,
Priority = Priority,
Timestamp = Timestamp,
TypeProperty = TypeProperty,
UserId = UserId,
Payload = Payload,
EnableMacros = EnableMacros,
Headers = Headers.Where(h => !h.IsEmpty()).ToDictionary(h => h.Key, h => h.Value)
};
}
public void LoadPublisherMessage(RawPublisherMessage message)
{
DeliveryMode = message.DeliveryMode;
ContentType = message.ContentType ?? "";
CorrelationId = message.CorrelationId ?? "";
AppId = message.AppId ?? "";
ContentEncoding = message.ContentEncoding ?? "";
Expiration = message.Expiration ?? "";
MessageId = message.MessageId ?? "";
Priority = message.Priority ?? "";
Timestamp = message.Timestamp ?? "";
TypeProperty = message.TypeProperty ?? "";
UserId = message.UserId ?? "";
Payload = message.Payload ?? "";
EnableMacros = message.EnableMacros;
if (message.Headers != null)
{
Headers.ReplaceAll(message.Headers.Select(p => new Header
{
Key = p.Key,
Value = p.Value
}));
}
else
Headers.Clear();
AddHeader();
}
private static bool AnyNotEmpty(params string?[] values) private static bool AnyNotEmpty(params string?[] values)
{ {
return values.Any(s => !string.IsNullOrEmpty(s)); return values.Any(s => !string.IsNullOrEmpty(s));

View File

@ -0,0 +1,90 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace PettingZoo.UI.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", "17.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class StoredPublisherMessagesStrings {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal StoredPublisherMessagesStrings() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("PettingZoo.UI.Tab.Publisher.StoredPublisherMessagesStrings", typeof(StoredPublisherMessagesStrings).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// Looks up a localized string similar to Do you want to delete the saved message &apos;{0}&apos;?.
/// </summary>
internal static string DeleteConfirmation {
get {
return ResourceManager.GetString("DeleteConfirmation", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Delete message.
/// </summary>
internal static string DeleteConfirmationTitle {
get {
return ResourceManager.GetString("DeleteConfirmationTitle", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Save as....
/// </summary>
internal static string DisplayNameDialogTitle {
get {
return ResourceManager.GetString("DisplayNameDialogTitle", resourceCulture);
}
}
}
}

View File

@ -0,0 +1,129 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="DeleteConfirmation" xml:space="preserve">
<value>Do you want to delete the saved message '{0}'?</value>
</data>
<data name="DeleteConfirmationTitle" xml:space="preserve">
<value>Delete message</value>
</data>
<data name="DisplayNameDialogTitle" xml:space="preserve">
<value>Save as...</value>
</data>
</root>

View File

@ -0,0 +1,96 @@
using System;
using System.Threading.Tasks;
using System.Windows;
using PettingZoo.Core.ExportImport.Publisher;
using PettingZoo.Core.Settings;
using PettingZoo.WPF.ViewModel;
namespace PettingZoo.UI.Tab.Publisher
{
public class StoredPublisherMessagesViewModel
{
private readonly IPublisherMessagesRepository publisherMessagesRepository;
public ObservableCollectionEx<StoredPublisherMessage> StoredMessages { get; } = new();
public StoredPublisherMessagesViewModel(IPublisherMessagesRepository publisherMessagesRepository)
{
this.publisherMessagesRepository = publisherMessagesRepository;
Task.Run(async () =>
{
var messages = await publisherMessagesRepository.GetStored();
await Application.Current.Dispatcher.BeginInvoke(() =>
{
StoredMessages.ReplaceAll(messages);
});
});
}
public void Save(StoredPublisherMessage overwriteMessage, PublisherMessage message, Action<StoredPublisherMessage> onSaved)
{
Task.Run(async () =>
{
var updatedMessage = await publisherMessagesRepository.Update(overwriteMessage.Id, overwriteMessage.DisplayName, message);
await Application.Current.Dispatcher.BeginInvoke(() =>
{
var index = StoredMessages.IndexOf(overwriteMessage);
if (index >= 0)
StoredMessages[index] = updatedMessage;
else
// Should not occur, but might as well handle it gracefully
StoredMessages.Add(updatedMessage);
onSaved(updatedMessage);
});
});
}
public void SaveAs(PublisherMessage message, string? originalDisplayName, Action<StoredPublisherMessage> onSaved)
{
var displayName = originalDisplayName ?? "";
if (!InputDialog.Execute(ref displayName, StoredPublisherMessagesStrings.DisplayNameDialogTitle))
return;
Task.Run(async () =>
{
var storedMessage = await publisherMessagesRepository.Add(displayName, message);
await Application.Current.Dispatcher.BeginInvoke(() =>
{
StoredMessages.Add(storedMessage);
onSaved(storedMessage);
});
});
}
public void Delete(StoredPublisherMessage message, Action onDeleted)
{
if (MessageBox.Show(
string.Format(StoredPublisherMessagesStrings.DeleteConfirmation, message.DisplayName),
StoredPublisherMessagesStrings.DeleteConfirmationTitle,
MessageBoxButton.YesNo,
MessageBoxImage.Question) != MessageBoxResult.Yes)
return;
Task.Run(async () =>
{
await publisherMessagesRepository.Delete(message.Id);
await Application.Current.Dispatcher.BeginInvoke(() =>
{
StoredMessages.Remove(message);
onDeleted();
});
});
}
}
}

View File

@ -1,6 +1,5 @@
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using PettingZoo.Core.Macros;
namespace PettingZoo.UI.Tab.Publisher namespace PettingZoo.UI.Tab.Publisher
{ {

View File

@ -3,6 +3,7 @@ using System.Text;
using System.Windows; using System.Windows;
using System.Windows.Input; using System.Windows.Input;
using PettingZoo.Core.Connection; using PettingZoo.Core.Connection;
using PettingZoo.Core.ExportImport.Publisher;
using PettingZoo.Core.Generator; using PettingZoo.Core.Generator;
using PettingZoo.Core.Macros; using PettingZoo.Core.Macros;
using PettingZoo.Core.Validation; using PettingZoo.Core.Validation;
@ -132,6 +133,29 @@ namespace PettingZoo.UI.Tab.Publisher
} }
public TapetiPublisherMessage GetPublisherMessage()
{
return new TapetiPublisherMessage
{
CorrelationId = CorrelationId,
Payload = Payload,
EnableMacros = EnableMacros,
ClassName = ClassName,
AssemblyName = AssemblyName
};
}
public void LoadPublisherMessage(TapetiPublisherMessage message)
{
CorrelationId = message.CorrelationId ?? "";
Payload = message.Payload ?? "";
EnableMacros = message.EnableMacros;
ClassName = message.ClassName ?? "";
AssemblyName = message.AssemblyName ?? "";
}
private void BrowseClassExecute() private void BrowseClassExecute()
{ {
exampleGenerator.Select(tabHostWindow, example => exampleGenerator.Select(tabHostWindow, example =>

View File

@ -8,6 +8,7 @@
xmlns:svgc="http://sharpvectors.codeplex.com/svgc/" xmlns:svgc="http://sharpvectors.codeplex.com/svgc/"
xmlns:avalonedit="http://icsharpcode.net/sharpdevelop/avalonedit" xmlns:avalonedit="http://icsharpcode.net/sharpdevelop/avalonedit"
xmlns:connection="clr-namespace:PettingZoo.Core.Connection;assembly=PettingZoo.Core" xmlns:connection="clr-namespace:PettingZoo.Core.Connection;assembly=PettingZoo.Core"
xmlns:controls="clr-namespace:PettingZoo.WPF.Controls;assembly=PettingZoo.WPF"
mc:Ignorable="d" mc:Ignorable="d"
d:DesignHeight="450" d:DesignHeight="450"
d:DesignWidth="800" d:DesignWidth="800"
@ -36,8 +37,8 @@
<ListBox.ItemsSource> <ListBox.ItemsSource>
<CompositeCollection> <CompositeCollection>
<CollectionContainer Collection="{Binding Source={StaticResource Messages}}" /> <CollectionContainer Collection="{Binding Source={StaticResource Messages}}" />
<ListBoxItem HorizontalContentAlignment="Stretch" IsEnabled="False" IsHitTestVisible="False"> <ListBoxItem HorizontalContentAlignment="Stretch" IsEnabled="False" IsHitTestVisible="False" Visibility="{Binding UnreadMessagesVisibility}">
<Grid Visibility="{Binding UnreadMessagesVisibility}"> <Grid>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="DateTime" /> <ColumnDefinition SharedSizeGroup="DateTime" />
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" />
@ -50,6 +51,17 @@
</Grid> </Grid>
</ListBoxItem> </ListBoxItem>
<CollectionContainer Collection="{Binding Source={StaticResource UnreadMessages}}" /> <CollectionContainer Collection="{Binding Source={StaticResource UnreadMessages}}" />
<ListBoxItem HorizontalContentAlignment="Stretch" IsEnabled="False" IsHitTestVisible="False" Visibility="{Binding StatusVisibility}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="DateTime" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="1" Text="{Binding StatusText}" HorizontalAlignment="Center" Background="{Binding Background, RelativeSource={RelativeSource AncestorType={x:Type ListBoxItem}}}" Foreground="{x:Static SystemColors.GrayTextBrush}" />
</Grid>
</ListBoxItem>
</CompositeCollection> </CompositeCollection>
</ListBox.ItemsSource> </ListBox.ItemsSource>
<ListBox.ItemTemplate> <ListBox.ItemTemplate>
@ -82,14 +94,14 @@
<RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/>
<RowDefinition Height="200"/> <RowDefinition Height="200"/>
</Grid.RowDefinitions> </Grid.RowDefinitions>
<ToolBar Grid.Column="0" Grid.Row="0" ToolBarTray.IsLocked="True" Margin="0,0,0,4" Background="Transparent" Loaded="Toolbar_Loaded"> <controls:AutoOverflowToolBar Grid.Column="0" Grid.Row="0" ToolBarTray.IsLocked="True" Margin="0,0,0,4" Background="Transparent">
<Button Command="{Binding CreatePublisherCommand}"> <Button Command="{Binding CreatePublisherCommand}">
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<Image Source="{svgc:SvgImage Source=/Images/Publish.svg, AppName=PettingZoo}" Width="16" Height="16" Style="{StaticResource ToolbarIcon}"/> <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 res:SubscriberViewStrings.ContextPublish}" /> <TextBlock Margin="3,0,0,0" Text="{x:Static res:SubscriberViewStrings.ContextPublish}" />
</StackPanel> </StackPanel>
</Button> </Button>
</ToolBar> </controls:AutoOverflowToolBar>
<Border Grid.Column="0" Grid.Row="1" Style="{StaticResource SidePanel}"> <Border Grid.Column="0" Grid.Row="1" Style="{StaticResource SidePanel}">
<DockPanel> <DockPanel>

View File

@ -1,6 +1,4 @@
using System.Windows; using System.Windows.Media;
using System.Windows.Controls;
using System.Windows.Media;
using ICSharpCode.AvalonEdit.Highlighting; using ICSharpCode.AvalonEdit.Highlighting;
namespace PettingZoo.UI.Tab.Subscriber namespace PettingZoo.UI.Tab.Subscriber
@ -35,17 +33,5 @@ namespace PettingZoo.UI.Tab.Subscriber
if (!System.ComponentModel.DesignerProperties.GetIsInDesignMode(this)) if (!System.ComponentModel.DesignerProperties.GetIsInDesignMode(this))
Background = Brushes.Transparent; Background = Brushes.Transparent;
} }
private void Toolbar_Loaded(object sender, RoutedEventArgs e)
{
// Hide arrow on the right side of the toolbar
var toolBar = sender as ToolBar;
if (toolBar?.Template.FindName("OverflowGrid", toolBar) is FrameworkElement overflowGrid)
overflowGrid.Visibility = Visibility.Collapsed;
if (toolBar?.Template.FindName("MainPanelBorder", toolBar) is FrameworkElement mainPanelBorder)
mainPanelBorder.Margin = new Thickness(0);
}
} }
} }

View File

@ -9,7 +9,7 @@ using System.Windows;
using System.Windows.Forms; using System.Windows.Forms;
using System.Windows.Input; using System.Windows.Input;
using PettingZoo.Core.Connection; using PettingZoo.Core.Connection;
using PettingZoo.Core.ExportImport; using PettingZoo.Core.ExportImport.Subscriber;
using PettingZoo.Core.Rendering; using PettingZoo.Core.Rendering;
using PettingZoo.WPF.ProgressWindow; using PettingZoo.WPF.ProgressWindow;
using PettingZoo.WPF.ViewModel; using PettingZoo.WPF.ViewModel;
@ -25,7 +25,7 @@ namespace PettingZoo.UI.Tab.Subscriber
{ {
private readonly ILogger logger; private readonly ILogger logger;
private readonly ITabFactory tabFactory; private readonly ITabFactory tabFactory;
private readonly IConnection? connection; private readonly IConnection connection;
private readonly ISubscriber subscriber; private readonly ISubscriber subscriber;
private readonly IExportImportFormatProvider exportImportFormatProvider; private readonly IExportImportFormatProvider exportImportFormatProvider;
private ReceivedMessageInfo? selectedMessage; private ReceivedMessageInfo? selectedMessage;
@ -106,8 +106,14 @@ namespace PettingZoo.UI.Tab.Subscriber
public Visibility ReplyTabVisibility => IsReplyTab ? Visibility.Visible : Visibility.Collapsed; public Visibility ReplyTabVisibility => IsReplyTab ? Visibility.Visible : Visibility.Collapsed;
// ReSharper restore UnusedMember.Global // ReSharper restore UnusedMember.Global
private readonly Guid subscribeConnectionId;
private ConnectionStatus connectionStatus = ConnectionStatus.Connecting;
public SubscriberViewModel(ILogger logger, ITabFactory tabFactory, IConnection? connection, ISubscriber subscriber, IExportImportFormatProvider exportImportFormatProvider, bool isReplyTab) public Visibility StatusVisibility => connectionStatus is ConnectionStatus.Connecting or ConnectionStatus.Disconnected or ConnectionStatus.Error ? Visibility.Visible : Visibility.Collapsed;
public string StatusText => connectionStatus == ConnectionStatus.Connecting ? SubscriberViewStrings.StatusConnecting : SubscriberViewStrings.StatusDisconnected;
public SubscriberViewModel(ILogger logger, ITabFactory tabFactory, IConnection connection, ISubscriber subscriber, IExportImportFormatProvider exportImportFormatProvider, bool isReplyTab)
{ {
IsReplyTab = isReplyTab; IsReplyTab = isReplyTab;
@ -133,6 +139,8 @@ namespace PettingZoo.UI.Tab.Subscriber
createPublisherCommand = new DelegateCommand(CreatePublisherExecute, CreatePublisherCanExecute); createPublisherCommand = new DelegateCommand(CreatePublisherExecute, CreatePublisherCanExecute);
subscribeConnectionId = connection.ConnectionId;
connection.StatusChanged += ConnectionStatusChanged;
subscriber.MessageReceived += SubscriberMessageReceived; subscriber.MessageReceived += SubscriberMessageReceived;
subscriber.Start(); subscriber.Start();
} }
@ -141,11 +149,47 @@ namespace PettingZoo.UI.Tab.Subscriber
public void Dispose() public void Dispose()
{ {
GC.SuppressFinalize(this); GC.SuppressFinalize(this);
connection.StatusChanged -= ConnectionStatusChanged;
newMessageTimer?.Dispose(); newMessageTimer?.Dispose();
subscriber.Dispose(); subscriber.Dispose();
} }
private void ConnectionStatusChanged(object? sender, StatusChangedEventArgs e)
{
// If another connection has been made, this does not concern us
if (e.ConnectionId != subscribeConnectionId)
return;
// The subscriber will not reconnect, so after the first disconnect it's over for us
if (connectionStatus is ConnectionStatus.Disconnected)
return;
switch (e.Status)
{
case ConnectionStatus.Connecting:
case ConnectionStatus.Error:
return;
case ConnectionStatus.Connected:
connectionStatus = ConnectionStatus.Connected;
break;
case ConnectionStatus.Disconnected:
default:
connectionStatus = ConnectionStatus.Disconnected;
break;
}
Application.Current.Dispatcher.BeginInvoke(() =>
{
RaisePropertyChanged(nameof(StatusVisibility));
RaisePropertyChanged(nameof(StatusText));
});
}
private void ClearExecute() private void ClearExecute()
{ {
Messages.Clear(); Messages.Clear();
@ -254,7 +298,7 @@ namespace PettingZoo.UI.Tab.Subscriber
private void CreatePublisherExecute() private void CreatePublisherExecute()
{ {
if (connection == null) if (connection.Status != ConnectionStatus.Connected)
return; return;
tabFactory.CreatePublisherTab(connection, SelectedMessage); tabFactory.CreatePublisherTab(connection, SelectedMessage);
@ -263,7 +307,7 @@ namespace PettingZoo.UI.Tab.Subscriber
private bool CreatePublisherCanExecute() private bool CreatePublisherCanExecute()
{ {
return connection != null && SelectedMessage != null; return SelectedMessage != null;
} }

View File

@ -194,5 +194,23 @@ namespace PettingZoo.UI.Tab.Subscriber {
return ResourceManager.GetString("ReplyTabTitle", resourceCulture); return ResourceManager.GetString("ReplyTabTitle", resourceCulture);
} }
} }
/// <summary>
/// Looks up a localized string similar to Connecting....
/// </summary>
public static string StatusConnecting {
get {
return ResourceManager.GetString("StatusConnecting", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Disconnected.
/// </summary>
public static string StatusDisconnected {
get {
return ResourceManager.GetString("StatusDisconnected", resourceCulture);
}
}
} }
} }

View File

@ -162,4 +162,10 @@
<data name="ReplyTabTitle" xml:space="preserve"> <data name="ReplyTabTitle" xml:space="preserve">
<value>Replies</value> <value>Replies</value>
</data> </data>
<data name="StatusConnecting" xml:space="preserve">
<value>Connecting...</value>
</data>
<data name="StatusDisconnected" xml:space="preserve">
<value>Disconnected</value>
</data>
</root> </root>

View File

@ -5,6 +5,7 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:undocked="clr-namespace:PettingZoo.UI.Tab.Undocked" xmlns:undocked="clr-namespace:PettingZoo.UI.Tab.Undocked"
xmlns:svgc="http://sharpvectors.codeplex.com/svgc/" xmlns:svgc="http://sharpvectors.codeplex.com/svgc/"
xmlns:controls="clr-namespace:PettingZoo.WPF.Controls;assembly=PettingZoo.WPF"
mc:Ignorable="d" mc:Ignorable="d"
d:DataContext="{d:DesignInstance undocked:DesignTimeUndockedTabHostViewModel, IsDesignTimeCreatable=True}" d:DataContext="{d:DesignInstance undocked:DesignTimeUndockedTabHostViewModel, IsDesignTimeCreatable=True}"
Title="{Binding Title}" Title="{Binding Title}"
@ -12,7 +13,7 @@
Width="800" Width="800"
WindowStyle="ThreeDBorderWindow"> WindowStyle="ThreeDBorderWindow">
<DockPanel> <DockPanel>
<ToolBar DockPanel.Dock="Top" ToolBarTray.IsLocked="True" Loaded="Toolbar_Loaded"> <controls:AutoOverflowToolBar DockPanel.Dock="Top" ToolBarTray.IsLocked="True">
<Button Command="{Binding DockCommand}"> <Button Command="{Binding DockCommand}">
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<Image Source="{svgc:SvgImage Source=/Images/Dock.svg, AppName=PettingZoo}" Width="16" Height="16" Style="{StaticResource ToolbarIcon}"/> <Image Source="{svgc:SvgImage Source=/Images/Dock.svg, AppName=PettingZoo}" Width="16" Height="16" Style="{StaticResource ToolbarIcon}"/>
@ -37,7 +38,7 @@
</DataTemplate> </DataTemplate>
</ItemsControl.ItemTemplate> </ItemsControl.ItemTemplate>
</ItemsControl> </ItemsControl>
</ToolBar> </controls:AutoOverflowToolBar>
<ContentControl Content="{Binding Content}" /> <ContentControl Content="{Binding Content}" />
</DockPanel> </DockPanel>

View File

@ -1,7 +1,4 @@
using System.Windows; namespace PettingZoo.UI.Tab.Undocked
using System.Windows.Controls;
namespace PettingZoo.UI.Tab.Undocked
{ {
/// <summary> /// <summary>
/// Interaction logic for UndockedTabHostWindow.xaml /// Interaction logic for UndockedTabHostWindow.xaml
@ -41,17 +38,5 @@ namespace PettingZoo.UI.Tab.Undocked
viewModel.Deactivate(); viewModel.Deactivate();
}; };
} }
private void Toolbar_Loaded(object sender, RoutedEventArgs e)
{
// Hide arrow on the right side of the toolbar
var toolBar = sender as ToolBar;
if (toolBar?.Template.FindName("OverflowGrid", toolBar) is FrameworkElement overflowGrid)
overflowGrid.Visibility = Visibility.Collapsed;
if (toolBar?.Template.FindName("MainPanelBorder", toolBar) is FrameworkElement mainPanelBorder)
mainPanelBorder.Margin = new Thickness(0);
}
} }
} }

View File

@ -1,7 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using PettingZoo.Core.Connection; using PettingZoo.Core.Connection;
using PettingZoo.Core.ExportImport; using PettingZoo.Core.ExportImport.Subscriber;
using PettingZoo.Core.Generator; using PettingZoo.Core.Generator;
using PettingZoo.Core.Macros; using PettingZoo.Core.Macros;
using PettingZoo.UI.Tab.Publisher; using PettingZoo.UI.Tab.Publisher;
@ -17,6 +17,7 @@ namespace PettingZoo.UI.Tab
private readonly IExampleGenerator exampleGenerator; private readonly IExampleGenerator exampleGenerator;
private readonly IExportImportFormatProvider exportImportFormatProvider; private readonly IExportImportFormatProvider exportImportFormatProvider;
private readonly IPayloadMacroProcessor payloadMacroProcessor; private readonly IPayloadMacroProcessor payloadMacroProcessor;
private readonly StoredPublisherMessagesViewModel storedPublisherMessagesViewModel;
// Not the cleanest way, but this factory itself can't be singleton without (justifyable) upsetting SimpleInjector // Not the cleanest way, but this factory itself can't be singleton without (justifyable) upsetting SimpleInjector
private static ISubscriber? replySubscriber; private static ISubscriber? replySubscriber;
@ -24,17 +25,18 @@ namespace PettingZoo.UI.Tab
public ViewTabFactory(ILogger logger, ITabHostProvider tabHostProvider, IExampleGenerator exampleGenerator, IExportImportFormatProvider exportImportFormatProvider, public ViewTabFactory(ILogger logger, ITabHostProvider tabHostProvider, IExampleGenerator exampleGenerator, IExportImportFormatProvider exportImportFormatProvider,
IPayloadMacroProcessor payloadMacroProcessor) IPayloadMacroProcessor payloadMacroProcessor, StoredPublisherMessagesViewModel storedPublisherMessagesViewModel)
{ {
this.logger = logger; this.logger = logger;
this.tabHostProvider = tabHostProvider; this.tabHostProvider = tabHostProvider;
this.exampleGenerator = exampleGenerator; this.exampleGenerator = exampleGenerator;
this.exportImportFormatProvider = exportImportFormatProvider; this.exportImportFormatProvider = exportImportFormatProvider;
this.payloadMacroProcessor = payloadMacroProcessor; this.payloadMacroProcessor = payloadMacroProcessor;
this.storedPublisherMessagesViewModel = storedPublisherMessagesViewModel;
} }
public void CreateSubscriberTab(IConnection? connection, ISubscriber subscriber) public void CreateSubscriberTab(IConnection connection, ISubscriber subscriber)
{ {
InternalCreateSubscriberTab(connection, subscriber, false); InternalCreateSubscriberTab(connection, subscriber, false);
} }
@ -63,7 +65,7 @@ namespace PettingZoo.UI.Tab
public void CreatePublisherTab(IConnection connection, ReceivedMessageInfo? fromReceivedMessage = null) public void CreatePublisherTab(IConnection connection, ReceivedMessageInfo? fromReceivedMessage = null)
{ {
var viewModel = new PublisherViewModel(this, connection, exampleGenerator, payloadMacroProcessor, fromReceivedMessage); var viewModel = new PublisherViewModel(this, connection, exampleGenerator, payloadMacroProcessor, storedPublisherMessagesViewModel, fromReceivedMessage);
var tab = new ViewTab<PublisherView, PublisherViewModel>( var tab = new ViewTab<PublisherView, PublisherViewModel>(
new PublisherView(viewModel), new PublisherView(viewModel),
viewModel, viewModel,
@ -73,7 +75,7 @@ namespace PettingZoo.UI.Tab
} }
private ITab InternalCreateSubscriberTab(IConnection? connection, ISubscriber subscriber, bool isReplyTab) private ITab InternalCreateSubscriberTab(IConnection connection, ISubscriber subscriber, bool isReplyTab)
{ {
var viewModel = new SubscriberViewModel(logger, this, connection, subscriber, exportImportFormatProvider, isReplyTab); var viewModel = new SubscriberViewModel(logger, this, connection, subscriber, exportImportFormatProvider, isReplyTab);
var tab = new ViewTab<SubscriberView, SubscriberViewModel>( var tab = new ViewTab<SubscriberView, SubscriberViewModel>(

View File

@ -2,7 +2,7 @@ image: Visual Studio 2022
install: install:
- choco install gitversion.portable -pre -y - choco install gitversion.portable -pre -y --version 5.12.0
before_build: before_build:
- nuget restore - nuget restore
@ -31,7 +31,7 @@ configuration:
deploy: deploy:
- provider: GitHub - provider: GitHub
auth_token: auth_token:
secure: dWOConKg3VTPvd9DmWOOKiX1SJCalaqKInuk9GlKQOZX2s+Bia49J7q+AHO8wFj7 secure: l7meLHe4l/JZh+J0Gfd+Z+fMviSg3FDz4mIjp+S+PGdPgkpcc45t8Ae3EP2+dTGJkTvhSKNZB5Hmc76DOpfNKfrIQdBlQLDrQVS1nZRunjqL0klHVFRKqvwyH0DK5GK3
artifact: /PettingZoo-.*\.zip/ artifact: /PettingZoo-.*\.zip/
draft: false draft: false
prerelease: false prerelease: false