I´ve installed nuget package websocket-sharp-customheaders for my WebSocket application and want to send a sessionId within the http-header-information.
Client-Code:
public class SocketController
{
public static readonly string ConnectionString = "ws://localhost:{0}/{1}";
private string sessionId;
public WebSocket NotificationSocket { get; private set; }
public SocketController(ServiceConfiguration config)
{
NotificationSocket = new WebSocket(string.Format(ConnectionString, config.Port, "Notify"));
}
public void Connect()
{
NotificationSocket.Connect();
}
public void SendNotification(string sessionId, Notification data)
{
if (NotificationSocket.ReadyState == WebSocketState.Open)
{
NotificationSocket.CustomHeaders = new Dictionary<string, string> { { "SessionId", sessionId } };
NotificationSocket.Send(data.SerializeJson());
}
}
public void SetNotificationSocketOnMessage(Action<NotifyMessage> onMessage)
{
NotificationSocket.OnMessage += (sender, e) => onMessage(e.Data.DeserializeJson<NotifyMessage>());
}
public void Stop()
{
NotificationSocket.Close();
}
}
In SendNotification the header is set. But the server doesn´t receive the header information.
Server Socket Behavior:
public class NotifyService : WebSocketBehavior
{
protected override void OnMessage(MessageEventArgs e)
{
var command = e.Data;
if (!Context.Headers.Contains("SessionId"))
return;
// Stuff todo with SessionId and Data
}
}
May someone tell me how to properly send header-information?
https://github.com/sta/websocket-sharp/pull/22
I believe they don't allow this.
Related
I implemented an observer pattern using events and delegates. The program is receiving and processing big amounts of data (around 3000 messages per second) but at some point, it starts sending messages with a delayed timestamp, which I am trying to fix. I have 3 main classes that do the job in my opinion:
public class MessageTracker : IObservable<MessageEventArgs>
{
private List<IObserver<MessageEventArgs>> observers;
public MessageTracker()
{
observers = new List<IObserver<MessageEventArgs>>();
}
private static readonly MessageTracker mInstance = new MessageTracker();
private static MessageTracker getInstance() => mInstance;
private class Unsubscriber : IDisposable
{
private List<IObserver<MessageEventArgs>> _observers;
private IObserver<MessageEventArgs> _observer;
public Unsubscriber(List<IObserver<MessageEventArgs>> observers, IObserver<MessageEventArgs> observer)
{
this._observers = observers;
this._observer = observer;
}
public void Dispose()
{
if (! (_observer == null)) _observers.Remove(_observer);
}
}
public IDisposable Subscribe(IObserver<MessageEventArgs> observer)
{
if (! observers.Contains(observer))
observers.Add(observer);
return new Unsubscriber(observers, observer);
}
public void MessageTrack(MessageEventArgs msg) {
observers.AsParallel().ForAll(observer =>
{
if (msg is null)
observer.OnError(new ArgumentException("MessageError."));
else
observer.OnNext(msg);
});
}
public void EndMessageTrans(){
foreach(var observer in observers.ToArray())
if (observers.Contains(observer))
observer.OnCompleted();
observers.Clear();
}
}
public class MessageReporter : IObserver<MessageEventArgs>
{
private IDisposable unsubscriber;
public MessageReporter()
{ }
public event EventHandler<MessageEventArgs> OnNextMessage;
public virtual void Subscribe(IObservable<MessageEventArgs> provider)
{
if (provider != null)
unsubscriber = provider.Subscribe(this);
}
public void OnCompleted()
{
this.Unsubscribe();
}
public void OnError(Exception error)
{
}
public void OnNext(MessageEventArgs value)
{
if (OnNextMessage != null)
{
OnNextMessage?.Invoke(this, value);
}
}
public virtual void Unsubscribe()
{
unsubscriber.Dispose();
}
}
public sealed class MessageDataWorker
{
private readonly bool mSubscribeAll;
private readonly IEnumerable<string> mMessages;
public MessageDataWorker(IEnumerable<string> messages)
{
mMessages = messages;
if ((mMessages?.Count() ?? 0) == 0)
mSubscribeAll = true;
}
public override void DoWork()
{
var messageReporter = new MessageReporter();
messageReporter.OnNextMessage += OnNewMessageReceived;
messageReporter.Subscribe(MessageTracker.GetInstance());
while (!mShouldStop.WaitOne(100)) ;
MessageReporter.Unsubscribe();
}
private void OnNewMessageReceived(object sender, MessageEventArgs e)
{
if (!mSubscribeAll && !mMessages.Contains(e.Message))
return;
string message = "Message|" +
$"{e.Time}|" +
$"{e.Text};
try
{
Console.WriteLine(message);
}
catch { }
}
}
What I am trying to achieve is skipping notifications or receiving data for X milliseconds after sending the last message and afterward send the newest received message. I tried sleeping the observers and the provider but it just increased the delay. I think I am missing something and any suggestion would be appreciated.
From what I can tell from your code you could write the three classes with this code:
var messageTrack = new Subject<MessageEventArgs>();
var query =
from e in messageTrack
where !mMessages.Contains(e.Message)
select $"Message|{e.Time}|{e.Text}";
query.Throttle(TimeSpan.FromMilliseconds(X)).Subscribe(Console.WriteLine);
You should never need to implement IObservable<> or IObserver<> yourself. It almost always ends in disaster.
The above code handles the throttling you wanted.
I want to convert the string the server is receiving from the client to in an array. However when I use the String.Split method the result variable is showing null. Anyone know why that might be?
namespace ExampleLib.Server
{
public class Server
{
private class ConnectedClient
{
public int ID { get; }
private TcpClient _client;
private StreamReader _streamReader;
public delegate void NetDataEventHandler(object sender, NetDataEventArgs e);
public event NetDataEventHandler NetData;
public virtual void OnNetData(NetDataEventArgs e)
{
NetData?.Invoke(this, e);
}
public class NetDataEventArgs
{
public NetDataEventArgs(int id, string message)
{
ID = id;
Message = message;
}
public string Message { get; }
public int ID { get; }
}
public ConnectedClient(int id, TcpClient client)
{
ID = id;
_client = client;
}
private void ClientReceiveData(object sender, ConnectedClient.NetDataEventArgs e)
{
if (string.IsNullOrEmpty(e.Message) == false)
{
Trace.WriteLine($" Client {e.ID}: {e.Message}");
var result = e.Message.Split(',');
}
}
You have stopped at the break-point.
You should proceed one more step, in order for that line to be executed.
Currently, you're at a step similar to this.
If you proceed one more step using F10 (or 'Step Over' button) , it will execute that line and assign the value of addition to c in this example.
I am just looking into implementing an MVVMCross Messenger solution that will enable me to upload information to Google Analytics when published from either the iOS application or the PCL.
The problem I have the is that the subscription delgates are not fired after I publish. Can you subscribe to MVVMCross Messenger subscriptions from a static class?
Subscriptions in static class
public static class GoogleAnalyticsWrapper //: IDisposable
{
private const string TrackingId = "xxxxxxxxxxx";
private static readonly IMvxMessenger messenger;
private static readonly MvxSubscriptionToken screenNameToken;
private static readonly MvxSubscriptionToken eventToken;
private static readonly MvxSubscriptionToken exceptionToken;
private static readonly MvxSubscriptionToken performanceToken;
private static readonly MvxSubscriptionToken publishToken;
private static bool disposed = false;
private static SafeHandle handle;
static GoogleAnalyticsWrapper()
{
Gai.SharedInstance.DispatchInterval = 60;
Gai.SharedInstance.TrackUncaughtExceptions = true;
Gai.SharedInstance.GetTracker(TrackingId);
messenger = new MvxMessengerHub();// Mvx.Resolve<IMvxMessenger>();
screenNameToken = messenger.Subscribe<GaScreenNameMessage>((m) => SetScreenName(m));
int count = messenger.CountSubscriptionsFor<GaScreenNameMessage>();
eventToken = messenger.Subscribe<GaEventMessage>(CreateEvent);
exceptionToken = messenger.Subscribe<GaExceptionMessage>(CreateException);
performanceToken = messenger.Subscribe<GaPerformanceTimingMessage>(CreatePerformanceMetric);
publishToken = messenger.Subscribe<GaPublishMessage>(PublishAll);
}
public static string Dummy { get; set; }
public static void SetScreenName(GaScreenNameMessage message)
{
System.Diagnostics.Debugger.Break();
Gai.SharedInstance.DefaultTracker.Set(GaiConstants.ScreenName, message.ScreenName);
Gai.SharedInstance.DefaultTracker.Send(DictionaryBuilder.CreateScreenView().Build());
}
public static void CreateEvent(GaEventMessage message)
=> Gai.SharedInstance.DefaultTracker.Send(DictionaryBuilder.CreateEvent(message.Category, message.Action, message.Label, message.Number).Build());
private static void CreateException(GaExceptionMessage message)
=> Gai.SharedInstance.DefaultTracker.Send(DictionaryBuilder.CreateException(message.ExceptionMessage, message.IsFatal).Build());
private static void CreatePerformanceMetric(GaPerformanceTimingMessage message)
=> Gai.SharedInstance.DefaultTracker.Send(DictionaryBuilder.CreateTiming(message.Category, message.Milliseconds, message.Name, message.Label).Build());
private static void PublishAll(GaPublishMessage message)
=> Gai.SharedInstance.Dispatch();
public static void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if (!disposed)
{
if (disposing)
{
// Dispose managed resources.
if (handle != null)
{
handle.Dispose();
}
}
// Dispose unmanaged managed resources.
disposed = true;
}
}
}
Publication
messengerService.Publish<GaEventMessage>(new GaEventMessage(this, "Event", "Publish Event", "Publish Event From First View Model", 123));
The problem is, that you are creating a new MvxMessengerHub in your static class, but (I guess) inject IMvxMessenger in your consuming classes, which is created by MvvMCross during the initialization lifecycle and so a different instance.
The easy solution would be to initialize it in your App.cs like
public class App : Cirrious.MvvmCross.ViewModels.MvxApplication
{
public override void Initialize()
{
// ...
var m = Cirrious.CrossCore.Mvx.Resolve<IMvxMessenger>();
GoogleAnalyticsWrapper.Initialize(m);
// ...
}
}
With a wrapper like this
public static class GoogleAnalyticsWrapper
{
static void Initialize(IMvxMessenger messenger)
{
Gai.SharedInstance.DispatchInterval = 60;
Gai.SharedInstance.TrackUncaughtExceptions = true;
Gai.SharedInstance.GetTracker(TrackingId);
screenNameToken = messenger.Subscribe<GaScreenNameMessage>((m) => SetScreenName(m));
int count = messenger.CountSubscriptionsFor<GaScreenNameMessage>();
eventToken = messenger.Subscribe<GaEventMessage>(CreateEvent);
exceptionToken = messenger.Subscribe<GaExceptionMessage>(CreateException);
performanceToken = messenger.Subscribe<GaPerformanceTimingMessage>(CreatePerformanceMetric);
publishToken = messenger.Subscribe<GaPublishMessage>(PublishAll);
}
// ...
}
Advanced Hint
But as far as I see, you don't even need messaging for this case, because it's one to one "communication". I think it would be nice, if you move the functionality of your GoogleAnalyticsWrapper into a well defined Service like:
interface ITrackingService
{
void SetScreenName(GaScreenNameMessage message);
void CreateEvent(GaEventMessage message);
void CreateException(GaExceptionMessage message);
void CreatePerformanceMetric(GaPerformanceTimingMessage message);
void PublishAll(GaPublishMessage message);
}
public class GoogleAnalyticsTrackingService : ITrackingService
{
private const string TrackingId = "xxxxxxxxxxx";
public GoogleAnalyticsTrackingService()
{
Gai.SharedInstance.DispatchInterval = 60;
Gai.SharedInstance.TrackUncaughtExceptions = true;
Gai.SharedInstance.GetTracker(TrackingId);
}
public void SetScreenName(GaScreenNameMessage message)
{
Gai.SharedInstance.DefaultTracker.Set(GaiConstants.ScreenName, message.ScreenName);
Gai.SharedInstance.DefaultTracker.Send(DictionaryBuilder.CreateScreenView().Build());
}
public void CreateEvent(GaEventMessage message)
{
Gai.SharedInstance.DefaultTracker.Send(DictionaryBuilder.CreateEvent(message.Category, message.Action, message.Label, message.Number).Build());
}
private void CreateException(GaExceptionMessage message)
{
Gai.SharedInstance.DefaultTracker.Send(DictionaryBuilder.CreateException(message.ExceptionMessage, message.IsFatal).Build());
}
private void CreatePerformanceMetric(GaPerformanceTimingMessage message)
{
Gai.SharedInstance.DefaultTracker.Send(DictionaryBuilder.CreateTiming(message.Category, message.Milliseconds, message.Name, message.Label).Build());
}
private void PublishAll(GaPublishMessage message)
{
Gai.SharedInstance.Dispatch();
}
}
That has to be registered in your App
Mvx.LazyConstructAndRegisterSingleton<ITrackingService, GoogleAnalyticsTrackingService>();
And can be consumed with constructor injection or manual resolves
class MyViewModel : MvxViewModel
{
public MyViewModel(ITrackingService tracking)
{
tracking.CreateEvent(new GaEventMessage(this, "Event", "Publish Event", "Publish Event From First View Model", 123));
}
}
// or
class MyViewModel : MvxViewModel
{
public MyViewModel()
{
var tracking = Mvx.Resolve<ITrackingService>();
tracking.CreateEvent(new GaEventMessage(this, "Event", "Publish Event", "Publish Event From First View Model", 123));
}
}
There is still one Problem: The interface has still a dependency to google analytics. But the dependency can be easily removed by using multiple parameters instead of a parameter object.
interface ITrackingService
{
void CreateEvent(string eventName, string title, string message, params object[] additionalParams);
// ...
}
// call:
tracking.CreateEvent("Event", "Publish Event", "Publish Event From First View Model", 123);
With this, you are able to unit test it and exchange the tracking service with litte effort, if your stakeholders decide to switch to adobe omniture or whatever.
I trying to allow people to write to NFC tags using my app, so that my app gets launched with a custom parameter. I want to be able to reprogram NFC tags which already have data on them.
I am using the following code but the problem is, that WP always recognizes the action which is already on the NFC tag and interrupts because it wants to launch the NFC tag action which was written anytime before.
How can I tell the OS to stop triggering the action of the tag so that I can immediately rewrite it?
public enum NfcHelperState
{
Initializing,
Waiting,
Ready,
Writing,
Finished,
Error,
NoDeviceFound
}
public class NfcHelper
{
private NfcHelperState _state = NfcHelperState.Initializing;
public NfcHelperState State
{
get { return _state; }
}
private ProximityDevice _nfcDevice;
private long _subscriptionId;
public NfcHelper()
{
Init();
}
public void Init()
{
UpdateState();
_nfcDevice = ProximityDevice.GetDefault();
if (_nfcDevice == null)
{
UpdateState(NfcHelperState.NoDeviceFound);
return;
}
UpdateState(NfcHelperState.Waiting);
}
private void UpdateState(NfcHelperState? state = null)
{
if (state.HasValue)
{
_state = state.Value;
}
if (OnStatusMessageChanged != null)
{
OnStatusMessageChanged(this, _state);
}
}
public void WriteToTag()
{
UpdateState(NfcHelperState.Ready);
_subscriptionId = _nfcDevice.SubscribeForMessage("WriteableTag", WriteableTagDetected);
}
private void WriteableTagDetected(ProximityDevice sender, ProximityMessage message)
{
UpdateState(NfcHelperState.Writing);
try
{
var str = "action=my_custom_action";
str += "\tWindowsPhone\t";
str += CurrentApp.AppId;
_nfcDevice.PublishBinaryMessage("LaunchApp:WriteTag", GetBufferFromString(str),
WriteToTagComplete);
}
catch (Exception e)
{
UpdateState(NfcHelperState.Error);
StopWaitingForTag();
}
}
private void WriteToTagComplete(ProximityDevice sender, long messageId)
{
sender.StopPublishingMessage(messageId);
UpdateState(NfcHelperState.Finished);
StopWaitingForTag();
}
private void StopWaitingForTag()
{
_nfcDevice.StopSubscribingForMessage(_subscriptionId);
}
private static IBuffer GetBufferFromString(string str)
{
using (var dw = new DataWriter())
{
dw.UnicodeEncoding = Windows.Storage.Streams.UnicodeEncoding.Utf16LE;
dw.WriteString(str);
return dw.DetachBuffer();
}
}
public delegate void NfcStatusMessageChangedHandler(object myObject, NfcHelperState newState);
public event NfcStatusMessageChangedHandler OnStatusMessageChanged;
}
WriteToTag is called when a button in my app is tapped and the app waits for a writable tag. If a writable tag is recognized, WriteableTagDetected gets called and immediately starts the writing process. However, this is interrupted by the WP dialog which asks whether to perform the NFC action or not. After writing, WriteToTagComplete should be called, where StopWaitingForTag gets called and ends the write process.
I hope you guys can help me :)
Turns out I thought the wrong way. I didn't need to wait for a tag to arrive in order to rewrite it. In fact, there's no need to do _nfcDevice.SubscribeForMessage("WriteableTag", WriteableTagDetected); before writing. Just start using PublishBinaryMessage and it will write to the tag once it arrives at the device.
My final code looks like the following:
public enum NfcHelperState
{
Initializing,
Ready,
WaitingForWriting,
FinishedWriting,
ErrorWriting,
NoDeviceFound
}
public class NfcHelper
{
private NfcHelperState _state = NfcHelperState.Initializing;
public NfcHelperState State
{
get { return _state; }
}
private ProximityDevice _nfcDevice;
private long? _writingMessageId;
public NfcHelper()
{
Init();
}
public void Init()
{
UpdateState();
_nfcDevice = ProximityDevice.GetDefault();
if (_nfcDevice == null)
{
UpdateState(NfcHelperState.NoDeviceFound);
return;
}
UpdateState(NfcHelperState.Ready);
}
private void UpdateState(NfcHelperState? state = null)
{
if (state.HasValue)
{
_state = state.Value;
}
if (OnStatusMessageChanged != null)
{
OnStatusMessageChanged(this, _state);
}
}
public void WriteToTag()
{
StopWritingMessage();
UpdateState(NfcHelperState.WaitingForWriting);
try
{
var str = new StringBuilder();
str.Append("action=my_custom_action");
str.Append("\tWindowsPhone\t{");
str.Append(CurrentApp.AppId);
str.Append("}");
_writingMessageId = _nfcDevice.PublishBinaryMessage("LaunchApp:WriteTag", GetBufferFromString(str.ToString()),
WriteToTagComplete);
}
catch
{
UpdateState(NfcHelperState.ErrorWriting);
StopWritingMessage();
}
}
private void WriteToTagComplete(ProximityDevice sender, long messageId)
{
UpdateState(NfcHelperState.FinishedWriting);
StopWritingMessage();
}
private void StopWritingMessage()
{
if (_writingMessageId.HasValue)
{
_nfcDevice.StopPublishingMessage(_writingMessageId.Value);
_writingMessageId = null;
}
}
private static IBuffer GetBufferFromString(string str)
{
using (var dw = new DataWriter())
{
dw.UnicodeEncoding = Windows.Storage.Streams.UnicodeEncoding.Utf16LE;
dw.WriteString(str);
return dw.DetachBuffer();
}
}
public delegate void NfcStatusMessageChangedHandler(object myObject, NfcHelperState newState);
public event NfcStatusMessageChangedHandler OnStatusMessageChanged;
}
before posting the question i did my research for 10 days so really hope someone can shed some light into solving this issue.
The issue is that any bindable control, does not update once the binding list from singleton class is changed. This is a common issue on multi-threaded apps. Most if not all solutions offer suggestions where the bindlinglist or collection is initialized from parent thread, and then some invocation to be made. Not what i'm looking for. The same issue persist if static class is used instead of singleton.
Basically, the application triggers some Tasks, which in turn create object(s) on different business classes. These objects post messages into the bindinglist, which should update the UI listbox, but does not. And yes, the message object is in the list, and binding after the TASK finished works (items displayed). Locking/unlocking object(s) access is also not an issue.
Appreciate any suggestions/solutions
A trimmed down version of business objects:
namespace MyNameSpace
{
public class Message
{
private string messageSummary;
public Message() { }
public string MessageSummary
{
set { messageSummary = value; }
get { return messageSummary; }
}
}
}
A trimmed down version of another class doing some ops:
namespace MyNameSpace
{
public class WorkDoingClass
{
public WorkDoingClass() { }
public void DoSomeWork()
{
//some routines
Message messageObj = new Message();
messageObj.MessageSummary = "DoSOmrWork Finished";
}
public void DoSomeOtherWork()
{
//some routines
Message messageObj = new Message();
messageObj.MessageSummary = "DoSomeOtherWork Finished";
AllMessages.Instance.AllMessagesBindingList.Add(messageObj);
}
}
}
Singleton:
namespace MyNameSpace
{
public sealed class AllMessages
{
private static readonly AllMessages _instance = new AllMessages();
private BindingList<Message> _allMessagesBL;
public WorkDoingClass() { _allMessagesBL = new BindingList<Message>(); }
public static AllMessages Instance
{
get { return _instance; }
}
public BindingList<Message> AllMessagesBindingList
{
get { return _allMessagesBL};
}
}
}
This is also a trimmed down version from where calls start:
namespace MyNameSpace
{
public partial class Form1 : Form
{
private Task _TaskSqlData;
private CancellationTokenSource cTokenSourceSql;
public Form1()
{
InitializeComponent();
listBox1.DataSource = AllMessages.Instance.AllMessagesBindingList;
listBox1.DisplayMember = "MessageSummary";
}
private void button1_Click(object sender, EventArgs e)
{
cTokenSourceSql = new CancellationTokenSource();
var tokenSqlData = cTokenSourceSql.Token;
if (this._TaskSqlData != null)
{
if (this._TaskSqlData.Status == TaskStatus.Running)
this.cTokenSourceSql.Cancel();
this._TaskSqlData.Dispose();
this._TaskSqlData = null;
}
_TaskSqlData = Task.Factory.StartNew(()
=> StartDoingWork(this, tokenSqlData, null), tokenSqlData);
}
public void StartDoingWork(object sender, CancellationToken ct, EventArgs e)
{
if (ct.IsCancellationRequested)
ct.ThrowIfCancellationRequested();
WorkDoingClass work = new WorkDoingClass();
work.DoSomeOtherWork();
}
Your problem is that the thread(the main UI thread) making the listbox is different from the thread(the worker thread) modifying the collection.
Try the following code. It could solve your issue. I use SynchronizationContext to synchronize the two threads, which serves as the same function with Control.Invoke().
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
private Task _TaskSqlData;
private CancellationTokenSource cTokenSourceSql;
WorkDoingClass _work;
public Form1()
{
InitializeComponent();
listBox1.DataSource = AllMessages.Instance.AllMessagesBindingList;
listBox1.DisplayMember = "MessageSummary";
_work = new WorkDoingClass(SynchronizationContext.Current);
}
private void button1_Click(object sender, EventArgs e)
{
cTokenSourceSql = new CancellationTokenSource();
var tokenSqlData = cTokenSourceSql.Token;
if (this._TaskSqlData != null)
{
if (this._TaskSqlData.Status == TaskStatus.Running)
this.cTokenSourceSql.Cancel();
this._TaskSqlData.Dispose();
this._TaskSqlData = null;
}
_TaskSqlData = Task.Factory.StartNew(()
=> StartDoingWork(this, tokenSqlData, null), tokenSqlData);
}
public void StartDoingWork(object sender, CancellationToken ct, EventArgs e)
{
if (ct.IsCancellationRequested)
ct.ThrowIfCancellationRequested();
_work.DoSomeOtherWork();
}
}
public class Message
{
private string messageSummary;
public Message() { }
public string MessageSummary
{
set { messageSummary = value; }
get { return messageSummary; }
}
}
public class WorkDoingClass
{
private SynchronizationContext _syncContext;
public WorkDoingClass() { }
public WorkDoingClass(SynchronizationContext _syncContext)
{
// TODO: Complete member initialization
this._syncContext = _syncContext;
}
public void DoSomeWork()
{
//some routines
Message messageObj = new Message();
messageObj.MessageSummary = "DoSOmrWork Finished";
}
public void DoSomeOtherWork()
{
_syncContext.Send(DoWork, null);
}
private static void DoWork(object arg)
{
//some routines
Message messageObj = new Message();
messageObj.MessageSummary = "DoSomeOtherWork Finished";
AllMessages.Instance.AllMessagesBindingList.Add(messageObj);
}
}
public sealed class AllMessages
{
private static readonly AllMessages _instance = new AllMessages();
private BindingList<Message> _allMessagesBL;
public AllMessages() { _allMessagesBL = new BindingList<Message>(); }
public static AllMessages Instance
{
get { return _instance; }
}
public BindingList<Message> AllMessagesBindingList
{
get { return _allMessagesBL; }
}
}
}