I have a hardware component that can be controlled via an SDK (a .DLL file referenced in the project).
I'm trying to implement a helper class that will handle the instantiation of the object and will serve the required object reference to each window that will require it.
This is how I create the object in the code behind of the window:
//private fields
SystemConnector myConn;
MyHardware mySystem; // this is the object i need a reference to
public Window1()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
SystemDiscoverer SystemDiscoverer = new SystemDiscoverer();
SystemDiscoverer.Discovered += (sysInfo) =>
{
myConn = new SystemConnector(sysInfo.IPAddress);
if (myConn != null)
mySystem = new MyHardware(myConn);
};
SystemDiscoverer.Discover();
}
private void WindowBase_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
if (myConn != null)
myConn.Dispose();
if (mySystem != null)
mySystem.Dispose();
}
I want to move this logic into a helper class but I have some questions I couldn't find a solution for:
all of the Singleton examples I could find were for custom objects
and did not explain the use of a referenced object.
How to handle
the creation of the object since it's done inside of an event handler?
To handle the creation from an event handler, try:
LazyInitializer.EnsureInitialized(...)
It is threadsafe, so you should be able to initialize a static property for your singleton just fine.
MSDN article here:
http://blogs.msdn.com/b/jolud/archive/2010/04/02/initialization-of-shared-resources.aspx
An example, from the ASP.NET MVC 4 standard project template.
public sealed class InitializeSimpleMembershipAttribute : ActionFilterAttribute {
private static SimpleMembershipInitializer _initializer;
private static object _initializerLock = new object();
private static bool _isInitialized;
public override void OnActionExecuting(ActionExecutingContext filterContext) {
// Ensure ASP.NET Simple Membership is initialized only once per app start
LazyInitializer.EnsureInitialized(ref _initializer, ref _isInitialized, ref _initializerLock);
}
private class SimpleMembershipInitializer {
public SimpleMembershipInitializer() {
Database.SetInitializer<UsersContext>(null);
try {
using(var context = new UsersContext()) {
if(!context.Database.Exists()) {
// Create the SimpleMembership database without Entity Framework migration schema
((IObjectContextAdapter)context).ObjectContext.CreateDatabase();
}
}
WebSecurity.InitializeDatabaseConnection("DefaultConnection", "UserProfile", "UserId", "UserName", autoCreateTables: true);
}
catch(Exception ex) {
throw new InvalidOperationException("The ASP.NET Simple Membership database could not be initialized. For more information, please see http://go.microsoft.com/fwlink/?LinkId=256588", ex);
}
}
}
}
I would wrap MyHardware class inside a singleton with an async method which wrap the SystemDiscoverer.Discovered Event:
public sealed class MyHardwareSingleton
{
static MyHardwareSingleton()
{ }
private MyHardwareSingleton()
{ }
private static readonly MyHardwareSingleton _myHardware = new MyHardwareSingleton();
private SystemConnector _myConn;
private MyHardware _mySystem;
public MyHardwareSingleton Instance
{
get { return _myHardware; }
}
public Task<MyHardware> GetHardwareAsync()
{
if (_mySystem != null)
{
return Task.FromResult(_mySystem);
}
var tcs = new TaskCompletionSource<MyHardware>();
SystemDiscoverer SystemDiscoverer = new SystemDiscoverer();
SystemDiscoverer.Discovered += (sysInfo) =>
{
myConn = new SystemConnector(sysInfo.IPAddress);
if (myConn != null)
{
mySystem = new MyHardware(myConn);
tcs.TrySetResult(mySystem);
return tcs.Task;
}
// This indicated that myConn came back null.
tcs.TrySetResult(null);
return tcs.Task;
};
// Make SystemDiscoverer run asynchrnously. We will await it so when it completes we will get the desired MyHardware instance.
SystemDiscoverer.DiscoverAsync();
return tcs.Task;
}
}
DiscoverAsync might be added inside the SystemConnector class and look like this:
public Task DiscoverAsync()
{
return Task.Run(() => Discover());
}
And then you can call it from your code via:
private async void Window_Loaded(object sender, RoutedEventArgs e)
{
var myHardware = await MyHardwareSingleton.Instance.DiscoverAsync();
}
Of course, i didn't handle any exception handling / cancellation. This is just a sample of what could be done using TAP
Related
I have a WPF (.NET Framework 4.6) application that uses websocket-sharp (version 3.0.0) to create a websocket server.
I have a WebsocketServer and using EventHandler to tranfer event to MainWindow.xaml.cs but it not working. The MainWindow.xaml.cs listened to a RaiseOnScanDevice event but not any event invoked here.
I think this issue is relative to different thread. I try using Dispatcher.Invoke but it still not working.
System.Windows.Application.Current.Dispatcher.Invoke(new System.Action(() =>
{
RaiseOnScanDevice(this, new EventArgs());
}));
I found an issue (https://github.com/sta/websocket-sharp/issues/350) but the answers do not resolve my issue.
Please help me a solution for this issue.
WebsocketServer.cs file
public class WebsocketServer : WebSocketBehavior
{
private static readonly Lazy<WebsocketServer> lazyInstance = new Lazy<WebsocketServer>(() => new WebsocketServer());
public static WebsocketServer Instance
{
get
{
return lazyInstance.Value;
}
}
private const string TAG = "WebsocketServer";
private const string HOST_IP_ADDRESS = "127.0.0.2"; // localhost
private const int PORT = 38001;
public WebSocketServer socket;
private PacketHandler packetHandler = new PacketHandler();
public event EventHandler<EventArgs> RaiseOnScanDevice = new EventHandler<EventArgs>((a, e) => { });
public WebsocketServer()
{
Initialize();
}
public void Initialize()
{
socket = new WebSocketServer(IPAddress.Parse(HOST_IP_ADDRESS), PORT);
socket.AddWebSocketService<WebsocketServer>("/");
StartServer();
}
public void StartServer()
{
socket.Start();
}
public void StopServer()
{
socket.Stop();
}
protected override Task OnOpen()
{
return base.OnOpen();
}
protected override Task OnClose(CloseEventArgs e)
{
return base.OnClose(e);
}
protected override Task OnError(ErrorEventArgs e)
{
return base.OnError(e);
}
protected override Task OnMessage(MessageEventArgs e)
{
System.IO.StreamReader reader = new System.IO.StreamReader(e.Data);
string message = reader.ReadToEnd();
//Converting the event back to 'eventName' and 'JsonPayload'
PacketModel packet = packetHandler.OpenPacket(message);
HandleMessageFromClient(packet);
return base.OnMessage(e);
}
private void HandleMessageFromClient(PacketModel packet) {
var eventName = packet.EventName;
var data = packet.Data;
if (eventName == null || eventName.Equals(""))
{
return;
}
switch (eventName)
{
case SocketEvent.Hello:
Send("OK");
break;
case SocketEvent.ScanDevice:
ScanDevice();
break;
default:
break;
}
}
private void ScanDevice()
{
try
{
RaiseOnScanDevice(this, new EventArgs());
// or dispatch to Main Thread
System.Windows.Application.Current.Dispatcher.Invoke(new System.Action(() =>
{
RaiseOnScanDevice(this, new EventArgs());
}));
}
catch (Exception exception)
{
Console.WriteLine(exception);
}
}
}
MainWindow.xaml.cs file
public partial class MainWindow : Window
{
public WebsocketServer WebsocketConnection
{
get { return WebsocketServer.Instance; }
}
public MainWindow()
{
InitializeComponent();
WebsocketConnection.RaiseOnScanDevice += SocketConnection_RaiseOnScanDevice;
}
private void SocketConnection_RaiseOnScanDevice(object sender, EventArgs e)
{
Console.WriteLine("SocketConnection_RaiseOnScanDevice");
}
The queue of messages is a good idea but you may want to use a lock to guard access to it. Most likely it won't be an issue but if you don't, you leave yourself open to the possibility of an error if the coroutine is reading from the queue as the websocket is writing to it. For example you could do something like this:
var queueLock = new object();
var queue = new Queue<MyMessageType>();
// use this to read from the queue
MyMessageType GetNextMessage()
{
lock (queueLock) {
if (queue.Count > 0) return queue.Dequeue();
else return null;
}
}
// use this to write to the queue
void QueueMessage(MyMessageType msg)
{
lock(queueLock) {
queue.Enqueue(msg);
}
}
I need to make a simple callback in Xamarin, to check if the network status is connected or disconnected.
I have so far been doing it with this code:
class NetworkControl : INetworkControl
{
private readonly INetworkControl.ICallback _callback;
private readonly Context _context;
private readonly NetworkBroadcastReceiver _receiver = new NetworkBroadcastReceiver();
public NetworkControl(INetworkControl.ICallback callback, Context context)
{
_callback = callback;
_context = context;
IntentFilter filter = new IntentFilter(ConnectivityManager.ConnectivityAction);
context.RegisterReceiver(_receiver, filter);
}
public INetworkControl.ICallback Callback => _callback;
public INetworkControl.NetworkStatus Status
{
get
{
var current = Connectivity.NetworkAccess;
if (current == NetworkAccess.Internet)
{
return INetworkControl.NetworkStatus.Connected;
}
return INetworkControl.NetworkStatus.Disconnected;
}
}
}
class NetworkBroadcastReceiver : BroadcastReceiver
{
private static String TAG = "NetworkBroadcastReceiver";
public override void OnReceive(Context context, Intent intent)
{
if (ShellBridge.Instance != null)
{
if (intent.Action.Equals(ConnectivityManager.ConnectivityAction))
{
NetworkInfo ni = (NetworkInfo)intent.Extras.Get(ConnectivityManager.ExtraNetworkInfo);
if (ni.isConnected)
{
// do something if connected
ShellBridge.Instance.NetworkBridge.Callback.NetworkStatusChanged(INetworkControl.NetworkStatus.Connected);
} else
{
ShellBridge.Instance.NetworkBridge.Callback.NetworkStatusChanged(INetworkControl.NetworkStatus.Connected);
}
}
}
}
The problem is, the function ConnectivityManager.ConnectivityAction in the Intent creating is depricated, and will soon be obsolete. After searching, I found that the pendingIntent should be used for that, but I could not find any valid example of how to use it.
The closest to what I need is this:
https://stackoverflow.com/questions/58588132/how-to-use-registernetworkcallback-with-pendingintent
But, it has not all the information I need.
I need it to be all programmatically, without changing the manifest, for, my app should be a fore- and background app.
Please help, and thank you for your time.
You can take a look at NetworkCallback .
public class ConnectionStateMonitor : NetworkCallback
{
NetworkRequest networkRequest;
public ConnectionStateMonitor()
{
networkRequest = new NetworkRequest.Builder().
AddTransportType(TransportType.Cellular).
AddTransportType(TransportType.Wifi).Build();
}
public void enable(Context context) {
ConnectivityManager connectivityManager = context.GetSystemService(Context.ConnectivityService) as ConnectivityManager;
connectivityManager.RegisterNetworkCallback(networkRequest, this);
}
public override void OnAvailable(Network network)
{
//network available
}
public override void OnLost(Network network)
{
//network lost
}
}
Usage
You just need to instantiate the class ConnectionStateMonitor and enable it , you could detect the network status with the method OnAvailable and OnLost .
ConnectionStateMonitor m = new ConnectionStateMonitor ();
m.enable(context);
Refer
https://github.com/xamarin/Essentials/issues/512
ConnectivityManager.CONNECTIVITY_ACTION deprecated
You don't need to reinvent the wheel. You can achieve all that with Xamarin Essentials' Connectivity.
Besides checking if there is a connectivity like this:
var current = Connectivity.NetworkAccess;
if (current == NetworkAccess.Internet)
{
// Connection to internet is available
}
you can also track when the connectivity type changes:
public class ConnectivityTest
{
public ConnectivityTest()
{
// Register for connectivity changes, be sure to unsubscribe when finished
Connectivity.ConnectivityChanged += Connectivity_ConnectivityChanged;
}
void Connectivity_ConnectivityChanged(object sender, ConnectivityChangedEventArgs e)
{
var access = e.NetworkAccess;
var profiles = e.ConnectionProfiles;
}
}
I currently have an installation "framework" that does specific things. What I need now to do is be able to call my form in parallel with my script. Something like this:
InstallationForm f = new InstallationForm();
Application.Run(f);
InstallSoftware(f);
private static void InstallSoftware(InstallationForm f) {
f.WriteToTextbox("Starting installation...");
Utils.Execute(#"C:\temp\setup.msi", #"-s C:\temp\instructions.xml");
...
f.WriteToTextbox("Installation finished");
The current way I can do this is by adding the Form.Shown handler in InstallSoftware, but that seems really messy. Is there anyway I can do this better?
Your code will not work, because Application.Run(f) returns not until the form was closed.
You may use a simplified Model/View/Controller pattern. Create an InstallationFormController class that has several events, e.g. for textual notifications to be written to your textbox. The InstallationForm registers on these events in it's OnLoad() method and then calls InstallationFormController.Initialize(). That method starts your installation (on a worker thread/task). That installation callback method fires several text events.
InstallationForm f = new InstallationForm(new InstallationFormController());
Application.Run(f);
internal class InstallationFormController
{
public event EventHandler<DataEventArgsT<string>> NotificationTextChanged;
public InstallationFormController()
{
}
public void Initialize()
{
Task.Factory.StartNew(DoInstallation);
}
private void DoInstallation()
{
...
OnNotificationTextChanged(new DataEventArgsT<string>("Installation finished"));
}
private void OnNotificationTextChanged(DataEventArgsT<string> e)
{
if(NotificationTextChanged != null)
NotificationTextChanged(this, e);
}
}
public class DataEventArgsT<T> : EventArgs
{
...
public T Data { get; set; }
}
internal class InstallationForm : Form
{
private readonly InstallationFormController _controller;
public InstallationForm()
{
InitializeComponent();
}
public InstallationForm(InstallationFormController controller) : this()
{
if(controller == null)
throw new ArgumentNullException("controller")
_controller = controller;
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
_controller.NotificationTextChanged += Controller_NotificationTextChanged;
_controller.Initialize();
}
protected virtual void Controller_NotificationTextChanged(object sender, DataEventArgsT<string> e)
{
if(this.InvokeRequired)
{ // call this method on UI thread!!!
var callback = new EventHandler<DataEventArgsT<string>>(Controller_NotificationTextChanged);
this.Invoke(callback, new object[] {sender, e});
}
else
{
_myTextBox.Text = e.Data;
}
}
...
}
I've an application where I'm not able to remove event handlers because I don't know when the last reference will be freed.
My application contains a PropertyChanged event source that is put into a container class that also implements INotifyPropertyChanged. This hierarchy contains more than 6 levels. Each instance of a level could be placed into multiple other instances. That's the reason why I couldn't determine when to free those instances.
The instances on the lowest level will live for the whole application runtime. This causes that all other instances will not be freed and I got a memory leak.
To avoid this event driven memory leak I tried to use WeakEventManager(TEventSource, TEventArgs). This class is only available in .Net 4.5 and because of compatibility to existing hardware I’ve to use .Net 4.0.
In .Net 4.0 is a PropertyChangedEventManager available that should do the same for INotifyPropertyChanged.
My classes are freed correctly.
But there is still a memory leak.
I simplified my application to the following code that produces a memory leak:
// This code will force the memory leak
while (true)
{
var eventSource = new StateChangedEventSource();
var eventReceiver = new StateChangedEventReceiver();
PropertyChangedEventManager.AddListener(eventSource, eventReceiver, string.Empty);
}
public class EventSource : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
}
public class EventReceiver : IWeakEventListener
{
public bool ReceiveWeakEvent(Type managerType, object sender, EventArgs e)
{
return true;
}
}
Yes I know there is no RemoveListener call. I couldn’t determine when an instance is never used and could be freed. If I knew that I could use normal event registration and deregistration. In that case I don’t have to use the PropertyChangedEventManager.
What is the problem of my sample code? Why does it produce a memory leak?
Edit 2014/02/17:
I tried the WeakEventManager(TEventSource, TEventArgs) and .Net 4.5 and the problem still exists.
var eventSource = new EventSource();
var i = 0;
while (true)
{
var eventReceiver = new EventReceiver();
// --> Use only one of the following three lines. Each of them will produce a memory leak.
WeakEventManager<EventSource, PropertyChangedEventArgs>.AddHandler(eventSource, "PropertyChanged", eventReceiver.OnEvent);
PropertyChangedEventManager.AddListener(eventSource, eventReceiver, string.Empty);
WeakEventManager<EventSource, EventArgs>.AddHandler(eventSource, "SomeOtherEvent", eventReceiver.OnSomeOtherEvent);
// <--
++i;
if (i == 1 << 18)
{
Thread.Sleep(10);
GC.Collect(2);
Thread.Sleep(10);
i = 0;
}
}
public class EventSource : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public event EventHandler<EventArgs> SomeOtherEvent;
}
public class EventReceiver : IWeakEventListener
{
public void OnSomeOtherEvent(object sender, EventArgs args)
{
}
public void OnEvent(object sender, PropertyChangedEventArgs args)
{
}
public bool ReceiveWeakEvent(Type managerType, object sender, EventArgs e)
{
return true;
}
}
This code compiled using .Net 4.5 do also run out of memory. I got the hint using the Thread.Sleep construct here.
I don't think the isue with WeakEventManager<,> is specific to non-WPF, as i can reproduce a memory leak in a WPF application as well.
The problem lies with the management of the event table. For each subscription, the WeakEventManager creates an entry in a table. This entry and table are (by necessity) strong-referenced.
The problem is, that by default, the WeakEventManager does not clean up the records. You have to call RemoveHandler. But beware. It's not thread safe. If you call it from another thread it may fail (doesn't throw an exception, you'll just experience that there's still a memory leak). When called from a finalizer, it doesn't work reliably either.
I also investigated into the source code and found that while it contains logic to do cleanup on AddHandler and when an event is received, it is disabled by default (see WeakEventManager.cs => WeakEventTable.CurrentWeakEventTable.IsCleanupEnabled). Also, you can't access the Cleanup method since the methods and properties necessary to do so are private or internal. So you can't even create subclass to access these methods / modify the behavior.
WeakEventManager<,> is broken
So basically (as far as i can understand) WeakEventManager<,> is broken by design (it keeps a StrongReference to the subscriber table-entry).
Instead of fixing a MemoryLeak it will only reduce the MemoryLeak (the event source and listener can be garbage collected, but the entry for the event subscription is not => new memory leak). Of course the memory leak introduced by WeakEventManager<,> is small.
Based on the information at msdn and codeproject I realized that the WeakEventManager(TEventSource, TEventArgs) class will only work in WPF applications. I'm using WinForms what's the reason why it seems not to work.
I decided to create my own WeakEventManager that works without using the build in WeakEventManager provided by the .Net framework.
The implementation of my WeakEventManager uses a background thread to clean up all instances. Maybe there's a better solution but this solution will work correctly in my application.
public static class ThreadedWeakEventManager
{
private static readonly TimeSpan CleanupInterval = TimeSpan.FromSeconds(1.0);
private static readonly List<IInternalWeakEventManager> EventManagers = new List<IInternalWeakEventManager>();
private static volatile bool _performCleanup = true;
static ThreadedWeakEventManager()
{
new Thread(Cleanup) { IsBackground = true, Priority = ThreadPriority.Lowest }.Start();
}
public static void AddHandler<TEventArgs>(object eventSource, string eventName, EventHandler<TEventArgs> eventHandler)
where TEventArgs : EventArgs
{
var weakEventManager = new InternalWeakEventManager<TEventArgs>(eventSource, eventName, eventHandler);
lock (EventManagers)
{
EventManagers.Add(weakEventManager);
}
}
public static void AddPropertyChangedHandler(INotifyPropertyChanged eventSource, EventHandler<PropertyChangedEventArgs> eventHandler)
{
AddHandler(eventSource, "PropertyChanged", eventHandler);
}
public static void AddCollectionChangedEventHandler(INotifyCollectionChanged eventSource, EventHandler<NotifyCollectionChangedEventArgs> eventHandler)
{
AddHandler(eventSource, "CollectionChanged", eventHandler);
}
public static void RemoveHandler<TEventArgs>(object eventSource, string eventName, EventHandler<TEventArgs> eventHandler)
where TEventArgs : EventArgs
{
if (eventSource == null || string.IsNullOrWhiteSpace(eventName) || eventHandler == null)
{
return;
}
lock (EventManagers)
{
EventManagers.RemoveAll(item => object.ReferenceEquals(item.EventData.EventSource, eventSource) && item.EventName.Equals(eventName) && eventHandler.Method.Equals(item.EventData.EventHandlerMethodInfo));
}
}
public static void RemovePropertyChangedHandler(INotifyPropertyChanged eventSource, EventHandler<PropertyChangedEventArgs> eventHandler)
{
RemoveHandler(eventSource, "PropertyChanged", eventHandler);
}
public static void RemoveCollectionChangedEventHandler(INotifyCollectionChanged eventSource, EventHandler<NotifyCollectionChangedEventArgs> eventHandler)
{
RemoveHandler(eventSource, "CollectionChanged", eventHandler);
}
public static void CancelCleanup()
{
_performCleanup = false;
}
private static void Cleanup()
{
while (_performCleanup)
{
Thread.Sleep(CleanupInterval);
lock (EventManagers)
{
for (var i = EventManagers.Count - 1; i >= 0; --i)
{
var item = EventManagers[i];
if (item.EventData.IsGarbageCollected)
{
item.UnwireEvent();
EventManagers.RemoveAt(i);
}
}
}
}
}
private interface IInternalWeakEventManager
{
string EventName { get; }
IWeakEventData EventData { get; }>
void UnwireEvent();
void OnEvent(object sender, EventArgs args);
}
private class InternalWeakEventManager<TEventArgs> : IInternalWeakEventManager
where TEventArgs : EventArgs
{
private static readonly MethodInfo OnEventMethodInfo = typeof(InternalWeakEventManager<TEventArgs>).GetMethod("OnEvent");
private EventInfo _eventInfo;
private Delegate _onEvent;
public InternalWeakEventManager(object eventSource, string eventName, EventHandler<TEventArgs> eventHandler)
{
this.EventData = new WeakEventData<TEventArgs>(eventSource, eventHandler);
this.WireEvent(eventSource, eventName);
}
public string EventName
{
get { return this._eventInfo.Name; }
}
public IWeakEventData EventData { get; private set; }
public void UnwireEvent()
{
var eventSource = this.EventData.EventSource;
if (eventSource == null)
{
return;
}
this._eventInfo.RemoveEventHandler(eventSource, this._onEvent);
}
public void OnEvent(object sender, EventArgs args)
{
this.EventData.ForwardEvent(sender, args);
}
private void WireEvent(object eventSource, string eventName)
{
this._eventInfo = eventSource.GetType().GetEvents().FirstOrDefault(item => item.Name == eventName);
if (this._eventInfo == null)
{
throw new InvalidOperationException(string.Format("The event source type {0} doesn't contain an event named {1}.", eventSource.GetType().FullName, eventName));
}
this._onEvent = Delegate.CreateDelegate(this._eventInfo.EventHandlerType, this, OnEventMethodInfo);
this._eventInfo.AddEventHandler(eventSource, this._onEvent);
}
}
private interface IWeakEventData
{
bool IsGarbageCollected { get; }
object EventSource { get; }>
MethodInfo EventHandlerMethodInfo { get; }
void ForwardEvent(object sender, EventArgs args);
}
private class WeakEventData<TEventArgs> : IWeakEventData
where TEventArgs : EventArgs
{
private readonly WeakReference _eventSource;
private readonly WeakReference _eventTargetInstance;
public WeakEventData(object eventSource, EventHandler<TEventArgs> eventHandler)
{
this._eventSource = new WeakReference(eventSource);
this._eventTargetInstance = new WeakReference(eventHandler.Target);
this.EventHandlerMethodInfo = eventHandler.Method;
}
public object EventSource
{
get { return this._eventSource.Target; }
}
public MethodInfo EventHandlerMethodInfo { get; private set; }
public bool IsGarbageCollected
{
get
{
return !this._eventSource.IsAlive || !this._eventTargetInstance.IsAlive;
}
}
public void ForwardEvent(object sender, EventArgs args)
{
var target = this._eventTargetInstance.Target;
if (target != null)
{
this.EventHandlerMethodInfo.Invoke(target, new[] { sender, args });
}
}
}
}
Memory leak in WeakEventManager can occur if AddListener is called from different threads.
Just call AddListener from the main UI thread and inner "CleanUp" will work fine.
I'm a beginner at using ActiveMQ with C#. I've created a simple windows form with one button and one label. When I click on the button, i send a message to the queue and the label is initialized with the message I just sent. Of course, I could initialize my label directly but I want my form to rather consume the message from the queue in order to update my label.
The problem is I don't manage to handle the message in the same form to update my label. My consumer code is not called at all and yet, its initialized in the Load event of my form.
Here's the code
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
InitializeHandlerAMQ();
}
private void InitializeHandlerAMQ()
{
Tchat tchat = null;
IDestination dest = _session.GetQueue(QUEUE_DESTINATION);
using(IMessageConsumer consumer = _session.CreateConsumer(dest))
{
IMessage message;
while((message = consumer.Receive(TimeSpan.FromMilliseconds(2000))) != null)
{
var objectMessage = message as IObjectMessage;
if(objectMessage != null)
{
tchat = objectMessage.Body as Tchat;
if (tchat != null)
{
textBox2.Text += string.Format("{0}{1}", tchat.Message, Environment.NewLine);
}
}
}
}
}
If I close my windows form and restart it, then my label is well updated but I don't want to close it and re open it.
Do you have any ideas guys ?
Try creating a class with an event delegate like this.
A subscriber class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Apache.NMS;
using Apache.NMS.ActiveMQ;
using Apache.NMS.ActiveMQ.Commands;
namespace Utilities
{
public delegate void QMessageReceivedDelegate(string message);
public class MyQueueSubscriber : IDisposable
{
private readonly string topicName = null;
private readonly IConnectionFactory connectionFactory;
private readonly IConnection connection;
private readonly ISession session;
private readonly IMessageConsumer consumer;
private bool isDisposed = false;
public event QMessageReceivedDelegate OnMessageReceived;
public MyQueueSubscriber(string queueName, string brokerUri, string clientId)
{
this.topicName = queueName;
this.connectionFactory = new ConnectionFactory(brokerUri);
this.connection = this.connectionFactory.CreateConnection();
this.connection.ClientId = clientId;
this.connection.Start();
this.session = connection.CreateSession();
ActiveMQQueue topic = new ActiveMQQueue(queueName);
//this.consumer = this.session.CreateDurableConsumer(topic, consumerId, "2 > 1", false);
this.consumer = this.session.CreateConsumer(topic, "2 > 1");
this.consumer.Listener += new MessageListener(OnMessage);
}
public void OnMessage(IMessage message)
{
ITextMessage textMessage = message as ITextMessage;
if (this.OnMessageReceived != null)
{
this.OnMessageReceived(textMessage.Text);
}
}
#region IDisposable Members
public void Dispose()
{
if (!this.isDisposed)
{
this.consumer.Dispose();
this.session.Dispose();
this.connection.Dispose();
this.isDisposed = true;
}
}
#endregion
}
}
Winforms
In your windows form Subscribe to the queue like this
MyQueueSubscriber QueueSubscriber = new MyQueueSubscriber(QueueName, ActiveMQHost, QueueClientId);
QueueSubscriber.OnMessageReceived += new QMessageReceivedDelegate(QueueSubscriber_OnMessageReceived);
static void QueueSubscriber_OnMessageReceived(string message)
{
SetText(message);
}
private void SetText(string text)
{
// InvokeRequired required compares the thread ID of the
// calling thread to the thread ID of the creating thread.
// If these threads are different, it returns true.
if (this.textBox1.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
this.Invoke(d, new object[] { text });
}
else
{
this.labelname.value = text;
}
}
Resources:
Unfortunately there are not that many resources to teach C# & ActiveMQ. Try using http://activemq.apache.org/nms/ as this was quite good.
Try looking at a small article from http://www.codersource.net/MicrosoftNet/CAdvanced/PublishSubscribeinCusingActiveMQ.aspx. Disclaimer: This is my website and the article was written by me. Sorry for the self publicity. But I feel this is relevant to the topic.