PropertyChangedEventHandler Ready to Work - c#

I initially had an async MVVM pattern; in debugging I've stripped it now down to only the following - synchronous - code:
XAML:
<Grid x:Name="LayoutRoot">
<Button x:Name="button" Content="{Binding Path=bText, FallbackValue=Initial}" Tapped="onTap"/>
</Grid>
C#
public partial class VPage : Page
{
public ViewModel viewModel;
public VPage()
{
DataContext = viewModel = new ViewModel();
InitializeComponent();
}
protected override void OnNavigatedTo(NavigationEventArgs e) { viewModel.onNavigatedTo(); }
private void onTap(object sender, TappedRoutedEventArgs eventArgs) { viewModel.onTap(); }
}
public class viewModel : INotifyPropertyChanged
{
private String _bText;
public String bText { get { return _bText; } set { _bText = value; DB.major("ViewModel: bText=" + _bText); NotifyPropertyChanged("bText"); } }
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged == null)
writeln("Null handler!");
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(info));
}
public void onNavigatedTo() { bText = "Updated via onNavigatedTo"; }
private int count = 0;
public void onTap() { bText = "Updated via onTap " + count++.ToString(); }
}
That's the entire code base now - I've eliminated all of the Model and Async code to aid in debugging.
When initiated the Page constructor runs and completes.
Subsequently, VPage.OnNavigatedTo gets called, and invokes ViewModel.onNavigatedTo();
On the call to OnNavigatedTo, the writeln triggers, indicating that the handler is null, suggesting that the component initialization hasn't finished in some way. Subsequently - ie onTap - all works fine and the handler is in place. Interestingly, the field is updated to "Updated via onNavigatedTo" even though the null handler writeln triggered.
When I had the full Async pattern in place I thought that it was a threading issue but now it's obvious that it's something much simpler.

I don't think you've given the UI enough time to register its handlers. A quick fix is to yield. Try this:
protected override async void OnNavigatedTo(NavigationEventArgs e)
{
// This forces the rest of your code to enqueue after UI is done.
await Task.Yield();
viewModel.populate();
}
UPDATE
The bindings will not activate until your page is in the visual tree. They may or may not retrieve the value first, but they'll subscribe to PropertyChanged later.
If you want to ensure that all bindings are in place, then don't subscribe to OnNavigatedTo, but use Loaded instead.
public partial class VPage : Page
{
public ViewModel viewModel;
public VPage()
{
DataContext = viewModel = new ViewModel();
InitializeComponent();
Loaded += OnLoaded;
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
viewModel.onNavigatedTo();
}
private void onTap(object sender, TappedRoutedEventArgs eventArgs) { viewModel.onTap(); }
}

Related

BackgroundWorker not updating UI

