Collection of Subroutines or Dynamic Reference to Sub in C# - c#

I'm working with a VSTO, that uses a whole lot of buttons on a work sheet. I had problems with the ActiveX controls going missing if I tried to just have them stored in the workbook. To fix that, I'm making all the buttons at runtime. Not supper elegant, but I have function to help me with that:
public object MakeOButton(double LL, double TT, string ButtonName, params object[] ActionArray)
{
Microsoft.Office.Tools.Excel.Controls.Button ButtonXX = new Microsoft.Office.Tools.Excel.Controls.Button();
string ActionCall = ButtonName + "_Click";
ButtonXX = this.Controls.AddButton(LL, TT, 18.75, 14.25, ButtonName);
ButtonXX.Click += Interaction.CallByName(this, ActionCall, CallType.Method, ActionArray);
return ButtonXX;
}
I then call this for all the buttons I want to make, and it drops in the right place and names them accordingly. What I'm having trouble with is setting each one of these buttons to the correct click interaction.
Each button should be handled by a different routine which has the name [ButtonName]_Click. I was hoping to not have to declare each button before hand, especially if I want to be dynamically changing which buttons are called.
To do that I need find some way to refer to the subroutine associated with the button dynamically. At the moment, the only way I can think I might have chance is using the string ButtonName_Click to refer to the subroutine. My thought was ether through a collection of all the subs that are in the worksheet, or to be able to refer to it directly.
My best idea (which is in the above code) successfully calls the sub dynamically, but doesn't set the interaction to the click, it just make's it click at creation:
ButtonXX.Click += Interaction.CallByName(this, ActionCall, CallType.Method, ActionArray);
I feel like I must be so close to the right answer. Any help would be appreciated.

The Interaction.CallByName method is actually invoking the method immediately. What you need to do is set it up so that it's called when the Click occurs. The easiest way to do that is to use an anonymous method
ButtonXX.Click += delegate {
Interaction.CallByName(this, ActionCall, CallType.Method, ActionArray);
};
This creates a method that will be called when the Click event happens. At that time the body of the method will run and execute Interaction.CallByName

Related

C# How can I pass a UI control reference to a class module "generically" and access it' s properties?

