Cannot implement a multiple generic parameter based method with constraints? - c#

I have the following Interface Declaration:
public interface IBasePresenter
{
void Run();
void ShowDialog<T, M>(T t, M m ) where T : UserControl where M : Form, ISomeInterface<SomeType>;
}
The ShowDialog() is basically a method that will show a modal dialog box to the user. Where 'T' is the parent Form and M is the unique dialog to show. M of which there are multiple different types! Hence the reason to choose a generic method!
A couple of ways I think this method could be used:
Presenter.ShowDialog(this, typeof(Form1)); // FigA
Or
Presenter.ShowDialog(this, new Form1()); // FigB
Based upon Fig A or B, what exactly will a sample ShowDialog() method implementation look like?
My questions stems from trying to figure how the generic parameter 'M' is instantiated inside of a ShowDialog() method implementation.

At a guess:
m.Controls.Add(t);
m.ShowDialog();
However, frankly I'm not sure this utility method adds much useful, and it could just as well be non-generic (void ShowDialog(Control t, Form m)). It could perhaps be more useful if using the : new() constraint, which would also avoid the risk of using the same control instance on multiple forms (illegal). But as I say: frankly I wouldn't bother with this method until it had demonstrated some non-trivial usefulness. And if I did keep it, I'd rename the parameters to be more illuminating; none of M, m, T, t tell me what they mean.

You cannot use the Fig A way because typeof(Form1) is a System.Type, not a Form; the code will not compile unless there is an overload that takes a second parameter of type System.Type.
how the generic parameter 'M' is instantiated inside of a ShowDialog() method implementation?
It is not "instantiated" it is "inferred:. You provided the instance already; the compiler infers the type from the invocation.

You could change the generic method signature as follows:
public void ShowDialog<T>() where T : Form, new() {
using(var dialog = new T()){
dialog.ShowDialog();
}
}
and then the call:
ShowDialog<MyCoolDialog>();
would result in the mtheod creating (not inferring this time ;)) a new instance of the form and showing it in a modal way.

Below is an slightly updated version of the interface method:
void ShowDialog<TParentForm, TDialogForm, TModel, TEntity>(TParentForm t, TDialogForm m, Action callback)
where TParentForm : UserControl
where TModel : class, IModel<TEntity>, new()
where TDialogForm : Form, IEditableItem<TEntity>, new();
I made some assumptions on the previous version so during my testing and refinement phase the method signature has changed. It's still more or a less a en educational exercise for me so I still wanted to know how to pull it off rather than simple chose the easy way out.
A sample implementation of the method:
public void ShowDialog<TParentForm, TDialogForm, TModel, TEntity>(TParentForm t, TDialogForm m, Action callback)
where TParentForm : UserControl
where TModel : class, IModel<TEntity>, new()
where TDialogForm : Form, IEditableItem<TEntity>, new()
{
using (var dialogToShow = new TDialogForm())
{
dialogToShow.StartPosition = FormStartPosition.CenterScreen;
dialogToShow.FormBorderStyle = FormBorderStyle.FixedSingle;
dialogToShow.Model = new TModel();
// 2. show the new user control/form to the user.
var result = dialogToShow.ShowDialog(t);
// 3. handle the dialog result returned and update the UI appropriately.
if (result == DialogResult.OK)
{
// print status label.
callback.Invoke();
}
}
}
I am not entirely sure why the 'TDialogForm m' parameter is still in there as it does not seem to be used anywhere.
How to use the method:
private void BtnAddNewServiceClick(object sender, EventArgs e)
{
Presenter.ShowDialog<ServerRolesControl, AddNewServiceForm, ServiceModel, Role>(this, new AddNewServiceForm(), SetAddedRolesLabel);
}
private void BtnViewAllServicesClick(object sender, EventArgs e)
{
Presenter.ShowDialog<ServerRolesControl, ViewRolesForm, ServiceModel, Role>(this, new ViewRolesForm(), SetDeletedRolesLabel);
}
I should update the interface method but it was so much pain getting it to work I would rather leave it alone now =).

Related

C# Delegate with Generic T as Button

