WPF MVVM Event Subscription - c#

For simplicity have a modelview (MyModelView) that contains a Scheduler property which has a timer inside of it (Scheduler). The timer is used to check against the current time, and if they match, signal an 'event match' that the modelview should somehow be notified of. Now MyModelView knows about the scheduler, but not the other way around.
public Scheduler()
{
ScheduleCollection = new ObservableCollection<Schedule>();
TimeSpan ts = new TimeSpan(30000);
_timer = new DispatcherTimer();
_timer.Interval = ts;
_timer.Tick += new EventHandler(EventTimerCheck_Tick);
_timer.Start();
}
private void EventTimerCheck_Tick(object sender, EventArgs e)
{
eventsToLaunch = LocateCurrentEvents();
if (eventsToLaunch.Count > 0) { RaiseHasEvents(); }
}
public void RaiseHasEvents()
{
EventHandler handler = this.HasEvents;
if (handler != null)
{
var e = new EventArgs();
handler(this, e);
}
}
public event EventHandler HasEvents;
public MyModelView()
{
Scheduler scheduler = new Scheduler();
HaveEvents += scheduler.HasEvents; <----*throws Error below
}
internal event EventHandler HaveEvents;
Scheduler.HasEvents' can only appear on the left hand side of += or -= (except when used from within the type)
How would I raise an event that the MyModelView can subscribe to? Or is this accomplished through RelayCommanding ?

You are trying to add additional handlers to an event exposed by your MyModelView, not handle the event (which is not valid syntax).
You should be doing something like the following to subscribe to the event:
public MyModelView() {
Scheduler scheduler = new Scheduler();
scheduler.HasEvents += OnHasEvents;
// ...
}
private void OnHasEvents(object sender, EventArgs e) {
// TODO: Do something here
}

Related

C# NullReferenceException while EventHandler

I get a NullRefernceException even though I subscribed to the event in an Start Methode.
Where I create my Event:
public EventHandler<CustomArgs> ClickEvent;
private void OnMouseDown()
{
Debug.Log("Clicked");
CustomArgs args = new CustomArgs();
args.Name = gebäude.ToString();
args.Level = Level;
args.MenuePosition = Menue;
ClickEvent?.Invoke(this, args);
}
Where I subscribe to my Event:
private void Start()
{
miene.ClickEvent += ClickEvent;
Debug.Log("Event Addedet");
}
private void ClickEvent(object sender, CustomArgs e)
{
//some useless stuff
}
Events are null when no-one has subscribed. Fortunately, modern C# makes this easy:
ClickEvent?.Invoke(this, args);
With older language versions, you need to be more verbose:
var handler = ClickEvent;
if (handler != null) handler(this, args);
They mean exactly the same thing.
As a small optimisation, you may wish to defer creating the CustomArgs object until you know someone cares, though:
ClickEvent?.Invoke(this, new CustomArgs {
Name = gebäude.ToString(),
Level = Level,
MenuePosition = Menue
});

Raising Events from another Class - workaround

I need to Raise an Event from another Class - i know that this is not possible - but I need a workaround for this.
For now Im doing the following
This is the class, which have to raise the event
internal class DataTransfer
{
public delegate void EventHandler(object sender, OnReaderCommonEventArgs e);
public event EventHandler _OnSerialNumber;
public event EventHandler _OnReaderType
Task DataHandler()
{
//Recieving-Data and Stuff
_OnSerialNumber(this, new OnReaderCommonEventArgs { SerialNumber = RFIDParser.ParseSerialNumber(data) });
_OnReaderType(this, new OnReaderCommonEventArgs { ReaderType = RFIDParser.ParseReaderType(data) });
}
}
And in the Main-Class, which will be used by the user. So the user can only subscribe to the event from this class-object:
public partial class PUR_100U
{
public delegate void EventHandler(object sender, OnReaderCommonEventArgs e);
public event EventHandler OnSerialNumber;
public event EventHandler OnReaderType;
public PUR_100U(int portnumber)
{
dataTransfer = new DataTransfer(portnumber, GetIdentifierList());
dataTransfer._OnSerialNumber += dataTransfer__OnSerialNumber;
dataTransfer._OnReaderType += dataTransfer__OnReaderType;
}
void dataTransfer__OnSerialNumber(object sender, OnReaderCommonEventArgs e)
{
if (OnSerialNumber != null) { OnSerialNumber(this, new OnReaderCommonEventArgs { SerialNumber = e.SerialNumber }); }
}
void dataTransfer__OnReaderType(object sender, OnReaderCommonEventArgs e)
{
if (OnReaderType != null) { OnReaderType(this, new OnReaderCommonEventArgs { ReaderType = e.ReaderType }); }
}
}
Example of user-usage:
rfid = new PUR_100U(20);
rfid.OnSerialNumber += rfid_OnSerialNumber;
rfid.OnReaderType += rfid_OnReaderType;
Is there a better way of doing this?
I need to Raise an Event from another Class - i know that this is not possible - but I need a workaround for this.
That is rather trivial:
class Foo
{
public event EventHandler Fooed; //note, name is not OnFoo
public void FireFooed() => Fooed?.Invoke(this, new EventArgs);
}
And now, fire the event at will:
var foo = new Foo();
foo.FireFooed();
The question is, why do you want to do this? It seems like a really bad idea. Fooed should fire only and only if the preconditions inside Foo for it to fire are met; if you need Fooed to fire, then make the preconditions happen!
Firing Fooed at will if the conditions aren't met will break all other listeners, don't do that.