I have done a fair amount of C# programming, but it's been all very small stuff, and coming from a C background (not C++) in the embedded space I haven't fully embraced the OO approach, and I'm happy to say I'm trying to change that.
I'm rewriting some serial comm code I've used over and over into a Class Module that I can just drop into future projects and instantiate. I have everything working except one thing: a logging function where I write the com characters to a textbox and the indices from a ring buffer into labels. I can make that all work, actually, but I was hoping to "generalize" more and pass one of any number of things with a ".Text" property for the ring buffer indices (for example, a Label, a TextBox, or a ToolStripStatusLabel).
Here’s the example, say I have a form with a text box, a label, and a ToolStripStatusLabel. The GUI is on one thread and my class module is running on another one (mostly because it is dealing with the serial port, which is perhaps inconsequential to the question?)
Anyway, lets say I have a modular variable in my class (declared as “Object”?) and I want to create a method in the object to pass in a refence to any one of those three UI elements, each one of which has the “.Text” property to which I can write.
The Class module has a delegate to invoke that will allow it to write to another gui element on the form called txtLog which is visually logging the data. At the same time I want to write something to this other passed-in UI object (say I want to display the index variable from the ring buffer).
It works fine if I stick to a Label (or any one of them) and declare everything as a Label:
===================
Up at the top, the modular variable to hold the control reference:
System.Windows.Forms.Label inPtrLbl;
And then a method to pass the assignment into the class:
public void TurnOnLogging(System.Windows.Forms.TextBox location, System.Windows.Forms.Label inLbl, System.Windows.Forms.Label outLbl)
{
comLogging = true;
logBox = location;
inPtrLbl= new System.Windows.Forms.Label();
inPtrLbl = inLbl;
}
Because the class and the form are on different threads, you need to use the Invoke stuff:
private delegate void UpdateUiTextDelegate(byte text, Int32 ptr);
“Receive” which runs for the event that fires when a char is received looks like this (“esLink” is what I named my serial port inside this class) and you can see the Invoke of “WriteData” happening to write the char into the textbox’s .Text property, which also “grants the right” (I know that’s’ the wrong thing to say) to write the text into the label on the same UI thread in the “WriteData” function below it:
private void Recieve(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
byte recieved_data;
// Collecting the characters received to our 'buffer' (string).
while (esLink.BytesToRead > 0)
{
recieved_data = (byte)esLink.ReadByte();
// add it to the circular buffer
rxBuffer[nextBufferInputPoint] = recieved_data;
if (++nextBufferInputPoint >= RX_BUFFER_SIZE)
nextBufferInputPoint = 0;
// then put it in the text box for logging
logBox.Invoke(new UpdateUiTextDelegate(WriteData), recieved_data, nextBufferInputPoint);
}
}
private void WriteData(byte text, Int32 ptr)
{
// Assign the value of the recieved_data to the TextBox and label.
if (comLogging)
{
logBox.Text += (char)text;
inPtrLbl.Text = ptr.ToString();
}
}
So, all this works like a charm, really. As long as I declare the variable in the class to be the same type as what I’m passing in. But I want to pass (almost) anything with a .Text property to it so I have more flexibility in designing my GUI. I tried declaring the passed item as an Object, it gets there but the IDE complains that the object doesn’t have a .Text property. I tried declaring it as something with a .Text property and then “changing” it with a “new” but that didn’t work either. I said, ok, I’ll limit it to three types and create overloaded methods for the three types. The problem there is I could only make that work if I declared the three different types at the top and only used one (and set some kind of control variable to decide which one to use when writing to the UI control).
I’m thinking there has to be an easier way. In principle, I want to declare a generic object that I can turn into anything based on what I pass in and access its .Text property. At the very least, creating an overloaded method for each type (realistically there might be 4 or 5 different types only) would be acceptable (but not ideal) and I could live with that.
(I hope I have explained this well, sorry if not...)
Thanks,
-Vin
Honestly it's a little weird for a serial port library to have a dependency on a UI control (see separation of concerns). I'd suggest you set things up so the caller can pass a delegate instead.
Action<char> _logHandler;
public void TurnOnLogging(Action<char> logHandler)
{
comLogging = true;
_logHandler = logHandler;
}
Then, when you have data to log, call the delegate.
private void WriteData(byte text)
{
if (comLogging)
{
_logHandler((char)text);
}
}
This way the caller can decide how the contents are displayed. They can use a control that has a Text property, or a different type of control if they want to. Or maybe they might not want to use a textbox, or a winforms control at all, but maybe log it to a file.
obj.TurnOnLogging( x => TextBox1.Text += x.ToString() );
Or
obj.TurnOnLogging( x => SomeOtherControl1.Caption += x.ToString() );
Or
obj.TurnOnLogging( x => _logger.Write(x) );
You might also consider getting rid of your unusual mechanism in favor of something more idiomatic, such as a custom event.
You may define the type (of the parameter?) as "Control" (System.Windows.Forms.Control), as most UI control classes are derived from this class. Actually, the Control class has really a large number of properties, such as "Text", "Location", "Size", "Parent", etc.
See https://learn.microsoft.com/dotnet/api/system.windows.forms.control

"Click" - Delegate to an instance method cannot have null 'this'

I know there are alot of questions allready to this topic. But I can't really seem to understand this whole delegate stuff. I'm kinda at a point where I just want it to work and move on. Everytime when I look at delegates I think to myself, there has to be a way to make this much easier to understand and do, but I can't seem to find it.
I have a FlowLayoutPanel that will be filled with a bunch of Panels. each of those panels needs to have a OnClick (or Click?) method to be attached to it.
So I went ahead and wrote (inside of the creator of my personal panel class):
IntDrawForm form = FindForm() as IntDrawForm;
Click += form.PointPanelClick;
And I ended up with the error message Delegate to an instance method cannot have null 'this' when tried to create one my panels.
Yes, that will happen if FindForm() returns either null, or something that isn't an instance of IntDrawForm. There's nothing particularly specific to delegates here - you'd get a similar result if you tried this as your second line:
form.CallSomeMethod();
(In that case it would be a NullReferenceException.)
Given that the second line basically fails when form is null, you shouldn't use as here - use a cast instead. That way, the first line will fail if you don't have an IntDrawForm, and you'll get a more informative exception.
IntDrawForm form = (IntDrawForm) FindForm();
Now we can't tell why FindForm() has returned either null or a non-IntDrawForm, but that's a different matter.

prevent unassigned objects, any reason this is a bad design consideration?

In my WPF application, I have a few places that based on a given ID of a record, I call a new form to be displayed as modal to view details. It then closes and returns back to calling source as expected. All this works no problem.
To keep this simplified in coding, I put a "ShowDialog()" call at the end of the constructor of the form being displayed. This prevents the need of every place that this form being called requires something like..
var myModalForm = new MyModalForm(someIdToDisplay);
myModalForm.ShowDialog();
Simplified, I just need to create the modal form with the Id, such as
new MyModalForm(someIdToDisplay);
But through the ReSharper inspector, it comes back with "Possible unassigned object created by 'new' expression".
I know the garbage collector will get it when it's finished, but being a modal form, once it's done, I don't need to do anything else with it. So, is this bad, or ok and just ignore this type of warning consideration. Everything else works fine in the application otherwise.
To keep this simplified in coding, I put a "ShowDialog()" call at the end of the constructor of the form being displayed.
That sounds like an ugly design to me, personally. Constructors are designed to return a usable object - and ideally that's all they should do.
I would change this to a static method in MyModalForm:
public static void ShowForId(int id)
{
var form = new MyModalForm(id);
form.ShowDialog();
}
Then your calling code can just be:
MyModalForm.ShowForId(someIdToDisplay);
Now it's clear what it's trying to do: the purpose is to show a form, not just create it.

