diff --git a/App.xaml b/App.xaml
index bffea81..a9025ab 100644
--- a/App.xaml
+++ b/App.xaml
@@ -5,7 +5,8 @@
-
+
+
diff --git a/Icons.xaml b/Icons.xaml
new file mode 100644
index 0000000..848e067
--- /dev/null
+++ b/Icons.xaml
@@ -0,0 +1,483 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Images/Clear.svg b/Images/Clear.svg
new file mode 100644
index 0000000..a93f38f
--- /dev/null
+++ b/Images/Clear.svg
@@ -0,0 +1,56 @@
+
+
+
+
diff --git a/Images/Clear.xaml b/Images/Clear.xaml
new file mode 100644
index 0000000..85ad8ee
--- /dev/null
+++ b/Images/Clear.xaml
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Images/Connect.png b/Images/Connect.png
deleted file mode 100644
index 1fba021..0000000
Binary files a/Images/Connect.png and /dev/null differ
diff --git a/Images/Connect.svg b/Images/Connect.svg
new file mode 100644
index 0000000..cc7e08e
--- /dev/null
+++ b/Images/Connect.svg
@@ -0,0 +1,85 @@
+
+
+
+
diff --git a/Images/Connect.xaml b/Images/Connect.xaml
new file mode 100644
index 0000000..a31ac6f
--- /dev/null
+++ b/Images/Connect.xaml
@@ -0,0 +1,203 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Images/Disconnect.png b/Images/Disconnect.png
deleted file mode 100644
index e86d993..0000000
Binary files a/Images/Disconnect.png and /dev/null differ
diff --git a/Images/Disconnect.svg b/Images/Disconnect.svg
new file mode 100644
index 0000000..34bc7c2
--- /dev/null
+++ b/Images/Disconnect.svg
@@ -0,0 +1,86 @@
+
+
+
+
diff --git a/Images/Disconnect.xaml b/Images/Disconnect.xaml
new file mode 100644
index 0000000..66d8822
--- /dev/null
+++ b/Images/Disconnect.xaml
@@ -0,0 +1,203 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Model/IConnection.cs b/Model/IConnection.cs
index a0967e8..394a721 100644
--- a/Model/IConnection.cs
+++ b/Model/IConnection.cs
@@ -2,6 +2,29 @@
namespace PettingZoo.Model
{
+ public enum ConnectionStatus
+ {
+ Disconnected,
+ Connecting,
+ Connected,
+ Error
+ }
+
+
+ public class StatusChangedEventArgs : EventArgs
+ {
+ public ConnectionStatus Status { get; private set; }
+ public string Context { get; private set; }
+
+
+ public StatusChangedEventArgs(ConnectionStatus status, string context)
+ {
+ Status = status;
+ Context = context;
+ }
+ }
+
+
public class MessageReceivedEventArgs : EventArgs
{
public MessageInfo MessageInfo { get; private set; }
@@ -14,8 +37,10 @@ namespace PettingZoo.Model
}
+
public interface IConnection : IDisposable
{
+ event EventHandler StatusChanged;
event EventHandler MessageReceived;
}
}
diff --git a/Model/RabbitMQClientConnection.cs b/Model/RabbitMQClientConnection.cs
index 2978baf..0b9c4b5 100644
--- a/Model/RabbitMQClientConnection.cs
+++ b/Model/RabbitMQClientConnection.cs
@@ -12,11 +12,14 @@ namespace PettingZoo.Model
{
public class RabbitMQClientConnection : IConnection
{
+ private const int ConnectRetryDelay = 5000;
+
private readonly CancellationTokenSource connectionTaskToken;
private RabbitMQ.Client.IConnection connection;
private IModel model;
+ public event EventHandler StatusChanged;
public event EventHandler MessageReceived;
@@ -44,6 +47,9 @@ namespace PettingZoo.Model
connection.Dispose();
connection = null;
}
+
+ StatusChanged = null;
+ MessageReceived = null;
}
@@ -58,18 +64,34 @@ namespace PettingZoo.Model
Password = connectionInfo.Password
};
- // ToDo exception handling
- connection = factory.CreateConnection();
- model = connection.CreateModel();
+ var statusContext = String.Format(@"{0}:{1}{2}", connectionInfo.Host, connectionInfo.Port, connectionInfo.VirtualHost);
- var queueName = model.QueueDeclare().QueueName;
- model.QueueBind(queueName, connectionInfo.Exchange, connectionInfo.RoutingKey);
+ while (!cancellationToken.IsCancellationRequested)
+ {
+ DoStatusChanged(ConnectionStatus.Connecting, statusContext);
+ try
+ {
+ connection = factory.CreateConnection();
+ model = connection.CreateModel();
+
+ var queueName = model.QueueDeclare().QueueName;
+ model.QueueBind(queueName, connectionInfo.Exchange, connectionInfo.RoutingKey);
- var consumer = new EventingBasicConsumer(model);
- consumer.Received += ClientReceived;
+ var consumer = new EventingBasicConsumer(model);
+ consumer.Received += ClientReceived;
- model.BasicConsume(queueName, true, consumer);
+ model.BasicConsume(queueName, true, consumer);
+ DoStatusChanged(ConnectionStatus.Connected, statusContext);
+
+ break;
+ }
+ catch (Exception e)
+ {
+ DoStatusChanged(ConnectionStatus.Error, e.Message);
+ Task.Delay(ConnectRetryDelay, cancellationToken).Wait(cancellationToken);
+ }
+ }
}
@@ -90,6 +112,13 @@ namespace PettingZoo.Model
}
+ private void DoStatusChanged(ConnectionStatus status, string context = null)
+ {
+ if (StatusChanged != null)
+ StatusChanged(this, new StatusChangedEventArgs(status, context));
+ }
+
+
private static Dictionary ConvertProperties(IBasicProperties basicProperties)
{
var properties = new Dictionary();
diff --git a/PettingZoo.csproj b/PettingZoo.csproj
index cc251c1..ed207e9 100644
--- a/PettingZoo.csproj
+++ b/PettingZoo.csproj
@@ -92,6 +92,10 @@
MSBuild:Compile
Designer
+
+ Designer
+ MSBuild:Compile
+
Designer
MSBuild:Compile
@@ -177,10 +181,6 @@
false
-
-
-
-
diff --git a/Properties/Resources.Designer.cs b/Properties/Resources.Designer.cs
index abbcb26..ba0c262 100644
--- a/Properties/Resources.Designer.cs
+++ b/Properties/Resources.Designer.cs
@@ -212,5 +212,41 @@ namespace PettingZoo.Properties {
return ResourceManager.GetString("PropertyValue", resourceCulture);
}
}
+
+ ///
+ /// Looks up a localized string similar to Connected.
+ ///
+ public static string StatusConnected {
+ get {
+ return ResourceManager.GetString("StatusConnected", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Connecting to {0}....
+ ///
+ public static string StatusConnecting {
+ get {
+ return ResourceManager.GetString("StatusConnecting", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Disconnected.
+ ///
+ public static string StatusDisconnected {
+ get {
+ return ResourceManager.GetString("StatusDisconnected", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Error: {0}.
+ ///
+ public static string StatusError {
+ get {
+ return ResourceManager.GetString("StatusError", resourceCulture);
+ }
+ }
}
}
diff --git a/Properties/Resources.resx b/Properties/Resources.resx
index 4758649..e9c49e4 100644
--- a/Properties/Resources.resx
+++ b/Properties/Resources.resx
@@ -168,4 +168,16 @@
Value
+
+ Connected
+
+
+ Connecting to {0}...
+
+
+ Disconnected
+
+
+ Error: {0}
+
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..bfe49bb
--- /dev/null
+++ b/README.md
@@ -0,0 +1,12 @@
+# Petting Zoo
+##### A RabbitMQ live message viewer
+
+ToDo: explain how it brings you coffee, fame and world peace. Or maybe just makes watching the messages flow slightly more comfortable.
+
+
+#### Icons
+
+Icons are from the Interaction Assets pack by Madebyoliver
+
+
+Designed by Freepik and distributed by Flaticon
\ No newline at end of file
diff --git a/View/ConnectionWindow.xaml b/View/ConnectionWindow.xaml
index 29b3776..700f928 100644
--- a/View/ConnectionWindow.xaml
+++ b/View/ConnectionWindow.xaml
@@ -40,6 +40,7 @@
+
diff --git a/View/ConnectionWindow.xaml.cs b/View/ConnectionWindow.xaml.cs
index 2298a57..04d9afd 100644
--- a/View/ConnectionWindow.xaml.cs
+++ b/View/ConnectionWindow.xaml.cs
@@ -1,4 +1,5 @@
using System.Windows;
+using System.Windows.Input;
using PettingZoo.Model;
using PettingZoo.ViewModel;
@@ -34,5 +35,12 @@ namespace PettingZoo.View
InitializeComponent();
DataContext = viewModel;
}
+
+
+ private void NumericPreviewTextInput(object sender, TextCompositionEventArgs args)
+ {
+ if (!char.IsDigit(args.Text, args.Text.Length - 1))
+ args.Handled = true;
+ }
}
}
diff --git a/View/MainWindow.xaml b/View/MainWindow.xaml
index e1b651a..1f907a8 100644
--- a/View/MainWindow.xaml
+++ b/View/MainWindow.xaml
@@ -17,27 +17,27 @@
-
+
diff --git a/ViewModel/MainViewModel.cs b/ViewModel/MainViewModel.cs
index d2e4114..e392b36 100644
--- a/ViewModel/MainViewModel.cs
+++ b/ViewModel/MainViewModel.cs
@@ -6,6 +6,7 @@ using System.Threading.Tasks;
using System.Windows.Input;
using PettingZoo.Infrastructure;
using PettingZoo.Model;
+using PettingZoo.Properties;
namespace PettingZoo.ViewModel
{
@@ -17,6 +18,7 @@ namespace PettingZoo.ViewModel
private ConnectionInfo connectionInfo;
private IConnection connection;
+ private string connectionStatus;
private readonly ObservableCollection messages;
private MessageInfo selectedMessage;
@@ -37,6 +39,19 @@ namespace PettingZoo.ViewModel
}
}
+ public string ConnectionStatus
+ {
+ get { return connectionStatus; }
+ private set
+ {
+ if (value == connectionStatus)
+ return;
+
+ connectionStatus = value;
+ RaisePropertyChanged();
+ }
+ }
+
public ObservableCollection Messages { get { return messages; } }
public MessageInfo SelectedMessage
@@ -81,6 +96,7 @@ namespace PettingZoo.ViewModel
this.connectionInfoBuilder = connectionInfoBuilder;
this.connectionFactory = connectionFactory;
+ connectionStatus = GetConnectionStatus(null);
messages = new ObservableCollection();
connectCommand = new DelegateCommand(ConnectExecute);
@@ -105,9 +121,13 @@ namespace PettingZoo.ViewModel
if (newInfo == null)
return;
+ if (connection != null)
+ connection.Dispose();
+
ConnectionInfo = newInfo;
connection = connectionFactory.CreateConnection(connectionInfo);
connection.MessageReceived += ConnectionMessageReceived;
+ connection.StatusChanged += ConnectionStatusChanged;
disconnectCommand.RaiseCanExecuteChanged();
}
@@ -122,6 +142,8 @@ namespace PettingZoo.ViewModel
}
ConnectionInfo = null;
+ ConnectionStatus = GetConnectionStatus(null);
+
disconnectCommand.RaiseCanExecuteChanged();
}
@@ -145,16 +167,41 @@ namespace PettingZoo.ViewModel
}
- private void ConnectionMessageReceived(object sender, MessageReceivedEventArgs e)
+ private void ConnectionStatusChanged(object sender, StatusChangedEventArgs args)
+ {
+ ConnectionStatus = GetConnectionStatus(args);
+ }
+
+
+ private void ConnectionMessageReceived(object sender, MessageReceivedEventArgs args)
{
RunFromUiScheduler(() =>
{
- messages.Add(e.MessageInfo);
+ messages.Add(args.MessageInfo);
clearCommand.RaiseCanExecuteChanged();
});
}
+ private string GetConnectionStatus(StatusChangedEventArgs args)
+ {
+ if (args != null)
+ switch (args.Status)
+ {
+ case Model.ConnectionStatus.Connecting:
+ return String.Format(Resources.StatusConnecting, args.Context);
+
+ case Model.ConnectionStatus.Connected:
+ return String.Format(Resources.StatusConnected, args.Context);
+
+ case Model.ConnectionStatus.Error:
+ return String.Format(Resources.StatusError, args.Context);
+ }
+
+ return Resources.StatusDisconnected;
+ }
+
+
private void RunFromUiScheduler(Action action)
{
Task.Factory.StartNew(action, CancellationToken.None, TaskCreationOptions.None, uiScheduler);