Before I get into the details here, I'm still in what I would consider to be the "learning" phase of my C#/WPF journey... so apologies if what I'm asking here is stupidly obvious...
I have a small application (WPF, .NET Framework 4.8) that does the following:
read a list of values
do something for each value in the list
I am trying to do this with a BackgroundWorker so that I can report back to the UI as the list is being processed, preferably with a progress bar.
For the moment, the DoWork method just has some code in there to indicate that it's actually going through the process as expected and so that I could check that all the UI is updating as expected before I put the actual "what I want it to do" in there.
It seems that all the properties are updating as expected, but the UI (i.e. the progress bar) just doesn't move.
And I have checked that the data context in the XAML is set correctly (both in the XAML and in the Code-Behind).
In my XAML I have the following:
<ProgressBar x:Name="ProgressBar"
Width="740"
Height="20"
Background="Transparent"
Foreground="#008DEB"
Grid.Row="3"
Minimum="0"
Maximum="100"
Value="{Binding ProgressBarIndicator}"/>
And in my class containing all my methods/properties etc, I have:
private int _measurementProgress;
public int MeasurementProgress
{
get { return _measurementProgress; }
set
{
_measurementProgress = value;
OnPropertyChanged();
}
}
private int _progressBarIndicator;
public int ProgressBarIndicator
{
get { return _progressBarIndicator; }
set
{
_progressBarIndicator = value;
OnPropertyChanged();
}
}
public void StartMeasurements(string ipAddress)
{
TotalMeasurementsInList = CommandsList.Count;
MeasurementProgress = 0;
measurementWorker.WorkerReportsProgress = true;
measurementWorker.DoWork += worker_DoWork;
measurementWorker.ProgressChanged += worker_ProgressChanged;
measurementWorker.RunWorkerCompleted += worker_RunWorkerCompleted;
measurementWorker.RunWorkerAsync();
}
public void worker_DoWork(object sender, DoWorkEventArgs e)
{
foreach (var command in CommandsList)
{
MessageBox.Show(String.Format("Measuring Sample: {0}",command.SampleName),"Measuring Sample");
measurementWorker.ReportProgress((int)((double)(MeasurementProgress / (double)TotalMeasurementsInList)*100));
Thread.Sleep(command.DelayTime*1000);
}
}
public void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
MeasurementProgress++;
MessageBox.Show(String.Format("Progress is {0}%", e.ProgressPercentage.ToString()));
ProgressBarIndicator = e.ProgressPercentage;
}
void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
ProgressBarIndicator = 100;
MessageBox.Show("Measurements are completed","Finished");
}
The "OnPropertyChanged()" method is inside my ObservableObject class, and the above class is set to inherit from this ObservableObject class. The ObservableObject class looks like this:
public class ObservableObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler? PropertyChanged;
public void OnPropertyChanged([CallerMemberName] string propertyname = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyname));
}
}
By placing break-points in strategic places, I can confirm that the value of MeasurementProgress does indeed increment as one would expect, as does the value of ProgressBarIndicator. This was double-confirmed by the MessageBox in the worker_ProgressChanged method as it does indeed display the appropriate percentage value.
Annoyingly, what would appear to me to be exactly the same code is working as expected in another part of the application. The code here is more or less a copy/paste from there... but I just can't see what I'm doing wrong.
Any help or pointers where I could look to try and debug this appreciated.
Many thanks
Colin

Continuous data update with MVVM