User-initiated action implementation

In Flash 10 there are methods that require that they are triggered from user-initiated actions like button click, keyboard up/down keys, etc.
Is it possible to implement this behaviour in .NET? For example, if we have this subroutine:
void SomeMethod() {
// Here some stuff
}
how to check inside it whether the method was called from a mouse click event handler?
Also we can think of this methods like the protected functions in World of Warcraft, if anyone knows what I mean.
EDIT: Looks like this behaviour is implemented in Silverlight — we can popup file dialogs (open, save) ONLY by mouse click or other user-initiated action, otherwise a SecurityException will be thrown. I want to achieve this bevaviour but in my case it’s not a file dialog but our own methods.
Why not just provide it as a parameter?
void SomeMethod(bool userInitiated) {
// Here some stuff
}
Given that you're already calling it, sometimes from an event handler and sometimes not, you already have that information.
EDIT: Another approach is to have a thread-static field which you set on entry to an event-handler and then reset on exit (in a finally block). Any code which wants to test whether they're "responding to a user action" can then test that field.
If that's not good enough, then I suspect the answer is simply "no".
EDIT: You can get at the call stack (see the StackTrace class) but that's relatively slow and can miss out stack frames due to inlining. There's also Code Access Security which may just about help you - but I doubt it.
It seems that you are writing some sort of plugin API. You want to provide a method that does something the user might not want, e.g. changes the clipboard contents, and you want to ensure that your plugins can call that method only in response to a user action.
The only way I can think of to do this is that the API needs to be continually aware of whether it is currently processing a user-initiated action or not. Presumably there will be some code in your program that calls the plugin-provided code, e.g.
if (plugin.HasHandlerForMouseClick)
plugin.HandleMouseClick();
At this point you will need to remember that this is a user-initiated action. Once the method returns, that’s the end of the user-initiated action:
if (plugin.HasHandlerForMouseClick)
{
_userInitiated = true;
try
{
plugin.HandleMouseClick();
}
finally
{
_userInitiated = false;
}
}
Then, in your “unsafe” method, e.g. the one to set the clipboard, you will have to check this flag:
public void SetClipboard(object newValue)
{
if (!_userInitiated)
return; // or throw AccessDeniedException?
// set clipboard here
}
As hinted by Jon, the field should be declared thread-static. This means that there is a separate copy of the field for each thread:
[ThreadStatic]
private static bool _userInitiated = false;

What's the Best Practice for Firing Manual OnClick Events?

I've got an XNA project that will be drawing several objects on the screen. I would like the user to be able to interact with those items. So I'm trying to build a method that checks to see which object the mouse is over, out of those which is the top most, and then fire an OnClick event for that object.
Checking for the things above is not the problem, but where to actually put that logic is most of the issue.
My initial feeling is that the checking should be handled by a master object - since it doesn't make sense for an object, who ideally knows only about itself, to determine information about the other objects. However, calling OnClick events remotely from the master object seems to be counter-intuitive as well.
What's the best practice in this situation?
Thanks,
Tyler
Don't put the logic in the event handler. Instead have the event handler call another method, passing the clicked object as argument:
// inside the OnClick event handler
ActOnObject(clickedObject);
Then you can call the same method anywhere else in the code, for any object:
ActOnObject(GetObjectUnderMouse()):
I would probably have something like an "ObjectManager", a class that would hold a collection of the objects and would handle the finding of the current object that should be clicked upon, and then call the click function on that object. Since the object itself isnt handling the click (it could but in my example technically the overall game itself, or possibly the ObjectManager is the one that catches the click) then i would just take the object that you want to click on and call something like
Object.Click(whatever parameters are logical for your situation)
in the end I think I am suggesting a very similar approach as Fredrik, however the main difference is I personally prefer the "Object" to know what should be done with the click, and thus call the function on the object itself - which might be what you would do in the function suggested above as well...
Well , for graphical objects (textures , sprites or the kind ..)
public MyObject()
{
....
public AreTheseMyCoordinates(int X, int Y);
}
Where , you get the screen coordinates of the mouse position.
Or you can make a Helper Class:
public static MouseHelper
{
public static IsObjectClicked(MyObject obj, int X , int Y)
{
....
}
}
I`d go with the static helper.

Categories