[How To...] Conditionally Assign Changed Event on Custom Control

I am creating a custom dotcontrol around the bp:NumericUpDown control to add some formatting and containing divs, etc…

<bp:NumericUpDown Value="{value: Value}"
                  IncludeInPage="{value: !IsReadOnly}"
                  MinValue="{value: MinValue}"
                  MaxValue="{value: MaxValue}"
                  Step="{value: Step}"
                  Placeholder="{resource: IsRequired ? RequiredText : string.Empty}"
                  UseFormatStringAsPlaceholder="false"
                  FormatString="{resource: FormatString}"
                  Changed="{command: HasChangedHandler ? ValueChanged() : SomethingThatDoesNotCausePostbackOrValidation}"
                  />

I cannot figure out how to conditionally assign a Changed event handler. I’ve created a bool property HasChangedHandlerthat returns true if the containing page has assigned a handler to the custom control as follows:

public bool HasChangedHandler => Changed != null;

The problem is that page validation is being called and a postback created whenever any one of these controls on the page is updated and is not being restricted to only the one changing.

A a simple example: assume there are two of these on the page. Only one of them has an event handler assigned by the page and expects to handle the change event. But when the user changes the one that does not have a handler, the event still fires and causes validation and a postback, which is wrong.

How do I only assign the Changed event handler when the containing page assigns one and not assign it when the page does not (the default)?

If ValueChanged is a control property, it should be enough to use Changed={staticCommand: ValueChanged()}. The staticCommand binding executes primarily client-side, and will automatically “upgrade” to a full command, if ValueChanged is a command.

In a more general case, you can use the dot:SupressPostbackHandler handler to avoid the command invocation

Thank you for the quick response.

I tried both of these solutions and neither work. I even tried duplicating the bp:NumericUpDown control within my dotcontrol file as follows but that doesn’t work either.

<div class="row mt-1 mb-1 pb-1">
    <div class="col-sm-4 col-md-3 col-xl-2 text-sm-right"
         Validator.InvalidCssClass="has-error"
         Validator.Value="{value: Value}">
        <span class="field-label">{{resource: LabelText}}</span>
        <dot:Validator Value="{value: Value}" InvalidCssClass="has-error">
            <span>*</span>
        </dot:Validator>
    </div>
    <div class="col-sm-8 col-md-9 col-xl-10">
        <bp:NumericUpDown Value="{value: Value}"
                          IncludeInPage="{value: !IsReadOnly && HasChangedHandler}"
                          MinValue="{value: MinValue}"
                          MaxValue="{value: MaxValue}"
                          Step="{value: Step}"
                          Placeholder="{resource: IsRequired ? RequiredText : string.Empty}"
                          UseFormatStringAsPlaceholder="false"
                          FormatString="{resource: FormatString}"
                          Changed="{command: ValueChanged()}" />
        <%-- Above when Changed has a handler in the containing page, below when it does not. --%>
        <bp:NumericUpDown Value="{value: Value}"
                          IncludeInPage="{value: !IsReadOnly && !HasChangedHandler}"
                          MinValue="{value: MinValue}"
                          MaxValue="{value: MaxValue}"
                          Step="{value: Step}"
                          Placeholder="{resource: IsRequired ? RequiredText : string.Empty}"
                          UseFormatStringAsPlaceholder="false"
                          FormatString="{resource: FormatString}" />
        <span IncludeInPage="{value: IsReadOnly}">{{value:  Value}}</span>
        <dot:Validator Value="{value: Value}" IncludeInPage="{value: !IsReadOnly && IsRequired}">
            <span class="validation-label label-danger"
                  Visible="{value: _page.EvaluatingOnClient && !Value.HasValue}">{{value: RequiredText}}</span>
        </dot:Validator>
        <div class="label-hint" IncludeInPage="{resource: !string.IsNullOrEmpty(HintText)}">
            {{resource: HintText}}
        </div>
    </div>
</div>

No matter what I’ve tred, the full page validation executes prior to the Changed event being called on the NumericUpDown.

What I am looking for is for the page validation to conditionally run. In my use case, I am trying to allow the page to determine what to do when the value of the NumericUpDown is changed on the client without all other fields being validated on the page.

Is there a better way to accomplish what I am trying to do?

If it would be helpful, I could provide all relevant files.

In which way does it not work? staticCommands or the control without the Changed handler in your solution has no way to trigger validation. If it does, I suppose that there must be some other issue, such as the HasChangedHandler being always true or validation being triggered by something else.

Could you please check the HasChangedHandler variable and what happens if you let both of your NumericUpDown versions displayed? Does the second one still trigger vaidation?

I have created 3 versions of the control and a test page for each version each trying a different way.

FieldNumeric.dotcontrol (1.6 KB)
FieldNumericViewModel.cs (7.7 KB)
Numeric.dothtml (2.0 KB)
NumericViewModel.cs (2.4 KB)

FieldNumeric2.dotcontrol (1.6 KB)
FieldNumeric2ViewModel.cs (7.7 KB)
Numeric2.dothtml (2.0 KB)
Numeric2ViewModel.cs (2.4 KB)

FieldNumeric3.dotcontrol (1.8 KB)
FieldNumeric3ViewModel.cs (7.7 KB)
Numeric3.dothtml (1.9 KB)
Numeric3ViewModel.cs (2.4 KB)

There are notes in each example page.

The goals are as follows:

  1. All regular page validations will occur when the page is submitted via the submit button.
  2. When the custom NumericUpDown’s value changes, execute the Changed event in the control if there is an assigned listener on the containing page.
  3. If there is no listener, do not do the partial page postback when the value changes.
  4. Do not check all page validations prior to the Change event when there is a listener, just notify the page and let it handle it.

Hopefully this is clear.

Ok, sorry I originally thought that ValueChanged was a control property. This way, you could only call methods compatible with staticCommands for the staticCommand to work propertly. I still don’t see a way how it could trigger validation, this way it should probably do nothing.

However, the approach with SuppressPostbackHandler is working for me without issue. If you don’t want the validation to fire on the Changed event, add Validation.Enabled=false on it.

Thank you.

Adding Validation.Enabled=false worked for what I was trying to do.