Introduction
I'm using delegates to pass along and store styling logic for individual form Controls. For example, I have a delegate containing some Button-styling logic like this:
button.BackColor = Color.Red;
button.ForeColor = Color.White;
button.FlatStyle = FlatStyle.Flat;
Of course there are many different other type of controls, like Labels, Panels, etc. So to store all these delegates I use a Dictionary<Type, Delegate>.
Although, the delegate itself looks like this:
delegate void StyleDel<in T>(T control) where T : Control;
So in order to use the logic inside the dictionary, the Delegate must be cast to StyleDel<T> first - whatever T might be at that moment.
The situation
After all of the styling is initialised and stored, the styling must be applied (using the StyleDels). For this I made a function StyleControl(control).
This function looks at the type of the control (e.g. a Button) and finds the corresponding StyleDel from the Dictionary, which in its turn applies the (Button-)styling.
public void StyleControl<T>(T control) where T : Control
{
Delegate storedDel;
if (_dict.TryGetValue(control.GetType(), out storedDel))
{
// Cast Delegate to StyleDel
var styleDel = (StyleDel<T>) storedDel;
// Execute StyleDel
styleDel(control);
}
}
The StyleDels are added to the dictionary with the Add function below:
public bool Add<T>(StyleDel<T> styleDel) where T : Control
{
var inDict = _dict.ContainsKey(typeof(T));
if (!inDict) _dict[typeof(T)] = styleDel;
return !inDict;
}
And the StyleControl function is called by another function, which makes sure everything is styled recursively:
public void Style<T>(T parent) where T : Control
{
StyleControl(parent);
// The problem might have to do with this
foreach (Control child in parent.Controls) Style(child);
}
The problem
An InvalidCastException is thrown, saying a StyleDel<Button> cannot be converted to StyleDel<Control>. So I believe it's saying that T is seen as a Control at this point, while it's actually a Button.
How do I cast this Delegate to a StyleDel<Button> successfully?
You can achieve this by adding a level of inderection; create a lambda that calls your delegate casting the argument to the right type:
Dictionary<Type, StyleDel<Control>> _dict = ...
public bool Add<T>(StyleDel<T> styleDel) where T : Control
{
var inDict = _dict.ContainsKey(typeof(T));
if (!inDict) _dict[typeof(T)] = d => StyleDel((T)d);
return inDict;
}
At first glance this might seem to not be type safe, but in this particular case it will be because the delegate is stored in a dictionary with the argument's true type as it's key. Intended usage will therefore always ensure that the delegate is always called with a correctly typed argument and a runtime cast exception will not happen.
Related
I'm trying to make a function that returns the actual control. I know you can do this when you know the type of the object:
public static Control GetCtl(this object obj)
{
//(obj as obj.GetType()) ?
return (TextBox)obj;
}
But what if I need to get one from some other (unknown) object type, for example TextBox or RichTextBox? I will then use that on:
private void TextBox1_Click(object sender, EventArgs e)
{
sender.GetCtl().Select(0, 0);
}
Though TextBox and RichTextBox seem to have the same Select method, when I tried the above GetCtl() function, it always threw an error. How do I do this properly?
Edit:
Since you cannot access the "complete" control property as sender in all native procedures/methods (i.e _Click(), _MouseMove()) I want to make a function that can return the actual control, not sure how to explain this better, but here's how I would use it:
Private void SomeControl_KeyDown(object sender, KeyEventArgs e){
sender.getCtl(). //<-- so I can access anything here
(sender as Button).Text = "sometext" // <-- so I won't have to do this
//I wish the following would work. But you get the idea
(sender.GetType())sender.Text = "";
//or...
(sender as sender.GetType()).Text = "";
}
RichTextBox and TextBox are both derived from TextBoxBase.
To tell the compiler that you want to treat an object as a specific type, you can perform a cast operation. In this case, you need to cast sender to TextBoxBase and then you can access the Select() method:
private void TextBox1_Click(object sender, EventArgs e)
{
var textbox = sender as TextBoxBase;
if (textbox != null)
textbox.Select(0, 0);
}
This uses the as keyword to cast the sender object to TextBoxBase. Note that you can't simply cast anything to anything else. The object being cast must actually be an instance of the type you're casting to. If sender is not an instance of something derived from TextBoxBase, the cast operation will return null.
Generally winforms event handlers should only be raised in very controlled and known circumstances - you should be very confident that this event is being raised by a control derived from TextBoxBase. There's not much point in raising it from anything else.
If you really want a method to convert sender to a TextBoxBase, you could write it as:
private TextBoxBase ToTextBoxBase(object sender)
{
var textbox = sender as TextBoxBase;
if (textbox == null)
throw new Exception("The given object is not derived from TextBoxBase");
return textbox;
}
You would use it like:
ToTextBoxBase(sender).Select(0, 0);
If the sender is not a TextBoxBase, this call would throw the "The given object is not derived from TextBoxBase" exception.
The important point to understand is that you are working with a strongly typed language. If you attempt to call a method of an object which is not of a type containing that method, the program won't even compile (unless the type is dynamic, but that's another discussion).
It is possible to create a method that you can call with this syntax:
sender.ToTextBoxBase()
This is called an extension method. However, you would be extending the System.Object class for a very specific purpose, which is not recommended.
I'm trying to update a datagridview with some data calculated in a different class and thread, using a delegate. Unfortunately I'm having trouble with a variety of different errors, depending on the approach I try.
The code I am trying to execute in the form thread looks like this:
public partial class AcquireForm : Form
//
// ...
//
// update the grid with results
public delegate void delUpdateResultsGrid(int Index, Dictionary<string, double> scoreCard);
public void UpdateResultsGrid(int Index, Dictionary<string, double> scoreCard)
{
if (!this.InvokeRequired)
{
//
// Some code to sort the data from scoreCard goes here
//
DataGridViewRow myRow = dataGridViewResults.Rows[Index];
DataGridViewCell myCell = myRow.Cells[1];
myCell.Value = 1; // placeholder - the updated value goes here
}
}
else
{
this.BeginInvoke(new delUpdateResultsGrid(UpdateResultsGrid), new object[] { Index, scoreCard});
}
}
Now, I need to get this method to run from my other thread and class. I have tried:
public class myOtherClass
//
// ...
//
private void myOtherClassMethod(int myIndex)
{
// ...
AcquireForm.delUpdateResultsGrid updatedelegate = new AcquireForm.delUpdateResultsGrid(AcquireForm.UpdateResultsGrid);
updatedelegate(myIndex, myScoreCard);
}
Unfortunately this gives an "Object reference is required for the non-static field, method, or property AcquireForm.UpdateResultsGrid(int, System.Collections.Generic.Dictionary)" error. I seem to be unable to reference the UpdateResultsGrid method at all...
I have noticed that
public class myOtherClass
//
// ...
//
private void myOtherClassMethod(int myIndex)
{
// ...
AcquireForm acquireForm = new AcquireForm();
acquireForm.UpdateResultsGrid(myIndex,myScoreCard);
}
does not throw any errors when compiling, but it tries to create a new form and that is something I do not want to do. I don't want to create a new instance of AcquireForm, I want to reference the pre-existing one, if that's possible.
I have also tried making the UpdateResultsGrid method static, but this throws up problems with several things incuding the use of "this.(anything)".
I've also tried moving the majority of the UpdateResultsGrid method into myOtherClassMethod, leaving behind in the AcquireForm class just the delegate. Again, this does not work because many of the references to UI objects break (there aren't any dataGridViews in scope).
I'm starting to run out of ideas here. Unfortunately I'm rather new to C# (as you can probably tell), and I'm editing someone else's code rather than writing my own entirely from scratch. If anyone could offer some advice on this problem it'd be most appreciated.
Make sure your objects are communicating with each other: Your myOtherClass is going to have to know about the AcquireForm object - you can't just create a new one (as you've discovered). You'll need to pass the AcquireForm object into the myOtherClass object (myOtherObject.SetForm(myAcquireForm, for example) and reference it when you need to.
In case you're having issues with invoking this might be of help - how I invoke a "next" button click:
BeginInvoke(new Action(()=>button_next_Click(null,null)));
Moreover, it sounds like maybe this should not be separate classes and you should be utilising a BackgroundWorkder instead.
I just wrote some code that I don't quite comprehend how it works.
My question is about the local variables in the for loop which are then referenced when the radio button event occurs.
How does it track the different "versions" of these local variables and operate correctly? (i.e. the resulting radio buttons each fire an event with their corresponding value which is derived from an outer local variable)
public class RadioButtonPanel<T> : FlowLayoutPanel
{
public RadioButtonPanel()
{
foreach (object value in Enum.GetValues(typeof(T)))
{
string name = Enum.GetName(typeof(T), value);
var radioButton = new RadioButton { Text = name };
radioButton.CheckedChanged += (s, e) =>
{
if (radioButton.Checked && this.Selected != null)
Selected((T)Enum.Parse(typeof(T), name));
};
this.Controls.Add(radioButton);
}
}
public event SelectedEvent Selected;
public delegate void SelectedEvent(T t);
}
This is done via a Closure.
Basically you can imagine that a small class has been created for you on your behalf, that has two properties for your local variables, and a single function. When your lambda gets called, it basically news one of those up and calls the function, thus preserving the values given to it.
The C# specification actually has some really good examples of how this is done by the compiler. Specifically section 6.5.3
They're called closures, see Wikipedia: http://en.wikipedia.org/wiki/Closure_(computer_science)
Basically, they allow for the use of non-local variables within the lambdas. From what I can remember, these variables are compiled externally from your function so they can be used globally.
I have a function that takes a control as parameter, and depending on the control's type (e.g.: TextBox, ComboBox, RadioButton etc...), it executes type-specific code:
internal static void DoSomething(Control control)
{
if (control is Button)
{
// code for button
}
else if (control is CheckBox)
{
// code for CheckBox
}
else if (control is TextBox)
{
// code for TextBox
}
// etc.....
}
I am wondering if this is the best way to do.
I know of some other ways to do the same thing (e.g.: looking for the control's type using GetType(), switching on the string representation of this type), and Microsoft's code analysis tool tells me to use 'as' instead of 'is' like this (because it is better performance-wise):
internal static void DoSomething(Control control)
{
Button button = control as Button
if (button != null)
{
// code for button
}
else
{
CheckBox checkBox = control as CheckBox;
if (checkBox != null)
{
// code for CheckBox
}
else
{
TextBox textBox = control as TextBox;
if (textBox != null)
{
// code for TextBox
}
// etc.....
}
}
}
but I find this last solution rather wordy and not very practical to read.
I'd like to be able to switch on the control's type directly, but am unable do to so without resorting to use the string representation (which I don't like at all) as a switch's case statement cannot contain a variable.
So what really is the best way to do performance-wise? and what is, in you opinion, the best way to do? (not necessarily performance-wise, but "code-readability-wise" for instance)
Edit: as a lot is going on on the subject of "why do I use one common function and not many type-specific methods", here is some more info:
I get a control variable from an other part of the app I'm working on (type = Control) and I have do "do something" with this variable, depending on its type.
so basically, I have the choice between 2 options: either I use one common function and check the control's type in the function's body so that I execute the right portion of the code at some point (options I have chosen for now, but this could change), or I check for the control's type BEFORE calling a type-specific method.
either way, I have to switch on the control's type at some point, and THIS is the subject of my question (regardless of what I do with it, if I may say so).
I would use Dictionary for it and (also separate methods for each handler):
private static readonly Dictionary<Type, Action<Control>> _handlers
= new Dictionary<Type, Action<Control>>();
// Handle.. methods
private static void HandleButton(Button button) { ... }
private static void HandleListbox(Listbox listbox) { ... }
private static void RegisterHandler<T>(Action<T> handler)
where T: Control
{
_handlers.Add(typeof(T), o => handler((T)o));
}
// invoke this method in static constructor
private static void InitializeHandlers()
{
RegisterHandler<Button>(HandleButton);
RegisterHandler<Listbox>(HandleListbox);
}
// finally usage:
internal static void DoSomething(Control control)
{
var handler = _handlers[control.GetType()];
handler(control);
}
The benefit for this approach is some maintainability improvement:
1. You will know that you haven't registered several handlers for same parameter type (dictionary will throw an exception)
2. You will have all your handler registrations separately which will allow you easily finding out which method handles particular parameter type.
3. Since all handler locating logic is not repeated at all it is pretty easy to modify it in order to handle inhereting types for example (my code doesn't do this but your code did)
Well, you don't need to nest on the second one use else if.
Second why are you putting all of this into one method? It would be better since at the time that you are calling this you should know what the type of the control that it is calling this method is and from there just Do the DoSomething for that control type instead of all of this conditional checking.
I think you're fine using the "is" operator here. It's more readable, and you don't really have any useful alternate path in the case that the control isn't what you were looking for anyway. I don't believe the timing differences will be that critical in this case.
You could swap out the "else if"s for a series of plain "if"s by returning from each individual if block, but that's a personal style choice.
It would be better to refactor the generic (control-agnostic) functionality into a separate function, and have the control-specific functionality in control-specific functions.
You can then call the generic function from the control-specific function where appropriate.
This is the kind of solution I would opt for:
internal class MyClass
{
private const string ButtonTypeAsString = "Button";
private const string CheckBoxTypeAsString = "CheckBox";
private const string TextBoxTypeAsString = "TextBox";
private static string GetTypeAsString(Control control)
{
string result = String.empty;
if (result.Length == 0 && (control as Button) != null)
{
result = MyClass.ButtonTypeAsString;
}
if (result.Length == 0 && (control as CheckBox) != null)
{
result = MyClass.CheckBoxTypeAsString;
}
if (result.Length == 0 && (control as TextBox) != null)
{
result = MyClass.TextBoxTypeAsString;
}
if (result.Length == 0)
{
throw new InvalidOperationException("Control type is not handled by this method.");
}
return result;
}
internal static void DoSomething(Control control)
{
string controlTypeAsString = MyClass.GetTypeAsString(control);
switch (controlTypeAsString)
{
case MyClass.ButtonTypeAsString:
// Button stuff
break;
case MyClass.CheckBoxTypeAsString:
// Checkbox stuff
break;
case MyClass.TextBoxTypeAsString:
// TextBox stuff
break;
default:
throw new InvalidOperationException("Unexpected Control type");
}
}
}
... but I'm sure some would find this overkill. Personally, I like the readability of the switch statement and try to use it whenever possible. Also, avoiding switching on "Magic Strings." Use const strings when possible.
If you don't mind me asking, what is it exactly you're trying to do? There may be a better solution that doesn't involve having to infer a Control's type.
if I have a delegate like so:
Delegate void Render();
Render ToRender;
And use it here:
ToRender += FunctionRender;
ToRender += SomeOtherRender;
How can I make it so I can invoke each function seperately? Something like this:
foreach(Render render in ToRender)
{
BeginRender();
render();
EndRender();
}
You can fetch each one separately using Delegate.GetInvocationList().
foreach (Render render in ToRender.GetInvocationList())
{
...
}
Note that GetInvocationList() just returns a Delegate[], but foreach has an implicit cast on each item, which is what makes the above loop work.
Oh, and you should check whether ToRender is null or not first, of course - otherwise you'll get a NullReferenceException. You could actually write a generic extension method to make this nicer, but you'd need a constraint on the delegate type which isn't allowed in C# :(
If you don't care about the lack of constraints, you could fake it:
public static IEnumerable<T> GetIndividualDelegates<T>(this T multiDelegate)
where T : class
{
if (multiDelegate == null)
{
yield break;
}
Delegate d = (Delegate)(object) multiDelegate;
foreach (Delegate item in d.GetInvocationList())
{
yield return (T)(object) item;
}
}
(It's awkward because of the restrictions on generic conversions.)
That way you could write:
foreach (Render render in ToRender.GetIndividualDelegates())
{
...
}
without worrying about whether ToRender was null or not.
foreach (Render render in ToRender.GetInvocationList())
Ideal Way:
Render temp = ToRender;
if (temp != null)
{
foreach (Render render in temp.GetInvocationList())
{
BeginRender();
render();
EndRender();
}
}
ToRender.GetInvocationList returns an array of all delegates contained on the "list".
thats not how delegates and events work. all methods will automatically be invoked by the framework. event handlers should be able to be executed completely independent of any other handlers. if you need to control the flow more tightly, you should think about redesigning your approach.
perhaps 3 events/delegates - similar to the way asp.net does it. PreRender, Render and PostRender. im not sure what you are doing, but this sounds like overkill to me. just thought i would throw it out.