I'm trying to learn the MVVM structure. How can I update a variable that changes constantly in another class in the UI.
I created a simple example because the project codes are too much. But I failed.
I would be very grateful if you could tell me where I went wrong. Thanks.
MyModel
public class Temperature : INotifyPropertyChanged
{
private double _memsTemperature;
private double _cpuTemperature;
private double _animalTemperature;
public double MemsTemperature
{
get { return _memsTemperature; }
set
{
_memsTemperature = value;
OnPropertyChanged("MemsTemperature");
}
}
public double CpuTemperature
{
get { return _cpuTemperature; }
set
{
_cpuTemperature = value;
OnPropertyChanged("CpuTemperature");
}
}
public double AnimalTemperature
{
get { return _animalTemperature; }
set
{
_animalTemperature = value;
OnPropertyChanged("AnimalTemperature");
}
}
System.Windows.Threading.DispatcherTimer dispatcherTimer = new System.Windows.Threading.DispatcherTimer();
public Temperature()
{
dispatcherTimer.Tick += DispatcherTimer_Tick;
dispatcherTimer.Interval = TimeSpan.FromSeconds(1);
dispatcherTimer.Start();
}
private void DispatcherTimer_Tick(object sender, System.EventArgs e)
{
MemsTemperature = MemsTemperature + 1;
CpuTemperature = CpuTemperature + 2;
AnimalTemperature = AnimalTemperature + 3;
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
MainWindowViewModel
public class MainWindowViewModel
{
public double MemTemp { get; set; }
public MainWindowViewModel()
{
MemTemp = new Temperature().MemsTemperature;
}
}
Main Window Xaml and C# Code
<TextBlock Text="{Binding MemTemp, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
public MainWindow()
{
InitializeComponent();
DataContext = new MainWindowViewModel();
}
The MainWindowViewModel should expose a Temperature property, e.g. like this:
public class MainWindowViewModel
{
public Temperature Temperature { get; } = new Temperature();
}
and the Binding should then look like this:
<TextBlock Text="{Binding Temperature.MemsTemperature}"/>
Neither Mode=TwoWay nor UpdateSourceTrigger=PropertyChanged makes sense on the Binding of a TextBlock's Text property.
The OnPropertyChanged method would simpler and safer be implemented like this:
private void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
You have a XAML page with UI controls that bind to those constantly-changing properties. When you send out the PropertyChanged notifications, the UI control will automatically update itself.
The problem with the code you wrote is that you never bound to the actual temperature. XAML doesn't know how to translate MemTemp into anything other than it's name unless you write a DataTemplate for it.
For example, (assuming a grid) something like this:
<TextBlock Grid.Row="0" Grid.Column="0" Text="Animal: "/>
<TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding MemTemp.AnimalTemperature}"/>
I would define an explicit worker class which performs the measurements. This class
has an event (OnMeasurement), which can be subscribed in the ViewModel:
// Arguments for the mesurement event (temperature, ...)
public class MeasurementEventArgs : EventArgs
{
public double Temperature { get; }
public MeasurementEventArgs(double temperature)
{
Temperature = temperature;
}
}
public class MeasurementWorker
{
private readonly CancellationTokenSource _tcs = new CancellationTokenSource();
// Provides an event we can subscribe in the view model.
public event Action<object, MeasurementEventArgs> OnMeasurement;
public void Stop()
{
_tcs.Cancel();
}
// Measurement routine. Perform a measurement every second.
public async Task Start()
{
try
{
var rnd = new Random();
while (!_tcs.IsCancellationRequested)
{
var temperature = 20 * rnd.NextDouble();
OnMeasurement?.Invoke(this, new MeasurementEventArgs(temperature));
await Task.Delay(1000, _tcs.Token);
}
}
catch (TaskCanceledException) { }
// TODO: Create an error event to catch exceptions from here.
catch { }
}
}
In your MainWindow class you instantiate your viewmodel and your worker:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new MainWindowViewModel(new MeasurementWorker());
}
// Register in XAML with <Window ... Closing="StopMeasurement">
public async void StopMeasurement(object sender, System.ComponentModel.CancelEventArgs e)
{
var vm = DataContext as MainWindowViewModel;
await vm.StopMeasurement();
}
}
In your view model you can subscribe to the worker event and raise OnPropertyChanged in your callback function:
public class MainWindowViewModel : INotifyPropertyChanged
{
private double _memsTemperature;
private readonly MeasurementWorker _mw;
private readonly Task _measurementWorkerTask;
public double MemsTemperature
{
get => _memsTemperature;
set
{
_memsTemperature = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(MemsTemperature)));
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void ProcessMeasurement(object sender, MeasurementEventArgs args)
{
MemsTemperature = args.Temperature;
}
// You can call this if you want to stop your measurement. Should be called if you close your app.
public async Task StopMeasurement()
{
_mw.OnMeasurement -= ProcessMeasurement;
_mw.Stop();
// Clean shutdown
await _measurementWorkerTask;
}
public MainWindowViewModel(MeasurementWorker mw)
{
_mw = mw;
_mw.OnMeasurement += ProcessMeasurement;
_measurementWorkerTask = _mw.Start();
}
}

Handling Backend Events in UI class