I'm losing my mind trying to figure this out. I am developing a large windows application that requires all the controls in the main form to have their values updated in near real time. I moved the processing of this continuous method to its own thread, which in general is fine, but I knew that it required me to create a Delegate for setting my controls that were created in a different thread. However, I have a series of buttons that need to have the same various properties set for each one, but with different values. I was thinking I could setup a Delegate with a Generic type being Button, so I could simply pass in the proper button control when it is time to update its properties. But I am missing something, and it doesn't work:
//If this is wrong, please let me know
private delegate void SafeButtonText<T>(string value) where T : Button;
private void SetButtonTextSafe<T>(string value) where T : Button
{
//Using the generic Button passed in, set its values
if (T.InvokeRequired) //This doesn't compile
{
var d = new SafeButtonText<T>(SetButtonTextSafe<T>);
T.Invoke(d, new object[] { value }); //This doesn't compile
}
else
T.Text = value; //This doesn't compile
}
I thought I could use it like this (which doesn't seem possible)
SetButtonTextSafe<qualityButton>(values[0]);
If this is possible, or if there is a much better way of doing this, please feel free to tell me in detail. (if I can using this on a Button, I'd create another delegate for other control types as well)
Thanks in advance.
A type is just that...a type. You can't invoke an instance of it because you have no instance. It is merely reflected metadata.
You need to pass an instance of your button to your method.
private delegate void SafeButtonText<T>(T button, string value) where T : Button;
private void SetButtonTextSafe<T>(T button, string value) where T : Button
{
//Using the generic Button passed in, set its values
if (button.InvokeRequired) //This now compiles
{
var d = new SafeButtonText<T>(SetButtonTextSafe<T>);
button.Invoke(d, new object[] { value }); //This now compiles
}
else
button.Text = value; //This now compiles
}

Passing Actions into generic functions

I'm trying to wrap my head around different concepts in Csharp by trying different things. A create a generic function that takes in an action. The action has one input parameter and returns void. I create a simple action that is linked to a lambda function (returns void has one parameter x). I am able to run the action but when I pass the function to my generic function I am not sure how to add the input parameter. act("Some Int") doesn't work.
How do I pass in a value to an action?
public MainWindow()
{
InitializeComponent();
Action<int> myAction = (x) => Console.WriteLine(x);
myAction(13);
test(myAction);
}
private static void test<T>(Action<T> act)
{
act(); // How do i pass in an int Here?
}
Simply calling act("Some Int") as you have just required the Action act to be a genric function. Therefore you cannot specifically invoke it with one fixed variable type. You can solve your problem by modifying the test-method
private static void test<T>(Action<T> act, T value)
{
act(value); // How do i pass in an int Here?
}
...
test(myAction,integerValue);
Now you can call the Action with a given intvalue.
I can see what you are trying to do, and just wanted to throw this pattern up, since we often do this when we have to use closures and the parameters could be wildly different.
In those cases, rather than define an Action<T> which kind of ties you down from being able to use closures, you would just simply define your method as Action. So test would look like this:
private static void test(Action act)
{
act(); // yup, that's all there is to it!
}
So how would you pass in the parameter(s)? Simple: use closures. Like this:
public MainWindow()
{
InitializeComponent();
var x = 13; // this defined outside now...
Action myAction = () => Console.WriteLine(x); // you're basically using the closure here.
myAction();
test(myAction);
}
We often use this sort of approach when we're context switching (aka thread jumping), and need the thread continuation to pick up one or more variable values at the point it executes. That's just one example, there's quite a few other valid use cases as well.
Your experimental example, if I'm reading it correctly, could also qualify as a situation where closures could be a good fit.

How to fire TouchUpInside of UIButton Programmatically in xamarin ios C#

I am trying to fire an touchupinside event of Uibutton programmatically. How can i do that ? The code I tried uses PerformSelector but i get this error
"Error MT4117: The registrar found a signature mismatch in the method
'VCPAckage2.ActivityViewController.TouchUpInsideEvent' - the
selector 'TouchUpInsideEvent:' indicates the method takes 1
parameters, while the managed method has 2 parameters. "
I want to achieve something like
UIButton.FireEvent("TouchUpInsideEvent") - this will fire the TouchUpInsideEvent
UIButton.PerformSelector(new MonoTouch.ObjCRuntime.Selector ("TouchUpInsideEvent:"), null, 2000f);
Here's the code
private void LoadFn()
{
UIButton btnSubmit = new UIButton(new RectangleF(0,0,View.Frame.Width,40));
btnSubmit.TouchUpInside+=TouchUpInsideEvent;
}
[Export("TouchUpInsideEvent:")]
private void TouchUpInsideEvent(object sender,EventArgs e){
float yy = AppConstants.ZeroVal;
if (FeedbackSubmittedReturnFlag == true) {
yy = ChildScrollView2.Subviews[1].Frame.Height+ChildScrollView2.Subviews[1].Frame.Y;
}
this.ParentScrollView.SetContentOffset (new PointF (View.Frame.Width, yy), false);
}
The following code snippet would be suffice
btnObj.SendActionForControlEvents(UIControlEvent.TouchUpInside);
It has to be called from Main thread
There's a few different things above.
First, the MT4117 is correct. It happens because your [Export] attribute specify a selector to a method that has only one parameter (i.e. it has only one :) while the managed method has two parameters (which is the default for .NET events). The registrar will spot such conditions and report errors.
Second, the PerformSelector methods are bindings over performSelector:... selectors (most are defined on the NSObject protocol, not class). As such they have the same limitations (e.g. the number of arguments they can handle).
Third, there are several ways you could call your own code. An easy one would be, like #jonathanpeppers suggested, to directly call your managed method when needed.
Another one would be to adjust your code to match both 1 and 2 requirements, e.g.
// assign one (two parameters) method as a .NET event
btnSubmit.TouchUpInside += TouchUpInsideEvent;
...
// call another (one parameter) method like a selector
any_nsobject.PerformSelector (new Selector ("TouchUpInsideEvent:"), sender as NSObject, 0f);
...
// have the 2 parameters method call the(1 parameter) export'ed method
private void TouchUpInsideEvent (object sender, EventArgs e)
{
TouchUpInsideEvent (sender as NSObject);
}
[Export ("TouchUpInsideEvent:")]
private void TouchUpInsideEvent (NSObject sender)
{
Console.WriteLine ("yay!");
}

storing/passing delegates as variables

I am fairly new to C# and was working on a a way to implement a dynamic GUI which uses the serial communication. I originally come from C, so the concept of function pointer is familiar.
Basically I want to invoke a answerFunction() function when the serial command has been processed.
In Theory:
I have a Class lbl_txtBox_Pair which is dynamically created on runtime.
I have a Class comObject which communicates with the serial Port.
I have a third class comPacket which holds all information regarding one serial command.
in an Object of Class lbl_txtBox_Pair I instantiate a Packet and tell it which function should be called when the serial command is finished.
I give the packet Object to the comObject Instance.
after being processed the comObject wants to signal the original sender of the packet by calling the delegate which is stored in the Packet Object.
For some reason I can't get it to work. It tells me that the Attribute of Packet is not callable. Am I doing something terribly wrong?
Here is the Code:
first the code in Class "lbl_txtBox_Pair". I create the comPacket here and give it to the comObject.
public delegate void answerHandler( comPacket packet);
public void txb_value_KeyPress(object sender, KeyPressEventArgs e)
{
if (e.KeyChar == (char)Keys.Return)
{
answerHandler answerMethod = new answerHandler(this.processAnswer);
comPacket question = new comPacket(this.command, answerMethod, 1);
comObject.addPacket(question);
}
}
The constructor of comPacket. Here the delegate gets stored to be called later.
public Delegate answerFunction;
public comPacket(string cmd, Delegate func, int prio)
{
this.cmd = cmd;
answerFunction = func;
this.prio = prio;
}
In the comObject the Packets get processed. When finished I want to call the function stored in the Packet. The comObject runs in a different Thread by the way.
if (this.isEndtocken(inputline))
{
listen = false;
packet.answerFunction(packet);
}
And here it is were it breaks. packet.answerFunction(packet); wont execute and says it can't be called as Method.
Can anybody see where it goes wrong? I think it seems like the delegate looses the information that it is a delegate or something.
Or do I have to completely restructure the code to use other types of callback / Event Methods?
Change your comPacket to take a strongly typed delegate:
public answerHandler answerFunction;
public comPacket(string cmd, answerHandler func, int prio)
{
this.cmd = cmd;
answerFunction = func;
this.prio = prio;
}
If you still want to keep the delegate reference weakly typed, you can leverage DynamicInvoke instead: http://msdn.microsoft.com/en-us/library/system.delegate.dynamicinvoke.aspx
EDIT: Another option if you want to maintain strongly typed delegates yet have different usages is to leverage generics. Your delegate can be housed in a generic class and tie its signature against that generic type.
I can't leave a comment so I have to post this as an answer instead.
Delegates (and events and stuff) can usually only be "invoked" by the object that contains them.
So if you have
class MyClass {
public event Action someEvent;
// you can also replace Action with the name of your delegate type
}
and you try to do
MyClass x = new MyClass();
x.someEvent.Invoke();
Then that's an error. If you want other objects to be able to invoke the event, you'll have do add a method to MyClass like this:
public void InvokeMyEvent() {
someEvent.Invoke();
}
(I forget whether you still have to do this for static events)

Generic Types: There is no implicit reference conversion from ToolStripStatusLabel to Control

I'm wanting to update the UI from a SerialPort DataReceived event handler. I discovered a problem because the event handler was implicitly running in a different thread to the form, so rather than simply update the UI...
myLabel.Text = "Some text";
...I had to take the following approach:
InvokeControlAction<Label>(myLabel, lbl=> lbl.Text= "Some text");
...
public static void InvokeControlAction<t>(t cont, Action<t> action) where t : Control
{
if (cont.InvokeRequired)
{
cont.Invoke(new Action<t, Action<t>>(InvokeControlAction),
new object[] { cont, action });
}
else
{
action(cont);
}
}
So far so good... However, now I want to update a ToolStripStatusLabel - using the same approach yields a 'there is no implicit reference conversion between ToolStripStatusLabel and Forms.Control' error.
From what I have read, the problems stems from the fact that you can't Invoke a ToolStripStatusLabel.
So how best do I handle this?
Note: delegates, etc are at the threshold of my current ability, so an explanation alongside a solution would be appreciated.
UPDATE 1: Just to clarify, I tried to create ToolStripStatusLabel equivalent of InvokeControlAction, but this won't work because it doesn't have an invoke method.
RESULT: After revisiting my solution, I've implemented it as an Extension Method as Jimmy originally suggested.
I created an static ExtensionMethod class (in it's own 'ExtensionMethods' namespace), added in the InvokeOnToolStripItem method, add a 'using ExtensionMethods;' directive in my original class and called the methods as follows:
tsStatusValue.InvokeOnToolStripItem(ts => ts.Text = "ALARM signal received");
ToolStripStatusLabel does not inherit from Control, and that's why your generic constraint fails for the exact reason you posted.
What is more, ToolStripStatusLabel (or any ToolStripItem in fact) doesn't have Invoke method. Luckily, containing ToolStrip has, which can be easily accessed using GetCurrentParent method.
Here's extension method that works on any ToolStripItem:
public static void InvokeOnToolStripItem<T>(this T item, Action<T> action)
where T : ToolStripItem
{
ToolStrip parent = item.GetCurrentParent();
if (parent.InvokeRequired)
{
parent.Invoke((Delegate)action, new object[] { item });
}
else
{
action(item);
}
}
You can use it by simply calling:
myToolStripLabel.InvokeOnToolStripItem(label => label.Text = "Updated!");
myToolStripProgressBar.InvokeOnToolStripItem(bar => bar.PerformStep());
To explain the error message, you have writen
where t : Control
but ToolStripStatusLabel does not inherit from Control.
Not sure if that helps you at all and don't really have a solution yet :(

Categories