Pass Strings through click EventHandler - c#

I'm trying to send 3 strings to a void/EventHandler
button.Click += new DownloadGame(gameZip, gameExe, gameTitle);
private void DownloadGame(string gameZip, string gameExe, string gameTitle)
{
if (!File.Exists(gameExe))
{
MessageBox.Show("Already Installed!");
}
string GamesDirectory = Path.Combine(Environment.CurrentDirectory, "Games");
if (!Directory.Exists(GamesDirectory))
Directory.CreateDirectory(GamesDirectory);
InstallGameFiles(Path.Combine(Directory.GetCurrentDirectory(), "Build", gameExe), gameZip, gameTitle);
How can I call the method with the arguments without this error?
Error CS0246 The type or namespace name 'DownloadGame' could not be found (are you missing a using directive or an assembly reference?)

The reason you're getting Error CS0246. is the new keyword:
// I N C O R R E C T
button.Click += new DownloadGame(gameZip, gameExe, gameTitle);
Because of the new, the compiler is attempting to make a new instance of a class named DownloadGame. It can't find a class by that name because it doesn't exist (there is only a method by that name, thus the error).
The other reason that your syntax is incorrect is that the delegate signature must match. For example, a Winforms button handler would assign like this:
public MainForm()
{
buttonDownload.Click += onClickDownloadButton;
}
private void onClickDownloadButton(object sender, EventArgs e)
{
}
Regardless of the platform, once you type button.Click += the intellisense should offer to make a method with the correct signature.
And as far as what your code is attempting to do, handling the Click event is to receive the event and data that the Button class is notifying and is not intended to send strings or anything else.
The thing that would seem to make sense here is that you have already performed other UI actions to populate some of these file names. For example you may have a button or menu that invokes an "open file" flow, or a settings prompt (example).
private void onClickDownloadButton(object sender, EventArgs e)
{
DownloadGame();
}
public void DownloadGame()
{
if (!File.Exists(gameExe))
{
MessageBox.Show("Already Installed!");
}
string GamesDirectory = Path.Combine(Environment.CurrentDirectory, "Games");
if (!Directory.Exists(GamesDirectory))
{
Directory.CreateDirectory(GamesDirectory);
}
InstallGameFiles(Path.Combine(Directory.GetCurrentDirectory(), "Build", gameExe), gameZip, gameTitle);
}
// These need to be populated some other
// way before enabling buttonDownload.
private string gameZip;
private string gameExe;
private string gameTitle;

You cannot pass string to Click event. But you can create variable/property under class where this button belong.
Also, when you are adding function to Event, you can't use 'new' keyword.
string gameZip, gameExe, gameTitle; // Set those variables i.e. here
button.Click += DownloadGame;
private void DownloadGame(Object sender, EventArgs e)
{
if (!File.Exists(gameExe))
{
MessageBox.Show("Already Installed!");
}
string GamesDirectory = Path.Combine(Environment.CurrentDirectory, "Games");
if (!Directory.Exists(GamesDirectory))
Directory.CreateDirectory(GamesDirectory);
InstallGameFiles(Path.Combine(Directory.GetCurrentDirectory(), "Build", gameExe), gameZip, gameTitle);
}

Related

Passing a variable between forms WINFORMS [duplicate]

Wierd behaviour when passing values to and from second form.
ParameterForm pf = new ParameterForm(testString);
works
ParameterForm pf = new ParameterForm();
pf.testString="test";
doesn't (testString defined as public string)
maybe i'm missing something? Anyway I'd like to make 2nd variant work properly, as for now - it returns null object reference error.
Thanks for help.
Posting more code here:
calling
Button ParametersButton = new Button();
ParametersButton.Click += delegate
{
ParameterForm pf = new ParameterForm(doc.GetElementById(ParametersButton.Tag.ToString()));
pf.ShowDialog(this);
pf.test = "test";
pf.Submit += new ParameterForm.ParameterSubmitResult(pf_Submit);
};
definition and use
public partial class ParameterForm : Form
{
public string test;
public XmlElement node;
public delegate void ParameterSubmitResult(object sender, XmlElement e);
public event ParameterSubmitResult Submit;
public void SubmitButton_Click(object sender, EventArgs e)
{
Submit(this,this.node);
Debug.WriteLine(test);
}
}
result:
Submit - null object reference
test - null object reference
pf.ShowDialog(this); is a blocking call, so pf.Submit += new ParameterForm.ParameterSubmitResult(pf_Submit); is never reached: switch the order.
Submit(this,this.node); throws a null object reference because no event is assigned to it (see above). Generally, you should always check first: if (Submit != null) Submit(this,this.node);
You should change ``pf.ShowDialog(this);topf.Show(this);` so that your main form isn't disabled while your dialog box is open, if that's what you want, or use the model below (typical for dialog boxes.)
I'm not sure what pf_Submit is supposed to do, so this might not be the best way to go about it in your application, but it's how general "Proceed? Yes/No" questions work.
Button ParametersButton = new Button();
ParametersButton.Click += delegate
{
ParameterForm pf = new ParameterForm(testString);
pf.ShowDialog(this); // Blocks until user submits
// Do whatever pf_Submit did here.
};
public partial class ParameterForm : Form
{
public string test; // Generally, encapsulate these
public XmlElement node; // in properties
public void SubmitButton_Click(object sender, EventArgs e)
{
Debug.WriteLine(test);
this.Close(); // Returns from ShowDialog()
}
}
When you want to use your second variant, you have to use a getString()-Method, where you can put the e.g. "testString". The way you wrote it, "testString" should be a method (and got brackets).
EDIT (a bit more precise):
You could write:
pf.getString(testString);
, if "pf" is an instance of your own class, otherwise you had to look up, whether you can retrieve a String in this class.
the thing was in line order :)
pf.Submit += new ParameterForm.ParameterSubmitResult(pf_Submit);
and
pf.Test = "test";
should have been set before
pf.ShowDialog(this);
my mistake thingking that parameter can be passed after 2nd form was displayed
thnx for answers

C# Get Value of a text box in a Static Void

I am trying to get the value of a text box called txtNum on the form FrmPhone as well as write to the text box txtLog however there seems to be no interacation between the Static Void and the Text Boxes on FrmPhone.
Below is the code for the CreateCall Static Void.
private void btnCall_Click(object sender, EventArgs e)
{
CreateCall();
}
static void CreateCall()
{
FrmPhone FrmPhone = new FrmPhone();
var numberToDial = FrmPhone.txtNum.Text;
FrmPhone.txtLog.Text += "\r\nCall " + numberToDial;
FrmPhone.txtLog.ScrollToCaret();
SetupDevices();
call = softphone.CreateCallObject(phoneLine, numberToDial);
call.CallStateChanged += call_CallStateChanged;
call.Start();
}
What am I missing to allow me to interact with the text boxes on the form while executing code in the static void?
Also worth noting is that If replace
var numberToDial = FrmPhone.txtNum.Text;
with
var numberToDial = "2788";
The call is made but the txtLog text does not change.
There are two options.
You make the method non-static. This is the easiest solution and the one that makes most sense. You are after all using instance variables.
void CreateCall()
{
var numberToDial = this.txtNum.Text;
}
Pass in the form to the method, as said, it doesn't make sense, but it does the job. txtNum needs to be internal or public, or CreateCall must reside in the same class:
static void CreateCall(FrmPhone frmPhone)
{
var numberToDial = frmPhone.txtNum.Text;
}
In calling the code you pass in this:
CreateCall(this);

Event Driven Programming

I've been reading this MSDN article and this question to try to understand events in .NET. Unfortunately, its not clicking for me and I'm having a lot of trouble. I'm trying to integrate this technique into my project, with little success.
Basically, I've got this class that will read numbers. Whenever it encounters a new number, I want it to fire an event called numberChanged.
So, I set up my event public event EventHandler numberChanged;. Later on, I fire my event when it encounters a number than isn't the same as the previous one.
if(currentNumber != previousNumber){
if(numberChanged != null){
numberChanged(this, new EventArgs());
}
}
But then I'm having trouble 'subscibing' to this event. If I do numberChanged += [something to do here] it errors saying that numberChanged is an event and not a type.
Is my explanation clear enough for some advice to be offered? Many thanks.
There are a number of ways to handle it, the most basic is to create a function:
public void MyNumberChangedHandler(object sender, EventArgs e)
{
//Your code goes here that gets called when the number changes
}
You then subscribe (one time only, usually in the constructor) by going:
numberChanged += MyNumberChangedHandler;
Or, you can use something called an anonymous (lambda) method, which is also assigned in your constructor (typically):
numberChanged += (sender, e) => {
//Your code here to handle the number changed event
};
To expand a little bit, care must be taken when using the lambda approach since you can create memory leaks and zombie objects. The .NET memory garbage collector is a mark-and-sweep system that removes objects when they are no longer in use. This post shows how hard it is to remove lambda event handlers: How to remove a lambda event handler .
Having an active event handler can keep your object alive even if it has been disposed! Here is an example of creating a zombie object (doesn't run in Fiddle but you can copy to your own console app) https://dotnetfiddle.net/EfNpZ5
Prints out:
I'm still alive
I'm still alive
I was disposed!
Press any key to quit
I'm still alive
I'm still alive
I'm still alive.
As everything else in the C# programming world, the events concept also follows specific rules and has it's own syntax. The wording is as follows:
an event defined as EventHandler is actually just a shortcut for a special method (delegate) signature - public delegate void EventHandler(object sender, EventArgs e)[1]. Whenever you have a signature in C# you always know what you need to write on the right sight or as a parameter, in order to connect/call some objects/methods/and so on.
after the event is defined, you need to subscribe in order to be informed whenever something happens. The syntax for subscribing an event is +=. Naturally for unsubscribing is -=. MSDN says that the syntax should be object.event += eventHandler (or object.event += new EventHandler(eventHandler);)
so after an event is defined (event Event SomeEvent;) all that left is to create a method that can be bound to this event. This method has to have the same signature as the EventHandler, so it should match the signature of [1] and can be something like private void numberChangedEventHandler(object sender, EventArgs eventArguments)
Now you know what you need to write on the right side of +=.
An example:
public class NumberSequence
{
// numbers to be compared
private readonly List<int> numbers = new List<int>();
// used to generate a random collection
private readonly Random random = new Random();
// tell me if the previous and next number are different
public event EventHandler DifferentNumbersEvent;
public NumberSequence()
{
// fill the list with random numbers
Enumerable.Range(1, 100).ToList().ForEach(number =>
{
numbers.Add(random.Next(1, 100));
});
}
public List<int> Numbers { get { return numbers; } }
public void TraverseList()
{
for (var i = 1; i < this.numbers.Count; i++)
{
if (this.numbers[i - 1] != this.numbers[i])
{
if (this.DifferentNumbersEvent != null)
{
// whoever listens - inform him
this.DifferentNumbersEvent(this, EventArgs.Empty);
}
}
}
}
}
Now before the class is used, define the event handler, that will listen and will be called, when the event is fired (wording again):
private void differentNumberEventHandler(Object sender, EventArgs eventArguments)
{
Console.WriteLine("Different numbers...");
}
And the usage:
var ns = new NumberSequence();
ns.DifferentNumbersEvent += differentNumberEventHandler;
ns.TraverseList();
Everything else is just syntactic sugar for this notation (lambda / anonymous methods / ...), for example:
object.Event += (s, e) => { // code ... }; is the same as object.Event += (Object sender, EventArgs eventArguments) => { // code ... };. Do you recognise the signature? - it is the same as the private void differentNumberEventHandler....
Often we need to pass information through the event, in this case maybe we want to see the two numbers. C# allows you to do this easily using custom event arguments. Just create a class that inherits the EventArgs class and add properties for the data that should be passed, in this case the numbers:
public class NumbersInfoEventArgs : EventArgs
{
public int Number1 { get; set; }
public int Number2 { get; set; }
}
And then specify, when declaring the event, that it will pass data of type NumbersInfoEventArgs (signatures again):
public event EventHandler<NumbersInfoEventArgs> DifferentNumbersEvent;
...
this.DifferentNumbersEvent(this, new NumbersInfoEventArgs
{
Number1 = this.numbers[i - 1],
Number2 = this.numbers[i]
});
And last but now least, the signature of the event handler should match the signature of the event:
private void differentNumberEventHandler(Object sender, NumbersInfoEventArgs eventArguments)
{
Console.WriteLine("Different numbers {0} - {1}", eventArguments.Number1, eventArguments.Number2);
}
And voila, the output is:
Different numbers 89 - 86
Different numbers 86 - 53
Different numbers 53 - 12
Different numbers 12 - 69
you can subscribe the event in this way:
using System;
public class Program
{
public static void Main()
{
Console.WriteLine("Hello World");
var num = new Number();
num.numberChanged +=(s,e) =>{
Console.WriteLine("Value was changed to {0}",num.Value); // in the demo below you can find another implementation for this sample using custom events
};
num.Value=10;
num.Value=100;
}
}
public class Number{
public event EventHandler numberChanged;
private int _value=0;
public int Value
{
get{
return _value;
}
set{
if(value!=_value){
_value=value;
if(numberChanged!=null)
numberChanged(this,null);
}
}
}
}
explanation:
since the EventHandler delegate has 2 parameters (sender, eventArgs) as mentioned here, you need to pass these params and I passed them as s and e
another way to subscribe this event like this:
var num = new Number();
num.numberChanged += NumberChanged_Event; // below is the delegate method
public void NumberChanged_Event(object sender,EventArgs e)
{
// your code goes here
}
I updated the demo to work with you own delegate to pass the old value and new value which can help in many cases.
here a working demo

Use of 'this' in an event

I need to retrieve some property of an element that trigger the event in this context:
SoundEffect alarm;
public MainPage()
{
InitializeComponent();
Pad1.MouseLeftButtonUp += new MouseButtonEventHandler(makeasound);
Pad2.MouseLeftButtonUp += new MouseButtonEventHandler(makeasound);
Pad3.MouseLeftButtonUp += new MouseButtonEventHandler(makeasound);
}
Pad1,2 and 3 are the names of some Ellipse I have in my xaml. Now if I try to do this in the code executed by the event ( declared immediately after the sample code above):
private void makeasound(object sender, MouseButtonEventArgs e)
{
string text = this.Name;
textBlock1.Text = text;
}
The Text Block becomes empty, so I guess the Name of the triggering element never gets there.
Now, things get more messy if I'm trying to retrieve a custom property of the "pads" called "Son", declared with the dependency method , which is a string, like this:
private void makeasound(object sender, MouseButtonEventArgs e)
{
string text = this.Son;
textBlock1.Text = text;
}
VS reports error:
'PhoneApplication.MainPage' does not contain a definition for 'Son' and no extension method 'Son' accepting a first argument of type 'PhoneApplication.MainPage' could be found (are you missing a using directive or an assembly reference?)
Where Phoneapplication is the name of the app and the main namespace of the code behind.
As if it weren't simple enough, what I'm tryin to do is this:
The custom property is actually an INT. I know I declared the dependency right since VS let me compile. Each Pad has this custom property storing an int, and I need to retrieve it to access an array element. The function triggered is this:
private void makeasound(object sender, MouseButtonEventArgs e)
{
int x = this.Son;
var sons = new[] { "sons/firstsound.wav", "sons/secondsound.wav", "sons/thirdsound.wav" };
string target = sons[x];
StreamResourceInfo info = Application.GetResourceStream(
new Uri(target, UriKind.Relative));
alarm = SoundEffect.FromStream(info.Stream);
Microsoft.Xna.Framework.FrameworkDispatcher.Update();
alarm.Play();
}
So, I declare an array storing URI's for sounds that I'd like to play("son" means sound in french, I'm from Belgium). I then use the INT associated with the triggering element to access the URI of a sound, then I play this sound.
The reason I do this is because I'd like to let the user change the INT value for each pad and therefore choose what sound each pad plays. The fact that I seem to have no choice but to declare this array each time the function is called (otherwise it's not in context) is not very elegant but I guess I can live with that ( array will have 50-60 elements in it)
So, for those who read this far, my problem is to use a property of the triggering event, which seems to be harder when it's a custom property. I put the rest of the logic in case someone had advices.
I thank anyone who read this message and who could maybe help me sorting this out. I read online documentation and I have two good c# books, but I havent found a solution for me.
Have a nice day.
EDIT: Some others are willing to help so here is the declaration of the dependency property(Sorry Daniel, hadn't seen you commented my original post)
namespace MyNamespace
{
public static class MyClass
{
public static readonly DependencyProperty SonProperty = DependencyProperty.RegisterAttached("Son",
typeof(string), typeof(MyClass), new PropertyMetadata(null));
public static string GetSon(UIElement element)
{
if (element == null)
throw new ArgumentNullException("element");
return (string)element.GetValue(SonProperty);
}
public static void SetSon(UIElement element, string value)
{
if (element == null)
throw new ArgumentNullException("element");
element.SetValue(SonProperty, value);
}
}
Mynamespace is nested inside the main namespace.
this refers to the current instance of MainPage. Not to the pad that was clicked. That's the sender:
var pad = (Pad)sender;
var text = pad.Name;
textBlock1.Text = text;
Son is an attached property, not a normal one. You can get its value like this:
var pad = (Pad)sender;
var son = MyClass.GetSon(pad);
Please note that you have declared it as a string. Seeing how you want to use it, it seems to make more sense to declare it as an int.
this refers to the instance of the type in which the method is defined in, in this case, MainPage. If you want to get the instance of the type that triggered the event, that's what's in the sender parameter:
private void makeasound(object sender, MouseButtonEventArgs e)
{
textBlock1.Text = ((Pad)sender).Name;
}

How to use event argument outside the event

I'm trying to create a custom download app. Its all working except for the download all button that cant pick up the "percent1" variable from the "DownloadProgressChangedEventArgs". I have instantiated it prior to the mainForm constructor but it wont read the changed value.
Here's the code, partially stripped since most of it isnt relevant to the question:
public partial class Main : Form
{
//Variables (not all, just the one im having issues with)
private double percentage1;
//Main form constructor
public Main(){...}
//Download File Async custom method
public void DldFile(string url, string fileName, string localPath, AsyncCompletedEventHandler completedName, DownloadProgressChangedEventHandler progressName)
{
WebClient webClient = new WebClient();
webClient.DownloadFileAsync(new Uri(url), localPath + "\\" + fileName);
webClient.DownloadFileCompleted += new AsyncCompletedEventHandler(completedName);
webClient.DownloadProgressChanged += new DownloadProgressChangedEventHandler(progressName);
}
//Button 1 click event to start download
private void btnDld1_Click(object sender, EventArgs e)
{
if (url1 != "" && Directory.Exists(localPath1))
{
_startDate1 = DateTime.Now;
DldFile(url1, fileName1, localPath1, completed1, progress1);
}
//took out the try/catch, other ifs to try and cut it down
}
//Download Progress Changed event for Download 1
public void progress1(object sender, DownloadProgressChangedEventArgs e)
{
percentage1 = e.ProgressPercentage; //THIS IS WHERE I WAS EXPECTING TO UPDATE "percentage1"
progressBar1.Value = int.Parse(Math.Truncate(percentage1).ToString());
}
//Button that starts all downloads click event where all my problems are at the moment
private void btnDldAll_Click(object sender, EventArgs e)
{
//The progress bar that should let me know the global status for all webClients
progressBarAll.Value = (
int.Parse(Math.Truncate(percentage1).ToString()) + //HERE IS MY PROBLEM
int.Parse(Math.Truncate(percentage2).ToString()) + //HERE IS MY PROBLEM
int.Parse(Math.Truncate(percentage3).ToString()) + //HERE IS MY PROBLEM
int.Parse(Math.Truncate(percentage4).ToString()) + //HERE IS MY PROBLEM
int.Parse(Math.Truncate(percentage5).ToString())) / 5; //HERE IS MY PROBLEM
//Checks if the link exists and starts it from the download button click event
if (url1 != "")
{
btnDld1.PerformClick();
}
//Continues for url2, 3, 4, 5 and else
}
}
So this is the shortest way i found of letting you know what im trying to pull off, if there's something missing please let me know, i'll try to add any info as fast as possible.
I have tried to instantiate "progress1" to try and acess its percentage1 variable, but it didnt work. I've tried doing the same thing with the webClient but didnt work either. I have used google and stackflow search to no avail. So im not sure if the question is too dumb, or there's a diferent way to look at the issue thats completely out of my mindset.
So main problem is updating the "percentage1" variable and using it.
There are other problems regarding the "progressBarAll.Value" calculation that will be solved when i can get my hands on the right value. So no need to worry about that if you see it.
Try not to think about 'using the event arguments outside the event'. Think about updating the state of your form.
Use properties to simplify the update logic:
public partial class Main : Form
{
private double percentage1;
private double percentage2;
private double percentage3;
private double percentage4;
private double percentage5;
private double Percentage1
{
get
{
return this.percentage1;
}
set
{
this.percentage1 = value;
this.UpdatePercentageAll(); // this will update overall progress whenever the first one changes
progressBar1.Value = GetValueFromPercentage(value);
}
}
private double Percentage2
// same code as for Percentage1
void UpdatePercentageAll()
{
this.PercentageAll = (this.Percentage1 + this.Percentage2 + this.Percentage3 + this.Percentage4 + this.Percentage5) / 5;
}
static int GetValueFromPercentage(double percentage)
{
return (int)Math.Truncate(percentage);
}
double percentageAll;
private double PercentageAll
{
get
{
return this.percentageAll;
}
set
{
this.percentageAll = value;
progressBarAll.Value = GetValueFromPercentage(value);
}
}
//Download File Async custom method
public void DldFile(string url, string fileName, string localPath, AsyncCompletedEventHandler completedName, DownloadProgressChangedEventHandler progressName)
{
WebClient webClient = new WebClient();
webClient.DownloadFileAsync(new Uri(url), localPath + "\\" + fileName);
webClient.DownloadFileCompleted += new AsyncCompletedEventHandler(completedName);
webClient.DownloadProgressChanged += new DownloadProgressChangedEventHandler(progressName);
}
//Button 1 click event to start download
private void btnDld1_Click(object sender, EventArgs e)
{
if (url1 != "" && Directory.Exists(localPath1))
{
this.StartDownloadFile1();
}
//took out the try/catch, other ifs to try and cut it down
}
void StartDownloadFile1()
{
this.Percentage1 = 0;
_startDate1 = DateTime.Now;
DldFile(url1, fileName1, localPath1, completed1, progress1);
}
//Download Progress Changed event for Download 1
public void progress1(object sender, DownloadProgressChangedEventArgs e)
{
this.Percentage1 = e.ProgressPercentage; // update property, not field
//this will be done in property setters
//progressBar1.Value = int.Parse(Math.Truncate(percentage1).ToString());
}
// then add similar code for other download buttons
//Button that starts all downloads click event where all my problems are at the moment
private void btnDldAll_Click(object sender, EventArgs e)
{
//Checks if the link exists and starts it from the download button click event
if (url1 != "")
{
this.StartDownloadFile1();
}
//Continues for url2, 3, 4, 5 and else
}
}
I would refactor the code even further, but I think it will be easier for you to understand if the code is closer to the original.
The main idea is to create a set of linked properties which work like mathematical functions. When writing the PercentageX properties I'm kind of saying 'let PercentageAll be the average of all percentages'. Then I have each download update it's own progress. Once any progress is updated it updates the average, and I don't have to rememver that inside the progress changed event handler.
And the last point is updating progress bars from percentage properties. It's quite straightforward: once a percentage is changed, I need to update a bar. If so, why bother writing something like
this.Percentage1 = x;
this.progressBar1.Value = (int)Math.Truncate(x);
In this case I have to remember everywhere that once I change the Percentage1 I have to update the bar. And in my example I just create a strict rule for that which is only in one place and works everytime. So I just cannot forget it. And if I need to change the rule, I need to change only one place, so again I cannot make a mistake.
The technique I demonstrate can be expressed as a well-known rule: 'one rule - one place', which means that you should try to have only single place in code that expresses each logical rule that exists in your program. It is a very important idea, I suggest you learn and use it.

Categories

Resources