EDIT: I have updated this with the two methods recommended
I am writing a simple custom PI (OSISoft) data viewer. I have two classes, one for the UI and one for the PI server interactions/program logic. The property for the data to be displayed has an event that fires when the property is changed. How do I get that change to propagate over to the UI class so the associated text box will automatically refresh?
Original code:
namespace PIViewer {
public partial class MainWindow : Window
{
ProgLogic logic;
public MainWindow() {
InitializeComponent();
logic = new ProgLogic();
InitializeValues();
}
private void InitializeValues() {
logic.SomeValue = logic.GetValFromServer(valueTag);
}
private void TextBoxSomeValue(object sender, TextChangedEventArgs e) {
// ??? something happens here?
}
}
public class ProgLogic {
private int someValue;
public event System.EventHandler SomeValueChanged;
protected void OnSomeValueChanged()
{
SomeValueChanged?.Invoke(this, EventHandlerArgs e);
}
public int SomeValue {
get => someValue;
set {
someValue = value;
OnSomeValueChanged();
}
}
public int GetValFromServer(string valueTag) {
int piValue;
piValue = PISDKMethodToGetServerValue(valueTag);
return piValue;
}
}
}
Method 1: Events
namespace PIViewer {
public partial class MainWindow : Window
{
ProgLogic logic;
public MainWindow() {
InitializeComponent();
logic = new ProgLogic();
logic.SomeValueChanged += Logic_SomeValueChanged;
InitializeValues();
}
private void Logic_SomeValueChanged(int obj) {
TextBoxSomeValue.Text = obj.toString();
}
private void InitializeValues() {
logic.SomeValue = logic.GetValFromServer(valueTag);
}
private void TextBoxSomeValue_TextChanged(object sender, TextChangedEventArgs e) {
}
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e) {
logic.SomeValueChanged -= Logic_SomeValueChanged;
}
}
public class ProgLogic {
private int someValue;
public event Action<int> SomeValueChanged;
public virtual void OnSomeValueChanged(int newValue) {
SomeValueChanged?.Invoke(newValue);
}
public int SomeValue {
get => someValue;
set {
someValue = value;
OnSomeValueChanged(value);
}
}
public int GetValFromServer(string valueTag) {
int piValue;
piValue = PISDKMethodToGetServerValue(valueTag);
return piValue;
}
}
}
Method 2: MVVM pattern
MainWindow.xaml:
<Window
Closing="Window_Closing"
Title="My App">
<TextBox x:name="TextBoxSomeValue" text="{binding SomeValue, UpdateSourceTrigger=PropertyChanged}" />
</Window>
The important part here is the binding parameter in the text field of the TextBox definition, which points to the PropertyChangedEventHandler.
C# code:
namespace PIViewer {
public partial class MainWindow : Window
{
ProgLogic logic;
public MainWindow() {
InitializeComponent();
logic = new ProgLogic();
InitializeValues();
}
private void InitializeValues() {
logic.SomeValue = logic.GetValFromServer(valueTag);
}
private void TextBoxSomeValue_TextChanged(object sender, TextChangedEventArgs e) {
// run some other code when the text box updates
}
}
public class ProgLogic : INotifyPropertyChanged {
private int someValue;
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChange(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public int SomeValue {
get => someValue;
set {
someValue = value;
OnPropertyChange("SomeValue")
}
}
public int GetValFromServer(string valueTag) {
int piValue;
piValue = PISDKMethodToGetServerValue(valueTag);
return piValue;
}
}
}
ProgLogic now implements INotifyPropertyChanged, which notifies the View of property changes, so that Bindings are updated.
I see you are heading the right way with C# event system. One thing I would change is event type from System.EventHandler to System.Action<int>. Even though people tend to propagate extending System.EventArgs class and writing custom delegates for handling events, using System.Action<T> is much easier to grasp for beginner.
So let's go with System.Action<int> example now. First, let's change ProgLogic class to be more like this:
public class ProgLogic
{
public event Action<int> SomeValueChanged;
//
// your other code goes here
//
private void OnSomeValueChanged(int newValue)
{
SomeValueChanged?.Invoke(newValue);
}
}
Now, you need to subscribe to the earlier written event in MainWindow class. So we do that as early as possible - in the constructor of MainWindow:
public MainWindow()
{
InitializeComponent();
logic = new ProgLogic();
logic.SomeValueChanged += OnSomeValueChanged;
InitializeValues();
}
Then, you describe your logic in the OnSomeValueChanged callback method, like:
private void OnSomeValueChanged(int newValue)
{
TextBoxSomeValue.text = newValue.ToString();
}
Make sure you unsubscribe from the event once MainWindow is getting destroyed to prevent memory leakage. This is just bare-bones for whole logic. I've left some space for interpretation. ;)
I'm not sure if I'm understanding the main point of your question but if you want to create a new value and have that value saved as the default value then you should create a string in your application setting and call on it on text changed.
At the top of your visual2019, in the menu options. open the debug menu and at the bottom you will see ("Your project name" + properties)
2.You will be brought into a new window with menu options on the left, go to the settings.
3.Create a string and set the value to "Some random text"
Note: In the example I placed one text box in front of the other, though this in not a great method it will prevent the text from appearing as a double or drawing a blank
Settings String Example
xaml
<Window x:Class="SaveNewText.MainWindow"
Title="MainWindow" Height="450" Width="800">
<Grid>
<TextBox x:Name="DefaultText" Height="250" Width="250"
Background="Transparent"
Foreground="Black" MouseDown="TextBlock_MouseDown" IsReadOnly="True"/>
<TextBox x:Name="NewText" Height="250" Width="250" Background="Transparent"
Foreground="Black" TextChanged="NewText_TextChanged"/>
</Grid>
</Window>
xaml.cs
namespace SaveNewText
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DefaultText.Text = Properties.Settings.Default.TextString;
}
private void TextBlock_MouseDown(object sender, MouseButtonEventArgs e)
{
NewText.Focus();
}
private void NewText_TextChanged(object sender, TextChangedEventArgs e)
{
Properties.Settings.Default.TextString = NewText.Text;
Properties.Settings.Default.Save();
DefaultText.Text = Properties.Settings.Default.TextString;
}
}
}

