Good evening,
I try to create an own EventHandler on the base of the DownloadProgressChangedEventHandler.
The reason is, I want to give the Callback function an extra parameter (fileName).
This is my EventHandler:
public class MyDownloadProgressChangedEventHandler : DownloadProgressChangedEventHandler
{
public object Sender { get; set; }
public DownloadProgressChangedEventArgs E { get; set; }
public string FileName { get; set; }
public MyDownloadProgressChangedEventHandler(object sender, DownloadProgressChangedEventArgs e, string fileName)
{
this.Sender = sender;
this.E = e;
this.FileName = fileName;
}
}
And this is my attempt:
WebClient client = new WebClient();
client.DownloadProgressChanged += new MyDownloadProgressChangedEventhandler(DownloadProgressChanged);
client.DownloadFileAsync(new Uri(String.Format("{0}/key={1}", srv, file)), localName);
Console.WriteLine(String.Format("Download of file {0} started.", localName));
Console.ReadLine();
But VS says that a conversation from MyDownloadProgressChangedEventHandler to the DownloadProgressChangedEventHandler is not possible.
Is this even possible like how I think?
Thanks in advance.
How should the WebClient know what to put inside your defined variable? (it can't)
Instead, wrap the handler you got inside another one:
string fileName = new Uri(String.Format("{0}/key={1}", srv, file));
client.DownloadProgressChanged +=
(sender, e) =>
DownloadProgressChanged(sender, e, fileName);
client.DownloadFileAsync(fileName);
Lambda expressions are always fun to use in these situations!
Related
I want to get my field >>> NumberOfElementsInMyList from sender, How Can I do it? I couldn't find this kind of question here (what I have seen, was about windows forms), so ...
class Program
{
static void Main(string[] args)
{
Publisher PublisherObject = new Publisher();
PublisherObject.NumberAdded += PublisherObject_NumberAdded;
PublisherObject.AddNumber(int.Parse(Console.ReadLine());
}
static void PublisherObject_NumberAdded(object sender, EventArgs e)
{
//I want to write on the console "NumberOfElementsInMylist"
//I tried:
//sender.NumberOfElementsInMylist -- not works
//Publisher obj=(publisher)sender and then sender.NumberOfElementsInMylist
//not works
Console.WriteLine("number of elements in list is ---> "+ ???? );
}
}
class Publisher
{
public event EventHandler NumberAdded;
public int NumberOfElementsInMyList;
List<int> MyList=new List<int>();
public void AddNumber(int NumberToAdd)
{
MyList.Add(NumberToAdd);
NumberOfElementsInMyList = MyList.Count;
NumberAdded(this, new EventArgs());
}
}
To literally answer your question, the reason why you can't access the NumberOfElementsInMyList field is because when you create the EventArgs, your instance of Publisher is being cast as an object (which you can do since all classes inherit from object.) So to see the properties (or field) of Publisher, you have to cast the sender as Publisher.
var numberOfElements = ((Publisher)sender).NumberOfElementsInMyList;
A downside to this is that hypothetically, sender might not be a Publisher. Because sender is an object, it could technically be anything.
You can also create your own event handler delegate and event args instead of using the boilerplate EventHandler delegate.
public delegate void NumberAdded(Publisher source, NumberAddedEventArgs eventArgs);
public class NumberAddedEventArgs : EventArgs
{
public NumberAddedEventArgs(int numberAdded, numberOfItemsInList)
{
NumberAdded = numberAdded;
NumberOfItemsInList = numberOfItemsInList;
}
public int NumberAdded { get; private set; }
public int NumberOfItemsInList { get; private set; }
}
public class Publisher
{
public event EventHandler NumberAddedEvent;
public int NumberOfElementsInMyList;
List<int> MyList = new List<int>();
public void AddNumber(int NumberToAdd)
{
MyList.Add(NumberToAdd);
NumberOfElementsInMyList = MyList.Count;
NumberAddedEvent?.Invoke(this, new NumberAddedEventArgs(NumberToAdd,
NumberOfElementsInMyList));
}
}
var numberOfElementsInList = args.NumberOfItemsInList; // much better!
The (object sender, EventArgs args) is a strange convention. In any other scenario we would create strongly-typed methods and delegates. But in this case there's a tendency to use something that's not strongly-typed because it's a convention.
You can simply cast the sender, can't you?
var publisher = (Publisher)sender;
Console.WriteLine(publisher.NumberOfElementsInMyList);
Or more safely:
Console.WriteLine(
(sender as Publisher)?.NumberOfElementsInMyList?.ToString() ?? "sender is not a publisher!");
I created a dll contain this event handler:
public void tcp1_Data(object sender, Sockets.DataEventArgs e)
{
Tcp tcp = (Tcp)sender;
response = "Socket Connection" + tcp.Tag.ToString() + " replied : " + e.Data.ToString();
tcp.Close();
}
this will fire when server write some thing in socket connection. so by this, I can read the data on socket.
I used this dll in another project. I want to know in my project (that used dll) exactly when server is writing data on socket connection. as you see in tcp1_Data event, I set result into response variable and in main project (that used dll), I checked this variable polling (if response is not null, it means that this event fired). but Its not what I want. I dont want check this variable all the time.
is there any other way?
I tried this as #ThorstenDittmar said:
my dll project (its name is ClientSample) contain:
TheClassInDLL Class
public class TheClassInDLL
{
public event EventHandler<MyEventArgs> DataEventCalled;
public void tcp1_Data(object sender, Sockets.DataEventArgs e)
{
Tcp tcp = (Tcp)sender;
// Note: LOCAL variable
string myresponse = "Socket Connection" + tcp.Tag.ToString() + " replied : " + e.Data.ToString();
// Call the new event here
if (DataEventCalled != null)
DataEventCalled(this, new MyEventArgs(myresponse));
tcp.Close();
}
// We use this class to pass arguments to the event handler
public class MyEventArgs : EventArgs
{
public MyEventArgs(string response)
{
this.Response = response;
}
public string Response
{
get;
private set;
}
}
}
TCPSample class
public class TCPSample
{
Tcp tcp = new Tcp();
tcp.Data += new System.EventHandler
and in another project that I used above dll:
public partial class Form1 : Form
{
private TheClassInDLL myClass;
ClientSample.TCPSample t = new ClientSample.TCPSample();
public Form1()
{
InitializeComponent();
myClass = new TheClassInDLL();
myClass.DataEventCalled += DataEvent;
}
private void button1_Click(object sender, EventArgs e)
{
t.newTCP();
}
private void DataEvent(object sender, TheClassInDLL.MyEventArgs e)
{
MessageBox.Show(e.Response);
}
}
but It didnt work, DataEvent never happend.
Thanks for any helping...
What you wrote here is an event handler that is called when something happens. There must be a class containing this event handler. Instead of writing a global response variable, declare and invoke another event you can subscribe to from outside that class like this:
public class <TheClassInDLL>
{
public event EventHandler<MyEventArgs> DataEventCalled;
// SNIP: All the other code that leads to tcp1_Data being called
...
// The event handler that's called by some code in the class
public void tcp1_Data(object sender, Dart.Sockets.DataEventArgs e)
{
Tcp tcp = (Tcp)sender;
// Note: LOCAL variable
string myresponse = "Socket Connection" + tcp.Tag.ToString() + " replied : " + e.Data.ToString();
// Call the new event here
if (DataEventCalled != null)
DataEventCalled(this, new MyEventArgs(myresponse));
tcp.Close();
}
// We use this class to pass arguments to the event handler
public class MyEventArgs : EventArgs
{
public MyEventArgs(string response)
{
this.Response = response;
}
public string Response
{
get;
private set;
}
}
}
From the caller, you use it like this:
public class <TheCallingClassOutsideDLL>
{
private <TheClassInDLL> myClass;
public TheCallingClassOutsideDLL()
{
myClass = new TheClassInDLL();
myClass.DataEventCalled += DataEvent;
}
private void DataEvent(object sender, <TheClassInDLL>.MyEventArgs e)
{
Console.WriteLine(e.Response);
}
}
Of course you need to replace <TheClassInDLL> and <TheCallingClassOutsideDLL> with the real class names! Creating additional classes of course doesn't work!
For that you got to define your own event and raise it when needed...
for Example -> In the class where you set the "response" variable define an event
//your custom event
public event EventHandler<CustomEventArgs> MyCustomEvent;
//This will raise your event and notify all who registered
private void RaiseMyCustomEvent(CustomEventArgs e)
{
var handler = MyCustomEvent;
if (handler != null)
{
handler(this, e);
}
}
Maybe you will also need CustomEventArgs (used in the example above)
public class CustomEventArgs : EventArgs
{
public String Message {get;private set;}
public CustomEventArgs(String message){
this.Message = message;
}
}
The class which is using the dll and that wants to get notified needs to register for this event
YourDllClassInstance.MyCustomEvent += OnMyCustomEvent;
public void OnMyCustomEvent(object sender, CustomEventArgs e){
Console.WriteLine("Event received");
}
That means in your dll class you got to do something like the following when you want to raise the event
response = "blablabla";
RaiseMyCustomEvent(new CustomEventArgs(response);
Is that what you where asking for?
I'm trying to programmatically call a function with event.
How to convert string to a event in general? My problem is actually not knowing How to do this?
How to convert str to event?
str = "test1";
// UserControlsBackgroundEventArgs = EventArgs
EventArgs arg = (EventArgs)str; --> ?
UserControlsBackgroundOutput(str);
//function
private string CLICKNAME = "test0";
private void UserControlsBackgroundOutput(EventArgs e)
{
if (CLICKNAME == e.output)
return;
if (e.output == "test1"){}
}
Error solved:
I had to do
UserControlsBackgroundEventArgs arg = new UserControlsBackgroundEventArgs(CLICKNAME);
instead of
UserControlsBackgroundEventArgs arg = new (UserControlsBackgroundEventArgs)(CLICKNAME);
i've written a code that mimic you code, hopefully you will find it useful:
public class UserControlsBackgroundEventArgs
{
public string output;
public UserControlsBackgroundEventArgs(string up)
{
output = up;
}
}
public delegate void UserControlsBackgroundOutputHandle(UserControlsBackgroundEventArgs e);
public class testEvent
{
public event UserControlsBackgroundOutputHandle UserControlsBackgroundOutput;
public void DoSomeThings()
{
// do some things
if (UserControlsBackgroundOutput != null)
{
string str = "test1";
UserControlsBackgroundEventArgs arg = new UserControlsBackgroundEventArgs(str);
UserControlsBackgroundOutput(arg); // you've done that with str, whitch makes me
// you don't know what the event param is
}
}
}
public class test
{
private testEvent myTest;
private const string CLICKNAME = "whatever"; // i don't know what you want here
public test()
{
myTest = new testEvent();
myTest.UserControlsBackgroundOutput += UserControlsBackgroundOutput;
}
void UserControlsBackgroundOutput(UserControlsBackgroundEventArgs e)
{
if (CLICKNAME == e.output)
return;
if (e.output == "test1")
{
}
}
}
Your event class needs to have a constructor accepting a string. Then you will be able to create a new event instance using a string. You can't "convert" a string to an instance of the event class. If the event class comes from a library or sth and doesn't have a string constructor, you can subclass it, implement a string constructor and override the output property.
If you want this kind of conversion to be possible, you have to use an explicit operator:
public static explicit operator UserControlsBackgroundEventArgs(string s)
{
var args = new UserControlsBackgroundEventArgs();
args.output = s;
return args;
}
This is only possible with a new class, not with EventArgs, because you can't change the code of that class.
Your UserControlsBackgroundEventArgs Implementation could provide implicit/explicit casts.
Take a look at implicit keyword documentation
However, the answer from Wojciech Budniak is better.
The BackgroundWorker object allows us to pass a single argument into the DoWorkEventHandler.
// setup/init:
BackgroundWorker endCallWorker = new BackgroundWorker();
endCallWorker.DoWork += new DoWorkEventHandler(EndCallWorker_DoWork);
...
endCallWorker.RunWorkerAsync(userName);
// the handler:
private void EndCallWorker_DoWork(object sender, DoWorkEventArgs e)
{
string userName = e.Argument as string;
...
}
To pass multiple arguments, I must wrap them in an object, like this poor string array:
// setup/init:
BackgroundWorker startCallWorker = new BackgroundWorker();
startCallWorker.DoWork += new DoWorkEventHandler(StartCallWorker_DoWork);
...
startCallWorker.RunWorkerAsync(new string[]{userName, targetNumber});
// the handler:
private void StartCallWorker_DoWork(object sender, DoWorkEventArgs e)
{
string[] args = e.Argument as string[];
string userName = args[0];
string targetNumber = args[1];
}
Is there another object or pattern that allows us pass multiple arguments nicely, or ideally, write our own signature?
You could use a closure (Lambda):
backgroundWorker.DoWork += (s, e) => MyWorkMethod(userName, targetNumber);
Or with delegate (anonymous method) syntax:
backgroundWorker.DoWork +=
delegate(object sender, DoWorkEventArgs e)
{
MyWorkMethod(userName, targetNumber);
};
What's wrong with using a typed object?
internal class UserArgs
{
internal string UserName { get; set; }
internal string TargetNumber { get; set; }
}
var args = new UserArgs() {UserName="Me", TargetNumber="123" };
startCallWorker.RunWorkerAsync(args);
instead of a typed object. C# 4.0 provides us with tuple. We could use a tuple to hold multiple args. Then there is no need to declare a new class.
Object can be a list or array or some such. Just make your object a container of some sort, then cast within the BackgroundWorker. You need to make sure you're always passing in the same type though.
Maybe pass a lambda function as your object? Then you'd call it in the DoWork handler.
endCallWorker.RunWorkerAsync(new Action( () => DelegatedCallTarget(userName, targetNumber) ));
Create a class that holds all your arguments
Class MyClass
{
private string m_Username = string.Empty;
private int m_Targetnumber;
public MyClass(){}
public string Username
{
get { return m_Username; }
set { m_Username = value; }
}
public int TargetNumber
{
get { return m_TargetNumber; }
set { m_TargetNumber = value; }
}
}
// setup/init:
BackgroundWorker startCallWorker = new BackgroundWorker();
startCallWorker.DoWork += new DoWorkEventHandler(StartCallWorker_DoWork);
...
MyClass thisClass = new MyClass();
thisClass.Username = "abcd";
thisClass.TargetNumber = 1234;
startCallWorker.RunWorkerAsync(thisClass);
// the handler:
private void StartCallWorker_DoWork(object sender, DoWorkEventArgs e)
{
MyClass args = (MyClass)e.Argument;
string userName = args.Username;
string targetNumber = args.TargetNumber;
}
Why not have the "one" object passed be an array of parameters? You only need to cast it back to array inside the method from the object parameter.
Visual Studio 2008, C# 3.0.
I have a method below which calls an event handler. I would like to pass the two arguments received by the method to the event handler.
I would like to do something like this:
wc.DownloadDataCompleted += wc.DownloadedDataCompleted(strtitle, placeid);
Is this even possible, if yes, how would I go about doing it ?
Code Snippet:
public void downloadphoto(string struri,string strtitle,string placeid)
{
using (WebClient wc = new WebClient())
{
wc.DownloadDataCompleted += wc_DownloadDataCompleted;
wc.DownloadDataAsync(new Uri(struri));
}
}
The easiest way to do this is to use an anonymous function (an anonymous method or a lambda expression) to subscribe to the event, then make your method have just the parameters you want:
public void downloadphoto(string struri, string strtitle, string placeid)
{
using (WebClient wc = new WebClient())
{
wc.DownloadDataCompleted += (sender, args) =>
DownloadDataCompleted(strtitle, placeid, args);
wc.DownloadDataAsync(new Uri(struri));
}
}
// Please rename the method to say what it does rather than where it's used :)
private void DownloadDataCompleted(string title, string id,
DownloadDataCompletedEventArgs args)
{
// Do stuff here
}
DownloadDataAsync has an overload which takes an object:
DownloadDataAsync(uri, object)
That object can be any arbitrary thing you want that gets passed into the DownloadDataCompleted handler:
public void downloadphoto(string struri,string strtitle,string placeid)
{
using (WebClient wc = new WebClient())
{
string[] data = new string[2] { strtitle, placeid };
wc.DownloadDataCompleted += wc_DownloadDataCompleted;
wc.DownloadDataAsync(new Uri(struri), data);
}
}
void wc_DownloadDataCompleted(object sender, DownloadDataCompletedEventArgs e)
{
string[] data = (string[])e.UserToken;
string strtitle = data[0];
string placeid = data[1];
}
You could create a private class and place the handler in there. E.g.
public void downloadphoto(string struri, string strtitle, string placeid)
{
using (WebClient wc = new WebClient())
{
wcHandler handler = new wcHandler() { Strtitle = strtitle, Placeid = placeid };
wc.DownloadDataCompleted += handler.wc_DownloadDataCompleted;
wc.DownloadDataAsync(new Uri(struri));
}
}
private class wcHandler
{
public string Strtitle { get; set; }
public string Placeid { get; set; }
public void wc_DownloadDataCompleted(object sender, DownloadDataCompletedEventArgs e)
{
// Do Stuff
}
}
Although, given the elegance of Jon's answer would probably use that!
Jon Skeet already answered this, showing how to use a lamda expression, but I was still unclear about it. I still needed some more examples, and eventually found this simple case using a button: http://social.msdn.microsoft.com/Forums/en-US/csharpgeneral/thread/74d03fe0-0fa5-438d-80e0-cf54fa15af0e
void A()
{
Popup parameter = new Popup();
buttonClose.Click += (sender, e) => { buttonClose_Click(sender, e, parameter); };
}
static void buttonClose_Click(object sender, EventArgs e, Popup parameter)
{
MakeSomethingWithPopupParameter(parameter);
}
In my case, I was using a context menu for a TreeView control, which ended up looking like this:
private void TreeViewCreateContextMenu(TreeNode node)
{
ContextMenuStrip contextMenu = new ContextMenuStrip();
// create the menu items
ToolStripMenuItem newMenuItem = new ToolStripMenuItem();
newMenuItem.Text = "New...";
// add the menu items to the menu
contextMenu.Items.AddRange(new ToolStripMenuItem[] { newMenuItem });
// add its event handler using a lambda expression, passing
// the additional parameter "myData"
string myData = "This is the extra parameter.";
newMenuItem.Click += (sender, e) => { newMenuItem_Click(sender, e, myData); };
// finally, set the node's context menu
node.ContextMenuStrip = contextMenu;
}
// the custom event handler, with "extraData":
private void newMenuItem_Click(object sender, EventArgs e, string extraData)
{
// do something with "extraData"
}