Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Prev Previous commit
Next Next commit
Add NotifyWinEvent in MonthCalendar
  • Loading branch information
LeafShi1 committed Jul 23, 2025
commit acb2d5933d571cc36fbb99d4caad8d5c0be5dd56
Original file line number Diff line number Diff line change
Expand Up @@ -2063,9 +2063,9 @@ private unsafe void WmDateChanged(ref Message m)
_selectionStart = start;
_selectionEnd = end;

AccessibilityNotifyClients(AccessibleEvents.Focus, -1);
AccessibilityNotifyClients(AccessibleEvents.NameChange, -1);
AccessibilityNotifyClients(AccessibleEvents.ValueChange, -1);
NotifyWinEvent(AccessibleEvents.Focus);
NotifyWinEvent(AccessibleEvents.NameChange);
NotifyWinEvent(AccessibleEvents.ValueChange);

// We should use the Date for comparison in this case. The user can work in the calendar only with dates,
// while the minimum / maximum date can contain the date and custom time, which, when comparing Ticks,
Expand Down Expand Up @@ -2095,6 +2095,16 @@ private unsafe void WmDateChanged(ref Message m)
OnDateChanged(new DateRangeEventArgs(start, end));
}

private void NotifyWinEvent(AccessibleEvents accessibleEvent)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this throw potentially and with that regress exiting scenarios?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The original method AccessibilityNotifyClients(...) internally checks the AppContextSwitches.NoClientNotifications flag. If this flag is set to true, it calls NotifyWinEvent(...); otherwise, it does nothing:

protected void AccessibilityNotifyClients(AccessibleEvents accEvent, int objectID, int childID)
{
if (IsHandleCreated && AppContextSwitches.NoClientNotifications)
{
PInvoke.NotifyWinEvent((uint)accEvent, this, objectID, childID + 1);
}
}

Since AppContextSwitches.NoClientNotifications defaults to false, the system event is never raised, which causes screen readers like NVDA to miss updates from MonthCalendar.

Unfortunately, this method is protected and not overridable, and enabling the switch globally would affect all controls—not just MonthCalendar—which could introduce regressions elsewhere.

To resolve this, I replaced the call with a direct invocation of NotifyWinEvent(...), and added a handle check to ensure safety:

private void NotifyWinEvent(AccessibleEvents accessibleEvent)
{
    try
    {
        if (this.IsHandleCreated)
        {
            PInvoke.NotifyWinEvent(
                (uint)accessibleEvent,
                this,
                (int)OBJECT_IDENTIFIER.OBJID_CLIENT,
                (int)PInvoke.CHILDID_SELF
            );
        }
    }
    catch (Exception ex)
    {
        Debug.WriteLine($"NotifyWinEvent failed: {ex.Message}");
    }
}

This ensures that NVDA receives the necessary accessibility events without relying on global switches, and avoids potential exceptions by validating the control’s handle.

{
PInvoke.NotifyWinEvent(
(uint)accessibleEvent,
this,
(int)OBJECT_IDENTIFIER.OBJID_CLIENT,
(int)PInvoke.CHILDID_SELF
);
}

/// <summary>
/// Handles the MCN_GETDAYSTATE notification by returning an array of bitmasks, one entry per month,
/// that specifies which dates to display in bold.
Expand Down