Changing viewmodel

I have simplified app to show my issue
When I click button, it changes Text property of ViewModel and TextBlock.Text is updated.
MainPage.xaml
<StackPanel>
<Button Click="ButtonBase_OnClick">Button to change text</Button>
<TextBlock Text="{x:Bind ViewModel.Text, Mode=OneWay}"></TextBlock>
</StackPanel>
MainPage.xaml.cs
public MainPage()
{
ViewModel = new ViewModel();
this.InitializeComponent();
}
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
ViewModel.Text = "x:Bind works";
}
ViewModel class has one string property (Text) and implemented INotifyPropertyChange interface.
Problem starts when ViewModel is not set in ctor (i.e. viewModel is null and changed in runtime):
public MainPage()
{
//ViewModel = new ViewModel();//this line has been removed
this.InitializeComponent();
}
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
ViewModel = new ViewModel();//this line has been added
ViewModel.Text = "x:Bind does not work";
}
Complited binding is not working (Text is not changed) and I could not figure out why it is so... I need to change viewModel from null (vm is null because it is waiting for some data in real app)
{x:Bind} bindings (often referred-to as compiled bindings) uses generated code to achieve its benefits. At XAML load time, {x:Bind} is converted into what you can think of as a binding object, and this object gets a value from a property on a data source. These generated code can be found in your obj folder, with names like (for C#) <view name>.g.cs.
For your code, the generated code will like following:
// Update methods for each path node used in binding steps.
private void Update_(global::UWP.BlankPage3 obj, int phase)
{
if (obj != null)
{
if ((phase & (NOT_PHASED | DATA_CHANGED | (1 << 0))) != 0)
{
this.Update_ViewModel(obj.ViewModel, phase);
}
}
}
private void Update_ViewModel(global::UWP.ViewModel obj, int phase)
{
this.bindingsTracking.UpdateChildListeners_ViewModel(obj);
if (obj != null)
{
if ((phase & (NOT_PHASED | DATA_CHANGED | (1 << 0))) != 0)
{
this.Update_ViewModel_Text(obj.Text, phase);
}
}
}
...
private global::UWP.ViewModel cache_ViewModel = null;
public void UpdateChildListeners_ViewModel(global::UWP.ViewModel obj)
{
if (obj != cache_ViewModel)
{
if (cache_ViewModel != null)
{
((global::System.ComponentModel.INotifyPropertyChanged)cache_ViewModel).PropertyChanged -= PropertyChanged_ViewModel;
cache_ViewModel = null;
}
if (obj != null)
{
cache_ViewModel = obj;
((global::System.ComponentModel.INotifyPropertyChanged)obj).PropertyChanged += PropertyChanged_ViewModel;
}
}
}
Here I just copy some method that related to your issue. From these method, you can find that before update TextBlock or PropertyChanged listeners, it will check if the ViewModel is null. If it is null, nothing will be done. So to make {x:Bind} work, we must initialize ViewModel before page loaded. And this is the reason why {x:Bind} doesn't work when you initialize ViewModel in Button.Click event.
To fix this issue, you can implement INotifyPropertyChanged interface for ViewModel like Filip said so that the generated code can be notified when ViewModel changed (from null to new ViewModel()) and update you UI.
But I think you can just initialize ViewModel in constructor. When you initialize ViewModel, you can set the properties that you are waiting for to null first like:
public MainPage()
{
ViewModel = new ViewModel() { Text = null };
this.InitializeComponent();
}
And then update these properties when your date is ready. In this way, you can do not implement INotifyPropertyChanged interface on your page.
Besides these, there is another cheaper way, you can call this.Bindings.Update(); method to force the bindings to be updated after you initialize ViewModel like following:
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
ViewModel = new ViewModel();
ViewModel.Text = "x:Bind does not work";
this.Bindings.Update();
}
Did you implement INotifyPropertyChanged on page like so
public sealed partial class MainPage : Page, INotifyPropertyChanged
{
private ViewModel viewModel;
public ViewModel ViewModel
{
get { return viewModel; }
set
{
viewModel = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ViewModel)));
}
}
public MainPage()
{
ViewModel = new ViewModel { };
this.InitializeComponent();
}
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
ViewModel = new ViewModel { };//this line has been added
ViewModel.Text = "x:Bind does not work";
}
public event PropertyChangedEventHandler PropertyChanged;
}
This works for me.

