Implemented JSON syntax checking
Implemented (re)storing of main window position
This commit is contained in:
parent
4b730b2a1a
commit
28d3548088
30
PettingZoo.Core/Settings/IUISettingsRepository.cs
Normal file
30
PettingZoo.Core/Settings/IUISettingsRepository.cs
Normal file
@ -0,0 +1,30 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace PettingZoo.Core.Settings
|
||||
{
|
||||
public interface IUISettingsRepository
|
||||
{
|
||||
Task<MainWindowPositionSettings?> GetMainWindowPosition();
|
||||
Task StoreMainWindowPosition(MainWindowPositionSettings settings);
|
||||
}
|
||||
|
||||
|
||||
public class MainWindowPositionSettings
|
||||
{
|
||||
public int Top { get; }
|
||||
public int Left { get; }
|
||||
public int Width { get; }
|
||||
public int Height { get; }
|
||||
public bool Maximized { get; }
|
||||
|
||||
|
||||
public MainWindowPositionSettings(int top, int left, int width, int height, bool maximized)
|
||||
{
|
||||
Top = top;
|
||||
Left = left;
|
||||
Width = width;
|
||||
Height = height;
|
||||
Maximized = maximized;
|
||||
}
|
||||
}
|
||||
}
|
@ -7,6 +7,11 @@ namespace PettingZoo.Settings.LiteDB
|
||||
{
|
||||
private readonly string databaseFilename;
|
||||
|
||||
protected static readonly BsonMapper Mapper = new()
|
||||
{
|
||||
EmptyStringToNull = false
|
||||
};
|
||||
|
||||
|
||||
public BaseLiteDBRepository(string databaseName)
|
||||
{
|
||||
@ -24,10 +29,7 @@ namespace PettingZoo.Settings.LiteDB
|
||||
|
||||
protected ILiteDatabaseAsync GetDatabase()
|
||||
{
|
||||
return new LiteDatabaseAsync(databaseFilename, new BsonMapper
|
||||
{
|
||||
EmptyStringToNull = false
|
||||
});
|
||||
return new LiteDatabaseAsync(databaseFilename, Mapper);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
84
PettingZoo.Settings.LiteDB/LiteDBUISettingsRepository.cs
Normal file
84
PettingZoo.Settings.LiteDB/LiteDBUISettingsRepository.cs
Normal file
@ -0,0 +1,84 @@
|
||||
using PettingZoo.Core.Settings;
|
||||
|
||||
namespace PettingZoo.Settings.LiteDB
|
||||
{
|
||||
public class LiteDBUISettingsRepository : BaseLiteDBRepository, IUISettingsRepository
|
||||
{
|
||||
private const string CollectionSettings = "settings";
|
||||
|
||||
|
||||
public LiteDBUISettingsRepository() : base(@"uiSettings")
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
public async Task<MainWindowPositionSettings?> GetMainWindowPosition()
|
||||
{
|
||||
using var database = GetDatabase();
|
||||
var collection = database.GetCollection(CollectionSettings);
|
||||
|
||||
var settings = await collection.FindByIdAsync(MainWindowPositionSettingsRecord.SettingsKey);
|
||||
if (settings == null)
|
||||
return null;
|
||||
|
||||
var position = Mapper.ToObject<MainWindowPositionSettingsRecord>(settings);
|
||||
return new MainWindowPositionSettings(
|
||||
position.Top,
|
||||
position.Left,
|
||||
position.Width,
|
||||
position.Height,
|
||||
position.Maximized);
|
||||
}
|
||||
|
||||
|
||||
public async Task StoreMainWindowPosition(MainWindowPositionSettings settings)
|
||||
{
|
||||
using var database = GetDatabase();
|
||||
var collection = database.GetCollection(CollectionSettings);
|
||||
|
||||
await collection.UpsertAsync(
|
||||
Mapper.ToDocument(new MainWindowPositionSettingsRecord
|
||||
{
|
||||
Top = settings.Top,
|
||||
Left = settings.Left,
|
||||
Width = settings.Width,
|
||||
Height = settings.Height,
|
||||
Maximized = settings.Maximized
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ReSharper disable MemberCanBePrivate.Local - for LiteDB
|
||||
// ReSharper disable PropertyCanBeMadeInitOnly.Local
|
||||
private class BaseSettingsRecord
|
||||
{
|
||||
// ReSharper disable once UnusedAutoPropertyAccessor.Local
|
||||
public Guid Id { get; }
|
||||
|
||||
protected BaseSettingsRecord(Guid id)
|
||||
{
|
||||
Id = id;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private class MainWindowPositionSettingsRecord : BaseSettingsRecord
|
||||
{
|
||||
public static readonly Guid SettingsKey = new("fc71cf99-0744-4f5d-ada8-6a78d1df7b62");
|
||||
|
||||
|
||||
public int Top { get; set; }
|
||||
public int Left { get; set; }
|
||||
public int Width { get; set; }
|
||||
public int Height { get; set; }
|
||||
public bool Maximized { get; set; }
|
||||
|
||||
public MainWindowPositionSettingsRecord() : base(SettingsKey)
|
||||
{
|
||||
}
|
||||
}
|
||||
// ReSharper restore PropertyCanBeMadeInitOnly.Local
|
||||
// ReSharper restore MemberCanBePrivate.Local
|
||||
}
|
||||
}
|
@ -1,5 +1,7 @@
|
||||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<s:Boolean x:Key="/Default/CodeInspection/CodeAnnotations/NamespacesWithAnnotations/=PettingZoo_002EAnnotations/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=DB/@EntryIndexedValue">DB</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=DBUI/@EntryIndexedValue">DBUI</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=UI/@EntryIndexedValue">UI</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=MQ/@EntryIndexedValue">MQ</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=WPF/@EntryIndexedValue">WPF</s:String></wpf:ResourceDictionary>
|
@ -1,10 +1,83 @@
|
||||
using System.Windows;
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Threading;
|
||||
using PettingZoo.Core.Settings;
|
||||
using PettingZoo.UI.Main;
|
||||
using SimpleInjector;
|
||||
using Point = System.Windows.Point;
|
||||
|
||||
namespace PettingZoo
|
||||
{
|
||||
public partial class App
|
||||
{
|
||||
private readonly Container container;
|
||||
|
||||
|
||||
public App()
|
||||
{
|
||||
throw new InvalidOperationException("Default main should not be used");
|
||||
}
|
||||
|
||||
|
||||
public App(Container container)
|
||||
{
|
||||
this.container = container;
|
||||
}
|
||||
|
||||
|
||||
protected override async void OnStartup(StartupEventArgs e)
|
||||
{
|
||||
var uitSettingsRepository = container.GetInstance<IUISettingsRepository>();
|
||||
var position = await uitSettingsRepository.GetMainWindowPosition();
|
||||
|
||||
var mainWindow = container.GetInstance<MainWindow>();
|
||||
|
||||
if (position != null)
|
||||
{
|
||||
var positionBounds = new Rect(
|
||||
new Point(position.Left, position.Top),
|
||||
new Point(position.Left + position.Width, position.Top + position.Height));
|
||||
|
||||
if (InScreenBounds(positionBounds))
|
||||
{
|
||||
mainWindow.WindowStartupLocation = WindowStartupLocation.Manual;
|
||||
mainWindow.Top = positionBounds.Top;
|
||||
mainWindow.Left = positionBounds.Left;
|
||||
mainWindow.Width = positionBounds.Width;
|
||||
mainWindow.Height = positionBounds.Height;
|
||||
}
|
||||
|
||||
mainWindow.WindowState = position.Maximized ? WindowState.Maximized : WindowState.Normal;
|
||||
}
|
||||
|
||||
mainWindow.Closing += (_, _) =>
|
||||
{
|
||||
var newPosition = new MainWindowPositionSettings(
|
||||
(int)mainWindow.RestoreBounds.Top,
|
||||
(int)mainWindow.RestoreBounds.Left,
|
||||
(int)mainWindow.RestoreBounds.Width,
|
||||
(int)mainWindow.RestoreBounds.Height,
|
||||
mainWindow.WasMaximized);
|
||||
|
||||
Task.Run(() => uitSettingsRepository.StoreMainWindowPosition(newPosition));
|
||||
};
|
||||
|
||||
mainWindow.Show();
|
||||
}
|
||||
|
||||
|
||||
private static bool InScreenBounds(Rect bounds)
|
||||
{
|
||||
var boundsRectangle = new Rectangle((int)bounds.Left, (int)bounds.Top, (int)bounds.Width, (int)bounds.Height);
|
||||
|
||||
// There doesn't appear to be any way to get this information other than from System.Windows.From/PInvoke at the time of writing
|
||||
return System.Windows.Forms.Screen.AllScreens.Any(screen => screen.Bounds.IntersectsWith(boundsRectangle));
|
||||
}
|
||||
|
||||
|
||||
private void App_OnDispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
|
||||
{
|
||||
_ = MessageBox.Show($"Unhandled exception: {e.Exception.Message}", "Petting Zoo - Exception", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
|
69
PettingZoo/Images/Error.svg
Normal file
69
PettingZoo/Images/Error.svg
Normal file
@ -0,0 +1,69 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Generator: Adobe Illustrator 18.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
|
||||
<svg
|
||||
version="1.1"
|
||||
id="Capa_1"
|
||||
x="0px"
|
||||
y="0px"
|
||||
viewBox="0 0 24 24"
|
||||
xml:space="preserve"
|
||||
sodipodi:docname="Error.svg"
|
||||
width="24"
|
||||
height="24"
|
||||
inkscape:version="1.1.1 (3bf5ae0d25, 2021-09-20)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><defs
|
||||
id="defs130">
|
||||
|
||||
|
||||
</defs><sodipodi:namedview
|
||||
id="namedview128"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
showgrid="false"
|
||||
inkscape:zoom="14.344828"
|
||||
inkscape:cx="-14.395433"
|
||||
inkscape:cy="-2.9627404"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1017"
|
||||
inkscape:window-x="1912"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="Capa_1" />
|
||||
<g
|
||||
id="g95"
|
||||
transform="translate(-34,-32)">
|
||||
|
||||
<circle
|
||||
style="fill:#ed7161"
|
||||
cx="46"
|
||||
cy="44"
|
||||
r="12"
|
||||
id="circle89" /><path
|
||||
style="fill:#ffffff"
|
||||
d="m 47.414,44 3.536,-3.536 c 0.391,-0.391 0.391,-1.023 0,-1.414 -0.391,-0.391 -1.023,-0.391 -1.414,0 l -3.536,3.536 -3.536,-3.536 c -0.391,-0.391 -1.023,-0.391 -1.414,0 -0.391,0.391 -0.391,1.023 0,1.414 l 3.536,3.536 -3.536,3.536 c -0.391,0.391 -0.391,1.023 0,1.414 0.195,0.195 0.451,0.293 0.707,0.293 0.256,0 0.512,-0.098 0.707,-0.293 l 3.536,-3.536 3.536,3.536 c 0.195,0.195 0.451,0.293 0.707,0.293 0.256,0 0.512,-0.098 0.707,-0.293 0.391,-0.391 0.391,-1.023 0,-1.414 z"
|
||||
id="path91" />
|
||||
</g>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</svg>
|
After Width: | Height: | Size: 1.8 KiB |
54
PettingZoo/Images/Ok.svg
Normal file
54
PettingZoo/Images/Ok.svg
Normal file
@ -0,0 +1,54 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Generator: Adobe Illustrator 18.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
|
||||
<svg
|
||||
version="1.1"
|
||||
id="Capa_1"
|
||||
x="0px"
|
||||
y="0px"
|
||||
viewBox="0 0 24 24"
|
||||
xml:space="preserve"
|
||||
sodipodi:docname="Ok.svg"
|
||||
width="24"
|
||||
height="24"
|
||||
inkscape:version="1.1.1 (3bf5ae0d25, 2021-09-20)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><defs
|
||||
id="defs13" /><sodipodi:namedview
|
||||
id="namedview11"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
showgrid="false"
|
||||
inkscape:zoom="7.1724138"
|
||||
inkscape:cx="-11.362981"
|
||||
inkscape:cy="7.1802885"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1017"
|
||||
inkscape:window-x="1912"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="Capa_1" />
|
||||
<g
|
||||
id="g8"
|
||||
transform="translate(-34,-32)">
|
||||
<g
|
||||
id="g6">
|
||||
<circle
|
||||
style="fill:#26b999"
|
||||
cx="46"
|
||||
cy="44"
|
||||
r="12"
|
||||
id="circle2" />
|
||||
<path
|
||||
style="fill:#ffffff"
|
||||
d="m 52.571,38.179 c -0.455,-0.316 -1.077,-0.204 -1.392,0.25 l -5.596,8.04 -3.949,-3.242 c -0.426,-0.351 -1.057,-0.288 -1.407,0.139 -0.351,0.427 -0.289,1.057 0.139,1.407 l 4.786,3.929 c 0.18,0.147 0.404,0.227 0.634,0.227 0.045,0 0.091,-0.003 0.137,-0.009 0.276,-0.039 0.524,-0.19 0.684,-0.419 l 6.214,-8.929 c 0.315,-0.454 0.203,-1.077 -0.25,-1.393 z"
|
||||
id="path4" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.6 KiB |
@ -5,6 +5,7 @@
|
||||
<TargetFramework>net6.0-windows</TargetFramework>
|
||||
<Version>0.1</Version>
|
||||
<UseWPF>true</UseWPF>
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
<Authors>Mark van Renswoude</Authors>
|
||||
<Product>Petting Zoo</Product>
|
||||
<Description>Petting Zoo - a live RabbitMQ message viewer</Description>
|
||||
@ -19,6 +20,8 @@
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="Images\Dock.svg" />
|
||||
<None Remove="Images\Error.svg" />
|
||||
<None Remove="Images\Ok.svg" />
|
||||
<None Remove="Images\PublishSend.svg" />
|
||||
<None Remove="Images\Undock.svg" />
|
||||
</ItemGroup>
|
||||
@ -28,6 +31,8 @@
|
||||
<Resource Include="Images\Connect.svg" />
|
||||
<Resource Include="Images\Disconnect.svg" />
|
||||
<Resource Include="Images\Dock.svg" />
|
||||
<Resource Include="Images\Error.svg" />
|
||||
<Resource Include="Images\Ok.svg" />
|
||||
<Resource Include="Images\Publish.svg" />
|
||||
<Resource Include="Images\PublishSend.svg" />
|
||||
<Resource Include="Images\Subscribe.svg" />
|
||||
@ -35,8 +40,9 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
<PackageReference Include="SharpVectors" Version="1.7.6" />
|
||||
<PackageReference Include="SharpVectors" Version="1.7.7" />
|
||||
<PackageReference Include="SimpleInjector" Version="5.3.2" />
|
||||
<PackageReference Include="System.Reactive" Version="5.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@ -75,6 +81,11 @@
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
</Compile>
|
||||
<Compile Update="UI\Tab\Publisher\PayloadEditorStrings.Designer.cs">
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>PayloadEditorStrings.resx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Update="UI\Tab\Publisher\TapetiPublisherViewStrings.Designer.cs">
|
||||
<DependentUpon>TapetiPublisherViewStrings.resx</DependentUpon>
|
||||
<DesignTime>True</DesignTime>
|
||||
@ -126,6 +137,10 @@
|
||||
<LastGenOutput>SubscribeWindowStrings.Designer.cs</LastGenOutput>
|
||||
<Generator>PublicResXFileCodeGenerator</Generator>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Update="UI\Tab\Publisher\PayloadEditorStrings.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>PayloadEditorStrings.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Update="UI\Tab\Publisher\TapetiPublisherViewStrings.resx">
|
||||
<LastGenOutput>TapetiPublisherViewStrings.Designer.cs</LastGenOutput>
|
||||
<Generator>PublicResXFileCodeGenerator</Generator>
|
||||
|
@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Markup;
|
||||
using PettingZoo.Core.Connection;
|
||||
@ -38,6 +39,7 @@ namespace PettingZoo
|
||||
container.Register<IConnectionDialog, WindowConnectionDialog>();
|
||||
container.Register<ISubscribeDialog, WindowSubscribeDialog>();
|
||||
container.Register<IConnectionSettingsRepository, LiteDBConnectionSettingsRepository>();
|
||||
container.Register<IUISettingsRepository, LiteDBUISettingsRepository>();
|
||||
|
||||
container.Register<MainWindow>();
|
||||
|
||||
@ -49,7 +51,7 @@ namespace PettingZoo
|
||||
{
|
||||
try
|
||||
{
|
||||
var app = new App();
|
||||
var app = new App(container);
|
||||
app.InitializeComponent();
|
||||
|
||||
#if DEBUG
|
||||
@ -64,9 +66,8 @@ namespace PettingZoo
|
||||
|
||||
// All this is the reason we only perform verification in debug builds
|
||||
#endif
|
||||
|
||||
var mainWindow = container.GetInstance<MainWindow>();
|
||||
_ = app.Run(mainWindow);
|
||||
|
||||
app.Run();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
@ -1,6 +1,5 @@
|
||||
Must-have
|
||||
---------
|
||||
- Option to not save password in profiles
|
||||
|
||||
|
||||
Should-have
|
||||
@ -11,5 +10,4 @@ Should-have
|
||||
|
||||
Nice-to-have
|
||||
------------
|
||||
- JSON validation
|
||||
- JSON syntax highlighting
|
29
PettingZoo/UI/DependencyObjectExtensions.cs
Normal file
29
PettingZoo/UI/DependencyObjectExtensions.cs
Normal file
@ -0,0 +1,29 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Reactive.Disposables;
|
||||
using System.Reactive.Linq;
|
||||
using System.Windows;
|
||||
|
||||
namespace PettingZoo.UI
|
||||
{
|
||||
public static class DependencyObjectExtensions
|
||||
{
|
||||
public static IObservable<T> OnPropertyChanges<T>(this DependencyObject source, DependencyProperty property)
|
||||
{
|
||||
return Observable.Create<T>(o =>
|
||||
{
|
||||
var dpd = DependencyPropertyDescriptor.FromProperty(property, property.OwnerType);
|
||||
if (dpd == null)
|
||||
o.OnError(new InvalidOperationException("Can not register change handler for this dependency property."));
|
||||
|
||||
void Handler(object? sender, EventArgs e)
|
||||
{
|
||||
o.OnNext((T)source.GetValue(property));
|
||||
}
|
||||
|
||||
dpd?.AddValueChanged(source, Handler);
|
||||
return Disposable.Create(() => dpd?.RemoveValueChanged(source, Handler));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
19
PettingZoo/UI/EnumBooleanConverter.cs
Normal file
19
PettingZoo/UI/EnumBooleanConverter.cs
Normal file
@ -0,0 +1,19 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Windows.Data;
|
||||
|
||||
namespace PettingZoo.UI
|
||||
{
|
||||
public class EnumBooleanConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
return value.Equals(parameter);
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
return ((bool)value) ? parameter : Binding.DoNothing;
|
||||
}
|
||||
}
|
||||
}
|
@ -17,6 +17,8 @@ namespace PettingZoo.UI.Main
|
||||
public partial class MainWindow : ITabContainer
|
||||
{
|
||||
private readonly MainWindowViewModel viewModel;
|
||||
|
||||
public bool WasMaximized;
|
||||
|
||||
|
||||
public MainWindow(IConnectionFactory connectionFactory, IConnectionDialog connectionDialog, ISubscribeDialog subscribeDialog)
|
||||
@ -28,6 +30,20 @@ namespace PettingZoo.UI.Main
|
||||
InitializeComponent();
|
||||
|
||||
Dispatcher.ShutdownStarted += OnDispatcherShutDownStarted;
|
||||
|
||||
|
||||
// If the WindowState is Minimized, we can't tell if it was maximized before. To properly store
|
||||
// the last window position, keep track of it.
|
||||
this.OnPropertyChanges<WindowState>(WindowStateProperty)
|
||||
.Subscribe(newState =>
|
||||
{
|
||||
WasMaximized = newState switch
|
||||
{
|
||||
WindowState.Maximized => true,
|
||||
WindowState.Normal => false,
|
||||
_ => WasMaximized
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
31
PettingZoo/UI/Tab/Publisher/PayloadEditorControl.xaml
Normal file
31
PettingZoo/UI/Tab/Publisher/PayloadEditorControl.xaml
Normal file
@ -0,0 +1,31 @@
|
||||
<UserControl x:Class="PettingZoo.UI.Tab.Publisher.PayloadEditorControl"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:publisher="clr-namespace:PettingZoo.UI.Tab.Publisher"
|
||||
xmlns:ui="clr-namespace:PettingZoo.UI"
|
||||
xmlns:svgc="http://sharpvectors.codeplex.com/svgc/"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="450" d:DesignWidth="800"
|
||||
Background="White">
|
||||
<UserControl.Resources>
|
||||
<ui:EnumBooleanConverter x:Key="EnumBooleanConverter" />
|
||||
</UserControl.Resources>
|
||||
<DockPanel x:Name="DataContextContainer" d:DataContext="{d:DesignInstance publisher:DesignTimePayloadEditorViewModel, IsDesignTimeCreatable=True}">
|
||||
<StackPanel Orientation="Horizontal" DockPanel.Dock="Top" Visibility="{Binding ContentTypeVisibility}" Margin="0,0,0,8">
|
||||
<RadioButton Content="JSON" Style="{StaticResource TypeSelection}" IsChecked="{Binding ContentTypeSelection, Converter={StaticResource EnumBooleanConverter}, ConverterParameter={x:Static publisher:PayloadEditorContentType.Json}}" />
|
||||
<RadioButton Content="Plain text" Style="{StaticResource TypeSelection}" IsChecked="{Binding ContentTypeSelection, Converter={StaticResource EnumBooleanConverter}, ConverterParameter={x:Static publisher:PayloadEditorContentType.Plain}}" />
|
||||
<RadioButton Content="Other" Style="{StaticResource TypeSelection}" IsChecked="{Binding ContentTypeSelection, Converter={StaticResource EnumBooleanConverter}, ConverterParameter={x:Static publisher:PayloadEditorContentType.Other}}" />
|
||||
<TextBox Width="200" Text="{Binding ContentType, UpdateSourceTrigger=PropertyChanged}" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Orientation="Horizontal" DockPanel.Dock="Bottom" Visibility="{Binding JsonValidationVisibility}" Margin="0,8,0,0">
|
||||
<Image Source="{svgc:SvgImage Source=/Images/Ok.svg, AppName=PettingZoo}" Width="16" Height="16" Margin="4" Visibility="{Binding JsonValidationOk}" />
|
||||
<Image Source="{svgc:SvgImage Source=/Images/Error.svg, AppName=PettingZoo}" Width="16" Height="16" Margin="4" Visibility="{Binding JsonValidationError}" />
|
||||
<TextBlock Text="{Binding JsonValidationMessage}" Margin="4" />
|
||||
</StackPanel>
|
||||
|
||||
<TextBox Text="{Binding Payload, UpdateSourceTrigger=PropertyChanged}" Style="{StaticResource Payload}" />
|
||||
</DockPanel>
|
||||
</UserControl>
|
141
PettingZoo/UI/Tab/Publisher/PayloadEditorControl.xaml.cs
Normal file
141
PettingZoo/UI/Tab/Publisher/PayloadEditorControl.xaml.cs
Normal file
@ -0,0 +1,141 @@
|
||||
using System;
|
||||
using System.Reactive.Linq;
|
||||
using System.Threading;
|
||||
using System.Windows;
|
||||
using System.Windows.Data;
|
||||
|
||||
namespace PettingZoo.UI.Tab.Publisher
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for PayloadEditorControl.xaml
|
||||
/// </summary>
|
||||
public partial class PayloadEditorControl
|
||||
{
|
||||
private readonly PayloadEditorViewModel viewModel = new();
|
||||
|
||||
|
||||
public static readonly DependencyProperty ContentTypeProperty
|
||||
= DependencyProperty.Register(
|
||||
"ContentType",
|
||||
typeof(string),
|
||||
typeof(PayloadEditorControl),
|
||||
new FrameworkPropertyMetadata("")
|
||||
{
|
||||
BindsTwoWayByDefault = true,
|
||||
DefaultUpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged
|
||||
}
|
||||
);
|
||||
|
||||
public string ContentType
|
||||
{
|
||||
get => viewModel.ContentType;
|
||||
set
|
||||
{
|
||||
if (value == viewModel.ContentType)
|
||||
return;
|
||||
|
||||
SetValue(ContentTypeProperty, value);
|
||||
viewModel.ContentType = value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static readonly DependencyProperty FixedJsonProperty
|
||||
= DependencyProperty.Register(
|
||||
"FixedJson",
|
||||
typeof(bool),
|
||||
typeof(PayloadEditorControl),
|
||||
new PropertyMetadata(false)
|
||||
);
|
||||
|
||||
public bool FixedJson
|
||||
{
|
||||
get => viewModel.FixedJson;
|
||||
set
|
||||
{
|
||||
if (value == viewModel.FixedJson)
|
||||
return;
|
||||
|
||||
SetValue(FixedJsonProperty, value);
|
||||
viewModel.FixedJson = value;
|
||||
}
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty PayloadProperty
|
||||
= DependencyProperty.Register(
|
||||
"Payload",
|
||||
typeof(string),
|
||||
typeof(PayloadEditorControl),
|
||||
new FrameworkPropertyMetadata("")
|
||||
{
|
||||
BindsTwoWayByDefault = true,
|
||||
DefaultUpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged
|
||||
}
|
||||
);
|
||||
|
||||
public string Payload
|
||||
{
|
||||
get => viewModel.Payload;
|
||||
set
|
||||
{
|
||||
if (value == viewModel.Payload)
|
||||
return;
|
||||
|
||||
SetValue(PayloadProperty, value);
|
||||
viewModel.Payload = value;
|
||||
}
|
||||
}
|
||||
|
||||
public PayloadEditorControl()
|
||||
{
|
||||
// Keep the exposed properties in sync with the ViewModel
|
||||
this.OnPropertyChanges<string>(ContentTypeProperty)
|
||||
.ObserveOn(SynchronizationContext.Current!)
|
||||
.Subscribe(value =>
|
||||
{
|
||||
viewModel.ContentType = value;
|
||||
});
|
||||
|
||||
|
||||
this.OnPropertyChanges<bool>(FixedJsonProperty)
|
||||
.ObserveOn(SynchronizationContext.Current!)
|
||||
.Subscribe(value =>
|
||||
{
|
||||
viewModel.FixedJson = value;
|
||||
});
|
||||
|
||||
|
||||
this.OnPropertyChanges<string>(PayloadProperty)
|
||||
.ObserveOn(SynchronizationContext.Current!)
|
||||
.Subscribe(value =>
|
||||
{
|
||||
viewModel.Payload = value;
|
||||
});
|
||||
|
||||
|
||||
viewModel.PropertyChanged += (_, args) =>
|
||||
{
|
||||
switch (args.PropertyName)
|
||||
{
|
||||
case nameof(viewModel.ContentType):
|
||||
SetValue(ContentTypeProperty, viewModel.ContentType);
|
||||
break;
|
||||
|
||||
case nameof(viewModel.FixedJson):
|
||||
SetValue(FixedJsonProperty, viewModel.FixedJson);
|
||||
break;
|
||||
|
||||
case nameof(viewModel.Payload):
|
||||
SetValue(PayloadProperty, viewModel.Payload);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
InitializeComponent();
|
||||
|
||||
// Setting the DataContext for the UserControl is a major PITA when binding the control's properties,
|
||||
// so I've moved the ViewModel one level down to get the best of both worlds...
|
||||
DataContextContainer.DataContext = viewModel;
|
||||
}
|
||||
}
|
||||
}
|
108
PettingZoo/UI/Tab/Publisher/PayloadEditorStrings.Designer.cs
generated
Normal file
108
PettingZoo/UI/Tab/Publisher/PayloadEditorStrings.Designer.cs
generated
Normal file
@ -0,0 +1,108 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.42000
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace PettingZoo.UI.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 PayloadEditorStrings {
|
||||
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
|
||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||
|
||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal PayloadEditorStrings() {
|
||||
}
|
||||
|
||||
/// <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.PayloadEditorStrings", typeof(PayloadEditorStrings).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 JSON.
|
||||
/// </summary>
|
||||
internal static string ContentTypeJson {
|
||||
get {
|
||||
return ResourceManager.GetString("ContentTypeJson", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Other.
|
||||
/// </summary>
|
||||
internal static string ContentTypeOther {
|
||||
get {
|
||||
return ResourceManager.GetString("ContentTypeOther", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Plain text.
|
||||
/// </summary>
|
||||
internal static string ContentTypePlain {
|
||||
get {
|
||||
return ResourceManager.GetString("ContentTypePlain", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Invalid JSON: {0}.
|
||||
/// </summary>
|
||||
internal static string JsonValidationError {
|
||||
get {
|
||||
return ResourceManager.GetString("JsonValidationError", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Valid JSON.
|
||||
/// </summary>
|
||||
internal static string JsonValidationOk {
|
||||
get {
|
||||
return ResourceManager.GetString("JsonValidationOk", resourceCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
135
PettingZoo/UI/Tab/Publisher/PayloadEditorStrings.resx
Normal file
135
PettingZoo/UI/Tab/Publisher/PayloadEditorStrings.resx
Normal file
@ -0,0 +1,135 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="ContentTypeJson" xml:space="preserve">
|
||||
<value>JSON</value>
|
||||
</data>
|
||||
<data name="ContentTypeOther" xml:space="preserve">
|
||||
<value>Other</value>
|
||||
</data>
|
||||
<data name="ContentTypePlain" xml:space="preserve">
|
||||
<value>Plain text</value>
|
||||
</data>
|
||||
<data name="JsonValidationError" xml:space="preserve">
|
||||
<value>Invalid JSON: {0}</value>
|
||||
</data>
|
||||
<data name="JsonValidationOk" xml:space="preserve">
|
||||
<value>Valid JSON</value>
|
||||
</data>
|
||||
</root>
|
154
PettingZoo/UI/Tab/Publisher/PayloadEditorViewModel.cs
Normal file
154
PettingZoo/UI/Tab/Publisher/PayloadEditorViewModel.cs
Normal file
@ -0,0 +1,154 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Reactive.Linq;
|
||||
using System.Reactive.Subjects;
|
||||
using System.Windows;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace PettingZoo.UI.Tab.Publisher
|
||||
{
|
||||
public enum PayloadEditorContentType
|
||||
{
|
||||
Json,
|
||||
Plain,
|
||||
Other
|
||||
};
|
||||
|
||||
|
||||
public class PayloadEditorViewModel : BaseViewModel
|
||||
{
|
||||
private const string ContentTypeJson = "application/json";
|
||||
private const string ContentTypePlain = "text/plain";
|
||||
|
||||
private string contentType = ContentTypeJson;
|
||||
private PayloadEditorContentType contentTypeSelection = PayloadEditorContentType.Json;
|
||||
private bool fixedJson;
|
||||
|
||||
private bool jsonValid = true;
|
||||
private string jsonValidationMessage;
|
||||
|
||||
private string payload = "";
|
||||
|
||||
|
||||
public string ContentType
|
||||
{
|
||||
get => ContentTypeSelection switch
|
||||
{
|
||||
PayloadEditorContentType.Json => ContentTypeJson,
|
||||
PayloadEditorContentType.Plain => ContentTypePlain,
|
||||
_ => contentType
|
||||
};
|
||||
|
||||
set
|
||||
{
|
||||
if (!SetField(ref contentType, value))
|
||||
return;
|
||||
|
||||
ContentTypeSelection = value switch
|
||||
{
|
||||
ContentTypeJson => PayloadEditorContentType.Json,
|
||||
ContentTypePlain => PayloadEditorContentType.Plain,
|
||||
_ => PayloadEditorContentType.Other
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public PayloadEditorContentType ContentTypeSelection
|
||||
{
|
||||
get => contentTypeSelection;
|
||||
set
|
||||
{
|
||||
if (!SetField(ref contentTypeSelection, value, otherPropertiesChanged: new [] { nameof(JsonValidationVisibility) }))
|
||||
return;
|
||||
|
||||
ContentType = ContentTypeSelection switch
|
||||
{
|
||||
PayloadEditorContentType.Json => ContentTypeJson,
|
||||
PayloadEditorContentType.Plain => ContentTypePlain,
|
||||
_ => ContentType
|
||||
};
|
||||
|
||||
ValidatePayload();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public bool FixedJson
|
||||
{
|
||||
get => fixedJson;
|
||||
set => SetField(ref fixedJson, value);
|
||||
}
|
||||
|
||||
public Visibility JsonValidationVisibility => ContentTypeSelection == PayloadEditorContentType.Json ? Visibility.Visible : Visibility.Collapsed;
|
||||
public Visibility JsonValidationOk => JsonValid ? Visibility.Visible : Visibility.Collapsed;
|
||||
public Visibility JsonValidationError => !JsonValid ? Visibility.Visible : Visibility.Collapsed;
|
||||
|
||||
|
||||
public string JsonValidationMessage
|
||||
{
|
||||
get => jsonValidationMessage;
|
||||
private set => SetField(ref jsonValidationMessage, value);
|
||||
}
|
||||
|
||||
|
||||
public bool JsonValid
|
||||
{
|
||||
get => jsonValid;
|
||||
private set => SetField(ref jsonValid, value, otherPropertiesChanged: new[] { nameof(JsonValidationOk), nameof(JsonValidationError) });
|
||||
}
|
||||
|
||||
public Visibility ContentTypeVisibility => FixedJson ? Visibility.Collapsed : Visibility.Visible;
|
||||
|
||||
|
||||
public string Payload
|
||||
{
|
||||
get => payload;
|
||||
set => SetField(ref payload, value);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public PayloadEditorViewModel()
|
||||
{
|
||||
jsonValidationMessage = PayloadEditorStrings.JsonValidationOk;
|
||||
|
||||
Observable.FromEventPattern<PropertyChangedEventHandler, PropertyChangedEventArgs>(
|
||||
h => PropertyChanged += h,
|
||||
h => PropertyChanged -= h)
|
||||
.Where(e => e.EventArgs.PropertyName == nameof(Payload))
|
||||
.Throttle(TimeSpan.FromMilliseconds(500))
|
||||
.Subscribe(_ => ValidatePayload());
|
||||
}
|
||||
|
||||
|
||||
private void ValidatePayload()
|
||||
{
|
||||
if (ContentTypeSelection != PayloadEditorContentType.Json)
|
||||
{
|
||||
JsonValid = true;
|
||||
JsonValidationMessage = PayloadEditorStrings.JsonValidationOk;
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (!string.IsNullOrEmpty(Payload))
|
||||
JToken.Parse(Payload);
|
||||
|
||||
JsonValid = true;
|
||||
JsonValidationMessage = PayloadEditorStrings.JsonValidationOk;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
JsonValid = false;
|
||||
JsonValidationMessage = string.Format(PayloadEditorStrings.JsonValidationError, e.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class DesignTimePayloadEditorViewModel : PayloadEditorViewModel
|
||||
{
|
||||
}
|
||||
}
|
@ -29,7 +29,6 @@
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="16"/>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
@ -95,38 +94,35 @@
|
||||
|
||||
<Label Grid.Row="2" Grid.Column="0" Content="{x:Static publisher:RawPublisherViewStrings.LabelProperties}" Style="{StaticResource SectionLabel}"/>
|
||||
|
||||
<Label Grid.Row="3" Grid.Column="0" Content="{x:Static publisher:RawPublisherViewStrings.LabelContentType}" />
|
||||
<TextBox Grid.Row="3" Grid.Column="1" Text="{Binding ContentType, UpdateSourceTrigger=PropertyChanged}" />
|
||||
<Label Grid.Row="3" Grid.Column="0" Content="{x:Static publisher:RawPublisherViewStrings.LabelCorrelationId}" />
|
||||
<TextBox Grid.Row="3" Grid.Column="1" Text="{Binding CorrelationId, UpdateSourceTrigger=PropertyChanged}" />
|
||||
|
||||
<Label Grid.Row="4" Grid.Column="0" Content="{x:Static publisher:RawPublisherViewStrings.LabelCorrelationId}" />
|
||||
<TextBox Grid.Row="4" Grid.Column="1" Text="{Binding CorrelationId, UpdateSourceTrigger=PropertyChanged}" />
|
||||
<Label Grid.Row="4" Grid.Column="0" Content="{x:Static publisher:RawPublisherViewStrings.LabelAppId}" Visibility="{Binding PropertiesExpandedVisibility}" />
|
||||
<TextBox Grid.Row="4" Grid.Column="1" Text="{Binding AppId, UpdateSourceTrigger=PropertyChanged}" Visibility="{Binding PropertiesExpandedVisibility}" />
|
||||
|
||||
<Label Grid.Row="5" Grid.Column="0" Content="{x:Static publisher:RawPublisherViewStrings.LabelAppId}" Visibility="{Binding PropertiesExpandedVisibility}" />
|
||||
<TextBox Grid.Row="5" Grid.Column="1" Text="{Binding AppId, UpdateSourceTrigger=PropertyChanged}" Visibility="{Binding PropertiesExpandedVisibility}" />
|
||||
<Label Grid.Row="5" Grid.Column="0" Content="{x:Static publisher:RawPublisherViewStrings.LabelContentEncoding}" Visibility="{Binding PropertiesExpandedVisibility}" />
|
||||
<TextBox Grid.Row="5" Grid.Column="1" Text="{Binding ContentEncoding, UpdateSourceTrigger=PropertyChanged}" Visibility="{Binding PropertiesExpandedVisibility}" />
|
||||
|
||||
<Label Grid.Row="6" Grid.Column="0" Content="{x:Static publisher:RawPublisherViewStrings.LabelContentEncoding}" Visibility="{Binding PropertiesExpandedVisibility}" />
|
||||
<TextBox Grid.Row="6" Grid.Column="1" Text="{Binding ContentEncoding, UpdateSourceTrigger=PropertyChanged}" Visibility="{Binding PropertiesExpandedVisibility}" />
|
||||
<Label Grid.Row="6" Grid.Column="0" Content="{x:Static publisher:RawPublisherViewStrings.LabelExpiration}" Visibility="{Binding PropertiesExpandedVisibility}" />
|
||||
<TextBox Grid.Row="6" Grid.Column="1" Text="{Binding Expiration, UpdateSourceTrigger=PropertyChanged}" Visibility="{Binding PropertiesExpandedVisibility}" />
|
||||
|
||||
<Label Grid.Row="7" Grid.Column="0" Content="{x:Static publisher:RawPublisherViewStrings.LabelExpiration}" Visibility="{Binding PropertiesExpandedVisibility}" />
|
||||
<TextBox Grid.Row="7" Grid.Column="1" Text="{Binding Expiration, UpdateSourceTrigger=PropertyChanged}" Visibility="{Binding PropertiesExpandedVisibility}" />
|
||||
<Label Grid.Row="7" Grid.Column="0" Content="{x:Static publisher:RawPublisherViewStrings.LabelMessageId}" Visibility="{Binding PropertiesExpandedVisibility}" />
|
||||
<TextBox Grid.Row="7" Grid.Column="1" Text="{Binding MessageId, UpdateSourceTrigger=PropertyChanged}" Visibility="{Binding PropertiesExpandedVisibility}" />
|
||||
|
||||
<Label Grid.Row="8" Grid.Column="0" Content="{x:Static publisher:RawPublisherViewStrings.LabelMessageId}" Visibility="{Binding PropertiesExpandedVisibility}" />
|
||||
<TextBox Grid.Row="8" Grid.Column="1" Text="{Binding MessageId, UpdateSourceTrigger=PropertyChanged}" Visibility="{Binding PropertiesExpandedVisibility}" />
|
||||
<Label Grid.Row="8" Grid.Column="0" Content="{x:Static publisher:RawPublisherViewStrings.LabelPriority}" Visibility="{Binding PropertiesExpandedVisibility}" />
|
||||
<TextBox Grid.Row="8" Grid.Column="1" Text="{Binding Priority, UpdateSourceTrigger=PropertyChanged}" Visibility="{Binding PropertiesExpandedVisibility}" />
|
||||
|
||||
<Label Grid.Row="9" Grid.Column="0" Content="{x:Static publisher:RawPublisherViewStrings.LabelPriority}" Visibility="{Binding PropertiesExpandedVisibility}" />
|
||||
<TextBox Grid.Row="9" Grid.Column="1" Text="{Binding Priority, UpdateSourceTrigger=PropertyChanged}" Visibility="{Binding PropertiesExpandedVisibility}" />
|
||||
<Label Grid.Row="9" Grid.Column="0" Content="{x:Static publisher:RawPublisherViewStrings.LabelTimestamp}" Visibility="{Binding PropertiesExpandedVisibility}" />
|
||||
<TextBox Grid.Row="9" Grid.Column="1" Text="{Binding Timestamp, UpdateSourceTrigger=PropertyChanged}" Visibility="{Binding PropertiesExpandedVisibility}" />
|
||||
|
||||
<Label Grid.Row="10" Grid.Column="0" Content="{x:Static publisher:RawPublisherViewStrings.LabelTimestamp}" Visibility="{Binding PropertiesExpandedVisibility}" />
|
||||
<TextBox Grid.Row="10" Grid.Column="1" Text="{Binding Timestamp, UpdateSourceTrigger=PropertyChanged}" Visibility="{Binding PropertiesExpandedVisibility}" />
|
||||
<Label Grid.Row="10" Grid.Column="0" Content="{x:Static publisher:RawPublisherViewStrings.LabelType}" Visibility="{Binding PropertiesExpandedVisibility}" />
|
||||
<TextBox Grid.Row="10" Grid.Column="1" Text="{Binding TypeProperty, UpdateSourceTrigger=PropertyChanged}" Visibility="{Binding PropertiesExpandedVisibility}" />
|
||||
|
||||
<Label Grid.Row="11" Grid.Column="0" Content="{x:Static publisher:RawPublisherViewStrings.LabelType}" Visibility="{Binding PropertiesExpandedVisibility}" />
|
||||
<TextBox Grid.Row="11" Grid.Column="1" Text="{Binding TypeProperty, UpdateSourceTrigger=PropertyChanged}" Visibility="{Binding PropertiesExpandedVisibility}" />
|
||||
|
||||
<Label Grid.Row="12" Grid.Column="0" Content="{x:Static publisher:RawPublisherViewStrings.LabelUserId}" Visibility="{Binding PropertiesExpandedVisibility}" />
|
||||
<TextBox Grid.Row="12" Grid.Column="1" Text="{Binding UserId, UpdateSourceTrigger=PropertyChanged}" Visibility="{Binding PropertiesExpandedVisibility}" />
|
||||
<Label Grid.Row="11" Grid.Column="0" Content="{x:Static publisher:RawPublisherViewStrings.LabelUserId}" Visibility="{Binding PropertiesExpandedVisibility}" />
|
||||
<TextBox Grid.Row="11" Grid.Column="1" Text="{Binding UserId, UpdateSourceTrigger=PropertyChanged}" Visibility="{Binding PropertiesExpandedVisibility}" />
|
||||
|
||||
|
||||
<Button Grid.Row="13" Grid.Column="1" Content="{Binding PropertiesExpandedCollapsedText}" Command="{Binding PropertiesExpandCollapseCommand}" Cursor="Hand">
|
||||
<Button Grid.Row="12" Grid.Column="1" Content="{Binding PropertiesExpandedCollapsedText}" Command="{Binding PropertiesExpandCollapseCommand}" Cursor="Hand">
|
||||
<Button.Template>
|
||||
<ControlTemplate TargetType="{x:Type Button}">
|
||||
<ContentPresenter />
|
||||
@ -134,7 +130,7 @@
|
||||
</Button.Template>
|
||||
</Button>
|
||||
|
||||
<Label Grid.Row="15" Grid.Column="0" Content="{x:Static publisher:RawPublisherViewStrings.LabelPayload}" />
|
||||
<TextBox Grid.Row="15" Grid.Column="1" Text="{Binding Payload, UpdateSourceTrigger=PropertyChanged}" Style="{StaticResource Payload}" Height="150" />
|
||||
<Label Grid.Row="14" Grid.Column="0" Content="{x:Static publisher:RawPublisherViewStrings.LabelPayload}" />
|
||||
<publisher:PayloadEditorControl Grid.Row="14" Grid.Column="1" Payload="{Binding Payload}" ContentType="{Binding ContentType}" Height="350"/>
|
||||
</ui:GridLayout>
|
||||
</UserControl>
|
||||
|
@ -48,6 +48,6 @@
|
||||
</Grid>
|
||||
|
||||
<Label Grid.Row="6" Grid.Column="0" Content="{x:Static publisher:TapetiPublisherViewStrings.LabelPayload}" />
|
||||
<TextBox Grid.Row="6" Grid.Column="1" Text="{Binding Payload, UpdateSourceTrigger=PropertyChanged}" Style="{StaticResource Payload}" Height="150" />
|
||||
<publisher:PayloadEditorControl Grid.Row="6" Grid.Column="1" Payload="{Binding Payload}" FixedJson="True" Height="350"/>
|
||||
</ui:GridLayout>
|
||||
</UserControl>
|
||||
|
Loading…
Reference in New Issue
Block a user