Added option to create publisher tab from received message
This commit is contained in:
parent
057cac4e22
commit
e9de38b556
@ -42,7 +42,6 @@ namespace PettingZoo
|
||||
container.Register<IConnectionFactory, RabbitMQClientConnectionFactory>();
|
||||
container.Register<IConnectionDialog, WindowConnectionDialog>();
|
||||
container.Register<ISubscribeDialog, WindowSubscribeDialog>();
|
||||
container.Register<ITabFactory, ViewTabFactory>();
|
||||
|
||||
container.Register<MainWindow>();
|
||||
|
||||
|
24
PettingZoo/UI/BindingProxy.cs
Normal file
24
PettingZoo/UI/BindingProxy.cs
Normal file
@ -0,0 +1,24 @@
|
||||
using System.Windows;
|
||||
|
||||
namespace PettingZoo.UI
|
||||
{
|
||||
public class BindingProxy : Freezable
|
||||
{
|
||||
protected override Freezable CreateInstanceCore()
|
||||
{
|
||||
return new BindingProxy();
|
||||
}
|
||||
|
||||
|
||||
public object Data
|
||||
{
|
||||
get => GetValue(DataProperty);
|
||||
set => SetValue(DataProperty, value);
|
||||
}
|
||||
|
||||
|
||||
// Using a DependencyProperty as the backing store for Data. This enables animation, styling, binding, etc...
|
||||
public static readonly DependencyProperty DataProperty =
|
||||
DependencyProperty.Register("Data", typeof(object), typeof(BindingProxy), new UIPropertyMetadata(null));
|
||||
}
|
||||
}
|
@ -16,12 +16,12 @@ namespace PettingZoo.UI.Main
|
||||
private readonly MainWindowViewModel viewModel;
|
||||
|
||||
|
||||
public MainWindow(IConnectionFactory connectionFactory, IConnectionDialog connectionDialog, ISubscribeDialog subscribeDialog, ITabFactory tabFactory)
|
||||
public MainWindow(IConnectionFactory connectionFactory, IConnectionDialog connectionDialog, ISubscribeDialog subscribeDialog)
|
||||
{
|
||||
WindowStartupLocation = WindowStartupLocation.CenterScreen;
|
||||
|
||||
InitializeComponent();
|
||||
viewModel = new MainWindowViewModel(connectionFactory, connectionDialog, subscribeDialog, tabFactory);
|
||||
viewModel = new MainWindowViewModel(connectionFactory, connectionDialog, subscribeDialog);
|
||||
DataContext = viewModel;
|
||||
|
||||
Dispatcher.ShutdownStarted += OnDispatcherShutDownStarted;
|
||||
|
@ -12,7 +12,7 @@ using PettingZoo.UI.Tab;
|
||||
|
||||
namespace PettingZoo.UI.Main
|
||||
{
|
||||
public class MainWindowViewModel : BaseViewModel, IAsyncDisposable
|
||||
public class MainWindowViewModel : BaseViewModel, IAsyncDisposable, ITabHost
|
||||
{
|
||||
private readonly IConnectionFactory connectionFactory;
|
||||
private readonly IConnectionDialog connectionDialog;
|
||||
@ -66,12 +66,11 @@ namespace PettingZoo.UI.Main
|
||||
|
||||
|
||||
public MainWindowViewModel(IConnectionFactory connectionFactory, IConnectionDialog connectionDialog,
|
||||
ISubscribeDialog subscribeDialog, ITabFactory tabFactory)
|
||||
ISubscribeDialog subscribeDialog)
|
||||
{
|
||||
this.connectionFactory = connectionFactory;
|
||||
this.connectionDialog = connectionDialog;
|
||||
this.subscribeDialog = subscribeDialog;
|
||||
this.tabFactory = tabFactory;
|
||||
|
||||
connectionStatus = GetConnectionStatus(null);
|
||||
|
||||
@ -81,6 +80,8 @@ namespace PettingZoo.UI.Main
|
||||
publishCommand = new DelegateCommand(PublishExecute, IsConnectedCanExecute);
|
||||
subscribeCommand = new DelegateCommand(SubscribeExecute, IsConnectedCanExecute);
|
||||
closeTabCommand = new DelegateCommand(CloseTabExecute, CloseTabCanExecute);
|
||||
|
||||
tabFactory = new ViewTabFactory(this, closeTabCommand);
|
||||
}
|
||||
|
||||
|
||||
@ -113,7 +114,7 @@ namespace PettingZoo.UI.Main
|
||||
if (connectionDialogParams.Subscribe)
|
||||
{
|
||||
var subscriber = connection.Subscribe(connectionDialogParams.Exchange, connectionDialogParams.RoutingKey);
|
||||
AddTab(tabFactory.CreateSubscriberTab(CloseTabCommand, subscriber));
|
||||
AddTab(tabFactory.CreateSubscriberTab(connection, subscriber));
|
||||
|
||||
}
|
||||
|
||||
@ -150,7 +151,7 @@ namespace PettingZoo.UI.Main
|
||||
subscribeDialogParams = newParams;
|
||||
|
||||
var subscriber = connection.Subscribe(subscribeDialogParams.Exchange, subscribeDialogParams.RoutingKey);
|
||||
AddTab(tabFactory.CreateSubscriberTab(CloseTabCommand, subscriber));
|
||||
AddTab(tabFactory.CreateSubscriberTab(connection, subscriber));
|
||||
}
|
||||
|
||||
|
||||
@ -159,7 +160,7 @@ namespace PettingZoo.UI.Main
|
||||
if (connection == null)
|
||||
return;
|
||||
|
||||
AddTab(tabFactory.CreatePublisherTab(CloseTabCommand, connection));
|
||||
AddTab(tabFactory.CreatePublisherTab(connection));
|
||||
}
|
||||
|
||||
|
||||
@ -194,7 +195,7 @@ namespace PettingZoo.UI.Main
|
||||
}
|
||||
|
||||
|
||||
private void AddTab(ITab tab)
|
||||
public void AddTab(ITab tab)
|
||||
{
|
||||
Tabs.Add(tab);
|
||||
ActiveTab = tab;
|
||||
@ -233,7 +234,7 @@ namespace PettingZoo.UI.Main
|
||||
|
||||
public class DesignTimeMainWindowViewModel : MainWindowViewModel
|
||||
{
|
||||
public DesignTimeMainWindowViewModel() : base(null!, null!, null!, null!)
|
||||
public DesignTimeMainWindowViewModel() : base(null!, null!, null!)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
using System.Windows.Input;
|
||||
using PettingZoo.Core.Connection;
|
||||
using PettingZoo.Core.Connection;
|
||||
|
||||
namespace PettingZoo.UI.Tab
|
||||
{
|
||||
@ -9,7 +8,7 @@ namespace PettingZoo.UI.Tab
|
||||
|
||||
public interface ITabFactory
|
||||
{
|
||||
ITab CreateSubscriberTab(ICommand closeTabCommand, ISubscriber subscriber);
|
||||
ITab CreatePublisherTab(ICommand closeTabCommand, IConnection connection);
|
||||
ITab CreateSubscriberTab(IConnection connection, ISubscriber subscriber);
|
||||
ITab CreatePublisherTab(IConnection connection, ReceivedMessageInfo? fromReceivedMessage = null);
|
||||
}
|
||||
}
|
||||
|
7
PettingZoo/UI/Tab/ITabHost.cs
Normal file
7
PettingZoo/UI/Tab/ITabHost.cs
Normal file
@ -0,0 +1,7 @@
|
||||
namespace PettingZoo.UI.Tab
|
||||
{
|
||||
public interface ITabHost
|
||||
{
|
||||
void AddTab(ITab tab);
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
using PettingZoo.Core.Connection;
|
||||
@ -73,7 +74,7 @@ namespace PettingZoo.UI.Tab.Publisher
|
||||
public IEnumerable<TabToolbarCommand> ToolbarCommands => toolbarCommands;
|
||||
|
||||
|
||||
public PublisherViewModel(IConnection connection)
|
||||
public PublisherViewModel(IConnection connection, ReceivedMessageInfo? fromReceivedMessage = null)
|
||||
{
|
||||
this.connection = connection;
|
||||
|
||||
@ -84,7 +85,10 @@ namespace PettingZoo.UI.Tab.Publisher
|
||||
new TabToolbarCommand(PublishCommand, PublisherViewStrings.CommandPublish, SvgIconHelper.LoadFromResource("/Images/PublishSend.svg"))
|
||||
};
|
||||
|
||||
SetMessageTypeControl(MessageType.Raw);
|
||||
if (fromReceivedMessage != null)
|
||||
SetMessageTypeControl(fromReceivedMessage);
|
||||
else
|
||||
SetMessageTypeControl(MessageType.Raw);
|
||||
}
|
||||
|
||||
|
||||
@ -126,6 +130,82 @@ namespace PettingZoo.UI.Tab.Publisher
|
||||
|
||||
publishCommand.RaiseCanExecuteChanged();
|
||||
}
|
||||
|
||||
|
||||
private void SetMessageTypeControl(ReceivedMessageInfo fromReceivedMessage)
|
||||
{
|
||||
// TODO move to individual viewmodels?
|
||||
if (IsTapetiMessage(fromReceivedMessage, out var assemblyName, out var className))
|
||||
{
|
||||
var tapetiPublisherViewModel = new TapetiPublisherViewModel(connection)
|
||||
{
|
||||
Exchange = fromReceivedMessage.Exchange,
|
||||
RoutingKey = fromReceivedMessage.RoutingKey,
|
||||
|
||||
AssemblyName = assemblyName,
|
||||
ClassName = className,
|
||||
CorrelationId = fromReceivedMessage.Properties.CorrelationId ?? "",
|
||||
ReplyTo = fromReceivedMessage.Properties.ReplyTo ?? "",
|
||||
Payload = Encoding.UTF8.GetString(fromReceivedMessage.Body)
|
||||
};
|
||||
|
||||
tapetiPublisherView ??= new TapetiPublisherView(tapetiPublisherViewModel);
|
||||
SetMessageTypeControl(MessageType.Tapeti);
|
||||
}
|
||||
else
|
||||
{
|
||||
var rawPublisherViewModel = new RawPublisherViewModel(connection)
|
||||
{
|
||||
Exchange = fromReceivedMessage.Exchange,
|
||||
RoutingKey = fromReceivedMessage.RoutingKey,
|
||||
|
||||
CorrelationId = fromReceivedMessage.Properties.CorrelationId ?? "",
|
||||
ReplyTo = fromReceivedMessage.Properties.ReplyTo ?? "",
|
||||
Priority = fromReceivedMessage.Properties.Priority?.ToString() ?? "",
|
||||
AppId = fromReceivedMessage.Properties.AppId ?? "",
|
||||
ContentEncoding = fromReceivedMessage.Properties.ContentEncoding ?? "",
|
||||
ContentType = fromReceivedMessage.Properties.ContentType ?? "",
|
||||
Expiration = fromReceivedMessage.Properties.Expiration ?? "",
|
||||
MessageId = fromReceivedMessage.Properties.MessageId ?? "",
|
||||
Timestamp = fromReceivedMessage.Properties.Timestamp?.ToString() ?? "",
|
||||
TypeProperty = fromReceivedMessage.Properties.Type ?? "",
|
||||
UserId = fromReceivedMessage.Properties.UserId ?? "",
|
||||
|
||||
Payload = Encoding.UTF8.GetString(fromReceivedMessage.Body)
|
||||
};
|
||||
|
||||
foreach (var header in fromReceivedMessage.Properties.Headers)
|
||||
rawPublisherViewModel.Headers.Add(new RawPublisherViewModel.Header
|
||||
{
|
||||
Key = header.Key,
|
||||
Value = header.Value
|
||||
});
|
||||
|
||||
rawPublisherView = new RawPublisherView(rawPublisherViewModel);
|
||||
SetMessageTypeControl(MessageType.Raw);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static bool IsTapetiMessage(ReceivedMessageInfo receivedMessage, out string assemblyName, out string className)
|
||||
{
|
||||
assemblyName = "";
|
||||
className = "";
|
||||
|
||||
if (receivedMessage.Properties.ContentType != @"application/json")
|
||||
return false;
|
||||
|
||||
if (!receivedMessage.Properties.Headers.TryGetValue(@"classType", out var classType))
|
||||
return false;
|
||||
|
||||
var parts = classType.Split(':');
|
||||
if (parts.Length != 2)
|
||||
return false;
|
||||
|
||||
className = parts[0];
|
||||
assemblyName = parts[1];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -18,9 +18,13 @@
|
||||
</Grid.ColumnDefinitions>
|
||||
<ListBox Grid.Column="0" Grid.Row="0"
|
||||
HorizontalAlignment="Stretch"
|
||||
HorizontalContentAlignment="Stretch"
|
||||
ItemsSource="{Binding Messages}"
|
||||
SelectedItem="{Binding Path=SelectedMessage, Mode=TwoWay}"
|
||||
ui:ListBox.AutoScroll="True">
|
||||
<ListBox.Resources>
|
||||
<ui:BindingProxy x:Key="ContextMenuProxy" Data="{Binding}" />
|
||||
</ListBox.Resources>
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Grid>
|
||||
@ -30,6 +34,13 @@
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Grid.Column="0" Grid.Row="0" Text="{Binding ReceivedTimestamp, StringFormat=g}" Style="{StaticResource Timestamp}"></TextBlock>
|
||||
<TextBlock Grid.Column="1" Grid.Row="0" Text="{Binding RoutingKey}" Style="{StaticResource RoutingKey}"></TextBlock>
|
||||
|
||||
<Grid.ContextMenu>
|
||||
<ContextMenu>
|
||||
<!-- ReSharper disable once Xaml.BindingWithContextNotResolved - binding is correct, just weird because of the required proxy -->
|
||||
<MenuItem Header="{x:Static res:SubscriberViewStrings.ContextPublish}" Command="{Binding Data.CreatePublisherCommand, Source={StaticResource ContextMenuProxy}}" InputGestureText="Ctrl+P" />
|
||||
</ContextMenu>
|
||||
</Grid.ContextMenu>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ListBox.ItemTemplate>
|
||||
@ -58,8 +69,10 @@
|
||||
<Label DockPanel.Dock="Top" Style="{StaticResource HeaderLabel}" Content="{x:Static res:SubscriberViewStrings.PanelTitleProperties}"/>
|
||||
<DataGrid ItemsSource="{Binding SelectedMessageProperties}" AutoGenerateColumns="False" IsReadOnly="True" Style="{StaticResource Properties}">
|
||||
<DataGrid.Columns>
|
||||
<!-- ReSharper disable Xaml.BindingWithContextNotResolved - bindings are correct -->
|
||||
<DataGridTextColumn Binding="{Binding Key}" Header="{x:Static res:SubscriberViewStrings.PropertyName}" Width="100"/>
|
||||
<DataGridTextColumn Binding="{Binding Value}" Header="{x:Static res:SubscriberViewStrings.PropertyValue}" Width="*"/>
|
||||
<!-- ReSharper restore Xaml.BindingWithContextNotResolved -->
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
</DockPanel>
|
||||
|
@ -9,12 +9,14 @@ using PettingZoo.Core.Rendering;
|
||||
|
||||
// TODO update title with unread message count if tab is not active
|
||||
// TODO export option (to Tapeti.Cmd compatible format / command-line of course)
|
||||
// TODO send message to (new) publisher tab
|
||||
|
||||
namespace PettingZoo.UI.Tab.Subscriber
|
||||
{
|
||||
public class SubscriberViewModel : BaseViewModel, ITabToolbarCommands
|
||||
{
|
||||
private readonly ITabHost tabHost;
|
||||
private readonly ITabFactory tabFactory;
|
||||
private readonly IConnection connection;
|
||||
private readonly ISubscriber subscriber;
|
||||
private readonly TaskScheduler uiScheduler;
|
||||
private ReceivedMessageInfo? selectedMessage;
|
||||
@ -22,8 +24,11 @@ namespace PettingZoo.UI.Tab.Subscriber
|
||||
private readonly TabToolbarCommand[] toolbarCommands;
|
||||
private IDictionary<string, string>? selectedMessageProperties;
|
||||
|
||||
private readonly DelegateCommand createPublisherCommand;
|
||||
|
||||
|
||||
public ICommand ClearCommand => clearCommand;
|
||||
public ICommand CreatePublisherCommand => createPublisherCommand;
|
||||
|
||||
public ObservableCollection<ReceivedMessageInfo> Messages { get; }
|
||||
|
||||
@ -52,8 +57,11 @@ namespace PettingZoo.UI.Tab.Subscriber
|
||||
public IEnumerable<TabToolbarCommand> ToolbarCommands => toolbarCommands;
|
||||
|
||||
|
||||
public SubscriberViewModel(ISubscriber subscriber)
|
||||
public SubscriberViewModel(ITabHost tabHost, ITabFactory tabFactory, IConnection connection, ISubscriber subscriber)
|
||||
{
|
||||
this.tabHost = tabHost;
|
||||
this.tabFactory = tabFactory;
|
||||
this.connection = connection;
|
||||
this.subscriber = subscriber;
|
||||
|
||||
uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
|
||||
@ -66,6 +74,8 @@ namespace PettingZoo.UI.Tab.Subscriber
|
||||
new TabToolbarCommand(ClearCommand, SubscriberViewStrings.CommandClear, SvgIconHelper.LoadFromResource("/Images/Clear.svg"))
|
||||
};
|
||||
|
||||
createPublisherCommand = new DelegateCommand(CreatePublisherExecute, CreatePublisherCanExecute);
|
||||
|
||||
subscriber.MessageReceived += SubscriberMessageReceived;
|
||||
subscriber.Start();
|
||||
}
|
||||
@ -84,6 +94,19 @@ namespace PettingZoo.UI.Tab.Subscriber
|
||||
}
|
||||
|
||||
|
||||
private void CreatePublisherExecute()
|
||||
{
|
||||
var publisherTab = tabFactory.CreatePublisherTab(connection, SelectedMessage);
|
||||
tabHost.AddTab(publisherTab);
|
||||
}
|
||||
|
||||
|
||||
private bool CreatePublisherCanExecute()
|
||||
{
|
||||
return SelectedMessage != null;
|
||||
}
|
||||
|
||||
|
||||
private void SubscriberMessageReceived(object? sender, MessageReceivedEventArgs args)
|
||||
{
|
||||
RunFromUiScheduler(() =>
|
||||
@ -96,6 +119,8 @@ namespace PettingZoo.UI.Tab.Subscriber
|
||||
|
||||
private void UpdateSelectedMessageProperties()
|
||||
{
|
||||
createPublisherCommand.RaiseCanExecuteChanged();
|
||||
|
||||
SelectedMessageProperties = SelectedMessage != null
|
||||
? MessagePropertiesRenderer.Render(SelectedMessage.Properties)
|
||||
: null;
|
||||
@ -111,7 +136,7 @@ namespace PettingZoo.UI.Tab.Subscriber
|
||||
|
||||
public class DesignTimeSubscriberViewModel : SubscriberViewModel
|
||||
{
|
||||
public DesignTimeSubscriberViewModel() : base(new DesignTimeSubscriber())
|
||||
public DesignTimeSubscriberViewModel() : base(null!, null!, null!, new DesignTimeSubscriber())
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -19,7 +19,7 @@ namespace PettingZoo.UI.Tab.Subscriber {
|
||||
// class via a tool like ResGen or Visual Studio.
|
||||
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||
// with the /str option, or rebuild your VS project.
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
public class SubscriberViewStrings {
|
||||
@ -69,6 +69,15 @@ namespace PettingZoo.UI.Tab.Subscriber {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Open in new Publisher tab.
|
||||
/// </summary>
|
||||
public static string ContextPublish {
|
||||
get {
|
||||
return ResourceManager.GetString("ContextPublish", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Non-persistent.
|
||||
/// </summary>
|
||||
|
@ -120,6 +120,9 @@
|
||||
<data name="CommandClear" xml:space="preserve">
|
||||
<value>Clear</value>
|
||||
</data>
|
||||
<data name="ContextPublish" xml:space="preserve">
|
||||
<value>Open in new Publisher tab</value>
|
||||
</data>
|
||||
<data name="DeliveryModeNonPersistent" xml:space="preserve">
|
||||
<value>Non-persistent</value>
|
||||
</data>
|
||||
|
@ -7,9 +7,20 @@ namespace PettingZoo.UI.Tab
|
||||
{
|
||||
public class ViewTabFactory : ITabFactory
|
||||
{
|
||||
public ITab CreateSubscriberTab(ICommand closeTabCommand, ISubscriber subscriber)
|
||||
private readonly ITabHost tabHost;
|
||||
private readonly ICommand closeTabCommand;
|
||||
|
||||
|
||||
public ViewTabFactory(ITabHost tabHost, ICommand closeTabCommand)
|
||||
{
|
||||
var viewModel = new SubscriberViewModel(subscriber);
|
||||
this.tabHost = tabHost;
|
||||
this.closeTabCommand = closeTabCommand;
|
||||
}
|
||||
|
||||
|
||||
public ITab CreateSubscriberTab(IConnection connection, ISubscriber subscriber)
|
||||
{
|
||||
var viewModel = new SubscriberViewModel(tabHost, this, connection, subscriber);
|
||||
return new ViewTab<SubscriberView, SubscriberViewModel>(
|
||||
closeTabCommand,
|
||||
new SubscriberView(viewModel),
|
||||
@ -18,9 +29,9 @@ namespace PettingZoo.UI.Tab
|
||||
}
|
||||
|
||||
|
||||
public ITab CreatePublisherTab(ICommand closeTabCommand, IConnection connection)
|
||||
public ITab CreatePublisherTab(IConnection connection, ReceivedMessageInfo? fromReceivedMessage = null)
|
||||
{
|
||||
var viewModel = new PublisherViewModel(connection);
|
||||
var viewModel = new PublisherViewModel(connection, fromReceivedMessage);
|
||||
return new ViewTab<PublisherView, PublisherViewModel>(
|
||||
closeTabCommand,
|
||||
new PublisherView(viewModel),
|
||||
|
Loading…
Reference in New Issue
Block a user