Passing a user defined method to the constructor - c#

I want to create a simple input dialog. so I added a Windows From to my application and in that window I've put a label, a textbox and a button; For now it's easy to fill the required proprieties of my controls with the predefined values using constructors and return the entered value with result;
public partial class GeneralInputForm : Form
{
public string result = "";
public GeneralInputForm(string label, string defaultValue = "")
{
InitializeComponent();
label1.Text = label;
textBox1.Text = defaultValue;
}
private void button1_Click(object sender, EventArgs e)
{
result = textBox1.Text;
Close();
}
}
And now creating an instance of the from and filling the parameters with the required values;
GeneralInputForm formInput = new GeneralInputForm("Please enter your age:");
formInput.ShowDialog();
//formInput.result holds the value
My questions raises here; What if I want to check for the validity of the value that the user has entered in the textbox in the GeneralInputForm itself (and not later when instantiating the form with checking the result field)? I mean is there such a functionality in C# (delegates maybe) so that the user himself can define a method and pass a it as one of the parameters of the constructor which that method checks the textbox1.text value to be sure that for example it's successfully can be parsed into an int or float or string?
I don't want to add an ugly string parameter called variableType to my constructor and ask user to say if he requires a 'int' or 'float' or 'string' or whatever and then I myself by checking the value of variableType, wrote different statements for making sure that the entered text can be successfully parsed into int or float or string (it would be an ugly and limited solution)

Change GeneralInputForm to GeneralInputForm<T>. Is a generic class, just like List<String>.
You can use two field Func one to cast the Text to the object you want and one to check the return just like this:
public partial class GeneralInputForm<T> : Form
{
public T result = default(T);
Func<T, Boolean> check = (input) => true; // Default check
Func<String, T> cast = null; // Default cast
public GeneralInputForm(string label, string defaultValue = "", Func<String, T> cast, Func<T, Boolean> check)
{
InitializeComponent();
label1.Text = label;
textBox1.Text = defaultValue;
if(check != null)
this.check = check;
this.cast = cast;
}
private void button1_Click(object sender, EventArgs e)
{
if(cast != null)
{
T casted = cast(textBox1.Text);
if(casted != null && check(casted)){
result = textBox1.Text;
Close();
}
}
}
}
And you can use it like this:
Func<String, int> cast = (input) => int.Parse(input);
Func<int, Boolean> check = (input) => input > 0;
GeneralInputForm<int> form = new GeneralInputForm<int>("Enter a number:", "1", cast, check);
form.ShowDialog();
//etc....
More info about Generics.
This way of doing thing is named Dependecy Injection, and this approach is the constructor injection type. More info.

You don't need to pass it as a parameter. The validation function can be overridden using inheritance.
public partial class GeneralInputForm : Form
{
// Returns true if this text is valid for a textbox
protected virtual bool IsValid(string text)
{
return true; // default behaviour
}
private void button1_Click(object sender, EventArgs e)
{
if (IsValid(textBox1.Text))
{
result = textBox1.Text; // ok
Close();
}
else
{
// Show error message
}
}
}
Now you can create a form which custom-validates textboxes, by overriding (ie. replacing) the IsValid() function:
public partial class SpecificInputForm : GeneralInputForm
{
// Blank textboxes are not valid, and are restricted to 31 characters
protected override bool IsValid(string text)
{
if (string.IsNullOrWhitespace(text))
return false;
return (text.Length < 32);
}
}

