EventHandler called once without event fired
EventHandler called once without event fired
public class Observable<T>
{
public delegate void ValueChanged(T value);
event ValueChanged Changed;
public T Value { get; private set; }
public Observable(T t)
{
Value = t;
}
/*
public void Set(T value)
{
if (Equals(value, Value))
{
return;
}
Value = value;
Changed?.Invoke(Value);
}
*/
public void Subscribe(ValueChanged action)
{
Changed += action;
}
}
I am currently seeing the issue in the user permission handling of a Xamarin mobile app I am working on
ViewModel reads local storage to get latest saved permission (mobile). It creates the Obervable<Permisson>
property passing the saved value in the constructor:
var savedCamera = HistoryService.GetStatus<Permissions.Camera>()
CameraPermissionProperty = new Observable<PermissionStatus>(savedCamera); // <--
Then subscription is made
CameraPermissionProperty.Subscribe(SendPermission<Permissions.Camera>);
The SendPermission<Permissions.Camera>
method is triggered once with value set in the Observable
despite not having invoked the ValueChanged
delegate.
I can't repoduce this in a console app.
What could be the issue, could be related to threading?
I added a number to see which method was called:
// ... Observable
public delegate void ValueChanged(T value, int number); //<--
event ValueChanged Changed;
public void Subscribe(ValueChanged action)
{
Changed += action;
Number = 18;
}
public void SubscribeWithInitialUpdate(ValueChanged action)
{
Changed += action;
Number = 10;
Changed.Invoke(Value, Number);
}
10
is returned even though I am calling the Subscribe
method
Solution 1:
I could not reproduce your error in Xamarin, so I will try to provide some suggestions.
First here is the code I tested:
public class Observable<T>
{
public delegate void ValueChanged(T v, int n);
public event ValueChanged Changed;
public T Value { get; private set; }
public int Number; //this was missing in your code example
public Observable(T t)
{
Value = t;
}
public void Set(T value)
{
if (Equals(value, Value))
{
return;
}
Value = value; //breakpoint here
Changed?.Invoke(Value, Number);
}
public void Subscribe(ValueChanged action)
{
Changed += action;
Number = 22; //breakpoint here
Console.WriteLine("setting 22");
}
public void SubscribeWithInitialUpdate(ValueChanged action)
{
Changed += action;
Number = 11; //breakpoint here
Console.WriteLine("setting 11 - is never called");
Changed?.Invoke(Value, Number);
}
}
I executed the code from an arbitrary viewmodel's c-tor:
public class SomeViewModel // : INotifyPropertyChanged
{
public SomeViewModel()
{
obs = new Observable<string>("baar");
// subscribe to event with anonymous delegate event handler
obs.Subscribe((v,n) => { //breakpoint here
Console.WriteLine("value=" + v + " number=" + n); //breakpoint here
});
obs.Set("foo"); //breakpoint here
}
}
//console output:
//setting 22
//value=foo number=22
In Xamarin Forms, you can use breakpoints and Console.WriteLine in DEBUG mode with the Android Emulator. For me this was working as expected. So I suggest setting breakpoints at the relevant places, especially the method that should not be called, but somehow is ("SubscribeWithInitialUpdate"), maybe you will catch, when it is called. In my scenario the method was never executed, as expected.
For me this has nothing to do with invoking the event. Number = 10 is set in the method SubscribeWithInitialUpdate(), which is independent from the event. Even if the event would have been invoked from some other method multiple times, the breakpoint in that method should never be reached.
It may be that your SendPermission<Permissions.Camera> method is called from somewhere else, not from the event (though the 10 would still be a mystery). So I suggest to add a breakpoint in there aswell. Also to be sure, don't add a method than can be called as a parameter, try adding an anonymous lambda delegate (at least for debugging), so you can be sure, no other caller exists.
So my last suggestion is to try my code in a new empty Xamarin project and compare that to your project.
I am assuming you don't want to do MVVM here and use custom listeners? Otherwise I would suggest to simply implement INotifyPropertyChanged. Examples: Link1 Link2 Link3
I hope some of this helps.
If you are using multiple threads, I assume you know what you are doing, otherwise I don't know why or how something would ever call any arbitrary method. You could check it's thread.ManagedThreadId Link