Creating an event in a dll and handling the event in a Form

I have created a DLL using the following code. I have compiled this code as a DLL.
namespace DllEventTrigger
{
public class Trigger
{
public delegate void AlertEventHandler(Object sender, AlertEventArgs e);
public Trigger()
{
}
public void isRinging()
{
AlertEventArgs alertEventArgs = new AlertEventArgs();
alertEventArgs.uuiData = "Hello Damn World!!!";
CallAlert(new object(), alertEventArgs);
}
public event AlertEventHandler CallAlert;
}
public class AlertEventArgs : EventArgs
{
#region AlertEventArgs Properties
private string _uui = null;
#endregion
#region Get/Set Properties
public string uuiData
{
get { return _uui; }
set { _uui = value; }
}
#endregion
}
}
Now I'm trying to handle the event triggered by this dll in a forms application with this code.
namespace DLLTriggerReciever
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
Trigger trigger = new Trigger();
trigger.isRinging();
trigger.CallAlert += new Trigger.AlertEventHandler(trigger_CallAlert);
}
void trigger_CallAlert(object sender, AlertEventArgs e)
{
label1.Text = e.uuiData;
}
}
}
My problem i'm not sure where i went wrong. Please suggest.
You need to assign your event handler before the event is actually fired, otherwise the code will throw a NullReferenceException.
trigger.CallAlert += new Trigger.AlertEventHandler(trigger_CallAlert);
trigger.isRinging();
Additionally, it's a recommended practice to check first, whether there are handlers assigned:
var handler = CallAlert; // local variable prevents a race condition to occur
if (handler != null)
{
handler(this, alertEventArgs);
}
as #Gene said, you need to register the event before raising it.
anyway, it's a good practice to check if someone is register to the event you're about to raise by checking for null.
like this:
if (this.CallAlert != null)
this.CallAlert(new object(), alertEventArgs);

Categories

Resources