While #Oscar's answer would work, making forms generic is a bit of a bad idea because the Winforms designer has aversion to them (try to inherit from that form and you'll see).
I'd probably make it so that it's not the form that is generic, but a method:
public partial class GeneralInputForm : Form
{
private GeneralInputForm() // Private constructor
{
InitializeComponent();
}
public static bool TryGetValue<T>(
string label,
string defaultValue,
Func<T, bool> check,
out T result
) where T : IConvertible
{
result = default(T);
using (var frm = new GeneralInputForm())
{
frm.label1.Text = label;
frm.textBox1.Text = defaultValue;
frm.Closing += (o, e) =>
{
var myForm = (GeneralInputForm) o;
// User closed not clicking the button?
if (myForm.DialogResult != DialogResult.OK)
return;
try
{
var checkval = (T) Convert.ChangeType(myForm.textBox1.Text, typeof (T));
if (!check(checkval))
e.Cancel = true;
}
catch
{
e.Cancel = true;
}
};
if (frm.ShowDialog() == DialogResult.OK)
{
result = (T)Convert.ChangeType(frm.textBox1.Text, typeof (T));
return true;
}
}
return false;
}
private void button1_Click(object sender, EventArgs e)
{
// Don't use `Close()` on modal forms, it's always better to
// return a dialog result. This will close the form aswell
DialogResult = DialogResult.OK;
}
}
Then use it like:
int result;
if(GeneralInputForm.TryGetValue("Enter an integer over 10",
"10",
(x) => x > 10,
out result))
MessageBox.Show(result.ToString());
else
/* User closed the form by other means */
Or:
string result;
if (GeneralInputForm.TryGetValue("Enter \"Hello\"",
"Goodbye",
(x) => x == "Hello",
out result))
This will not let you close the form by clicking button1 unless the value is correct, but will let you close it by other means (alt+f4, clicking the x, etc.). It'll return true if it was closed by the button, or false if it wasn't (you can put a Cancel button there).
You could also make the defaultValue parameter be of type T and assign defaultValue.ToString() to the textbox
A messagebox can be shown to the user in the Closing delegate if the value is not valid, or you can make your own logic
This requires that T is IConvertible, but about any type having a Parse method is

Thanks to everyone for the answers; I ended up to this simple and clear solution using delegates;
public partial class SimpleInputForm : Form
{
public string Value = "";
public delegate bool VerifyDel(string input);
private VerifyDel Methods;
public SimpleInputForm(string lable, VerifyDel method)
{
InitializeComponent();
label1.Text = lable;
Methods = new VerifyDel(method);
}
private void button1_Click(object sender, EventArgs e)
{
if (Methods(textBox1.Text))
{
Value = textBox1.Text;
Close();
}
}
}
and now I show the form with user defined functionality for the evaluation of input;
SimpleInputForm simpleInput = new SimpleInputForm("Enter your age:", (input => {
try
{
int age = int.Parse(input);
return true;
}
catch
{
MessageBox.Show("Invalid value for age!");
return false;
}
}));
//simpleInput.Value holds the input value

Related

Get value from dialog window

I'm having trouble getting the value of a textbox of a popped up dialog. I've followed the advice from other StackOverflow questions that said to create a public variable in program.cs:
public static string cashTendered { get; set; }
Then I created my dialog like this:
Cash cashform = new Cash();
cashform.ShowDialog();
And when the user presses the button on the dialog, this is called:
if (isNumeric(textBox1.Text, System.Globalization.NumberStyles.Float))
{
Program.cashTendered = textBox1.Text;
this.Close();
}
else
{
MessageBox.Show("Please enter a valid amount of cash tendered. E.g. '5.50'");
}
Yet Program.cashTendered stays null. Am I doing something wrong? Thanks!
For starters your form called Cash should use an object oriented design. It should have a public property called CashEntered or something similar of type decimal instead of string. You would call the Form like so:
using (var cashDialog = new CashDialog())
{
// pass a reference to the Form or a control in the Form which "owns" this dialog for proper modal display.
if (cashDialog.ShowDialog(this) == DialogResult.OK)
{
ProcessTender(cashDialog.CashEntered);
}
else
{
// user cancelled the process, you probably don't need to do anything here
}
}
Using a static variable to hold the results of a temporary dialog is a bad practice. Here is the better implementation of a dialog:
public class CashDialog : Form
{
public decimal CashEntered { get; private set; }
private void ok_btn_Clicked
{
decimal value;
if (Decimal.TryParse(cashEntered_txt.Text, out value))
{
// add business logic here if you want to validate that the number is nonzero, positive, rounded to the nearest penny, etc.
CashEntered = value;
DialogResult = DialogResult.OK;
}
else
{
MessageBox.Show("Please enter a valid amount of cash tendered. E.g. '5.50'");
}
}
}
On your main form that you want to get the value for, you'd have some sort of code like this;
var cashTendered;
using (var frm = new Cash())
{
if (frm.ShowDialog() == DialogResult.OK)
cashTendered = frm.GetText()
}
Then on your dialog form, you'd have something like this:
public string GetText()
{
return textBox1.Text;
}
public void btnClose_Click(object sender, EventArgs e)
{
this.DialogResult = DialogResult.OK;
this.Close();
}
public void btnCancel_Click(object sender, EventArgs e)
{
this.Close();
}
Alternatively, you can just perform those lines in the btnClose_Click event in the FormClosing event instead if you don't have a button for them to click to 'submit' their value.
Edit You might want to add some sort of validation on your textbox inside the btnClose event, such as:
decimal myDecimal;
if (decimal.TryParse(textBox1.Text, out myDecimal))
{
this.DialogResult = DialogResult.OK;
this.Close();
}
else
{
MessageBox.Show("Invalid entry", "Error");
textBox1.SelectAll();
}

