开发者

How to prevent value changed events from firing on form initialization in .NET?

开发者 https://www.devze.com 2023-01-05 11:20 出处:网络
Consider a simple .NET form with a couple of radio buttons and a checkbox. Each of the radio buttons has a CheckedChanged handler setup that performs some action based on the state of the checkbox.

Consider a simple .NET form with a couple of radio buttons and a checkbox.

Each of the radio buttons has a CheckedChanged handler setup that performs some action based on the state of the checkbox.

My problem is, when I initialize on the default radiobutton to be checked (from the designer properties window) the CheckedChanged event is fired for that radio button, but the Checkbox hasn't been initialized yet so I either get a null pointer exception or the wrong value is used in the handler. Either way, I don't want that handler code to be run unless the user picks a radio button after the form has been loaded.

I currently get around this by not initializing the radio button, but 开发者_C百科I need to set that default eventually and the best place is from the designer. I also can add a boolean field that's not set to true until the form is fully loaded and not process the events if that is false, but it's a dirty hack.

What can I do to prevent that handler from running its code?


To make it feel slightly less dirty, if you initialize the controls in the constructor of the form you might be able to use the forms IsHandleCreated property rather than your own bool to check if it should actually validate or not.
I would think that normally you wouldn't want to validate anything before it's been shown for the first time and handle isn't created until it is.

Code Example:

Private Sub myRadioButton_CheckedChanged(sender As Object, e As EventArgs) Handles myRadioButton.CheckedChanged
If myRadioButton.Checked AndAlso myRadioButton.IsHandleCreated Then
    'Do Work
End If
End Sub


"I also can put a boolean field that's not set to true until the form is fully loaded and not process the events if that is false, but it's a dirty hack."

It's also the easist and best way to do it!

Lets say .NET provides a neat way to turn an and off all the event handlers until the form is loaded. Even just the ones YOU are handling. It would still not be sufficiently flexible to disable what you wanted to enable but disable what you didn't. Often form setups happen and you want the events to fire. Also the form won't build right if no events fire.


The easy solution is to declare an initializing variable:

  Private Initializing as boolean = True

  Private Sub rb_CheckedChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles rbNuevos.CheckedChanged, RbDesaparecidos.CheckedChanged, RbModificados.CheckedChanged, RbNoDesap.CheckedChanged, RbDesHoy.CheckedChanged, RbChT.CheckedChanged
      if Initializing then return

      'Your Code    
  End Sub

  Public Sub New()

       ' Llamada necesaria para el Diseñador de Windows Forms.
       InitializeComponent()    
       ' Agregue cualquier inicialización después de la llamada a InitializeComponent().

       initializing = false
  end sub

Most sophisticated: Remove the "handles" from the method, and use AddHandler on the new method.

  Public Sub New()

       ' Llamada necesaria para el Diseñador de Windows Forms.
       InitializeComponent()    
       ' Agregue cualquier inicialización después de la llamada a InitializeComponent().

       AddHandler RbChT.CheckedChanged, AddressOf rb_CheckedChanged
  end sub


For radiobutton see Hans Olsson answer

For numeric up down, do it like this

Private Sub myNumeric_ValueChanged(sender As Object, e As EventArgs) Handles myNumeric.ValueChanged
        If myNumeric.Value >= 0 AndAlso myNumeric.IsHandleCreated Then
            'Do the work
        End If
End Sub

The keyword is myNumeric.Value and IsHandleCreated


Yet another way:

Private Sub dgvGroups_CellValueChanged(sender As System.Object, e As System.Windows.Forms.DataGridViewCellEventArgs) Handles dgvGroups.CellValueChanged

    If Me.Visible = False Then Exit Sub ' Sub gets called on form load which causes problems

    wksGroups.Cells(e.RowIndex + 1, 1) = dgvGroups.Item(e.ColumnIndex, e.RowIndex).Value
    wksGroups.Cells(1, 5) = dgvGroups.RowCount


One thing I've found that works is adding the events manually after you load the form.

To do this you can simply go into the generated form code found in the designer file of that form, and pull out the lines that add the event. It would look something like this:

this.controlName.CheckedChanged += new System.EventHandler(this.controlName_CheckedChanged);

Then put all of these calls into a method that you call after the InitializeComponent call in your form's constructor.


Just in case anyone is still searching for this the event is fired upon initializing the form BUT the form is not yet visible, Also Say that you have a foreign key relationship upon which you have a default value needed issue that gets fired every row update too. So the following code worked for me....

    if (Visible && !(e.ColumnIndex == 0))
        {
            phoneEdited = true;
            MessageBox.Show("A Phone entry has been entered");
        }


  1. Don't set checked on a control that really does much in designer.
  2. The global flag and conditional exits where needed.
  3. Try..Catch the sore spots to ignore a meaningless exception.

(Using VS 2017) It appears to me that it is an annoyance but not a bug. It is consistent with the model in use. The event is fired by normal operation of code, but code I did not write (but can access where fools fear to tread) and where there appears to be no (decent) place earlier in the normal flow to anticipate it.

The cleanest answer seems to be not to check radio button or checkbox controls in the designer at all if they trigger any significant code. Instead these controls should be changed by code (e.g. checked = true) in the Load event (for example) AFTER all the initialization is done.

There is no loss of flexibility here since both are fixed before the build, only in different places. The event handlers will handle it exactly as if a user had clicked the control in the natural flow of a well designed GUI application. (This reminds me of the ancient RPG proverb "Don't buck the cycle". (Anyone here remember RPG? I, not part of IBM-oriented team, never used it but had interesting discussions with some who did. ) Pre-checking controls hits the wrong part of the VS cycle.)

If for any reason that will not work, the next best thing is the kludge suggested elsewhere of a single status boolean initialized false and set true at the appropriate time with conditional exits in the necessary places to prevent them from crashing until then. It will get the job done, but it's ugly. Better than failure.

Another thing I tried before I decided that designer level pre-set checks were the problem and there was a very acceptable alternative was to put the danger spots in a Try..Catch to be able to ignore the exception. Also a kludge.


For the cleanest code, reverse the True/False approach used in some other examples. Focus on 'ready' rather than 'busy'. Here's an example for a Windows Form:

At the Class level, add Private app_ready As Boolean (it will be False by default).

At the end of the Form.Shown event handler, add app_ready = True.

In each control event handler where it's needed, add:

If app_ready Then
  ' code
End If

Starting a routine with something like If initialising Then Exit Sub just doesn't feel right!


Maybe for some functionality you can use the click event instead of the check changed event.


I put a public variable in the Module1 file Dim Public bolForm_LoadingTF as Boolean = True

In each formLoad event I put bolForm_LoadingTF = True

In each control with an OnSelectedIndexChanged event I put if bolForm_LoadingTF = True then Exit Sub

At the end of the form load event I put bolForm_LoadingTF = False

I am probably breaking a bunch of rules but this works for me.

0

精彩评论

暂无评论...
验证码 换一张
取 消