Bind a textbox to a property class

I have these :
A Form with a Label on it
A class "Business"
A class "TimerHelper"
I'd like when I update the property MyTime, update the textbox too
My "Business" class look like this :
public class MyBusinessClass : INotifyPropertyChanged
{
public void MakeSound(object sender, ElapsedEventArgs e)
{
// I change MyTime here
}
public event PropertyChangedEventHandler PropertyChanged;
private int myTime;
public int MyTime
{
get { return myTime; }
set
{
myTime= value;
InvokePropertyChanged(new PropertyChangedEventArgs("MyTime"));
}
}
public void InvokePropertyChanged(PropertyChangedEventArgs e)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, e);
}
}
The "TimerHelper" look like this :
public class TimerHelper
{
private Timer _timer;
public void Run()
{
_timer = new Timer(1000);
_timer.Enabled = true;
_timer.Elapsed += new ElapsedEventHandler(MyBusinessClass.MakeSound);
}
}
In the forms, I tried this :
myTextBox.DataBindings.Add("Text", new MyBusinessClass(), "MyTime");
but I get an exception in the method "InvokePropertyChanged"
I tried this :
Invoke((MethodInvoker)delegate { myTextBox.Text = new MyBusinessClass().MyTime; });
but the textBox is never updated
I found the answer there :
A generic asynchronous INotifyPropertyChanged helper
I think your problem is you are trying to update the text box through databibding from other thread.
I mean, you're using System.Timers.Timer class which invokes Elapsed event in a threadpool.
Databinding fails updating the control because you're changing your business class in that threadpool.
You should change of thread context in the Elapsed event handler and update your business model in the main thread (where text box was created).
public class TimerHelper
{
private Timer _timer;
public void Run()
{
_timer = new Timer(1000);
_timer.Enabled = true;
_timer.Elapsed += new ElapsedEventHandler(OnTimerElapsed);
}
}
void OnTimerElapsed (object sender, ElapsedEventArgs e)
{
if (myTextBox.InvokeRequired)
{
myTextBox.Invoke(MyBusinessClass.MakeSound);
}
}
}
Although I do not see where you are setting the value of MyTime (I assume it is in MakeSound?), you are setting the value of MyTime on a thread other than the UI thread. You are probably using the System.Timers.Timer class, which does not execute the event handler on the UI thread. If you want to use System.Timers.Timer and make changes to the UI in its ElapsedEventHandler delegate, you need to make use of the SynchronizingObject property on the timer. See this MSDN article for more information: Comparing the Timer Classes

looping through a folder of images in c# / WPF