Cannot convert expression type error in generic method

I have two custom userControls. And when I want to set some property to customUserControl, I have to do something like that:
private void OnRightMouseDown(object sender, MouseButtonEventArgs e)
{
var userControl = sender as UserControl;
if (userControl != null)
switch (userControl.Name)
{
case "UserControl01":
var uc01 = sender as UserControl01;
if (uc01 != null)
{
uc01.ViewModel.IsSelected = true;
}
break;
case "UserControl02":
var uc02 = sender as UserControl02;
if (uc02 != null)
{
uc02.ViewModel.IsSelected = true;
}
break;
}
e.Handled = true;
}
and I want to do it this way:
private void OnRightMouseDown(object sender, MouseButtonEventArgs e)
{
var userControl = sender as UserControl;
if (userControl != null)
{
var tempUc = GetUserControlType(userControl);
tempUc.ViewModel.IsSelected = true;
}
e.Handled = true;
}
For that purpose I made GetUserControlType method:
private static T GetUserControlType<T>(T userControl)
{
if (userControl != null)
{
var uc = userControl as UserControl;
switch (uc.Name)
{
case "UserControl01":
var tempUc1 = userControl as UserControl01;
return tempUc1;
case "UserControl02":
var tempUc2 = userControl as UserControl02;
return tempUc2;
}
}
return default(T);
}
And I get error - Cannot convert expression type '' to return type 'T' in line return tempUc1;
How can I avoid it, because I need to return one of this two types?
You have a problem getting the body of your method GetUserControlType to typecheck. Leaving that aside for a moment, imagine that we had an implementation of GetUserControlType. What this does is return its argument cast to one of your types.
The return type is T, the same as the argument type, so your line
var tempUc = GetUserControlType(userControl);
could be rewritten as
UserControl tempUc = GetUserControlType(userControl);
since the type of userControl is UserControl. So basically, even if you could get it to typecheck, the method would just return its argument unchanged, and have the same type. You should also think what you mean by var in that line - it will have one particular type, it cannot have both type UserControl01 and UserControl02.
As to the method itself, the lines
var tempUc1 = userControl as UserControl01;
return tempUc1;
don't typecheck because the return type T is not statically UserControl01. It doesn't matter whether they would be at runtime in that particular branch of the if statement, they have to have the correct type at compile time.
As noted in a comment, you can use an interface, eg:
interface IControlWithViewModel { public ISelectableViewModel { get; } }
interface ISelectableViewModel { public bool IsSelected { get; set; }
and make the user controls both implement this interface - then instead you write
var tempUc = (IControlWithViewModel)userControl;
tempUc.ViewModel.IsSelected = true;
Further to your question as to "can it be done with generics", you should think of generics as something which can be used when the type doesn't matter (when the function can be written generically without some kind of special-case analysis on the possible types).
If you really need to have the same MouseDown handler for both UserControls, you may write it like this:
private void OnRightMouseDown(object sender, MouseButtonEventArgs e)
{
var uc01 = sender as UserControl01;
if (uc01 != null)
{
uc01.ViewModel.IsSelected = true;
return;
}
var uc02 = sender as UserControl02;
if (uc02 != null)
{
uc02.ViewModel.IsSelected = true;
}
}
Anyway, the better solution would be to have two handlers:
private void UserControl01_RightMouseDown(object sender, MouseButtonEventArgs e)
{
((UserControl01)sender).ViewModel.IsSelected = true;
}
private void UserControl02_RightMouseDown(object sender, MouseButtonEventArgs e)
{
((UserControl02)sender).ViewModel.IsSelected = true;
}

C# ErrorProvider Want to know if any are Active

I want to know if any ErrorProvider are active in my form.
being able to find this out might help reduce my code..
I did find this thing here Counting ErrorProvider
but incase someone knows a better way... so here goes.
Ok so basically I have a WinForm which has many TextBoxes
Now when user enters values I use Validating to perform validation and if it does not match Regex I set the ErrorProvider ON for that Control.. similarly if the user changes the value to a acceptable one I switch ErrorProvider OFF for that Control..
but when SAVE is clicked i have to do another check anyways incase the user did not listen to me and change the thing like he was supposed to and still clicked SAVE.. I dont want the thing crashing..
soo mm is there like a thing where I could say if ErrorProviders is not active then proceed with save else message box saying change it.
[ANOTHER QUESTION]
Umm When Validating it only Validates when the Control loses Focus... I kinda of want it to do validation when user stops typing.. I hope you get what I mean
Like Email Address(textbox) when user is typing his/her name in I [DON'T] want it to do validation yet, but when user has finished entering is waiting for ErrorProvider to disappear(But it doesn't coz it only does that when control loses focus) 2 odd seconds after typing can i make the validation take place?
Unfortunately, the ErrorProvider control doesn't provide such functionality. You'd best go with the custom error provider classes from the link you posted.
Otherwise, you could create a method that you would call instead of SetError
int errorCount;
void SetError(Control c, string message)
{
if (message == "")
errorCount--;
else
errorCount++;
errorProvider.SetError(c, message);
}
Or you could make an extension method for the ErrorProvider class that would set the error and increment a counter or something along those lines.
And last but not least, you could iterate through all the controls. Slow, but it works:
bool IsValid()
{
foreach (Control c in errorProvider1.ContainerControl.Controls)
if (errorProvider1.GetError(c) != "")
return false;
return true;
}
Edit
I've written a quick extension class for the error provider:
public static class ErrorProviderExtensions
{
private static int count;
public static void SetErrorWithCount(this ErrorProvider ep, Control c, string message)
{
if (message == "")
{
if (ep.GetError(c) != "")
count--;
}
else
count++;
ep.SetError(c, message);
}
public static bool HasErrors(this ErrorProvider ep)
{
return count != 0;
}
public static int GetErrorCount(this ErrorProvider ep)
{
return count;
}
}
I haven't tested it extensively, so you might want to do a bit more validation before calling SetError on your ErrorProvider.
I know this is a bit older question and the extension is working except if someone try to SetErrorWithCount twice for the same object, the count is counted twice.
so, here I come with the update extension base on Netfangled extension
public static class ErrorProviderExtensions
{
private static int count;
public static void SetErrorWithCount(this ErrorProvider ep, Control c, string message)
{
if (message == "")
{
if (ep.GetError(c) != "")
count--;
}
else
if (ep.GetError(c) == "")
count++;
ep.SetError(c, message);
}
public static bool HasErrors(this ErrorProvider ep)
{
return count != 0;
}
public static int GetErrorCount(this ErrorProvider ep)
{
return count;
}
}
OK let me use easier method:
currently you are using implicit validation approach... to immediately validate the control.
I think you want to check if all the controls in the form is validated before do some actions, so just check that all the child control is validated. by using The explicit validation approach
in the validating event for each control you can use:-
Private Sub ProductIDTextBox_Validating(sender As System.Object, e As System.ComponentModel.CancelEventArgs) Handles ProductIDTextBox.Validating
If ProductIDTextBox.Text = "" Then
ErrorProvider1.SetError(ProductIDTextBox, "you have to enter text")
e.Cancel = True
Return
End If
ErrorProvider1.SetError(ProductIDTextBox, "")
End Sub
then you can check for all the controls by :-
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
If ValidateChildren() Then
MessageBox.Show("Validation succeeded!")
Else
MessageBox.Show("Validation failed.")
End If
End Sub
hope this will help since i spend hours to find the proper method
It seems like a logical thing to have but unfortunately it's not provided natively.
You could extend the ErrorProvider as other mentioned, or simply iterate all the controls under it and look for an error, something like
bool IsValidationError(ErrorProvider errorProvider, Control.ControlCollection controlCollection)
{
foreach(Control child in controlCollection)
{
// The child or one of its children has an error.
if (!errorProvider.GetError(child).IsNullOrEmpty() || IsValidationError(errorProvider, child.Controls))
return true;
}
return false;
}
and you'd call IsValidationError(errorProvider, errorProvider.ContainerControl.Controls), or passing a more limited control collection.
Obviously you'd want to avoid iterating tons of controls, but that simple solution should be fine in lots of cases. Also even if you do have tons of controls, you'd probably group them together using Panel, TabControl, GroupBox, ... so you could easily avoid iterating absolutely all the controls.
Note: this is similar to one of the possibilities described in https://stackoverflow.com/a/12327212/276648 except it looks for both null and empty, and it iterates possible grand children recursively.
I have multiple Control elements (TextBoxes) attached to their corresponding ErrorProviders.
I was trying to find a way to countAllErrors(), or even better, to handleEachError(),
so that's what I came up with:
In the Class:
internal TextBox email_textbox;
internal TextBox city_textbox;
internal TextBox address_textbox;
internal TextBox phone_textbox;
internal TextBox lastName_textbox;
internal TextBox firstName_textbox;
private ErrorProvider firstName_errPro;
private ErrorProvider lastName_errPro;
private ErrorProvider phone_errPro;
private ErrorProvider address_errPro;
private ErrorProvider city_errPro;
private ErrorProvider email_errPro;
internal Dictionary<ErrorProvider, Control> errors;
In the Form's Constructor:
errors = new Dictionary<ErrorProvider, Control>(6);
errors.Add( firstName_errPro ,firstName_textbox );
errors.Add( lastName_errPro ,lastName_textbox );
errors.Add( phone_errPro ,phone_textbox );
errors.Add( address_errPro ,address_textbox );
errors.Add( city_errPro ,city_textbox );
errors.Add( email_errPro ,email_textbox );
Counting all errors:
int countAllErrors()
{
int numOfErrors = errors.Count<KeyValuePair<ErrorProvider, Control>>(ep => ep.Key.GetError(ep.Value) != "");
return numOfErrors;
}
Handling each error:
void handleEachError()
{
foreach (KeyValuePair<ErrorProvider, Control> errPair in errors.Where(ep => ep.Key.GetError(ep.Value) != ""))
{
ErrorProvider errorProvider = errPair.Key;
Control control = errPair.Value;
string errorStr = errorProvider.GetError(control);
// handle the error:
// for example - show it's text in a MessageBox:
MessageBox.Show(errorStr);
}
}
lemme know if it was helpful.. ;)
You can also simply create an inherited class.
public class TrackedErrorProvider : ErrorProvider
{
public TrackedErrorProvider() : base() { }
public TrackedErrorProvider(ContainerControl parentControl) : base(parentControl) { }
public TrackedErrorProvider(IContainer container) : base(container) { }
public int ErrorsCount { get; protected set; } = 0;
public bool HasErrors
{
get { return ErrorsCount > 0; }
}
public new void SetError(Control control, string message)
{
//Check if there is already an error linked to the control
bool errorExistsForControl = !string.IsNullOrEmpty(GetError(control));
//If removing error from the control
if (string.IsNullOrEmpty(message))
{
/* Decreases the counter only if:
* - an error already existed for the control
* - the counter is not 0
*/
if (errorExistsForControl && ErrorsCount > 0) ErrorsCount--;
}
else //If setting error message to the control
{
//Increments the error counter only if an error wasn't set for the control (otherwise it is just replacing the error message)
if (!errorExistsForControl) ErrorsCount++;
}
base.SetError(control, message);
}
public void RemoveError(Control control)
{
SetError(control, null);
}
}
Some answers here are extremely error prone, because they share a static count variable in the extension method. No!
My extension methods use my Nuget package Overby.Extensions.Attachments to store the associated controls along with the ErrorProvider so that the # of errors can be counted.
using System.Collections.Generic;
using System.Windows.Forms;
using System.Linq;
using Overby.Extensions.Attachments; // PM> Install-Package Overby.Extensions.Attachments
namespace MyApp
{
public static class ErrorProviderExtensions
{
public static void TrackControl(this ErrorProvider ep, Control c)
{
var controls = ep.GetOrSetAttached(() => new HashSet<Control>()).Value;
controls.Add(c);
}
public static void SetErrorWithTracking(this ErrorProvider ep, Control c, string error)
{
ep.TrackControl(c);
ep.SetError(c, error);
}
public static int GetErrorCount(this ErrorProvider ep)
{
var controls = ep.GetOrSetAttached(() => new HashSet<Control>()).Value;
var errControls = from c in controls
let err = ep.GetError(c)
let hasErr = !string.IsNullOrEmpty(err)
where hasErr
select c;
var errCount = errControls.Count();
return errCount;
}
public static void ClearError(this ErrorProvider ep, Control c)
{
ep.SetError(c, null);
}
}
}
My way is with extension methods so I can simply call errorProvider.Valid(). The ErrorProvider actually has a reference to its main control(the form) if implemented correctly, so it should work on all forms with a single instance. ValidateChildren() doesn't seem to return a useful value.
This is what I use:
public static bool Valid(this ErrorProvider ep)
{
ep.ContainerControl.ValidateChildren();
return ep.ChildrenAreValid(ep.ContainerControl);
}
private static bool ChildrenAreValid(this ErrorProvider ep, Control control)
{
if (!string.IsNullOrWhiteSpace(ep.GetError(control))) return false;
foreach (Control c in control.Controls)
if (!(ep.ChildrenAreValid(c))) return false;
return true;
}
Typically I have a method to enable/disable a save button or something as such:
private bool VerifyIntegrity() => (btnSave.Enabled = errorProvider.Valid());
Which is run at input events.
In my case, I didn't use a static class but an instance of error counter.
public class ErrorCounter
{
private List<string> _propertiesError = new List<string>();
private static ObjectIDGenerator _IDGenerator = new ObjectIDGenerator();
public bool HasErrors
{
get => ErrorCount != 0;
}
public int ErrorCount
{
get => _propertiesError.Count;
}
/// <summary>
/// Record object validation rule state.
/// </summary>
/// <param name="sender">"this" object reference must be passed into parameter each time SetError is called</param>
/// <param name="message"></param>
/// <param name="property"></param>
/// <returns></returns>
public string SetError(object sender, string property, string message)
{
string propertyUniqueID = GetPropertyUniqueID(sender, property);
if (string.IsNullOrWhiteSpace(message))
{
if (_propertiesError.Exists(x => x == propertyUniqueID))
{
_propertiesError.Remove(propertyUniqueID);
}
}
else
{
if (!_propertiesError.Exists(x => x == propertyUniqueID))
{
_propertiesError.Add(propertyUniqueID);
}
}
return message;
}
private string GetPropertyUniqueID(object sender, string property)
{
bool dummyFirstTime;
return property + "_" + _IDGenerator.GetId(sender, out dummyFirstTime);
}
}
Usage :
Declare in your main ViewModel
public class MainViewModel : ViewModelBase, IDataErrorInfo
...
private ErrorCounter _errorCounter = new ErrorCounter();
...
// Entry validation rules
public string Error => string.Empty;
public string this[string columnName]
{
get
{
switch (columnName)
{
case nameof(myProperty_1):
if (string.IsNullOrWhiteSpace(myProperty_1))
return _errorCounter.SetError(this, columnName, "Error 1");
break;
case nameof(myProperty_2):
if (string.IsNullOrWhiteSpace(myProperty_2))
return _errorCounter.SetError(this, columnName, "Error 2");
break;
default:
break;
}
return _errorCounter.SetError(this, columnName, string.Empty);
}
}
ObjectIDGenerator combined with the propertie name allows to count only once each properties.
If you need to use the same instance of _errorCounter in an object collection member of another class, pass it to the constructor of the other class.
and that's all :-)

