Any efficient/reliable way to expose one event?
I have a class, MultipleDocumentCopier that copies multiple documents thru an instance of SingleDocumentCopier.
SingleDocumentCopier exposes an event CopyCompleted that is fired when a file is copied.
Suppose that, I am copying 10 files, instead of raising SingleDocumentCopier.CopyCompleted 10 times,
I would like to expose an event, MultipleDocumentCopier.MultipleCopyCompleted.
But is there a standard way/technique to combine multiple events and fire it once?
I would like to raise MultipleDocumentCopier.MultipleCopyCompleted only once
within 'MultipleDocumentCopier.singleDocumentCopier_CopyCompleted', instead of 10 times.
Here is the sample code
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
namespace CombineEvents
{
public class Program
{
public static void Main(string[] args)
{
var copier = new MultipleDocumentCopier();
copier.MultipleCopyCompleted += MultipleDocumentCopyCompleted;
copier.CopyDocuments(new[] {"File1", "File2", "File3"});
}
private static void MultipleDocumentCopyCompleted(
object sender, FileNameEventArgs e)
{
Debug.Print("Following documents have been copied");
foreach (var fileName in e.FileNames)
{
Debug.Print("\t\t\"{0}\"", fileName);
}
}
}
internal class SingleDocumentCopier
{
public event EventHandler CopyCompleted;
protected virtual void OnCopyCompleted()
{
if (CopyCompleted != null) CopyCompleted(this, EventArgs.Empty);
}
public void Copy(string fileName)
{
Debug.Print("Copying = '{0}'", fileName);
OnCopyCompleted();
}
}
public class MultipleDocumentCopier
{
public event EventHandler<FileNameEventArgs> MultipleCopyCompleted;
protected virtual void OnCopyCompleted(FileNameEventArgs e)
{
EventHandler<FileNameEventArgs> completed = MultipleCopyCompleted;
if (completed != null) completed(this, e);
}
public void CopyDocuments(IEnumerable<string> fileNames)
{
var copier = new SingleDocumentCopier();
copier.CopyCompleted += singleDocumentCopier_CopyCompleted;
foreach (var fileName in fileNames)
{
copier.Copy(fileName);
}
}
public static void singleDocumentCopier_CopyCompleted(
object sender, EventArgs e)
{
// I want to raise "MultipleDocumentCopier.MultipleCopyCompleted" when
// all files, `fileNames` in "CopyDocuments" have been copied,
// not for every file being copied.
}
}
public class FileNameEventArgs : EventArgs
{
private readonly List<string> _FileNames;
public List<string> FileNames
{
get { return _FileNames; }
}
public FileNameEventArgs(IEnumerable<string> fileNames)
{
_FileNames = fileNames.ToList();
}
}
}
Why not call MultipleDocumentCopier.OnCopyCompleted from the end of CopyDocuments, and forget singleDocumentCopier_CopyCompleted entirely?
Or maybe this is pseudocode, and your real code is more complicated? Maybe you could keep a collection of outstanding file names inside MultipleDocumentCopier, and each time the singleDocumentCopier_CopyCompleted is raised, you remove one document from the collection. Once the collection becomes empty you call MultipleDocumentCopier.OnCopyCompleted.
Edit: Re 'is there a standard way?' -- not that I'm aware of in C#; F# has an interesting set of mechanisms for combining events like this, but I assume a change in programming language isn't an option.
Related
I am modifying to first question attempt.
I need help passing the data from a listbox and pass it to another method so every time the listbox gets data add it from the tread, it should also send that data to my new method and it to the my list because in that method I will be doing some parsing because the data from the listbox is a long string barcode but I don't need help on parsing the data because I can do that, the part I need help only is to pass the data from thread that is passing it to the listbox it should also be send to my method ReadAllData() so in that method I will received the data and then I will do the parsing the make a return.
Here is the method where the listbox is stored and receives the data from a thread from telnet port 23
Here is the code I need to send the data from the lst_BarcodeScan to the method ReadAllData method and store to my list.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Runtime.Remoting.Messaging;
using System.Security.Authentication.ExtendedProtection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace BarcodeReceivingApp
{
public class TelnetConnection
{
private Thread _readWriteThread;
private TcpClient _client;
private NetworkStream _networkStream;
private string _hostname;
private int _port;
private BarcodeReceivingForm _form;
private bool _isExiting = false;
public TelnetConnection(string hostname, int port)
{
this._hostname = hostname;
this._port = port;
}
public TelnetConnection()
{
}
public void ServerSocket(string ip, int port, BarcodeReceivingForm f)
{
this._form = f;
try
{
_client = new TcpClient(ip, port);
}
catch (SocketException)
{
MessageBox.Show(#"Failed to connect to server");
return;
}
_networkStream = _client.GetStream();
_readWriteThread = new Thread(ReadWrite);
//_readWriteThread = new Thread(() => ReadWrite(f));
_readWriteThread.Start();
}
public void Exit()
{
_isExiting = true;
}
public void ReadWrite()
{
var received = "";
do
{
received = Read();
if (received == null)
break;
if (_form.lst_BarcodeScan.InvokeRequired)
{
_form.lst_BarcodeScan.Invoke(new MethodInvoker(delegate
{
_form.lst_BarcodeScan.Items.Add(received + Environment.NewLine);
}));
}
} while (!_isExiting);
CloseConnection();
}
public List<string> ReadAllData()
{
var obtainData = new List<string>();
return obtainData;
}
public string Read()
{
var data = new byte[1024];
var received = "";
var size = _networkStream.Read(data, 0, data.Length);
if (size == 0)
return null;
received = Encoding.ASCII.GetString(data, 0, size);
return received;
}
public void CloseConnection()
{
MessageBox.Show(#"Closed Connection",#"Important Message");
_networkStream.Close();
_client.Close();
}
}
}
Main class that will call the methods from the telnetconnection class or any other classes I will add.
using System;
using System.Windows.Forms;
namespace BarcodeReceivingApp
{
public partial class BarcodeReceivingForm : Form
{
//GLOBAL VARIABLES
private const string Hostname = "myip";
private const int Port = 23;
private TelnetConnection _connection;
public BarcodeReceivingForm()
{
InitializeComponent();
//FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
WindowState = FormWindowState.Maximized;
}
private void btn_ConnectT_Click(object sender, EventArgs e)
{
_connection = new TelnetConnection(Hostname, Port);
_connection.ServerSocket(Hostname, Port, this);
}
private void btn_StopConnection_Click(object sender, EventArgs e)
{
//_connection = new TelnetConnection(Hostname, Port);
//_connection.ServerSocket(Hostname, Port, this);
_connection.Exit();
}
private void btn_RemoveItemFromListAt_Click(object sender, EventArgs e)
{
for (var i = lst_BarcodeScan.SelectedIndices.Count - 1; i >= 0; i--)
{
lst_BarcodeScan.Items.RemoveAt(lst_BarcodeScan.SelectedIndices[i]);
}
}
private void BarcodeReceivingForm_Load(object sender, EventArgs e)
{
lst_BarcodeScan.SelectionMode = SelectionMode.MultiSimple;
}
private void btn_ApplicationSettings_Click(object sender, EventArgs e)
{
var bcSettingsForm = new BarcodeReceivingSettingsForm();
bcSettingsForm.Show();
}
private void btn_ClearBarcodeList_Click(object sender, EventArgs e)
{
lst_BarcodeScan.Items.Clear();
}
}
}
The easiest way to implement without adding more complexity to the thread is to simply raise an event on the listbox when and item is added to it. The problem is that the listbox do no have any events that allow to do that but you can create your own version with a dozen lines of code.
The goal is to create a derived control of the listbox then you add to it a method to trigger a custom event when an item is added and bingo.
Here's the custom listbox class with the custom EventArgs.
// custom override class over the list box so we can create an event when items are added
public class ListBoxWithEvents : ListBox
{
// the event you need to bind to know when items are added
public event EventHandler<ListBoxItemEventArgs> ItemAdded;
// method to call to add items instead of lst.Items.Add(x);
public void AddItem(object data)
{
// add the item normally to the internal list
var index = Items.Add(data);
// invoke the event to notify the binded handlers
InvokeItemAdded(index);
}
public void InvokeItemAdded(int index)
{
// invoke the event if binded anywhere
ItemAdded?.Invoke(this, new ListBoxItemEventArgs(index));
}
}
// basic event handler that will hold the index of the item added
public class ListBoxItemEventArgs : EventArgs
{
public int Index { get; set; } = -1;
public ListBoxItemEventArgs(int index)
{
Index = index;
}
}
Now you need to change your listbox on your form with the ListBoxWithEvents instead. You have 2 ways to do this but i'll give you the easiest. Compile your code and go in the design window for the form. In your toolbox you should have the ListBoxWithEvents control now and you can simply drag and drop in your form and replace the one you have. Since it derive from the listbox all it has is extra features. It didn't lose anything it had before.
Now you need to change 2 things. In your form select the new ListBoxWithEvents control and go in the properties events and you will find the new event called ItemAdded you can double click that and it should create an event like the following. I have also thrown in a sample MessageBox that display all you will need.
private void ListBox1_ItemAdded(object sender, ListBoxItemEventArgs e)
{
MessageBox.Show("Item was added at index " + e.Index + " and the value is " + listBox1.Items[e.Index].ToString());
}
Finally in order to trigger that event you need to use the new method lst.AddItem(object); instead of lst.Items.Add(object); so according to your sample code you need to change this :
_form.lst_BarcodeScan.Invoke(new MethodInvoker(delegate
{
_form.lst_BarcodeScan.Items.Add(received + Environment.NewLine);
}));
to this :
_form.lst_BarcodeScan.Invoke(new MethodInvoker(delegate
{
_form.lst_BarcodeScan.AddItem(received + Environment.NewLine);
}));
Try it and now you should have that event fire every time something is added to the list.
Since you are pretty new to programming i find it important to mention that this event will trigger on the UI thread and not the thread you created. This mean it behave normally like clicking on a button triggers a button_click(object sender, EventArgs e) event. No special thread involved whatsoever.
I wonder if I can make multiple delegates from a single local function and rely on them to be all equal?
In other words, if I make multiple delegates referencing the same local function, are they all equivalent? Or should I make sure to only create one if I need them to be equal?
Of course, assuming they are all created during a single method call. Otherwise, multiple closure objects must be created.
As an example, is this code guaranteed to always print only one line?
class EventSource
{
public event EventHandler Event;
public void Raise() => Event?.Invoke(this, EventArgs.Empty);
}
static class Program
{
static void Main()
{
var source = new EventSource();
var root = Math.Sqrt(2).ToString();
void PrintThenUnsubscribe(object sender, EventArgs e)
{
Console.WriteLine("Square root of 2: " + root);
source.Event -= PrintThenUnsubscribe;
}
source.Event += PrintThenUnsubscribe;
source.Raise();
source.Raise(); // Doesn't print the second time.
}
}
Edit:
Here's a simpler example:
using System;
using System.Diagnostics;
static class Program
{
static void Main()
{
var root = Math.Sqrt(2);
void print() => Console.WriteLine(root);
Action a1 = print;
Action a2 = print;
Debug.Assert(a1 == a2);
}
}
I have a form that has a button to get a method executed in another class.
Code on the form:
public delegate void CustomPreviewCreate();
public static event CustomPreviewCreate CustomPreviewCreate_Do;
private void CreatePreview()
{
if (CustomPreviewCreate_Do !=null)
{
CustomPreviewCreate_Do();
}
}
This event then gets handled in another class. What I would like to achieve is that I can feed back to the form some form of return value if the method correctly executed.
What I tried so far does not get me the result.
Here is the code:
public void Initialize()
{
SubAsstViewPartControl.CustomPreviewCreate_Do += SubAsstViewPartControl_CustomPreviewCreate_Do;
// this gives me a the compiler error that the return type is wrong
}
private bool SubAsstViewPartControl_CustomPreviewCreate_Do()
{
// do stuff
return false;
}
Is there any direct way to return value from an event handler or I need to use a separate static field to store the event result in?
Update:
Per #Jon's comment, which seemed the simplest to me, I added an answer below demonstrating the simplest approach.
The common approach is to encapsulate your value in the type of EventArgs your event expects. For example, the Framework's CancelEventArgs contains a settable bool Cancel property, allowing each CancelEventHandler to assign a value. The sender can then read the property after the event has been invoked. You could also use a container-like EventArgs class if you want to collect separate values from individual event handlers. For example:
using System;
using System.Collections.Generic;
namespace ConsoleApplication1
{
public class SingleValueEventArgs : EventArgs
{
public int Value { get; set; }
}
public class MultiValueEventArgs : EventArgs
{
private List<int> _values = new List<int>(); // Private to prevent handlers from messing with each others' values
public IEnumerable<int> Values
{
get { return _values; }
}
public void AddValue(int value) { _values.Add(value); }
}
public class Exposer
{
public event EventHandler<SingleValueEventArgs> WantSingleValue;
public event EventHandler<MultiValueEventArgs> WantMultipleValues;
public void Run()
{
if (WantSingleValue != null)
{
var args = new SingleValueEventArgs();
WantSingleValue(this, args);
Console.WriteLine("Last handler produced " + args.Value.ToString());
}
if (WantMultipleValues != null)
{
var args = new MultiValueEventArgs();
WantMultipleValues(this, args);
foreach (var value in args.Values)
{
Console.WriteLine("A handler produced " + value.ToString());
}
}
}
}
public class Handler
{
private int _value;
public Handler(Exposer exposer, int value)
{
_value = value;
exposer.WantSingleValue += exposer_WantSingleValue;
exposer.WantMultipleValues += exposer_WantMultipleValues;
}
void exposer_WantSingleValue(object sender, SingleValueEventArgs e)
{
Console.WriteLine("Handler assigning " + _value.ToString());
e.Value = _value;
}
void exposer_WantMultipleValues(object sender, MultiValueEventArgs e)
{
Console.WriteLine("Handler adding " + _value.ToString());
e.AddValue(_value);
}
}
class Program
{
static void Main(string[] args)
{
var exposer = new Exposer();
for (var i = 0; i < 5; i++)
{
new Handler(exposer, i);
}
exposer.Run();
}
}
}
Per Jon Skeet's comment, which seemed the simplest to me, the simplest approach seems to be as follows:
public delegate bool CustomPreviewCreate(); // here we declare a return type
public static event CustomPreviewCreate CustomPreviewCreate_Do;
private void CreatePreview()
{
if (CustomPreviewCreate_Do !=null)
{
bool returnval = CustomPreviewCreate_Do();
}
}
And then:
// the method is declared to return the same type
bool SubAsstViewPartControl_CustomPreviewCreate_Do()
{
// do stuff
return true; // return the value of the type declared
}
Consider the following program:
class Program
{
static void Main(string[] args)
{
var MyEventThrower = new EventThrower();
var log= new List<string>();
log.Add("Log Initialized");
MyEventThrower.Event += LogEvent;
MyEventThrower.RaiseEvent();
foreach (var item in log)
{
Console.WriteLine(item);
}
}
static void LogEvent(object sender, EventArgs e)
{
log.Add(sender.ToString()); //obviously doesn't work,
//but this is the sort of
//behavior I want to achieve.
}
}
for reference:
public delegate void EventHandler(object sender, EventArgs e)
public class EventThrower
{
public event EventHandler Event;
protected virtual void OnEvent(EventArgs e)
{
if (Event!= null)
{
Event(this, e);
}
}
public void RaiseEvent()
{
OnEvent(EventArgs.Empty);
}
}
Edit: My problem here is that log is not within the same context as the event handler. I see now that I can fix that by making it a static class member.
The easiest option is to use a lambda to close over the list:
var MyEventThrower = new EventThrower();
var log= new List<string>();
log.Add("Log Initialized");
MyEventThrower.Event += (sender, args) => log.Add(sender.ToString());
MyEventThrower.RaiseEvent();
foreach (var item in log)
{
Console.WriteLine(item);
}
I'm not sure if you need to use your log variable/list for anything else, so I'm making the assumption you only have to facilitate logging in the way you designed your code.
I'd suggest something like the following:
static void Main()
{
var logger = new Logger();
logger.Log("Log Initialized");
var MyEventThrower = new EventThrower();
MyEventThrower.Event += (sender, _) => logger.Log(Convert.ToString(sender));
MyEventThrower.RaiseEvent();
}
public class Logger
{
public void Log(string message)
{
Console.WriteLine(message);
}
}
This let's you wrap the implementation detail inside of a logger class. You could even extend this further by creating your own sort of ILogger interface and making different Logger implementations (i..e one for a file, one for console etc). If you're only ever planning on writing to the console, this is potentially overkill.
The one thing I do like about using a class to wrap the actual logging is that the implementation is in one spot. If you ever decide you want all your log messages to have "DEBUG: " prepended to the message you're logging, you only need to do it in one spot.
The other thing I like about this compared to the original solution is that you're not adding things to a list and then iterating over them again to print out information. You're doing it as the event is triggered. This will save on memory usage too (i.e. not holding all of your log messages in a list before you're ready to output them). If I misunderstood why you actually have that log list of strings, then I apologize for this.
I'm building a game and trying to use an event system.
This is the main idea of how it is implemented:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Blocker2
{
public delegate void OnPlayerMoved(PlayerMovedEvent e);
public delegate void OnPlayerSpawned(PlayerSpawnedEvent e);
public delegate void OnBlockBreak(BlockBreakEvent e);
public delegate void OnBlockPlaced(BlockPlacedEvent e);
public static class EventHandler
{
private static List<OnPlayerMoved> _onPlayerMoved;
private static List<OnPlayerSpawned> _onPlayerSpawned;
private static List<OnBlockBreak> _onBlockBreak;
private static List<OnBlockPlaced> _onBlockPlaced;
static EventHandler()
{
}
public static void Subscribe()
{
}
// -------------------------- Player Related Events --------------------------
public static void OnPlayerMoved(PlayerMovedEvent e)
{
foreach (OnPlayerMoved del in _onPlayerMoved)
{
del(e);
}
}
public static void OnPlayerSpawned(PlayerSpawnedEvent e)
{
foreach (OnPlayerSpawned del in _onPlayerSpawned)
{
del(e);
}
}
// -------------------------- Block Related Events --------------------------
public static void OnBlockBreak(BlockBreakEvent e)
{
foreach (OnBlockBreak del in _onBlockBreak)
{
del(e);
}
}
public static void OnBlockPlaced(BlockPlacedEvent e)
{
foreach (OnBlockPlaced del in _onBlockPlaced)
{
del(e);
}
}
}
}
And there going to be alot more events, and I think this method going to make the code very very complex. There is a better way to do it? (Considering performance and maintability of the code).
Thanks in advanced!
Sorry for my bad english.
Why don't you use standard C# events? They will handle this the same way, since an event allows more than a single subscriber.
The standard event mechanism in C# allows multiple subscribers to subscribe to an event, and would look like:
public static event EventHandler<PlayerMovedEventArgs> PlayerMoved;
// Often, you'll have a method to raise the event:
public static void OnPlayerMoved(PlayerMovedEventArgs args)
{
var handler = PlayerMoved;
if (handler != null)
handler(null, args);
}
That being said, I would recommend putting these into the class where they are related, and not having them all global/static. You could then potentially make the method to raise the event private to that class, which would allow you to keep the design more maintainable.
For example, the PlayerMoved event probably would be more appropriate within some class representing your world (or a piece of the world), and in there in a non-static fashion.