So i'm trying to loop through a folder and change the image source each 2 seconds.
I think my code is right, but I seem to be missing something since my image won't update, but I don't get an error.
The code populates my array of files so it finds the pictures, I'm just doing something wrong to set the image source.
XAML code
<Grid>
<Image x:Name="Picture" Source="{Binding ImageSource}" Width="980" Height="760" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="350,50,0,0"></Image>
<Grid>
C# code
private string[] files;
private System.Timers.Timer timer;
private int counter;
private int Imagecounter;
Uri _MainImageSource = null;
public Uri MainImageSource {
get
{
return _MainImageSource;
}
set
{
_MainImageSource = value;
}
}
public IntroScreen()
{
InitializeComponent();
this.Loaded += new RoutedEventHandler(this.MainWindow_Loaded);
}
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
setupPics();
}
private void setupPics()
{
timer = new System.Timers.Timer();
timer.Elapsed += new ElapsedEventHandler(timer_Tick);
timer.Interval = (2000);
timer.Start();
files = Directory.GetFiles("../../Resources/Taken/", "*.jpg", SearchOption.TopDirectoryOnly);
Imagecounter = files.Length;
MessageBox.Show(Imagecounter.ToString());
counter = 0;
}
private void timer_Tick(object sender, EventArgs e)
{
counter++;
_MainImageSource = new Uri(files[counter - 1], UriKind.Relative);
if (counter == Imagecounter)
{
counter = 0;
}
}
Anyone know what I'm doing wrong ?
Updated code
XAML
<Image x:Name="Picture" Source="{Binding MainImageSource}" Width="980" Height="760" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="350,50,0,0"></Image>
C#
public partial class IntroScreen : UserControl, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
private string[] files;
private System.Timers.Timer timer;
private int counter;
private int Imagecounter;
Uri _MainImageSource = null;
public Uri MainImageSource
{
get
{
return _MainImageSource;
}
set
{
_MainImageSource = value;
OnPropertyChanged("MainImageSource");
}
}
public IntroScreen()
{
InitializeComponent();
this.Loaded += new RoutedEventHandler(this.MainWindow_Loaded);
}
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
setupPics();
}
private void setupPics()
{
files = Directory.GetFiles("../../Resources/Taken/", "*.jpg", SearchOption.TopDirectoryOnly);
Imagecounter = files.Length;
counter = 0;
timer = new System.Timers.Timer();
timer.Elapsed += new ElapsedEventHandler(timer_Tick);
timer.Interval = (2000);
timer.Enabled = true;
timer.Start();
}
private void timer_Tick(object sender, EventArgs e)
{
counter++;
MainImageSource = new Uri(files[counter - 1], UriKind.Relative);
if (counter == Imagecounter)
{
counter = 0;
}
}
I'm not getting any error's but the image still isen't switching. I'm wondering if my paths are even working. Is there any way to test this ?
You have forgot to do notify the update to MainImageSource to the binding.
To do so, you have to implement the interface : INotifyPropertyChanged and define DataContext.
And, as written in the MSDN documentation "Setting Enabled to true is the same as calling Start, while setting Enabled to false is the same as calling Stop.".
Like this:
public partial class IntroScreen : Window, INotifyPropertyChanged
{
private string[] files;
private Timer timer;
private int counter;
private int Imagecounter;
BitmapImage _MainImageSource = null;
public BitmapImage MainImageSource // Using Uri in the binding was no possible because the Source property of an Image is of type ImageSource. (Yes it is possible to write directly the path in the XAML to define the source, but it is a feature of XAML (called a TypeConverter), not WPF)
{
get
{
return _MainImageSource;
}
set
{
_MainImageSource = value;
OnPropertyChanged("MainImageSource"); // Don't forget this line to notify WPF the value has changed.
}
}
public IntroScreen()
{
InitializeComponent();
DataContext = this; // The DataContext allow WPF to know the initial object the binding is applied on. Here, in the Binding, you have written "Path=MainImageSource", OK, the "MainImageSource" of which object? Of the object defined by the DataContext.
Loaded += MainWindow_Loaded;
}
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
setupPics();
}
private void setupPics()
{
timer = new Timer();
timer.Elapsed += timer_Tick;
timer.Interval = 2000;
// Initialize "files", "Imagecounter", "counter" before starting the timer because the timer is not working in the same thread and it accesses these fields.
files = Directory.GetFiles(#"../../Resources/Taken/", "*.jpg", SearchOption.TopDirectoryOnly);
Imagecounter = files.Length;
MessageBox.Show(Imagecounter.ToString());
counter = 0;
timer.Start(); // timer.Start() and timer.Enabled are equivalent, only one is necessary
}
private void timer_Tick(object sender, EventArgs e)
{
// WPF requires all the function that modify (or even read sometimes) the visual interface to be called in a WPF dedicated thread.
// IntroScreen() and MainWindow_Loaded(...) are executed by this thread
// But, as I have said before, the Tick event of the Timer is called in another thread (a thread from the thread pool), then you can't directly modify the MainImageSource in this thread
// Why? Because a modification of its value calls OnPropertyChanged that raise the event PropertyChanged that will try to update the Binding (that is directly linked with WPF)
Dispatcher.Invoke(new Action(() => // Call a special portion of your code from the WPF thread (called dispatcher)
{
// Now that I have changed the type of MainImageSource, we have to load the bitmap ourselves.
BitmapImage bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.UriSource = new Uri(files[counter], UriKind.Relative);
bitmapImage.CacheOption = BitmapCacheOption.OnLoad; // Don't know why. Found here (http://stackoverflow.com/questions/569561/dynamic-loading-of-images-in-wpf)
bitmapImage.EndInit();
MainImageSource = bitmapImage; // Set the property (because if you set the field "_MainImageSource", there will be no call to OnPropertyChanged("MainImageSource"), then, no update of the binding.
}));
if (++counter == Imagecounter)
counter = 0;
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
And your XAML does not refer to the correct property:
<Grid>
<Image x:Name="Picture" Source="{Binding MainImageSource}" Width="980" Height="760" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="350,50,0,0"></Image>
<Grid>
Why do you need to implement INotifyPropertyChanged?
Basically, when you define a binding, WPF will check if the class that contains the corresponding property defines INotifyPropertyChanged. If so, it will subscribe to the event PropertyChanged of the class.
I'm not seeing any use of the INotifyPropertyChanged interface, which would be required to update a UI item the way you are using it. As it is now, the UI control has no way of knowing that the value was updated.

Prevent next event handler being called

I have two event handlers wired up to a button click in a Windows form like so:
this.BtnCreate.Click += new System.EventHandler(new RdlcCreator().FirstHandler);
this.BtnCreate.Click += new System.EventHandler(this.BtnCreate_Click);
both are being called correctly.
However is it possible within FirstHandler() to prevent BtnCreate_Click() being executed? Something like:
void FirstHandler(object sender, EventArgs e)
{
if (ConditionSatisfied)
//Prevent next handler in sequence being executed
}
I know I could just unsubscribe the event, but can this be done programmatically (from within the method)?
As far as I know there is no solution for this. That's because there is no guarantee for the order in which the event handlers are called when the event happens.
Because of that you are not supposed to rely on their order in any way.
Why don't you just replace them with one eventhandler? Something like this:
var rdlc = new RdlcCreator();
this.BtnCreate.Click += (sender, e) => {
rdlc.FirstHandler(sender, e);
if (!rdlc.HasHandledStuff) { // <-- You would need some kind of flag
this.BtnCreate_Click(sender, e);
}
};
That way you can also guarantee the order of the handlers. Alternatively, use the above implementation, but change the signature of FirstHandler to return a bool indicating the condition (as in this case it doesn't really need to have the event's signature anymore):
if (!rdlc.FirstHandler(sender, e)) {
this.BtnCreate_Click(sender, e);
}
EDIT: OR, you just pass the second handler to FirstHandler.
Change the signature of FirstHandler to this:
void FirstHandler(object sender, EventArgs e, EventHandler nextHandler) {
if (ConditionSatisfied) {
// do stuff
}
else if (nextHandler != null) {
nextHandler(sender, e);
}
}
and then:
this.BtnCreate.Click +=
(s, e) => new RdlcCreator().Firsthandler(s, e, this.BtnCreate_Click);
System.ComponentModel namespace contains a CancelEventHandler delegate which is used for this purpose. One of the arguments it provides is a CancelEventArgs instance which contains a boolean Cancel property which can be set be any of the handlers to signal that execution of the invocation list should be stopped.
However, to attach it to a plain EventHandler delegate, you will need to create your own wrapper, something like:
public static class CancellableEventChain
{
public static EventHandler CreateFrom(params CancelEventHandler[] chain)
{
return (sender, dummy) =>
{
var args = new CancelEventArgs(false);
foreach (var handler in chain)
{
handler(sender, args);
if (args.Cancel)
break;
}
};
}
}
For your example, you would use it like this:
this.BtnCreate.Click += CancellableEventChain.CreateFrom(
new RdlcCreator().FirstHandler,
this.BtnCreate_Click
/* ... */
);
Of course, you would need to capture the created chain handler in a field if you need to unsubscribe (detach) it later.
Add the following condition in this.BtnCreate_Click which is the the second event
BtnCreate_Click(object sender, EventArgs e)
{
if (!ConditionSatisfied) //Prevent next handler in sequence being executed
{
// your implementation goes here
}
}
I suggest you to create a some kind of class wrapper. So, you could store there some kind of event flag group (16bit integer, for example) and a few methods to set or unset individual bits (where each means to invoke or not particular EventHandler). You can easily store any count of the Eventhandlers or even Actions, in the class, and invoke in any order you want.
Was finding the solution to the same question, but no luck. So had to resolve myself.
A base class for Cancelable event args
public class CancelableEventArgs
{
public bool Cancelled { get; set; }
public void CancelFutherProcessing()
{
Cancelled = true;
}
}
Next defines the extension method for the EventHandler, note that Invocation List subscribers invoked in backward order (in my case UI elements subscibe the event as they added to components, so which element is rendered later has most visiblility and more priority)
public static class CommonExtensions
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void SafeInvokeWithCancel<T>(this EventHandler<T> handler, object sender, T args) where T : CancelableEventArgs
{
if (handler != null)
{
foreach (var d in handler.GetInvocationList().Reverse())
{
d.DynamicInvoke(sender, args);
if (args.Cancelled)
{
break;
}
}
}
}
And here is the usage
public class ChessboardEventArgs : CancelableEventArgs
{
public Vector2 Position { get; set; }
}
So if an UI element has some behaviour on the event, it cancells futher processing
game.OnMouseLeftButtonDown += (sender, a) =>
{
var xy = GetChessboardPositionByScreenPosition(a.XY);
if (IsInside(xy))
{
var args = new ChessboardEventArgs { Position = xy };
OnMouseDown.SafeInvokeWithCancel(this, args);
a.CancelFutherProcessing();
}
};

Categories