How do I pass variables to a buttons event method?

I need to be able to pass along two objects to the method being fired when I click a button. How do I do this?
So far I've been looking at creating a changed EventArgs:
public class CompArgs : System.EventArgs
{
private object metode;
private Type typen;
public CompArgs(object m, Type t)
{
this.metode = m;
this.typen = t;
}
public object Metode()
{
return metode;
}
public Type Typen()
{
return typen;
}
}
But how would I use it? Is it possible to somehow override the click-event of the button to use a custom eventhandler, which takes CompArgs as a parameter?
private void button1_Click(object sender, EventArgs e)
{
Assembly assembly = Assembly.LoadFile(#"c:\components.dll");
int counter = 0;
foreach (Type type in assembly.GetTypes())
{
if (type.IsClass == true)
{
Button btn = new Button();
btn.Location = new Point(174 + (counter * 100),10);
btn.Size = new Size(95, 23);
btn.Name = type.Name;
btn.Text = type.Name;
btn.UseVisualStyleBackColor = true;
this.Controls.Add(btn);
object obj = Activator.CreateInstance(type);
//I need to pass on obj and type to the btn_Click
btn.Click += new eventHandler(btn_Click);
counter++;
}
}
}
And the event-method where I need it:
private void btn_Click(object sender, CompArgs ca)
{
MessageBox.Show((string)ca.Typen().InvokeMember("getMyName",
BindingFlags.Default | BindingFlags.InvokeMethod,
null,
ca.Metode(),
null));
}
Wow, you guys are making this entirely to difficult. No need for any custom classes or method overrides. In this example I just need to pass a tab index number. You can specify whatever you want, so long as your method is expecting that value type.
button.Click += (sender, EventArgs) => { buttonNext_Click(sender, EventArgs, item.NextTabIndex); };
void buttonNext_Click(object sender, EventArgs e, int index)
{
//your code
}
Cant you just set a property or member variable on the form that hosts the button and access these from the button click event?
EDIT: custom button class suggestion after feedback comment (not the same suggestion as above)
class MyButton : Button
{
private Type m_TYpe;
private object m_Object;
public object Object
{
get { return m_Object; }
set { m_Object = value; }
}
public Type TYpe
{
get { return m_TYpe; }
set { m_TYpe = value; }
}
}
Button1Click(object sender, EventArgs args)
{
MyButton mb = (sender as MyButton);
//then you can access Mb.Type
//and Mb.object
}
I'd create a new Button and override the OnClick method. Rather than passing down the EventArgs, pass a new derived class in with your additional members.
On the delegate receiving the event, cast the given EventArgs to the more derived class you're expecting to get, alternatively setup a new Event that will be triggered at the same time when the button is pressed and hook up to that instead to make things more implicit.
Example Code:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
ButtonEx b1 = new ButtonEx();
b1.OnCustomClickEvent += new ButtonEx.OnCustomClickEventHandler(b1_OnCustomClickEvent);
}
void b1_OnCustomClickEvent(object sender, ButtonEx.CustomEventArgs eventArgs)
{
string p1 = eventArgs.CustomProperty1;
string p2 = eventArgs.CustomProperty2;
}
}
public class ButtonEx : Button
{
public class CustomEventArgs : EventArgs
{
public String CustomProperty1;
public String CustomProperty2;
}
protected override void OnClick(EventArgs e)
{
base.OnClick(e);
if(OnCustomClickEvent != null)
{
OnCustomClickEvent(this, new CustomEventArgs());
}
}
public event OnCustomClickEventHandler OnCustomClickEvent;
public delegate void OnCustomClickEventHandler(object sender , CustomEventArgs eventArgs);
}
You could use the Tag property of the button. You can add a string value to this property from the designer properties window and then pick it up within the handler as so:
private void MyButton_Click(object sender, EventArgs e)
{
string tagValue = ((Button) sender).Tag;
if(tag == "blah")
{
// Do something
}
}
Not sure if this exists in Winforms but it does in WPF: There is a "tag" object on all controls which you can attach any object to. You could save the object that you want to pass and then in the event handler read it back out of the sender object.
private void Button_Click(object sender, RoutedEventArgs e)
{
var note = (sender as FrameworkElement).Tag as Note;
//Do something with note here
}
You can't use your own custom event argument class for a predefined event handler signature. At least, the custom event argument type will never be utilised by any default calls to the handler (which will only ever be of type EventArgs in the case of a button); you could, potentially, call the handler yourself, passing your custom type, however, you would need to have logic in order to cast it back from an EventArgs into that which it had been cast from.
As a possible solution (depending on your situation), consider a composite type to encapsulate the items you require, as with your event argument type, but keep the required instance as an accessible variable which can be utilised from within the event handler, or, at least, by the method/s which the even handler invokes.
For example, define your type...
public class MyType
{
public object AccessibleItem { get; set; }
}
And, in your form class...
private MyType MyTypeInstance = new MyType();
private void Button_Click(object sender, EventArgs e)
{
//here we can set the item, if needs be...
MyTypeInstance.AccessibleItem = new Anything();
//or access the item to use as required...
DoSomeMagicWithMyObject(MyTypeInstance.AccessibleItem);
}
EDIT:
Okay, looking at your current code I can only offer you this for now (it doesn't add the items to the forms control container and it uses a variable iterator within Linq (which I think is either frowned upon or just down-right wrong (?), but hey...):
private void BuildButtonToObjectDictionary()
{
int counter = 0;
var assembly = Assembly.LoadFile(#"c:\components.dll");
var buttonToObjectDictionary = (
from type in assembly.GetTypes()
where type.IsClass && !type.IsAbstract
select new
{
Button = new Button
{
Name = type.Name,
Text = type.Name,
Size = new Size(95, 25),
Location = new Point(175 + (counter * 100), 10),
UseVisualStyleBackColor = true
},
Item = Activator.CreateInstance(type),
Index = counter++
});
}
Would need to see more code to give a better answer, but you could create an event that takes your CompArgs as a parameter and is fired when the buttonEvent is captured
If you open yourForm.Designer.cs file you will see all the code auto generated by VS. Here you can alter the method called when clicking on the button (or add a second method).
this.yourButton.Location = new System.Drawing.Point(211, 51);
this.yourButton.Name = "yourButton";
this.yourButton.Size = new System.Drawing.Size(75, 23);
this.yourButton.TabIndex = 0;
this.yourButton.Text = "yourButton";
this.yourButton.UseVisualStyleBackColor = true;
this.yourButton.Click += new System.EventHandler(this.yourMethodHere(object1, object2);
Hope this helps.

My dialog return value doesn't work

I have a custom form that's returning the values to the main form but it's not seeing the variables. I don't think I'm making this very clear so I'll include the links to the examples of what I'm trying to do.
Return values from dialog box
too long to display
I know I'm probably overlooking something very easy and or obvious but here is what I have.
form1.cs:
private void addTime_Click(object sender, EventArgs e)
{
Form add = new addTime(false, new string[] { "", "" });
if (add.ShowDialog(this) == DialogResult.OK)
{
// the line not working
Label1.Text = add.Details;
// reports with:'System.Windows.Forms.Form' does not contain a
// definition for 'Details' and no extension method 'Details' accepting
// a first argument of type 'System.Windows.Forms.Form' could be found (are you
// missing a using directive or an assembly reference?)
}
}
addTime.cs:
internal class addTime : Form
{
//..
private string _details;
public string Details
{
get { return _details; }
private set { _details = value; }
}
private string _goalTime;
public string GoalTime
{
get { return _goalTime; }
private set { _goalTime = value; }
}
private void applybtn_Click(object sender, EventArgs e)
{
Details = detailslbl.Text;
GoalTime = goalTimelbl.Text;
}
}
Your 'add' variable is of type Form, not addTime and the Form type does not have a Details property.
Try this line instead:
addTime add = new addTime(false, new string[] { "", "" });
You need to set the DialogResult property of the child form
DialogResult = DialogResult.OK
in the button click .
you need to set the form's dialogResult property to OK. You haven't specified it in your code.
After the correct criteria have been met you would set it like this.
If (//condition)
{
this.DialogResult = DialogResult.OK;
This.Close();
}

Categories