Suppose I have a simple EventArgs subclass:
class MyArgs : EventArgs { }
Consider I have two classes with events:
class MyData {
public event EventHandler<MyArgs> Method;
}
class MyObject {
public event EventHandler Method;
}
And a simple program that uses them:
static void Main(string[] args){
MyObject o = new MyObject();
o.Method += MyMethod;
MyData data = new MyData();
data.Method += MyMethod;
}
static void MyMethod(object sender, EventArgs e) { }
Thanks to Contravariance, MyMethod counts as both an EventHandler and an EventHandler<MyArgs>. However, if I change MyObject's event handler into a property that forwards the method to a MyData:
class MyObject {
MyData data = new MyData();
public event EventHandler Method {
add { data.Method += value; }
remove { data.Method += value; }
}
}
The event property is unable to forward the EventHandler to the EventHandler. This seems strange to me because it seems to fall into the contravariance category - a handler with a weaker signature (base classes) should be able to accept arguments with a stronger signature (subclasses).
Why won't C# let me do this? Is there a way to tunnel a generic EventHandler down through an event property into an EventHandler? Is there some sort of legal cast that can be performed on the delegates?
There is no implicit conversion from EventHandler to EventHandler<T>. However since both are of compatible types with each other, you could just pass the EventHandler to the constructor to "convert" it.
public event EventHandler Method
{
add { data.Method += new EventHandler<MyArgs>(value); }
remove { data.Method -= new EventHandler<MyArgs>(value); }
}
Related
I have the following sample TFS CheckIn Policy:
[Serializable()]
public class AuditControlsPolicy : PolicyBase
{
public List<string> list;
public AuditControlsPolicy() : base()
{
list = new List<string>() { "a", "b", "c" };
System.Windows.Forms.MessageBox.Show("in constructor");
}
public override string Description
{
get { return "my description"; }
}
public override string Type
{
get { return "my policy"; }
}
public override string TypeDescription
{
get { return "description"; }
}
public override string InstallationInstructions
{
get { return "install instructions"; }
}
public override Microsoft.TeamFoundation.VersionControl.Client.PolicyFailure[] Evaluate()
{
List<PolicyFailure> policyFailures = new List<PolicyFailure>();
if (list == null)
System.Windows.Forms.MessageBox.Show("list is null");
else
System.Windows.Forms.MessageBox.Show(String.Join(",", list.ToArray()));
return policyFailures.ToArray();
}
public override void DisplayHelp(PolicyFailure failure)
{
MessageBox.Show("No help available at this time");
}
public override void Activate(PolicyFailure failure)
{
MessageBox.Show(failure.Message);
}
protected override void OnPolicyStateChanged(PolicyFailure[] failures)
{
base.OnPolicyStateChanged(failures);
}
public override void Initialize(IPendingCheckin pendingCheckin)
{
base.Initialize(pendingCheckin);
pendingCheckin.PendingChanges.CheckedPendingChangesChanged += PendingCheckinCheckedPendingChangesChanged;
}
public override void Dispose()
{
PendingCheckin.PendingChanges.CheckedPendingChangesChanged -= PendingCheckinCheckedPendingChangesChanged;
base.Dispose();
}
private void PendingCheckinCheckedPendingChangesChanged(object sender, EventArgs e)
{
OnPolicyStateChanged(Evaluate());
}
public override bool Edit(IPolicyEditArgs policyEditArgs)
{
return true;
}
}
It is properly registered and "works" -- however, it appears that the instance member field list is not initialized when the Evaluate method is called.
When I toggle to Pending Changes view in Visual Studio with at least one pending change I get the message box of "In Constructor" multiple times. This is followed by "list is null", even though I clearly initialize the field in my instance constructor. If I declare my list as a static and initialize it in the instance constructor, then it display my list of values.
It almost seems as if Visual Studio is invoking the Evaluate method on a static object, even though it is not declared as such.
Is the Evaluate method being invoked as a static? Am I missing something about how I should be constructing my Policy Object?
Evaluate method shouldn't be invoked on a static class and the Visual Studio also will not invoke the Evaluate method on a static object. There must be something wrong in your code, try to move list = new List<string>() { "a", "b", "c" }; to public override void Initialize(IPendingCheckin pendingCheckin) and try again.
According to your description and code, guess the Evaluate() method had being invoked on the parent class PolicyBase before AuditControlsPolicy, that's why you got the list is nul.
When developing for all three mobile platforms using MvvmLight with Xamarin forms, what is the recommended way of binding an event in the view to a command in the viewmodel for events that do not support the command pattern? Is it possible to use EventToCommand?
Thanks!
Not sure about MVVMLight but what you could do is define the Events in an Interface (IPageLifeCycleEvents) which are implemented in the relevant ViewModel. Within the View you would then set the BindingContext as an instance of type IPageLifeCycleEvents and pass the events from the View to the ViewModel through the interface. E.G.
public interface IPageLifeCycleEvents
{
void OnAppearing ();
void OnDisappearing();
void OnLayoutChanged();
}
public class SampleView : ContentPage
{
public BaseView () {
var lifecycleHandler = (IPageLifeCycleEvents) this.BindingContext;
base.Appearing += (object sender, EventArgs e) => {
lifecycleHandler.OnAppearing();
};
base.Disappearing += (object sender, EventArgs e) => {
lifecycleHandler.OnDisappearing ();
};
base.LayoutChanged += (object sender, EventArgs e) => {
lifecycleHandler.OnLayoutChanged();
};
}
}
public class SampleViewModel : IPageLifeCycleEvents
{
#region IPageLifeCycleEvents Methods
public void OnAppearing ()
{
//Do something here
}
public void OnDisappearing ()
{
//Do something here
}
public void OnLayoutChanged ()
{
//Do something here
}
#endregion
}
in my actual implementations I use a slightly different setup because of the utilisation of IOC and Base models.
Good luck
I'm spinning my wheels with what I think is a basic concept: receiving a 'data changed' notification from a GlobalData class.
I have Form1 with a single control on it (textBox1) which, when the number is changed, I set a corresponding property on 'global variable' class.
Now, when the property is set, I want to raise an event that Form2 can subscribe to and updates its indicator accordingly.
The basic structure is:
Form1 has textBox1 and button1, Form2 has textBox2, and GlobalClass has testValue.
Form1 stuff....
public partial class Form1 : Form {
public GlobalClass myGlobals= new GlobalClass();
private void button1_Click(object sender, EventArgs e) {
myGlobals.testValue= Convert.ToInt32(textBox1.Text);
}
}
GlobalClass stuff...
public class GlobalClass{
private int mynum;
public int testValue{
get{
return mynum;}
set{
mynum = value;
// NEED TO RAISE MY 'HEY YOUR testVALUE HAS CHANGED!!' EVENT HERE?
}
}
}
Form2 stuff
public partial class Form2 : Form {
public GlobalClass myGlobals2;
//NEED TO RECEIVE NOTIFICATION THAT myGlobals2.testValue HAS BEEN UPDATED?
//THEN I CAN DO textBox2.Text = myGlobals2.testValue.ToString();
}
Thank you in advance for the help.
You need to read on .NET events and delegates.
You have to make the GlobalClass instance a true global variable:
public class GlobalClass
{
public static GlobalClass Instance = new GlobalClass();
public event EventHandler ValueChanged;
private int mynum;
public int testValue{
get{
return mynum;}
set{
if (mynum != value)
{
mynum = value;
// NEED TO RAISE MY 'HEY YOUR testVALUE HAS CHANGED!!' EVENT HERE?
if (ValueChanged != null)
{
EventArgs e = new EventArgs();
ValueChanged(this, e);
}
}
}
}
}
and address it like this:
GlobalClass.Instance.testValue = 3;
in Form2:
protected void OnValueChanged(Object sender, EventArgs e)
{
<your code>
}
In Form2 constructor:
GlobalClass.Instance.ValueChanged += new System.EventHandler(this.OnValueChanged);
class A
{
public event EventHanler MyEvent;
protected virtual void OnMyEvent(EventArgs e)
{
if (MyEvent!=null)
MyEvent(this, e);
}
public void DoEvent()
{
//................
MyEvent(this, new EventArgs());
}
}
class B: A
{
private A a = new A();
public B ()
{
a.MyEvent += MyMethod;
}
public void MyMethod(object sender, EventArgs e)
{
Console.WriteLine("Event handler");
}
}
class C : A
{
private A a = new A();
protected override void OnMyEvent(EventArgs e)
{
base.OnMyEvent(e);
Console.WriteLine("OnMyEvent overriding");
}
}
I subscribe to the event and override the method OnMyEvent() in the classes B and C. Pay attension calling the method base.OnMyEvent(e) is in the beginning of the method C.OnMyEvent(...).
As far as I'm concerned there are no differences here. In other words if I call base.OnMyEvent(e) in the beginning of the overriding method, it would mean the same as I just subscribe to the event?
Are there actually no differences?
There is a difference:
Invoking C.OnMyEvent() raises MyEvent. (Conversely, raising MyEvent will not invoke C.OnMyEvent().)
B.MyMethod handles MyEvent. Thus, raising MyEvent will invoke B.MyMethod. (Conversely, invoking B.MyMethod will not raise MyEvent.)
say I have three classes: class1, control1 and form1; form1 instantiate contorl. and control1 instantiate class1, the later produces some event that I need to 'bypass' to form1, to achieve that I have made an intermediate function as shown below:
public delegate void TestHandler(String^ str);
public ref Class class1
{
event TestHandler^ TestHappen;
void someFunction()
{
TestHappen("test string");
}
};
public ref Class control1
{
event TestHandler^ TestHappen;
class1^ class1Obj;
control1()
{
class1Obj= gcnew class1();
class1Obj->TestHappen+= gcnew TestHandler(this,&control1::onTest);
}
void onTest(String^ str)
{
TestHappen(str);
}
};
public ref Class form1
{
control1^ control1Obj;
form1()
{
control1Obj= gcenw control1();
control1Obj->TestHappen+= gcnew TestHandler(this,&form1::onTest);
}
void onTest(String^ str)
{
//do something with the string...
}
};
I don't want to use class1 in form1, are there a way to remove the intermediate onTest() function.
Yes, if you use a custom event, you can write its add-handler and remove-handler functions so that they add and remove the delegate directly from another object's event.
For example:
public ref class control1 // in "ref class", class is lowercase!
{
class1 class1Obj; // stack-semantics syntax, locks class1Obj lifetime to be same as the containing control1 instance
public:
event TestHandler^ TestHappen {
void add(TestHandler^ handler) { class1Obj.TestHappen += handler; }
void remove(TestHandler^ handler) { class1Obj.TestHappen -= handler; }
}
};