From b8620787edc55e7b2cc2b0f102abfa3c3b106a41 Mon Sep 17 00:00:00 2001 From: Mikhail Lipin Date: Tue, 15 Oct 2019 14:58:10 -0700 Subject: [PATCH] UI Automation providers for MonthCalendar control Proposed changes: * Added UI Automation server providers to MonthCalendar and switched the accessibility model from MSAA-provided to UIA. * Added/moved from combined Safe/Unsafe NativeMethods native structure definitions and constants: MCGIF, MCGIP, MCGRIDINFO, MCHITTESTINFO, MCHT, MCM, MCS, MCSC, POINT, SYSTEMTIME, MOUSEEVENTF. Description for all definitions can be found in Windows API reference. Some managed-code definitions for structures were moved with changing the class-defined to structure-defined, so changed corresponding native methods to pass corresponding objects by ref. * Implemented wrappers for native method calls to fetch month calendar control inf (bounding rectangles, dates, texts). Implemented marshaling for accessible names/texts with passing strings of defined length. Passing string or pointer with length which does not correspond to passed length of text may lead to heap corruption. Passing null will lead to protected memory write violation. Adding required resources for month calendar elements which text/name cannot be fetched from Windows API calls. * Implemented the hierarchy of MonthCalendar control accessibility children with navigation in all directions using FragmentNavigation (UIA server provided navigation - UIA providers support should be set to true), implemented navigation for all calendar view types (dates, months, years, decades). * Added Table and Grid providers for the root element (but not for the calendar body to prevent announcing "table exit" when navigating to calendar header or today button. Added TableItem and GridItem providers for calendar items (date/months/years/decades). Note: getting number of rows and number of columns is little tricky as there is no documented API for that, so getting these basing on the dimensions and coordinates of cells and container calendar body. * Added Runtime IDs to elements to correctly associate navigated element and its properties in Inspect tree tab and properties tab. * Implemented accessibility hit test to allow getting month calendar child accessible element by specific coordinates. * Implemented accessibility default action invoke (trigger mouse click on the coordinates of specific child element to invoke its native click handler) - same as in client providers. * Improved Narrator support: allowing CAPS+Arrow/Arrow navigation accompanied with blue rectangle emphasizing, correct and detailed announcement for cells, headers, buttons and other MonthCalendar UI elements. Allowed triggering default action/click for MonthCalendar child element using CAPS+Enter. * Changed accessible control type from table to calendar except the case when there is a label before the MonthCalendar control with custom name for below MonthCalendar control. * Fixed AccessibilityInsights issues. Resolves #1908 Resolves #1909 Resolves #1910 Resolves #1911 --- .../src/Interop/ComCtl32/Interop.MCGIF.cs | 34 + .../src/Interop/ComCtl32/Interop.MCGIP.cs | 61 ++ .../Interop/ComCtl32/Interop.MCGRIDINFO.cs | 37 + .../Interop/ComCtl32/Interop.MCHITTESTINFO.cs | 27 + .../src/Interop/ComCtl32/Interop.MCHT.cs | 116 +++ .../src/Interop/ComCtl32/Interop.MCM.cs | 101 +++ .../src/Interop/ComCtl32/Interop.MCS.cs | 41 + .../src/Interop/ComCtl32/Interop.MCSC.cs | 46 ++ src/Common/src/Interop/Interop.POINT.cs | 30 + .../Interop/Kernel32/Interop.SYSTEMTIME.cs | 21 + .../src/Interop/User32/Interop.MOUSEEVENTF.cs | 30 + src/Common/src/NativeMethods.cs | 87 +- src/Common/src/UnsafeNativeMethods.cs | 16 +- .../src/System.Windows.Forms.Design.csproj | 6 + .../src/Resources/SR.resx | 12 + .../src/Resources/xlf/SR.cs.xlf | 20 + .../src/Resources/xlf/SR.de.xlf | 20 + .../src/Resources/xlf/SR.es.xlf | 20 + .../src/Resources/xlf/SR.fr.xlf | 20 + .../src/Resources/xlf/SR.it.xlf | 20 + .../src/Resources/xlf/SR.ja.xlf | 20 + .../src/Resources/xlf/SR.ko.xlf | 20 + .../src/Resources/xlf/SR.pl.xlf | 20 + .../src/Resources/xlf/SR.pt-BR.xlf | 20 + .../src/Resources/xlf/SR.ru.xlf | 20 + .../src/Resources/xlf/SR.tr.xlf | 20 + .../src/Resources/xlf/SR.zh-Hans.xlf | 20 + .../src/Resources/xlf/SR.zh-Hant.xlf | 20 + .../System/Windows/Forms/DateTimePicker.cs | 60 +- ...thCalendar.CalendarBodyAccessibleObject.cs | 73 ++ ...Calendar.CalendarButtonAccessibleObject.cs | 47 ++ .../Forms/MonthCalendar.CalendarButtonType.cs | 15 + ...thCalendar.CalendarCellAccessibleObject.cs | 111 +++ ...hCalendar.CalendarChildAccessibleObject.cs | 75 ++ .../Forms/MonthCalendar.CalendarChildType.cs | 23 + ...endar.CalendarGridChildAccessibleObject.cs | 25 + ...Calendar.CalendarHeaderAccessibleObject.cs | 52 ++ ...ndar.CalendarNextButtonAccessibleObject.cs | 41 + ....CalendarPreviousButtonAccessibleObject.cs | 40 + ...nthCalendar.CalendarRowAccessibleObject.cs | 55 ++ ...endar.CalendarTodayLinkAccessibleObject.cs | 51 ++ ...hCalendar.MonthCalendarAccessibleObject.cs | 748 ++++++++++++++++++ .../src/System/Windows/Forms/MonthCalendar.cs | 325 +++----- 43 files changed, 2327 insertions(+), 339 deletions(-) create mode 100644 src/Common/src/Interop/ComCtl32/Interop.MCGIF.cs create mode 100644 src/Common/src/Interop/ComCtl32/Interop.MCGIP.cs create mode 100644 src/Common/src/Interop/ComCtl32/Interop.MCGRIDINFO.cs create mode 100644 src/Common/src/Interop/ComCtl32/Interop.MCHITTESTINFO.cs create mode 100644 src/Common/src/Interop/ComCtl32/Interop.MCHT.cs create mode 100644 src/Common/src/Interop/ComCtl32/Interop.MCM.cs create mode 100644 src/Common/src/Interop/ComCtl32/Interop.MCS.cs create mode 100644 src/Common/src/Interop/ComCtl32/Interop.MCSC.cs create mode 100644 src/Common/src/Interop/Interop.POINT.cs create mode 100644 src/Common/src/Interop/Kernel32/Interop.SYSTEMTIME.cs create mode 100644 src/Common/src/Interop/User32/Interop.MOUSEEVENTF.cs create mode 100644 src/System.Windows.Forms/src/System/Windows/Forms/MonthCalendar.CalendarBodyAccessibleObject.cs create mode 100644 src/System.Windows.Forms/src/System/Windows/Forms/MonthCalendar.CalendarButtonAccessibleObject.cs create mode 100644 src/System.Windows.Forms/src/System/Windows/Forms/MonthCalendar.CalendarButtonType.cs create mode 100644 src/System.Windows.Forms/src/System/Windows/Forms/MonthCalendar.CalendarCellAccessibleObject.cs create mode 100644 src/System.Windows.Forms/src/System/Windows/Forms/MonthCalendar.CalendarChildAccessibleObject.cs create mode 100644 src/System.Windows.Forms/src/System/Windows/Forms/MonthCalendar.CalendarChildType.cs create mode 100644 src/System.Windows.Forms/src/System/Windows/Forms/MonthCalendar.CalendarGridChildAccessibleObject.cs create mode 100644 src/System.Windows.Forms/src/System/Windows/Forms/MonthCalendar.CalendarHeaderAccessibleObject.cs create mode 100644 src/System.Windows.Forms/src/System/Windows/Forms/MonthCalendar.CalendarNextButtonAccessibleObject.cs create mode 100644 src/System.Windows.Forms/src/System/Windows/Forms/MonthCalendar.CalendarPreviousButtonAccessibleObject.cs create mode 100644 src/System.Windows.Forms/src/System/Windows/Forms/MonthCalendar.CalendarRowAccessibleObject.cs create mode 100644 src/System.Windows.Forms/src/System/Windows/Forms/MonthCalendar.CalendarTodayLinkAccessibleObject.cs create mode 100644 src/System.Windows.Forms/src/System/Windows/Forms/MonthCalendar.MonthCalendarAccessibleObject.cs diff --git a/src/Common/src/Interop/ComCtl32/Interop.MCGIF.cs b/src/Common/src/Interop/ComCtl32/Interop.MCGIF.cs new file mode 100644 index 00000000000..28581f27a5e --- /dev/null +++ b/src/Common/src/Interop/ComCtl32/Interop.MCGIF.cs @@ -0,0 +1,34 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; + +internal static partial class Interop +{ + internal static partial class ComCtl32 + { + /// + /// Represents MonthCalendar Control Grid Info Flags. + /// Copied form CommCtrl.h + /// + [Flags] + public enum MCGIF + { + /// + /// Represetns MCGIF_DATE const. + /// + DATE = 0x00000001, + + /// + /// Represents MCGIF_RECT cosnt. + /// + RECT = 0x00000002, + + /// + /// Represetns MCGIF_NAME const. + /// + NAME = 0x00000004 + } + } +} diff --git a/src/Common/src/Interop/ComCtl32/Interop.MCGIP.cs b/src/Common/src/Interop/ComCtl32/Interop.MCGIP.cs new file mode 100644 index 00000000000..adffede8f8e --- /dev/null +++ b/src/Common/src/Interop/ComCtl32/Interop.MCGIP.cs @@ -0,0 +1,61 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +internal static partial class Interop +{ + internal static partial class ComCtl32 + { + /// + /// Represents MonthCalendar control part constants. + /// Copied form CommCtrl.h + /// + public enum MCGIP : uint + { + /// + /// Represents MCGIP_CALENDARCONTROL const. + /// + CALENDARCONTROL = 0, + + /// + /// Represents MCGIP_NEXT const. + /// + NEXT = 1, + + /// + /// Represents MCGIP_PREV const. + /// + PREV = 2, + + /// + /// Represents MCGIP_FOOTER const. + /// + FOOTER = 3, + + /// + /// Represents MCGIP_CALENDAR const. + /// + CALENDAR = 4, + + /// + /// Represents MCGIP_CALENDARHEADER const. + /// + CALENDARHEADER = 5, + + /// + /// Represents MCGIP_CALENDARBODY const. + /// + CALENDARBODY = 6, + + /// + /// Represents MCGIP_CALENDARROW const. + /// + CALENDARROW = 7, + + /// + /// Represents MCGIP_CALENDARCELL const. + /// + CALENDARCELL = 8 + } + } +} diff --git a/src/Common/src/Interop/ComCtl32/Interop.MCGRIDINFO.cs b/src/Common/src/Interop/ComCtl32/Interop.MCGRIDINFO.cs new file mode 100644 index 00000000000..6406b870904 --- /dev/null +++ b/src/Common/src/Interop/ComCtl32/Interop.MCGRIDINFO.cs @@ -0,0 +1,37 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Runtime.InteropServices; +using static System.Windows.Forms.NativeMethods; + +internal static partial class Interop +{ + internal static partial class ComCtl32 + { + /// + /// MonthCalendar grid info structure. + /// Copied form CommCtrl.h + /// + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] + public unsafe struct MCGRIDINFO + { + public uint cbSize; + public MCGIP dwPart; + public MCGIF dwFlags; + public int iCalendar; + public int iRow; + public int iCol; + public bool bSelected; + public Kernel32.SYSTEMTIME stStart; + public Kernel32.SYSTEMTIME stEnd; + public RECT rc; + public string pszName; + public uint cchName; + } + + [DllImport(ExternDll.User32, CharSet = CharSet.Auto)] + public extern static IntPtr SendMessage(HandleRef hWnd, int Msg, int wParam, [In, Out] ref MCGRIDINFO gridInfo); + } +} diff --git a/src/Common/src/Interop/ComCtl32/Interop.MCHITTESTINFO.cs b/src/Common/src/Interop/ComCtl32/Interop.MCHITTESTINFO.cs new file mode 100644 index 00000000000..c266ab9865c --- /dev/null +++ b/src/Common/src/Interop/ComCtl32/Interop.MCHITTESTINFO.cs @@ -0,0 +1,27 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class ComCtl32 + { + /// + /// + /// + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] + public struct MCHITTESTINFO + { + public int cbSize; + public POINT pt; + public int uHit; + public Kernel32.SYSTEMTIME st; + public RECT rc; + public int iOffset; + public int iRow; + public int iCol; + } + } +} diff --git a/src/Common/src/Interop/ComCtl32/Interop.MCHT.cs b/src/Common/src/Interop/ComCtl32/Interop.MCHT.cs new file mode 100644 index 00000000000..78c666d79e7 --- /dev/null +++ b/src/Common/src/Interop/ComCtl32/Interop.MCHT.cs @@ -0,0 +1,116 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +internal static partial class Interop +{ + internal static partial class ComCtl32 + { + /// + /// Represents MonthCalendar Control HitTest values. + /// Copied form CommCtrl.h + /// + public enum MCHT + { + /// + /// MCHT_TITLE + /// + TITLE = 0x00010000, + + /// + /// Represents MCHT_CALENDAR const. + /// + CALENDAR = 0x00020000, + + /// + /// Represents MCHT_TODAYLINK const. + /// + TODAYLINK = 0x00030000, + + /// + /// Represents MCHT_CALENDARCONTROL const. + /// + CALENDARCONTROL = 0x00100000, + + /// + /// Represents MCHT_NEXT const. + /// + NEXT = 0x01000000, + + /// + /// Represents MCHT_PREV const. + /// + PREV = 0x02000000, + + /// + /// Represents MCHT_NOWHERE const. + /// + NOWHERE = 0x00000000, + + /// + /// Represents MCHT_TITLEBK const. + /// + TITLEBK = TITLE, + + /// + /// Represents MCHT_TITLEMONTH const. + /// + TITLEMONTH = TITLE | 0x0001, + + /// + /// Represents MCHT_TITLEYEAR const. + /// + TITLEYEAR = TITLE | 0x0002, + + /// + /// Represents MCHT_TITLEBTNNEXT const. + /// + TITLEBTNNEXT = TITLE | NEXT | 0x0003, + + /// + /// Represents MCHT_TITLEBTNPREV const. + /// + TITLEBTNPREV = TITLE | PREV | 0x0003, + + /// + /// Represents MCHT_CALENDARBK const. + /// + CALENDARBK = CALENDAR, + + /// + /// Represents MCHT_CALENDARDATE const. + /// + CALENDARDATE = CALENDAR | 0x0001, + + /// + /// Represents MCHT_CALENDARDATENEXT const. + /// + CALENDARDATENEXT = CALENDARDATE | NEXT, + + /// + /// Represents MCHT_CALENDARDATEPREV const. + /// + CALENDARDATEPREV = CALENDARDATE | PREV, + + /// + /// Represents MCHT_CALENDARDAY const. + /// + CALENDARDAY = CALENDAR | 0x0002, + + /// + /// Represents MCHT_CALENDARWEEKNUM const. + /// + CALENDARWEEKNUM = CALENDAR | 0x0003, + + /// + /// Represents MCHT_CALENDARDATEMIN const. + /// + CALENDARDATEMIN = CALENDAR | 0x0004, + + /// + /// Represents MCHT_CALENDARDATEMAX const. + /// + CALENDARDATEMAX = CALENDAR | 0x0005 + } + } +} diff --git a/src/Common/src/Interop/ComCtl32/Interop.MCM.cs b/src/Common/src/Interop/ComCtl32/Interop.MCM.cs new file mode 100644 index 00000000000..9547c236941 --- /dev/null +++ b/src/Common/src/Interop/ComCtl32/Interop.MCM.cs @@ -0,0 +1,101 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +internal static partial class Interop +{ + internal static partial class ComCtl32 + { + /// + /// Represents MonthCalendar Control Messages. + /// Copied form CommCtrl.h + /// + public enum MCM + { + /// + /// Represents MCM_FIRST const. + /// + FIRST = 0x1000, + + /// + /// Represents MCM_GETCURSEL const. + /// + GETCURSEL = FIRST + 1, + + /// + /// Represents MCM_SETMAXSELCOUNT const. + /// + SETMAXSELCOUNT = FIRST + 4, + + /// + /// Represents MCM_GETSELRANGE const. + /// + GETSELRANGE = FIRST + 5, + + /// + /// Represents MCM_SETSELRANGE const. + /// + SETSELRANGE = FIRST + 6, + + /// + /// Represents MCM_GETMONTHRANGE const. + /// + GETMONTHRANGE = FIRST + 7, + + /// + /// Represents MCM_GETMINREQRECT const. + /// + GETMINREQRECT = FIRST + 9, + + /// + /// Represents MCM_SETCOLOR const. + /// + SETCOLOR = FIRST + 10, + + /// + /// Represents MCM_SETTODAY const. + /// + SETTODAY = FIRST + 12, + + /// + /// Represents MCM_GETTODAY const. + /// + GETTODAY = FIRST + 13, + + /// + /// Represents MCM_HITTEST const. + /// + HITTEST = FIRST + 14, + + /// + /// Represents MCM_SETFIRSTDAYOFWEEK const. + /// + SETFIRSTDAYOFWEEK = FIRST + 15, + + /// + /// Represents MCM_GETRANGE const. + /// + GETRANGE = FIRST + 17, + + /// + /// Represents MCM_SETRANGE const. + /// + SETRANGE = FIRST + 18, + + /// + /// Represents MCM_SETMONTHDELTA const. + /// + SETMONTHDELTA = FIRST + 20, + + /// + /// Represents MCM_GETMAXTODAYWIDTH const. + /// + GETMAXTODAYWIDTH = FIRST + 21, + + /// + /// Represents MCM_GETCALENDARGRIDINFO const. + /// + GETCALENDARGRIDINFO = FIRST + 24 + } + } +} diff --git a/src/Common/src/Interop/ComCtl32/Interop.MCS.cs b/src/Common/src/Interop/ComCtl32/Interop.MCS.cs new file mode 100644 index 00000000000..78b739dfa22 --- /dev/null +++ b/src/Common/src/Interop/ComCtl32/Interop.MCS.cs @@ -0,0 +1,41 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +internal static partial class Interop +{ + internal static partial class ComCtl32 + { + /// + /// Represents MonthCalendar Control styles constatnts. + /// Copied form CommCtrl.h + /// + public enum MCS + { + /// + /// Represents MCS_DAYSTATE const. + /// + DAYSTATE = 0x0001, + + /// + /// Represents MCS_MULTISELECT const. + /// + MULTISELECT = 0x0002, + + /// + /// Represents MCS_WEEKNUMBERS const. + /// + WEEKNUMBERS = 0x0004, + + /// + /// Represents MCS_NOTODAYCIRCLE const. + /// + NOTODAYCIRCLE = 0x0008, + + /// + /// Represents MCS_NOTODAY const. + /// + NOTODAY = 0x0010 + } + } +} diff --git a/src/Common/src/Interop/ComCtl32/Interop.MCSC.cs b/src/Common/src/Interop/ComCtl32/Interop.MCSC.cs new file mode 100644 index 00000000000..e0a51e8c028 --- /dev/null +++ b/src/Common/src/Interop/ComCtl32/Interop.MCSC.cs @@ -0,0 +1,46 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +internal static partial class Interop +{ + internal static partial class ComCtl32 + { + /// + /// Represents MonthCalendar control size and color constants. + /// Copied form CommCtrl.h + /// + public enum MCSC + { + /// + /// Represents MCSC_BACKGROUND const. + /// + BACKGROUND = 0, + + /// + /// Represents MCSC_TEXT const. + /// + TEXT = 1, + + /// + /// Represents MCSC_TITLEBK const. + /// + TITLEBK = 2, + + /// + /// Represents MCSC_TITLETEXT const. + /// + TITLETEXT = 3, + + /// + /// Represents MCSC_MONTHBK const. + /// + MONTHBK = 4, + + /// + /// Represents MCSC_TRAILINGTEXT const. + /// + TRAILINGTEXT = 5 + } + } +} diff --git a/src/Common/src/Interop/Interop.POINT.cs b/src/Common/src/Interop/Interop.POINT.cs new file mode 100644 index 00000000000..4166f242fef --- /dev/null +++ b/src/Common/src/Interop/Interop.POINT.cs @@ -0,0 +1,30 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Drawing; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + /// + /// Represetns the point structure. + /// + [StructLayout(LayoutKind.Sequential)] + internal struct POINT + { + internal int x; + internal int y; + + internal POINT(int x, int y) + { + this.x = x; + this.y = y; + } + + static public explicit operator POINT(Point pt) + { + return checked(new POINT((int)pt.X, (int)pt.Y)); + } + } +} diff --git a/src/Common/src/Interop/Kernel32/Interop.SYSTEMTIME.cs b/src/Common/src/Interop/Kernel32/Interop.SYSTEMTIME.cs new file mode 100644 index 00000000000..965b081789d --- /dev/null +++ b/src/Common/src/Interop/Kernel32/Interop.SYSTEMTIME.cs @@ -0,0 +1,21 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +internal partial class Interop +{ + internal partial class Kernel32 + { + public struct SYSTEMTIME + { + public short wYear; + public short wMonth; + public short wDayOfWeek; + public short wDay; + public short wHour; + public short wMinute; + public short wSecond; + public short wMilliseconds; + } + } +} diff --git a/src/Common/src/Interop/User32/Interop.MOUSEEVENTF.cs b/src/Common/src/Interop/User32/Interop.MOUSEEVENTF.cs new file mode 100644 index 00000000000..40955b96df1 --- /dev/null +++ b/src/Common/src/Interop/User32/Interop.MOUSEEVENTF.cs @@ -0,0 +1,30 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; + +internal static partial class Interop +{ + internal static partial class User32 + { + [Flags] + public enum MOUSEEVENTF : uint + { + MOVE = 0x0001, + LEFTDOWN = 0x0002, + LEFTUP = 0x0004, + RIGHTDOWN = 0x0008, + RIGHTUP = 0x0010, + MIDDLEDOWN = 0x0020, + MIDDLEUP = 0x0040, + XDOWN = 0x0080, + XUP = 0x0100, + WHEEL = 0x0800, + HWHEEL = 0x1000, + MOVE_NOCOALESCE = 0x2000, + VIRTUALDESK = 0x4000, + ABSOLUTE = 0x8000, + } + } +} diff --git a/src/Common/src/NativeMethods.cs b/src/Common/src/NativeMethods.cs index c847ad317fc..51a7c0ef19b 100644 --- a/src/Common/src/NativeMethods.cs +++ b/src/Common/src/NativeMethods.cs @@ -852,48 +852,15 @@ public static int MAKELCID(int lgid, int sort) MDITILE_VERTICAL = 0x0000, MDITILE_HORIZONTAL = 0x0001, MDITILE_SKIPDISABLED = 0x0002, - MCM_SETMAXSELCOUNT = (0x1000 + 4), - MCM_SETSELRANGE = (0x1000 + 6), - MCM_GETMONTHRANGE = (0x1000 + 7), - MCM_GETMINREQRECT = (0x1000 + 9), - MCM_SETCOLOR = (0x1000 + 10), - MCM_SETTODAY = (0x1000 + 12), - MCM_GETTODAY = (0x1000 + 13), - MCM_HITTEST = (0x1000 + 14), - MCM_SETFIRSTDAYOFWEEK = (0x1000 + 15), - MCM_SETRANGE = (0x1000 + 18), - MCM_SETMONTHDELTA = (0x1000 + 20), - MCM_GETMAXTODAYWIDTH = (0x1000 + 21), - MCHT_TITLE = 0x00010000, - MCHT_CALENDAR = 0x00020000, - MCHT_TODAYLINK = 0x00030000, - MCHT_TITLEBK = (0x00010000), - MCHT_TITLEMONTH = (0x00010000 | 0x0001), - MCHT_TITLEYEAR = (0x00010000 | 0x0002), - MCHT_TITLEBTNNEXT = (0x00010000 | 0x01000000 | 0x0003), - MCHT_TITLEBTNPREV = (0x00010000 | 0x02000000 | 0x0003), - MCHT_CALENDARBK = (0x00020000), - MCHT_CALENDARDATE = (0x00020000 | 0x0001), - MCHT_CALENDARDATENEXT = ((0x00020000 | 0x0001) | 0x01000000), - MCHT_CALENDARDATEPREV = ((0x00020000 | 0x0001) | 0x02000000), - MCHT_CALENDARDAY = (0x00020000 | 0x0002), - MCHT_CALENDARWEEKNUM = (0x00020000 | 0x0003), - MCSC_TEXT = 1, - MCSC_TITLEBK = 2, - MCSC_TITLETEXT = 3, - MCSC_MONTHBK = 4, - MCSC_TRAILINGTEXT = 5, + MCN_VIEWCHANGE = (0 - 750), // MCN_SELECT -4 - give state of calendar view MCN_SELCHANGE = ((0 - 750) + 1), MCN_GETDAYSTATE = ((0 - 750) + 3), MCN_SELECT = ((0 - 750) + 4), - MCS_DAYSTATE = 0x0001, - MCS_MULTISELECT = 0x0002, - MCS_WEEKNUMBERS = 0x0004, - MCS_NOTODAYCIRCLE = 0x0008, - MCS_NOTODAY = 0x0010, + MSAA_MENU_SIG = (unchecked((int)0xAA0DF00D)); + public const int NIM_ADD = 0x00000000, NIM_MODIFY = 0x00000001, NIM_DELETE = 0x00000002, @@ -2707,27 +2674,6 @@ public class Ole public const int PICTYPE_ENHMETAFILE = 4; } - [StructLayout(LayoutKind.Sequential)] - public class SYSTEMTIME - { - public short wYear; - public short wMonth; - public short wDayOfWeek; - public short wDay; - public short wHour; - public short wMinute; - public short wSecond; - public short wMilliseconds; - - public override string ToString() - { - return "[SYSTEMTIME: " - + wDay.ToString(CultureInfo.InvariantCulture) + "/" + wMonth.ToString(CultureInfo.InvariantCulture) + "/" + wYear.ToString(CultureInfo.InvariantCulture) - + " " + wHour.ToString(CultureInfo.InvariantCulture) + ":" + wMinute.ToString(CultureInfo.InvariantCulture) + ":" + wSecond.ToString(CultureInfo.InvariantCulture) - + "]"; - } - } - [StructLayout(LayoutKind.Sequential)] public class COMRECT { @@ -3745,36 +3691,19 @@ public struct NMCUSTOMDRAW public IntPtr lItemlParam; } - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] - public class MCHITTESTINFO - { - public int cbSize = Marshal.SizeOf(); - public int pt_x = 0; - public int pt_y = 0; - public int uHit = 0; - public short st_wYear = 0; - public short st_wMonth = 0; - public short st_wDayOfWeek = 0; - public short st_wDay = 0; - public short st_wHour = 0; - public short st_wMinute = 0; - public short st_wSecond = 0; - public short st_wMilliseconds = 0; - } - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] public class NMSELCHANGE { public NMHDR nmhdr; - public SYSTEMTIME stSelStart = null; - public SYSTEMTIME stSelEnd = null; + public Interop.Kernel32.SYSTEMTIME stSelStart; + public Interop.Kernel32.SYSTEMTIME stSelEnd; } [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] public class NMDAYSTATE { public NMHDR nmhdr; - public SYSTEMTIME stStart = null; + public Interop.Kernel32.SYSTEMTIME stStart; public int cDayState = 0; public IntPtr prgDayState; } @@ -4033,7 +3962,7 @@ public class NMDATETIMECHANGE { public NMHDR nmhdr; public int dwFlags = 0; - public SYSTEMTIME st = null; + public Interop.Kernel32.SYSTEMTIME st; } [StructLayout(LayoutKind.Sequential)] @@ -4093,6 +4022,8 @@ public struct HARDWAREINPUT public short wParamH; } + public const int INPUT_MOUSE = 0; + [StructLayout(LayoutKind.Sequential)] public struct INPUT { diff --git a/src/Common/src/UnsafeNativeMethods.cs b/src/Common/src/UnsafeNativeMethods.cs index bcd7a32c855..a8c89992027 100644 --- a/src/Common/src/UnsafeNativeMethods.cs +++ b/src/Common/src/UnsafeNativeMethods.cs @@ -316,6 +316,9 @@ public static StringBuilder GetModuleFileNameLongPath(HandleRef hModule) [DllImport(ExternDll.User32, ExactSpelling = true, SetLastError = true, CharSet = CharSet.Auto)] public static extern uint SendInput(uint nInputs, NativeMethods.INPUT[] pInputs, int cbSize); + [DllImport(ExternDll.User32, SetLastError = true)] + internal static extern int SendInput(int nInputs, ref NativeMethods.INPUT input, int cbSize); + #endregion [DllImport(ExternDll.User32, ExactSpelling = true)] @@ -568,10 +571,10 @@ public static StringBuilder GetModuleFileNameLongPath(HandleRef hModule) // For MonthCalendar // [DllImport(ExternDll.User32, CharSet = CharSet.Auto)] - public static extern IntPtr SendMessage(HandleRef hWnd, int msg, int wParam, NativeMethods.MCHITTESTINFO lParam); + public static extern IntPtr SendMessage(HandleRef hWnd, int msg, int wParam, ref Interop.ComCtl32.MCHITTESTINFO lParam); [DllImport(ExternDll.User32, CharSet = CharSet.Auto)] - public static extern IntPtr SendMessage(HandleRef hWnd, int msg, int wParam, NativeMethods.SYSTEMTIME lParam); + public static extern IntPtr SendMessage(HandleRef hWnd, int msg, int wParam, ref Interop.Kernel32.SYSTEMTIME lParam); [DllImport(ExternDll.User32, CharSet = CharSet.Auto)] public static extern IntPtr SendMessage(HandleRef hWnd, int msg, int wParam, NativeMethods.SYSTEMTIMEARRAY lParam); @@ -1110,8 +1113,8 @@ int GetExtendedControl( [PreserveSig] Interop.HRESULT TransformCoords( - Point *pPtlHimetric, - PointF *pPtfContainer, + Point* pPtlHimetric, + PointF* pPtfContainer, uint dwFlags); [PreserveSig] @@ -2832,7 +2835,7 @@ Interop.HRESULT GetExtent( uint dwDrawAspect, int lindex, NativeMethods.tagDVTARGETDEVICE ptd, - Size *lpsizel); + Size* lpsizel); } [ComImport(), Guid("0000010C-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] @@ -4694,5 +4697,8 @@ public interface IScrollItemProvider /// void ScrollIntoView(); } + + [DllImport(ExternDll.User32, CharSet = CharSet.Auto, ExactSpelling = true, SetLastError = true)] + internal static extern bool GetPhysicalCursorPos([In, Out] ref Interop.POINT pt); } } diff --git a/src/System.Windows.Forms.Design/src/System.Windows.Forms.Design.csproj b/src/System.Windows.Forms.Design/src/System.Windows.Forms.Design.csproj index d935ace9788..372cdae3327 100644 --- a/src/System.Windows.Forms.Design/src/System.Windows.Forms.Design.csproj +++ b/src/System.Windows.Forms.Design/src/System.Windows.Forms.Design.csproj @@ -44,10 +44,15 @@ + + + + + @@ -55,6 +60,7 @@ + diff --git a/src/System.Windows.Forms/src/Resources/SR.resx b/src/System.Windows.Forms/src/Resources/SR.resx index c361074b762..8768e3d9f78 100644 --- a/src/System.Windows.Forms/src/Resources/SR.resx +++ b/src/System.Windows.Forms/src/Resources/SR.resx @@ -4511,6 +4511,9 @@ Stack trace where the illegal operation occurred was: Indicates which annual dates should be boldface. + + Calendar body + The number of rows and columns of months in a month calendar. @@ -4541,12 +4544,18 @@ Stack trace where the illegal operation occurred was: Indicates which monthly dates to bold. + + Next + Occurs when the range of dates changes due to user selection, or through next/previous month navigation. Occurs when the user selects a date or a range of dates. + + Previous + Unable to set the MonthCalendar range from {0} to {1}. @@ -4580,6 +4589,9 @@ Stack trace where the illegal operation occurred was: The color used to display text within the calendar's title. + + Today: {0} + The current day. diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.cs.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.cs.xlf index 86ea6478f3f..6d7fda9be24 100644 --- a/src/System.Windows.Forms/src/Resources/xlf/SR.cs.xlf +++ b/src/System.Windows.Forms/src/Resources/xlf/SR.cs.xlf @@ -7481,6 +7481,11 @@ Trasování zásobníku, kde došlo k neplatné operaci: Určuje, která data v rámci roku mají být zobrazena tučně. + + Calendar body + Calendar body + + The number of rows and columns of months in a month calendar. Počet řádků a sloupců měsíců v měsíčním kalendáři. @@ -7531,6 +7536,11 @@ Trasování zásobníku, kde došlo k neplatné operaci: Určuje, která data v rámci měsíce mají být zobrazena tučně. + + Next + Next + + Occurs when the range of dates changes due to user selection, or through next/previous month navigation. Vyvolá se v případě, že se rozsah dat změní z důvodu výběru uživatele nebo prostřednictvím přechodu na další nebo předchozí měsíc. @@ -7541,6 +7551,11 @@ Trasování zásobníku, kde došlo k neplatné operaci: Vyvolá se v případě, že uživatel vybere datum nebo rozsah dat. + + Previous + Previous + + Unable to set the MonthCalendar range from {0} to {1}. Rozsah objektu MonthCalendar nelze nastavit od {0} do {1}. @@ -7616,6 +7631,11 @@ Trasování zásobníku, kde došlo k neplatné operaci: Barva pozadí použitá k zobrazení textu v nadpisu kalendáře. + + Today: {0} + Today: {0} + + The current day. Aktuální den diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.de.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.de.xlf index e889c39c5f1..6ed0b5852f9 100644 --- a/src/System.Windows.Forms/src/Resources/xlf/SR.de.xlf +++ b/src/System.Windows.Forms/src/Resources/xlf/SR.de.xlf @@ -7481,6 +7481,11 @@ Stapelüberwachung, in der der unzulässige Vorgang auftrat: Gibt an, welche jährlichen Tage fett angezeigt werden sollen. + + Calendar body + Calendar body + + The number of rows and columns of months in a month calendar. Die Anzahl der Spalten und Zeilen von Monaten in einem Monatskalender. @@ -7531,6 +7536,11 @@ Stapelüberwachung, in der der unzulässige Vorgang auftrat: Gibt an, welche monatlichen Daten fett angezeigt werden sollen. + + Next + Next + + Occurs when the range of dates changes due to user selection, or through next/previous month navigation. Tritt ein, wenn sich der Datumsbereich aufgrund einer Benutzerauswahl oder einer Navigation zum nächsten oder vorherigen Monat ändert. @@ -7541,6 +7551,11 @@ Stapelüberwachung, in der der unzulässige Vorgang auftrat: Tritt ein, wenn der Benutzer ein Datum oder einen Datumsbereich auswählt. + + Previous + Previous + + Unable to set the MonthCalendar range from {0} to {1}. Der MonthCalendar-Bereich kann nicht auf {0} bis {1} festgelegt werden. @@ -7616,6 +7631,11 @@ Stapelüberwachung, in der der unzulässige Vorgang auftrat: Die Farbe, in der der Text in der Kalenderüberschrift angezeigt wird. + + Today: {0} + Today: {0} + + The current day. Der aktuelle Tag. diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.es.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.es.xlf index 3a7e21ada46..22274f5b853 100644 --- a/src/System.Windows.Forms/src/Resources/xlf/SR.es.xlf +++ b/src/System.Windows.Forms/src/Resources/xlf/SR.es.xlf @@ -7481,6 +7481,11 @@ El seguimiento de la pila donde tuvo lugar la operación no válida fue: Indica qué fechas anuales se deben poner en negrita. + + Calendar body + Calendar body + + The number of rows and columns of months in a month calendar. Número de filas y columnas de meses en un calendario mensual. @@ -7531,6 +7536,11 @@ El seguimiento de la pila donde tuvo lugar la operación no válida fue: Indica qué fechas mensuales deben aparecer en negrita. + + Next + Next + + Occurs when the range of dates changes due to user selection, or through next/previous month navigation. Tiene lugar cuando cambia el intervalo de fechas debido a una selección del usuario, o por navegación al mes anterior o posterior. @@ -7541,6 +7551,11 @@ El seguimiento de la pila donde tuvo lugar la operación no válida fue: Tiene lugar cuando el usuario selecciona una fecha o intervalo de fechas. + + Previous + Previous + + Unable to set the MonthCalendar range from {0} to {1}. No se puede establecer el intervalo del elemento MonthCalendar entre {0} y {1}. @@ -7616,6 +7631,11 @@ El seguimiento de la pila donde tuvo lugar la operación no válida fue: Color utilizado para mostrar texto en el título del calendario. + + Today: {0} + Today: {0} + + The current day. Día actual. diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.fr.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.fr.xlf index 36e25c88ec6..1725c6476fb 100644 --- a/src/System.Windows.Forms/src/Resources/xlf/SR.fr.xlf +++ b/src/System.Windows.Forms/src/Resources/xlf/SR.fr.xlf @@ -7481,6 +7481,11 @@ Cette opération non conforme s'est produite sur la trace de la pile : Indique les dates annuelles à mettre en gras. + + Calendar body + Calendar body + + The number of rows and columns of months in a month calendar. Le nombre de lignes et de colonnes de mois dans un calendrier mensuel. @@ -7531,6 +7536,11 @@ Cette opération non conforme s'est produite sur la trace de la pile : Indique les dates mensuelles à mettre en gras. + + Next + Next + + Occurs when the range of dates changes due to user selection, or through next/previous month navigation. Se produit lorsque la plage de dates change en raison d'une sélection de l'utilisateur ou lors de l'utilisation de la navigation entre les mois. @@ -7541,6 +7551,11 @@ Cette opération non conforme s'est produite sur la trace de la pile : Se produit lorsqu'un utilisateur sélectionne une date ou une plage de dates. + + Previous + Previous + + Unable to set the MonthCalendar range from {0} to {1}. Impossible de définir la plage MonthCalendar de {0} à {1}. @@ -7616,6 +7631,11 @@ Cette opération non conforme s'est produite sur la trace de la pile : La couleur utilisée pour afficher le texte dans le titre du calendrier. + + Today: {0} + Today: {0} + + The current day. Le jour actuel. diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.it.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.it.xlf index ac861493d42..a9eb773e9ce 100644 --- a/src/System.Windows.Forms/src/Resources/xlf/SR.it.xlf +++ b/src/System.Windows.Forms/src/Resources/xlf/SR.it.xlf @@ -7481,6 +7481,11 @@ Traccia dello stack da cui si è verificata l'operazione non valida: Indica le date dell'anno da visualizzare in grassetto. + + Calendar body + Calendar body + + The number of rows and columns of months in a month calendar. Numero di righe e colonne dei mesi in un calendario mensile. @@ -7531,6 +7536,11 @@ Traccia dello stack da cui si è verificata l'operazione non valida: Indica le date mensili da visualizzare in grassetto. + + Next + Next + + Occurs when the range of dates changes due to user selection, or through next/previous month navigation. Generato quando l'intervallo delle date cambia in seguito alla selezione dell'utente o alla navigazione al mese precedente o successivo. @@ -7541,6 +7551,11 @@ Traccia dello stack da cui si è verificata l'operazione non valida: Generato quando l'utente seleziona una data o un intervallo di date. + + Previous + Previous + + Unable to set the MonthCalendar range from {0} to {1}. Impossibile impostare l'intervallo di MonthCalendar da {0} a {1}. @@ -7616,6 +7631,11 @@ Traccia dello stack da cui si è verificata l'operazione non valida: Colore utilizzato per visualizzare il testo del titolo del calendario. + + Today: {0} + Today: {0} + + The current day. La data odierna. diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.ja.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.ja.xlf index f9eb1adb976..16c293aeab7 100644 --- a/src/System.Windows.Forms/src/Resources/xlf/SR.ja.xlf +++ b/src/System.Windows.Forms/src/Resources/xlf/SR.ja.xlf @@ -7481,6 +7481,11 @@ Stack trace where the illegal operation occurred was: 年間のどの日付を太字にするかを示します。 + + Calendar body + Calendar body + + The number of rows and columns of months in a month calendar. 月表示カレンダーの月の行と列の数です。 @@ -7531,6 +7536,11 @@ Stack trace where the illegal operation occurred was: 月ごとに太字で表示する日付を示します。 + + Next + Next + + Occurs when the range of dates changes due to user selection, or through next/previous month navigation. ユーザーの選択、または翌/先月の変更により、日にちの範囲が変更されたときに発生します。 @@ -7541,6 +7551,11 @@ Stack trace where the illegal operation occurred was: ユーザーがある日付を選択したとき、または日数の範囲を指定したときに発生します。 + + Previous + Previous + + Unable to set the MonthCalendar range from {0} to {1}. MonthCalendar の範囲を {0} から {1} に設定できません。 @@ -7616,6 +7631,11 @@ Stack trace where the illegal operation occurred was: カレンダーの表題内のテキストの色です。 + + Today: {0} + Today: {0} + + The current day. 現在の日です。 diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.ko.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.ko.xlf index 3d56ca01edd..d35c34c5a4a 100644 --- a/src/System.Windows.Forms/src/Resources/xlf/SR.ko.xlf +++ b/src/System.Windows.Forms/src/Resources/xlf/SR.ko.xlf @@ -7481,6 +7481,11 @@ Stack trace where the illegal operation occurred was: 매년 굵게 표시할 날짜를 나타냅니다. + + Calendar body + Calendar body + + The number of rows and columns of months in a month calendar. 달력에서 달의 행 및 열 개수입니다. @@ -7531,6 +7536,11 @@ Stack trace where the illegal operation occurred was: 매월 굵게 표시할 날짜를 나타냅니다. + + Next + Next + + Occurs when the range of dates changes due to user selection, or through next/previous month navigation. 사용자 선택이나 달 탐색으로 인해 날짜 범위가 변경될 때 발생합니다. @@ -7541,6 +7551,11 @@ Stack trace where the illegal operation occurred was: 날짜 또는 날짜 범위를 선택할 때 발생합니다. + + Previous + Previous + + Unable to set the MonthCalendar range from {0} to {1}. MonthCalendar의 범위를 {0}에서 {1} 사이로 설정할 수 없습니다. @@ -7616,6 +7631,11 @@ Stack trace where the illegal operation occurred was: 달력 제목의 텍스트를 표시하는 데 사용할 색입니다. + + Today: {0} + Today: {0} + + The current day. 현재 날짜입니다. diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.pl.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.pl.xlf index 897c72a4910..b82077fa54b 100644 --- a/src/System.Windows.Forms/src/Resources/xlf/SR.pl.xlf +++ b/src/System.Windows.Forms/src/Resources/xlf/SR.pl.xlf @@ -7481,6 +7481,11 @@ Stos śledzenia, w którym wystąpiła zabroniona operacja: Wskazuje, które daty lat mają być pogrubione. + + Calendar body + Calendar body + + The number of rows and columns of months in a month calendar. Liczba wierszy i kolumn miesięcy w kalendarzu miesięcznym. @@ -7531,6 +7536,11 @@ Stos śledzenia, w którym wystąpiła zabroniona operacja: Wskazuje, które dni miesiąca mają zostać pogrubione. + + Next + Next + + Occurs when the range of dates changes due to user selection, or through next/previous month navigation. Występuje, gdy zakres dat zmieni się w wyniku zaznaczenia przez użytkownika lub przez nawigację do następnego/poprzedniego miesiąca. @@ -7541,6 +7551,11 @@ Stos śledzenia, w którym wystąpiła zabroniona operacja: Występuje, gdy użytkownik zaznaczy datę lub zakres dat. + + Previous + Previous + + Unable to set the MonthCalendar range from {0} to {1}. Nie można ustawić zakresu formantu MonthCalendar od {0} do {1}. @@ -7616,6 +7631,11 @@ Stos śledzenia, w którym wystąpiła zabroniona operacja: Kolor używany do wyświetlania tekstu w tytule kalendarza. + + Today: {0} + Today: {0} + + The current day. Bieżący dzień. diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.pt-BR.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.pt-BR.xlf index ffc28652938..450da311f21 100644 --- a/src/System.Windows.Forms/src/Resources/xlf/SR.pt-BR.xlf +++ b/src/System.Windows.Forms/src/Resources/xlf/SR.pt-BR.xlf @@ -7481,6 +7481,11 @@ Rastreamento de pilha em que a operação ilegal ocorreu: Indica as datas do ano que devem ser exibidas em negrito. + + Calendar body + Calendar body + + The number of rows and columns of months in a month calendar. O número de linhas e colunas dos meses em um calendário mensal. @@ -7531,6 +7536,11 @@ Rastreamento de pilha em que a operação ilegal ocorreu: Indica as datas mensais que devem ser exibidas em negrito. + + Next + Next + + Occurs when the range of dates changes due to user selection, or through next/previous month navigation. Ocorre quando o intervalo de datas é alterado devido à seleção de usuário ou através de navegação para o mês seguinte/anterior. @@ -7541,6 +7551,11 @@ Rastreamento de pilha em que a operação ilegal ocorreu: Ocorre quando o usuário seleciona uma data ou um intervalo de datas. + + Previous + Previous + + Unable to set the MonthCalendar range from {0} to {1}. Não é possível definir o intervalo de MonthCalendar de {0} a {1}. @@ -7616,6 +7631,11 @@ Rastreamento de pilha em que a operação ilegal ocorreu: A cor usada para exibir texto no título do calendário. + + Today: {0} + Today: {0} + + The current day. O dia atual. diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.ru.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.ru.xlf index ad57689dd7d..6bdc33c51b0 100644 --- a/src/System.Windows.Forms/src/Resources/xlf/SR.ru.xlf +++ b/src/System.Windows.Forms/src/Resources/xlf/SR.ru.xlf @@ -7482,6 +7482,11 @@ Stack trace where the illegal operation occurred was: Показывает, будут ли даты отображаться жирным шрифтом. + + Calendar body + Calendar body + + The number of rows and columns of months in a month calendar. Число строк и столбцов для месяца в месячном календаре. @@ -7532,6 +7537,11 @@ Stack trace where the illegal operation occurred was: Указывает, какие числа месяца выделять жирным шрифтом. + + Next + Next + + Occurs when the range of dates changes due to user selection, or through next/previous month navigation. Происходит при изменении пользователем выбранного диапазона дат путем выделения или выбора предыдущего/следующего месяца. @@ -7542,6 +7552,11 @@ Stack trace where the illegal operation occurred was: Происходит при выборе пользователем даты или диапазона дат. + + Previous + Previous + + Unable to set the MonthCalendar range from {0} to {1}. Невозможно установить диапазон MonthCalendar от {0} до {1}. @@ -7617,6 +7632,11 @@ Stack trace where the illegal operation occurred was: Цвет для отображения текста в заглавии календаря. + + Today: {0} + Today: {0} + + The current day. Текущая дата. diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.tr.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.tr.xlf index 323556ca24b..5cef44e49f3 100644 --- a/src/System.Windows.Forms/src/Resources/xlf/SR.tr.xlf +++ b/src/System.Windows.Forms/src/Resources/xlf/SR.tr.xlf @@ -7481,6 +7481,11 @@ Geçersiz işlemin gerçekleştiği yığın izi: Yıl içinde hangi tarihlerin kalın görüntülenmesi gerektiğini gösterir. + + Calendar body + Calendar body + + The number of rows and columns of months in a month calendar. Aylık takvimde ayların gösterileceği satır ve sütun sayısı. @@ -7531,6 +7536,11 @@ Geçersiz işlemin gerçekleştiği yığın izi: Ayın hangi günlerinin kalın görüntüleneceğini gösterir. + + Next + Next + + Occurs when the range of dates changes due to user selection, or through next/previous month navigation. Kullanıcı seçimi veya takvimde önceki/sonraki aya gitme dolayısıyla tarih aralıkları değiştiğinde gerçekleşir. @@ -7541,6 +7551,11 @@ Geçersiz işlemin gerçekleştiği yığın izi: Kullanıcı bir tarih veya tarih aralığı seçtiğinde gerçekleşir. + + Previous + Previous + + Unable to set the MonthCalendar range from {0} to {1}. MonthCalendar aralığı {0} değerinden {1} değerine ayarlanamıyor. @@ -7616,6 +7631,11 @@ Geçersiz işlemin gerçekleştiği yığın izi: Takvim başlığında metin görüntülemek için kullanılan renk. + + Today: {0} + Today: {0} + + The current day. Geçerli gün. diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.zh-Hans.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.zh-Hans.xlf index 81a6c288001..96cb3a094a0 100644 --- a/src/System.Windows.Forms/src/Resources/xlf/SR.zh-Hans.xlf +++ b/src/System.Windows.Forms/src/Resources/xlf/SR.zh-Hans.xlf @@ -7481,6 +7481,11 @@ Stack trace where the illegal operation occurred was: 指示一年中用粗体显示的日期。 + + Calendar body + Calendar body + + The number of rows and columns of months in a month calendar. 月历中各月的行数和列数。 @@ -7531,6 +7536,11 @@ Stack trace where the illegal operation occurred was: 指示一月中要用粗体显示的日期。 + + Next + Next + + Occurs when the range of dates changes due to user selection, or through next/previous month navigation. 在因用户选择或通过下个月/上个月导航而导致日期范围更改时发生。 @@ -7541,6 +7551,11 @@ Stack trace where the illegal operation occurred was: 在用户选择日期或日期范围时发生。 + + Previous + Previous + + Unable to set the MonthCalendar range from {0} to {1}. 无法将 MonthCalendar 的范围设置为从 {0} 到 {1}。 @@ -7616,6 +7631,11 @@ Stack trace where the illegal operation occurred was: 用于显示日历标题中文本的颜色。 + + Today: {0} + Today: {0} + + The current day. 当前日期。 diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.zh-Hant.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.zh-Hant.xlf index 990c13773b1..0649870bfce 100644 --- a/src/System.Windows.Forms/src/Resources/xlf/SR.zh-Hant.xlf +++ b/src/System.Windows.Forms/src/Resources/xlf/SR.zh-Hant.xlf @@ -7481,6 +7481,11 @@ Stack trace where the illegal operation occurred was: 表示要以粗體表示一年之中的哪些日期。 + + Calendar body + Calendar body + + The number of rows and columns of months in a month calendar. 月曆中月份欄和列的數目。 @@ -7531,6 +7536,11 @@ Stack trace where the illegal operation occurred was: 要以粗體表示一月之中的哪些日期。 + + Next + Next + + Occurs when the range of dates changes due to user selection, or through next/previous month navigation. 當因為使用者選取或透過下個月/上個月巡覽所造成日期範圍變更時發生。 @@ -7541,6 +7551,11 @@ Stack trace where the illegal operation occurred was: 當使用者選取日期或日期範圍時發生。 + + Previous + Previous + + Unable to set the MonthCalendar range from {0} to {1}. 無法將 MonthCalendar 的範圍設為從 {0} 到 {1}。 @@ -7616,6 +7631,11 @@ Stack trace where the illegal operation occurred was: 用來顯示日曆標題內文字的色彩。 + + Today: {0} + Today: {0} + + The current day. 目前的日期。 diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/DateTimePicker.cs b/src/System.Windows.Forms/src/System/Windows/Forms/DateTimePicker.cs index 71aaffa9867..d474ae2eaf6 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/DateTimePicker.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/DateTimePicker.cs @@ -221,7 +221,7 @@ public Color CalendarForeColor if (!value.Equals(calendarForeColor)) { calendarForeColor = value; - SetControlColor(NativeMethods.MCSC_TEXT, value); + SetControlColor(ComCtl32.MCSC.TEXT, value); } } } @@ -300,7 +300,7 @@ public Color CalendarTitleBackColor if (!value.Equals(calendarTitleBackColor)) { calendarTitleBackColor = value; - SetControlColor(NativeMethods.MCSC_TITLEBK, value); + SetControlColor(ComCtl32.MCSC.TITLEBK, value); } } } @@ -329,7 +329,7 @@ public Color CalendarTitleForeColor if (!value.Equals(calendarTitleForeColor)) { calendarTitleForeColor = value; - SetControlColor(NativeMethods.MCSC_TITLETEXT, value); + SetControlColor(ComCtl32.MCSC.TITLETEXT, value); } } } @@ -358,7 +358,7 @@ public Color CalendarTrailingForeColor if (!value.Equals(calendarTrailingText)) { calendarTrailingText = value; - SetControlColor(NativeMethods.MCSC_TRAILINGTEXT, value); + SetControlColor(ComCtl32.MCSC.TRAILINGTEXT, value); } } } @@ -387,7 +387,7 @@ public Color CalendarMonthBackground if (!value.Equals(calendarMonthBackground)) { calendarMonthBackground = value; - SetControlColor(NativeMethods.MCSC_MONTHBK, value); + SetControlColor(ComCtl32.MCSC.MONTHBK, value); } } } @@ -408,8 +408,8 @@ public bool Checked // the information from win32 DateTimePicker is reliable only when ShowCheckBoxes is True if (ShowCheckBox && IsHandleCreated) { - NativeMethods.SYSTEMTIME sys = new NativeMethods.SYSTEMTIME(); - int gdt = (int)UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.DTM_GETSYSTEMTIME, 0, sys); + Kernel32.SYSTEMTIME sys = new Kernel32.SYSTEMTIME(); + int gdt = (int)UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.DTM_GETSYSTEMTIME, 0, ref sys); return gdt == NativeMethods.GDT_VALID; } else @@ -427,14 +427,13 @@ public bool Checked if (value) { int gdt = NativeMethods.GDT_VALID; - NativeMethods.SYSTEMTIME sys = DateTimePicker.DateTimeToSysTime(Value); - UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.DTM_SETSYSTEMTIME, gdt, sys); + Kernel32.SYSTEMTIME sys = DateTimePicker.DateTimeToSysTime(Value); + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.DTM_SETSYSTEMTIME, gdt, ref sys); } else { int gdt = NativeMethods.GDT_NONE; - NativeMethods.SYSTEMTIME sys = null; - UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.DTM_SETSYSTEMTIME, gdt, sys); + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.DTM_SETSYSTEMTIME, gdt, IntPtr.Zero); } } // this.validTime is used when the DateTimePicker receives date time change notification @@ -1054,8 +1053,8 @@ public DateTime Value * get propagated to createHandle */ int gdt = NativeMethods.GDT_VALID; - NativeMethods.SYSTEMTIME sys = DateTimePicker.DateTimeToSysTime(value); - UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.DTM_SETSYSTEMTIME, gdt, sys); + Kernel32.SYSTEMTIME sys = DateTimePicker.DateTimeToSysTime(value); + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.DTM_SETSYSTEMTIME, gdt, ref sys); } if (valueChanged) @@ -1151,14 +1150,13 @@ protected override void CreateHandle() * get propagated to setValue */ int gdt = NativeMethods.GDT_VALID; - NativeMethods.SYSTEMTIME sys = DateTimePicker.DateTimeToSysTime(Value); - UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.DTM_SETSYSTEMTIME, gdt, sys); + Kernel32.SYSTEMTIME sys = DateTimePicker.DateTimeToSysTime(Value); + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.DTM_SETSYSTEMTIME, gdt, ref sys); } else if (!validTime) { int gdt = NativeMethods.GDT_NONE; - NativeMethods.SYSTEMTIME sys = null; - UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.DTM_SETSYSTEMTIME, gdt, sys); + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.DTM_SETSYSTEMTIME, gdt, IntPtr.Zero); } if (format == DateTimePickerFormat.Custom) @@ -1386,8 +1384,8 @@ private void ResetValue() if (IsHandleCreated) { int gdt = NativeMethods.GDT_VALID; - NativeMethods.SYSTEMTIME sys = DateTimePicker.DateTimeToSysTime(value); - UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.DTM_SETSYSTEMTIME, gdt, sys); + Kernel32.SYSTEMTIME sys = DateTimePicker.DateTimeToSysTime(value); + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.DTM_SETSYSTEMTIME, gdt, ref sys); } // Updating Checked to false will set the control to "no date", @@ -1401,11 +1399,11 @@ private void ResetValue() /// /// If the handle has been created, this applies the color to the control /// - private void SetControlColor(int colorIndex, Color value) + private void SetControlColor(Interop.ComCtl32.MCSC colorIndex, Color value) { if (IsHandleCreated) { - SendMessage(NativeMethods.DTM_SETMCCOLOR, colorIndex, ColorTranslator.ToWin32(value)); + SendMessage(NativeMethods.DTM_SETMCCOLOR, (int)colorIndex, ColorTranslator.ToWin32(value)); } } @@ -1425,11 +1423,11 @@ private void SetControlCalendarFont() /// private void SetAllControlColors() { - SetControlColor(NativeMethods.MCSC_MONTHBK, calendarMonthBackground); - SetControlColor(NativeMethods.MCSC_TEXT, calendarForeColor); - SetControlColor(NativeMethods.MCSC_TITLEBK, calendarTitleBackColor); - SetControlColor(NativeMethods.MCSC_TITLETEXT, calendarTitleForeColor); - SetControlColor(NativeMethods.MCSC_TRAILINGTEXT, calendarTrailingText); + SetControlColor(ComCtl32.MCSC.MONTHBK, calendarMonthBackground); + SetControlColor(ComCtl32.MCSC.TEXT, calendarForeColor); + SetControlColor(ComCtl32.MCSC.TITLEBK, calendarTitleBackColor); + SetControlColor(ComCtl32.MCSC.TITLETEXT, calendarTitleForeColor); + SetControlColor(ComCtl32.MCSC.TRAILINGTEXT, calendarTrailingText); } /// @@ -1450,7 +1448,7 @@ private void SetRange(DateTime min, DateTime max) NativeMethods.SYSTEMTIMEARRAY sa = new NativeMethods.SYSTEMTIMEARRAY(); flags |= NativeMethods.GDTR_MIN | NativeMethods.GDTR_MAX; - NativeMethods.SYSTEMTIME sys = DateTimePicker.DateTimeToSysTime(min); + Kernel32.SYSTEMTIME sys = DateTimeToSysTime(min); sa.wYear1 = sys.wYear; sa.wMonth1 = sys.wMonth; sa.wDayOfWeek1 = sys.wDayOfWeek; @@ -1459,7 +1457,7 @@ private void SetRange(DateTime min, DateTime max) sa.wMinute1 = sys.wMinute; sa.wSecond1 = sys.wSecond; sa.wMilliseconds1 = sys.wMilliseconds; - sys = DateTimePicker.DateTimeToSysTime(max); + sys = DateTimeToSysTime(max); sa.wYear2 = sys.wYear; sa.wMonth2 = sys.wMonth; sa.wDayOfWeek2 = sys.wDayOfWeek; @@ -1747,9 +1745,9 @@ protected override void WndProc(ref Message m) /// Takes a DateTime value and returns a SYSTEMTIME struct /// Note: 1 second granularity /// - internal static NativeMethods.SYSTEMTIME DateTimeToSysTime(DateTime time) + internal static Kernel32.SYSTEMTIME DateTimeToSysTime(DateTime time) { - NativeMethods.SYSTEMTIME sys = new NativeMethods.SYSTEMTIME + Kernel32.SYSTEMTIME sys = new Kernel32.SYSTEMTIME { wYear = (short)time.Year, wMonth = (short)time.Month, @@ -1767,7 +1765,7 @@ internal static NativeMethods.SYSTEMTIME DateTimeToSysTime(DateTime time) /// Takes a SYSTEMTIME struct and returns a DateTime value /// Note: 1 second granularity. /// - internal static DateTime SysTimeToDateTime(NativeMethods.SYSTEMTIME s) + internal static DateTime SysTimeToDateTime(Kernel32.SYSTEMTIME s) { return new DateTime(s.wYear, s.wMonth, s.wDay, s.wHour, s.wMinute, s.wSecond); } diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/MonthCalendar.CalendarBodyAccessibleObject.cs b/src/System.Windows.Forms/src/System/Windows/Forms/MonthCalendar.CalendarBodyAccessibleObject.cs new file mode 100644 index 00000000000..eee7e774721 --- /dev/null +++ b/src/System.Windows.Forms/src/System/Windows/Forms/MonthCalendar.CalendarBodyAccessibleObject.cs @@ -0,0 +1,73 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using static Interop; + +namespace System.Windows.Forms +{ + public partial class MonthCalendar + { + /// + /// Represents the calendar body accessible object. + /// + internal class CalendarBodyAccessibleObject : CalendarChildAccessibleObject + { + private const int ChildId = 4; + + public CalendarBodyAccessibleObject(MonthCalendarAccessibleObject calendarAccessibleObject, int calendarIndex) + : base(calendarAccessibleObject, calendarIndex, CalendarChildType.CalendarBody) + { + } + + protected override RECT CalculateBoundingRectangle() + { + _calendarAccessibleObject.GetCalendarPartRectangle(_calendarIndex, ComCtl32.MCGIP.CALENDARBODY, 0, 0, out RECT calendarPartRectangle); + return calendarPartRectangle; + } + + internal override int GetChildId() => ChildId; + + internal override UnsafeNativeMethods.IRawElementProviderFragment FragmentNavigate(UnsafeNativeMethods.NavigateDirection direction) => + direction switch + { + UnsafeNativeMethods.NavigateDirection.NextSibling => new Func(() => + { + MonthCalendar owner = (MonthCalendar)_calendarAccessibleObject.Owner; + return owner.ShowToday ? _calendarAccessibleObject.GetCalendarChildAccessibleObject(_calendarIndex, CalendarChildType.TodayLink) : null; + })(), + UnsafeNativeMethods.NavigateDirection.PreviousSibling => _calendarAccessibleObject.GetCalendarChildAccessibleObject(_calendarIndex, CalendarChildType.CalendarHeader), + UnsafeNativeMethods.NavigateDirection.FirstChild => + _calendarAccessibleObject.GetCalendarChildAccessibleObject(_calendarIndex, CalendarChildType.CalendarRow, this, _calendarAccessibleObject.HasHeaderRow ? -1 : 0), + UnsafeNativeMethods.NavigateDirection.LastChild => + _calendarAccessibleObject.GetCalendarChildAccessibleObject(_calendarIndex, CalendarChildType.CalendarRow, this, _calendarAccessibleObject.RowCount - 1), + _ => base.FragmentNavigate(direction), + + }; + + public CalendarChildAccessibleObject GetFromPoint(ComCtl32.MCHITTESTINFO hitTestInfo) + { + switch ((ComCtl32.MCHT)hitTestInfo.uHit) + { + case ComCtl32.MCHT.CALENDARDAY: + case ComCtl32.MCHT.CALENDARWEEKNUM: + case ComCtl32.MCHT.CALENDARDATE: + AccessibleObject rowAccessibleObject = + _calendarAccessibleObject.GetCalendarChildAccessibleObject(_calendarIndex, CalendarChildType.CalendarRow, this, hitTestInfo.iRow); + return _calendarAccessibleObject.GetCalendarChildAccessibleObject(_calendarIndex, CalendarChildType.CalendarCell, rowAccessibleObject, hitTestInfo.iCol); + } + + return this; + } + + internal override object GetPropertyValue(int propertyID) => + propertyID switch + { + NativeMethods.UIA_NamePropertyId => SR.MonthCalendarBodyAccessibleName, + NativeMethods.UIA_IsGridPatternAvailablePropertyId => true, + NativeMethods.UIA_IsTablePatternAvailablePropertyId => true, + _ => base.GetPropertyValue(propertyID) + }; + } + } +} diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/MonthCalendar.CalendarButtonAccessibleObject.cs b/src/System.Windows.Forms/src/System/Windows/Forms/MonthCalendar.CalendarButtonAccessibleObject.cs new file mode 100644 index 00000000000..d6da1040dfb --- /dev/null +++ b/src/System.Windows.Forms/src/System/Windows/Forms/MonthCalendar.CalendarButtonAccessibleObject.cs @@ -0,0 +1,47 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using static Interop; + +namespace System.Windows.Forms +{ + public partial class MonthCalendar + { + internal abstract class CalendarButtonAccessibleObject : CalendarChildAccessibleObject + { + public CalendarButtonAccessibleObject(MonthCalendarAccessibleObject calendarAccessibleObject, int calendarIndex, CalendarButtonType buttonType) + : base(calendarAccessibleObject, calendarIndex, (CalendarChildType)buttonType) + { + } + + protected abstract CalendarButtonType ButtonType { get; } + + protected override RECT CalculateBoundingRectangle() + { + ComCtl32.MCGIP dwPart; + switch (ButtonType) + { + case CalendarButtonType.Previous: + dwPart = ComCtl32.MCGIP.PREV; + break; + + case CalendarButtonType.Next: + dwPart = ComCtl32.MCGIP.NEXT; + break; + + default: + return new RECT(); + } + + _calendarAccessibleObject.GetCalendarPartRectangle(_calendarIndex, dwPart, -1, -1, out RECT rectangle); + return rectangle; + } + + internal override bool IsPatternSupported(int patternId) => + (patternId == NativeMethods.UIA_InvokePatternId) || base.IsPatternSupported(patternId); + + internal override void Invoke() => RaiseMouseClick(); + } + } +} diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/MonthCalendar.CalendarButtonType.cs b/src/System.Windows.Forms/src/System/Windows/Forms/MonthCalendar.CalendarButtonType.cs new file mode 100644 index 00000000000..f87c88e4633 --- /dev/null +++ b/src/System.Windows.Forms/src/System/Windows/Forms/MonthCalendar.CalendarButtonType.cs @@ -0,0 +1,15 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Windows.Forms +{ + public partial class MonthCalendar + { + internal enum CalendarButtonType + { + Next = 1, + Previous = 2 + } + } +} diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/MonthCalendar.CalendarCellAccessibleObject.cs b/src/System.Windows.Forms/src/System/Windows/Forms/MonthCalendar.CalendarCellAccessibleObject.cs new file mode 100644 index 00000000000..e59038a7032 --- /dev/null +++ b/src/System.Windows.Forms/src/System/Windows/Forms/MonthCalendar.CalendarCellAccessibleObject.cs @@ -0,0 +1,111 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using static Interop; + +namespace System.Windows.Forms +{ + public partial class MonthCalendar + { + /// + /// Represents the calendar cell accessible object. + /// + internal class CalendarCellAccessibleObject : CalendarGridChildAccessibleObject + { + private int _rowIndex; + private int _columnIndex; + private string _name; + + public CalendarCellAccessibleObject(MonthCalendarAccessibleObject calendarAccessibleObject, int calendarIndex, AccessibleObject parentAccessibleObject, int rowIndex, int columnIndex, string name) + : base(calendarAccessibleObject, calendarIndex, CalendarChildType.CalendarCell, parentAccessibleObject, rowIndex * columnIndex) + { + _rowIndex = rowIndex; + _columnIndex = columnIndex; + _name = name; + } + + public override string Name => _name; + + internal override int Row => _rowIndex; + + internal override int Column => _columnIndex; + + internal override UnsafeNativeMethods.IRawElementProviderSimple ContainingGrid => _calendarAccessibleObject; + + internal override int[] RuntimeId => + new int[5] + { + RuntimeIDFirstItem, + _calendarAccessibleObject.Owner.Handle.ToInt32(), + Parent.Parent.GetChildId(), + Parent.GetChildId(), + GetChildId() + }; + + protected override RECT CalculateBoundingRectangle() + { + _calendarAccessibleObject.GetCalendarPartRectangle(_calendarIndex, ComCtl32.MCGIP.CALENDARCELL, _rowIndex, _columnIndex, out RECT rectangle); + return rectangle; + } + + internal override int GetChildId() => _columnIndex + 1; + + internal override UnsafeNativeMethods.IRawElementProviderFragment FragmentNavigate(UnsafeNativeMethods.NavigateDirection direction) => + direction switch + { + UnsafeNativeMethods.NavigateDirection.Parent => _parentAccessibleObject, + UnsafeNativeMethods.NavigateDirection.NextSibling => + _calendarAccessibleObject.GetCalendarChildAccessibleObject(_calendarIndex, CalendarChildType.CalendarCell, _parentAccessibleObject, _columnIndex + 1), + UnsafeNativeMethods.NavigateDirection.PreviousSibling => + _calendarAccessibleObject.GetCalendarChildAccessibleObject(_calendarIndex, CalendarChildType.CalendarCell, _parentAccessibleObject, _columnIndex - 1), + _ => base.FragmentNavigate(direction) + }; + + internal override object GetPropertyValue(int propertyID) => + propertyID switch + { + NativeMethods.UIA_ControlTypePropertyId => + (_rowIndex == -1) ? NativeMethods.UIA_HeaderControlTypeId : NativeMethods.UIA_DataItemControlTypeId, + NativeMethods.UIA_NamePropertyId => Name, + var p when + p == NativeMethods.UIA_HasKeyboardFocusPropertyId || + p == NativeMethods.UIA_IsGridItemPatternAvailablePropertyId || + p == NativeMethods.UIA_IsTableItemPatternAvailablePropertyId => true, + _ => base.GetPropertyValue(propertyID) + }; + + internal override bool IsPatternSupported(int patternId) => + patternId switch + { + var p when + p == NativeMethods.UIA_GridItemPatternId || + p == NativeMethods.UIA_InvokePatternId || + p == NativeMethods.UIA_TableItemPatternId => true, + _ => base.IsPatternSupported(patternId) + }; + + internal override void Invoke() + { + RaiseMouseClick(); + } + + internal override UnsafeNativeMethods.IRawElementProviderSimple[] GetRowHeaderItems() => null; + + internal override UnsafeNativeMethods.IRawElementProviderSimple[] GetColumnHeaderItems() + { + if (!_calendarAccessibleObject.HasHeaderRow) + { + return null; + } + + AccessibleObject headerRowAccessibleObject = + _calendarAccessibleObject.GetCalendarChildAccessibleObject(_calendarIndex, CalendarChildType.CalendarRow, _parentAccessibleObject.Parent, -1); + AccessibleObject headerCellAccessibleObject = + _calendarAccessibleObject.GetCalendarChildAccessibleObject(_calendarIndex, CalendarChildType.CalendarCell, headerRowAccessibleObject, _columnIndex); + + return new UnsafeNativeMethods.IRawElementProviderSimple[1] { headerCellAccessibleObject }; + } + } + } +} diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/MonthCalendar.CalendarChildAccessibleObject.cs b/src/System.Windows.Forms/src/System/Windows/Forms/MonthCalendar.CalendarChildAccessibleObject.cs new file mode 100644 index 00000000000..a601d49dc10 --- /dev/null +++ b/src/System.Windows.Forms/src/System/Windows/Forms/MonthCalendar.CalendarChildAccessibleObject.cs @@ -0,0 +1,75 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Drawing; +using System.Runtime.InteropServices; +using static Interop; + +namespace System.Windows.Forms +{ + public partial class MonthCalendar + { + /// + /// Represents the calendar child accessible object. + /// + internal abstract class CalendarChildAccessibleObject : AccessibleObject + { + protected MonthCalendarAccessibleObject _calendarAccessibleObject; + protected int _calendarIndex; + protected CalendarChildType _itemType; + + public CalendarChildAccessibleObject(MonthCalendarAccessibleObject calendarAccessibleObject, int calendarIndex, CalendarChildType itemType) + { + _calendarAccessibleObject = calendarAccessibleObject; + _calendarIndex = calendarIndex; + _itemType = itemType; + } + + internal override UnsafeNativeMethods.IRawElementProviderFragmentRoot FragmentRoot => _calendarAccessibleObject; + + public override AccessibleObject Parent => _calendarAccessibleObject; + + internal override Rectangle BoundingRectangle => CalculateBoundingRectangle(); + + protected virtual RECT CalculateBoundingRectangle() => new RECT(); + + internal override UnsafeNativeMethods.IRawElementProviderFragment FragmentNavigate(UnsafeNativeMethods.NavigateDirection direction) => + direction switch + { + UnsafeNativeMethods.NavigateDirection.Parent => Parent, + _ => base.FragmentNavigate(direction) + }; + + internal override object GetPropertyValue(int propertyID) => + propertyID switch + { + NativeMethods.UIA_IsEnabledPropertyId => _calendarAccessibleObject.Enabled, + _ => base.GetPropertyValue(propertyID) + }; + + internal override int[] RuntimeId => + new int[] + { + RuntimeIDFirstItem, + _calendarAccessibleObject.Owner.Handle.ToInt32(), + GetChildId() + }; + + public void RaiseMouseClick() + { + // Make sure that the control is enabled. + if (!SafeNativeMethods.IsWindowEnabled(new HandleRef(null, _calendarAccessibleObject.Owner.Handle))) + { + return; + } + + var rectangle = CalculateBoundingRectangle(); + int x = rectangle.left + ((rectangle.right - rectangle.left) / 2); + int y = rectangle.top + ((rectangle.bottom - rectangle.top) / 2); + + _calendarAccessibleObject.RaiseMouseClick(x, y); + } + } + } +} diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/MonthCalendar.CalendarChildType.cs b/src/System.Windows.Forms/src/System/Windows/Forms/MonthCalendar.CalendarChildType.cs new file mode 100644 index 00000000000..f4f26546520 --- /dev/null +++ b/src/System.Windows.Forms/src/System/Windows/Forms/MonthCalendar.CalendarChildType.cs @@ -0,0 +1,23 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Windows.Forms +{ + public partial class MonthCalendar + { + internal enum CalendarChildType + { + Undefined = -1, + NextButton = 1, + PreviousButton = 2, + Footer = 3, + Calendar = 4, + CalendarHeader = 5, + CalendarBody = 6, + CalendarRow = 7, + CalendarCell = 8, + TodayLink = 9 + } + } +} diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/MonthCalendar.CalendarGridChildAccessibleObject.cs b/src/System.Windows.Forms/src/System/Windows/Forms/MonthCalendar.CalendarGridChildAccessibleObject.cs new file mode 100644 index 00000000000..d216e2ce86d --- /dev/null +++ b/src/System.Windows.Forms/src/System/Windows/Forms/MonthCalendar.CalendarGridChildAccessibleObject.cs @@ -0,0 +1,25 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Windows.Forms +{ + public partial class MonthCalendar + { + /// + /// Represents the calendar grid child accessible object. + /// + internal abstract class CalendarGridChildAccessibleObject : CalendarChildAccessibleObject + { + protected AccessibleObject _parentAccessibleObject; + + public CalendarGridChildAccessibleObject(MonthCalendarAccessibleObject calendarAccessibleObject, int calendarIndex, CalendarChildType itemType, + AccessibleObject parentAccessibleObject, int itemIndex) : base(calendarAccessibleObject, calendarIndex, itemType) + { + _parentAccessibleObject = parentAccessibleObject; + } + + public override AccessibleObject Parent => _parentAccessibleObject; + } + } +} diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/MonthCalendar.CalendarHeaderAccessibleObject.cs b/src/System.Windows.Forms/src/System/Windows/Forms/MonthCalendar.CalendarHeaderAccessibleObject.cs new file mode 100644 index 00000000000..0dc37df2732 --- /dev/null +++ b/src/System.Windows.Forms/src/System/Windows/Forms/MonthCalendar.CalendarHeaderAccessibleObject.cs @@ -0,0 +1,52 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using static Interop; + +namespace System.Windows.Forms +{ + public partial class MonthCalendar + { + internal class CalendarHeaderAccessibleObject : CalendarChildAccessibleObject + { + private const int ChildId = 3; + + public CalendarHeaderAccessibleObject(MonthCalendarAccessibleObject calendarAccessibleObject, int calendarIndex) + : base(calendarAccessibleObject, calendarIndex, CalendarChildType.CalendarHeader) + { + } + + public override string Name => _calendarAccessibleObject.GetCalendarChildName(_calendarIndex, CalendarChildType.CalendarHeader); + + internal override int GetChildId() => ChildId; + + internal override UnsafeNativeMethods.IRawElementProviderFragment FragmentNavigate(UnsafeNativeMethods.NavigateDirection direction) => + direction switch + { + UnsafeNativeMethods.NavigateDirection.PreviousSibling => + _calendarAccessibleObject.GetCalendarChildAccessibleObject(_calendarIndex, CalendarChildType.NextButton), + UnsafeNativeMethods.NavigateDirection.NextSibling => _calendarAccessibleObject.GetCalendarChildAccessibleObject(_calendarIndex, CalendarChildType.CalendarBody), + _ => base.FragmentNavigate(direction) + }; + + internal override object GetPropertyValue(int propertyID) => + propertyID switch + { + NativeMethods.UIA_ControlTypePropertyId => NativeMethods.UIA_ButtonControlTypeId, + NativeMethods.UIA_NamePropertyId => Name, + _ => base.GetPropertyValue(propertyID) + }; + + protected override RECT CalculateBoundingRectangle() + { + _calendarAccessibleObject.GetCalendarPartRectangle(_calendarIndex, ComCtl32.MCGIP.CALENDARHEADER, -1, -1, out RECT rectangle); + return rectangle; + } + + internal override bool IsPatternSupported(int patternId) => (patternId == NativeMethods.UIA_InvokePatternId) || base.IsPatternSupported(patternId); + + internal override void Invoke() => RaiseMouseClick(); + } + } +} diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/MonthCalendar.CalendarNextButtonAccessibleObject.cs b/src/System.Windows.Forms/src/System/Windows/Forms/MonthCalendar.CalendarNextButtonAccessibleObject.cs new file mode 100644 index 00000000000..08b103cb47c --- /dev/null +++ b/src/System.Windows.Forms/src/System/Windows/Forms/MonthCalendar.CalendarNextButtonAccessibleObject.cs @@ -0,0 +1,41 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Windows.Forms +{ + public partial class MonthCalendar + { + internal class CalendarNextButtonAccessibleObject : CalendarButtonAccessibleObject + { + private const int ChildId = 2; + + public CalendarNextButtonAccessibleObject(MonthCalendarAccessibleObject calendarAccessibleObject, int calendarIndex) + : base(calendarAccessibleObject, calendarIndex, CalendarButtonType.Next) + { + } + + protected override CalendarButtonType ButtonType => CalendarButtonType.Next; + + internal override int GetChildId() => ChildId; + + internal override UnsafeNativeMethods.IRawElementProviderFragment FragmentNavigate(UnsafeNativeMethods.NavigateDirection direction) => + direction switch + { + UnsafeNativeMethods.NavigateDirection.PreviousSibling => + _calendarAccessibleObject.GetCalendarChildAccessibleObject(_calendarIndex, CalendarChildType.PreviousButton), + UnsafeNativeMethods.NavigateDirection.NextSibling => _calendarAccessibleObject.GetCalendarChildAccessibleObject(_calendarIndex, CalendarChildType.CalendarHeader), + _ => base.FragmentNavigate(direction) + }; + + internal override object GetPropertyValue(int propertyID) => + propertyID switch + { + NativeMethods.UIA_BoundingRectanglePropertyId => BoundingRectangle, + NativeMethods.UIA_ControlTypePropertyId => NativeMethods.UIA_ButtonControlTypeId, + NativeMethods.UIA_NamePropertyId => SR.MonthCalendarNextButtonAccessibleName, + _ => base.GetPropertyValue(propertyID) + }; + } + } +} diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/MonthCalendar.CalendarPreviousButtonAccessibleObject.cs b/src/System.Windows.Forms/src/System/Windows/Forms/MonthCalendar.CalendarPreviousButtonAccessibleObject.cs new file mode 100644 index 00000000000..9da0575b570 --- /dev/null +++ b/src/System.Windows.Forms/src/System/Windows/Forms/MonthCalendar.CalendarPreviousButtonAccessibleObject.cs @@ -0,0 +1,40 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Windows.Forms +{ + public partial class MonthCalendar + { + internal class CalendarPreviousButtonAccessibleObject : CalendarButtonAccessibleObject + { + private const int ChildId = 1; + + public CalendarPreviousButtonAccessibleObject(MonthCalendarAccessibleObject calendarAccessibleObject, int calendarIndex) + : base(calendarAccessibleObject, calendarIndex, CalendarButtonType.Previous) + { + } + + protected override CalendarButtonType ButtonType => CalendarButtonType.Previous; + + internal override int GetChildId() => ChildId; + + internal override UnsafeNativeMethods.IRawElementProviderFragment FragmentNavigate(UnsafeNativeMethods.NavigateDirection direction) => + direction switch + { + UnsafeNativeMethods.NavigateDirection.NextSibling => + _calendarAccessibleObject.GetCalendarChildAccessibleObject(_calendarIndex, CalendarChildType.NextButton), + _ => base.FragmentNavigate(direction) + }; + + internal override object GetPropertyValue(int propertyID) => + propertyID switch + { + NativeMethods.UIA_BoundingRectanglePropertyId => BoundingRectangle, + NativeMethods.UIA_ControlTypePropertyId => NativeMethods.UIA_ButtonControlTypeId, + NativeMethods.UIA_NamePropertyId => SR.MonthCalendarPreviousButtonAccessibleName, + _ => base.GetPropertyValue(propertyID) + }; + } + } +} diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/MonthCalendar.CalendarRowAccessibleObject.cs b/src/System.Windows.Forms/src/System/Windows/Forms/MonthCalendar.CalendarRowAccessibleObject.cs new file mode 100644 index 00000000000..a901182ba32 --- /dev/null +++ b/src/System.Windows.Forms/src/System/Windows/Forms/MonthCalendar.CalendarRowAccessibleObject.cs @@ -0,0 +1,55 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using static Interop; + +namespace System.Windows.Forms +{ + public partial class MonthCalendar + { + internal class CalendarRowAccessibleObject : CalendarGridChildAccessibleObject + { + int _rowIndex; + + public CalendarRowAccessibleObject(MonthCalendarAccessibleObject calendarAccessibleObject, int calendarIndex, CalendarBodyAccessibleObject parentAccessibleObject, int rowIndex) + : base(calendarAccessibleObject, calendarIndex, CalendarChildType.CalendarRow, parentAccessibleObject, rowIndex) + { + _rowIndex = rowIndex; + } + + public int RowIndex => _rowIndex; + + internal override int[] RuntimeId => + new int[4] + { + RuntimeIDFirstItem, + _calendarAccessibleObject.Owner.Handle.ToInt32(), + Parent.GetChildId(), + GetChildId() + }; + + protected override RECT CalculateBoundingRectangle() + { + _calendarAccessibleObject.GetCalendarPartRectangle(_calendarIndex, ComCtl32.MCGIP.CALENDARROW, _rowIndex, -1, out RECT calendarPartRectangle); + return calendarPartRectangle; + } + + internal override int GetChildId() => _rowIndex + 1; + + internal override UnsafeNativeMethods.IRawElementProviderFragment FragmentNavigate(UnsafeNativeMethods.NavigateDirection direction) => + direction switch + { + UnsafeNativeMethods.NavigateDirection.NextSibling => + _calendarAccessibleObject.GetCalendarChildAccessibleObject(_calendarIndex, CalendarChildType.CalendarRow, _parentAccessibleObject, _rowIndex + 1), + UnsafeNativeMethods.NavigateDirection.PreviousSibling => + _calendarAccessibleObject.GetCalendarChildAccessibleObject(_calendarIndex, CalendarChildType.CalendarRow, _parentAccessibleObject, _rowIndex - 1), + UnsafeNativeMethods.NavigateDirection.FirstChild => + _calendarAccessibleObject.GetCalendarChildAccessibleObject(_calendarIndex, CalendarChildType.CalendarCell, this, 0), + UnsafeNativeMethods.NavigateDirection.LastChild => + _calendarAccessibleObject.GetCalendarChildAccessibleObject(_calendarIndex, CalendarChildType.CalendarCell, this, _calendarAccessibleObject.ColumnCount - 1), + _ => base.FragmentNavigate(direction) + }; + } + } +} diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/MonthCalendar.CalendarTodayLinkAccessibleObject.cs b/src/System.Windows.Forms/src/System/Windows/Forms/MonthCalendar.CalendarTodayLinkAccessibleObject.cs new file mode 100644 index 00000000000..2ae48a135c6 --- /dev/null +++ b/src/System.Windows.Forms/src/System/Windows/Forms/MonthCalendar.CalendarTodayLinkAccessibleObject.cs @@ -0,0 +1,51 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using static Interop; + +namespace System.Windows.Forms +{ + public partial class MonthCalendar + { + internal class CalendarTodayLinkAccessibleObject : CalendarChildAccessibleObject + { + private const int ChildId = 5; + + public CalendarTodayLinkAccessibleObject(MonthCalendarAccessibleObject calendarAccessibleObject, int calendarIndex, CalendarChildType type) + : base(calendarAccessibleObject, calendarIndex, CalendarChildType.TodayLink) + { + } + + protected override RECT CalculateBoundingRectangle() + { + _calendarAccessibleObject.GetCalendarPartRectangle(_calendarIndex, ComCtl32.MCGIP.FOOTER, -1, -1, out RECT calendarPartRectangle); + return calendarPartRectangle; + } + + internal override int GetChildId() => ChildId; + + internal override bool IsPatternSupported(int patternId) => + (patternId == NativeMethods.UIA_InvokePatternId) || base.IsPatternSupported(patternId); + + internal override object GetPropertyValue(int propertyID) => + propertyID switch + { + NativeMethods.UIA_BoundingRectanglePropertyId => BoundingRectangle, + NativeMethods.UIA_ControlTypePropertyId => NativeMethods.UIA_ButtonControlTypeId, + NativeMethods.UIA_NamePropertyId => _calendarAccessibleObject.GetCalendarChildName(_calendarIndex, CalendarChildType.TodayLink), + _ => base.GetPropertyValue(propertyID) + }; + + internal override UnsafeNativeMethods.IRawElementProviderFragment FragmentNavigate(UnsafeNativeMethods.NavigateDirection direction) => + direction switch + { + UnsafeNativeMethods.NavigateDirection.PreviousSibling => + _calendarAccessibleObject.GetCalendarChildAccessibleObject(_calendarIndex, CalendarChildType.CalendarBody), + _ => base.FragmentNavigate(direction) + }; + + internal override void Invoke() => RaiseMouseClick(); + } + } +} diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/MonthCalendar.MonthCalendarAccessibleObject.cs b/src/System.Windows.Forms/src/System/Windows/Forms/MonthCalendar.MonthCalendarAccessibleObject.cs new file mode 100644 index 00000000000..711a616d12e --- /dev/null +++ b/src/System.Windows.Forms/src/System/Windows/Forms/MonthCalendar.MonthCalendarAccessibleObject.cs @@ -0,0 +1,748 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics; +using System.Drawing; +using System.Globalization; +using System.Runtime.InteropServices; +using static Interop; + +namespace System.Windows.Forms +{ + public partial class MonthCalendar + { + [ComVisible(true)] + internal class MonthCalendarAccessibleObject : ControlAccessibleObject + { + internal const int MAX_DAYS = 7; + internal const int MAX_WEEKS = 6; + + private readonly MonthCalendar _owner; + private int _calendarIndex = 0; + private AccessibleObject _focused; + + public MonthCalendarAccessibleObject(Control owner) + : base(owner) + { + _owner = owner as MonthCalendar; + } + + public int ControlType => + string.IsNullOrEmpty(base.Name) ? NativeMethods.UIA_CalendarControlTypeId : NativeMethods.UIA_TableControlTypeId; + + public bool Enabled => _owner.Enabled; + + public bool HasHeaderRow + { + get + { + bool result = GetCalendarGridInfoText(ComCtl32.MCGIP.CALENDARCELL, _calendarIndex, -1, 0, out string text); + if (!result || string.IsNullOrEmpty(text)) + { + return false; + } + + return true; + } + } + + public override AccessibleRole Role => + (_owner?.AccessibleRole != AccessibleRole.Default) ? _owner.AccessibleRole : AccessibleRole.Table; + + public override string Help + { + get + { + var help = base.Help; + if (help != null) + { + return help; + } + else + { + if (_owner != null) + { + return _owner.GetType().Name + "(" + _owner.GetType().BaseType.Name + ")"; + } + } + return string.Empty; + } + } + + public override string Name + { + get + { + string name = base.Name; + if (name != null) + { + return name; + } + + name = string.Empty; + if (_owner == null) + { + return name; + } + + if (_owner.mcCurView == NativeMethods.MONTCALENDAR_VIEW_MODE.MCMV_MONTH) + { + if (DateTime.Equals(_owner.SelectionStart.Date, _owner.SelectionEnd.Date)) + { + return string.Format(SR.MonthCalendarSingleDateSelected, _owner.SelectionStart.ToLongDateString()); + } + else + { + return string.Format(SR.MonthCalendarRangeSelected, _owner.SelectionStart.ToLongDateString(), _owner.SelectionEnd.ToLongDateString()); + } + } + else if (_owner.mcCurView == NativeMethods.MONTCALENDAR_VIEW_MODE.MCMV_YEAR) + { + if (DateTime.Equals(_owner.SelectionStart.Month, _owner.SelectionEnd.Month)) + { + return string.Format(SR.MonthCalendarSingleDateSelected, _owner.SelectionStart.ToString("y")); + } + else + { + return string.Format(SR.MonthCalendarRangeSelected, _owner.SelectionStart.ToString("y"), _owner.SelectionEnd.ToString("y")); + } + } + else if (_owner.mcCurView == NativeMethods.MONTCALENDAR_VIEW_MODE.MCMV_DECADE) + { + if (DateTime.Equals(_owner.SelectionStart.Year, _owner.SelectionEnd.Year)) + { + return string.Format(SR.MonthCalendarSingleYearSelected, _owner.SelectionStart.ToString("yyyy")); + } + else + { + return string.Format(SR.MonthCalendarYearRangeSelected, _owner.SelectionStart.ToString("yyyy"), _owner.SelectionEnd.ToString("yyyy")); + } + } + else if (_owner.mcCurView == NativeMethods.MONTCALENDAR_VIEW_MODE.MCMV_CENTURY) + { + return string.Format(SR.MonthCalendarSingleDecadeSelected, _owner.SelectionStart.ToString("yyyy")); + } + + return name; + } + } + + public override string Value + { + get + { + var value = string.Empty; + if (_owner == null) + { + return value; + } + + try + { + if (_owner.mcCurView == NativeMethods.MONTCALENDAR_VIEW_MODE.MCMV_MONTH) + { + if (System.DateTime.Equals(_owner.SelectionStart.Date, _owner.SelectionEnd.Date)) + { + value = _owner.SelectionStart.ToLongDateString(); + } + else + { + value = string.Format("{0} - {1}", _owner.SelectionStart.ToLongDateString(), _owner.SelectionEnd.ToLongDateString()); + } + } + else if (_owner.mcCurView == NativeMethods.MONTCALENDAR_VIEW_MODE.MCMV_YEAR) + { + if (System.DateTime.Equals(_owner.SelectionStart.Month, _owner.SelectionEnd.Month)) + { + value = _owner.SelectionStart.ToString("y"); + } + else + { + value = string.Format("{0} - {1}", _owner.SelectionStart.ToString("y"), _owner.SelectionEnd.ToString("y")); + } + } + else + { + value = string.Format("{0} - {1}", _owner.SelectionRange.Start.ToString(), _owner.SelectionRange.End.ToString()); + } + } + catch + { + value = base.Value; + } + + return value; + } + set + { + base.Value = value; + } + } + + internal override int ColumnCount + { + get + { + GetCalendarGridInfo( + ComCtl32.MCGIF.RECT, + ComCtl32.MCGIP.CALENDARBODY, + _calendarIndex, + -1, + -1, + out RECT calendarBodyRectangle, + out Kernel32.SYSTEMTIME endDate, + out Kernel32.SYSTEMTIME startDate); + + int columnCount = 0; + bool success = true; + while (success) + { + success = GetCalendarGridInfo( + ComCtl32.MCGIF.RECT, + ComCtl32.MCGIP.CALENDARCELL, + _calendarIndex, + 0, + columnCount, + out RECT calendarPartRectangle, + out endDate, + out startDate); + + // Out of the body, so this is out of the grid column. + if (calendarPartRectangle.right > calendarBodyRectangle.right) + { + break; + } + + columnCount++; + } + + return columnCount; + } + } + + internal override int RowCount + { + get + { + GetCalendarGridInfo( + ComCtl32.MCGIF.RECT, + ComCtl32.MCGIP.CALENDARBODY, + _calendarIndex, + -1, + -1, + out RECT calendarBodyRectangle, + out Kernel32.SYSTEMTIME endDate, + out Kernel32.SYSTEMTIME startDate); + + int rowCount = 0; + bool success = true; + while (success) + { + success = GetCalendarGridInfo( + ComCtl32.MCGIF.RECT, + ComCtl32.MCGIP.CALENDARCELL, + _calendarIndex, + rowCount, + 0, + out RECT calendarPartRectangle, + out endDate, + out startDate); + + // Out of the body, so this is out of the grid row. + if (calendarPartRectangle.bottom > calendarBodyRectangle.bottom) + { + break; + } + + rowCount++; + } + + return rowCount; + } + } + + internal override UnsafeNativeMethods.RowOrColumnMajor RowOrColumnMajor => UnsafeNativeMethods.RowOrColumnMajor.RowOrColumnMajor_RowMajor; + + internal override UnsafeNativeMethods.IRawElementProviderSimple[] GetRowHeaderItems() => null; + + internal override UnsafeNativeMethods.IRawElementProviderFragment ElementProviderFromPoint(double x, double y) + { + int innerX = (int)x; + int innerY = (int)y; + + ComCtl32.MCHITTESTINFO hitTestInfo = GetHitTestInfo(innerX, innerY); + switch ((ComCtl32.MCHT)hitTestInfo.uHit) + { + case ComCtl32.MCHT.TITLEBTNPREV: + return GetCalendarChildAccessibleObject(_calendarIndex, CalendarChildType.PreviousButton); + + case ComCtl32.MCHT.TITLEBTNNEXT: + return GetCalendarChildAccessibleObject(_calendarIndex, CalendarChildType.NextButton); + + case ComCtl32.MCHT.TITLE: + case ComCtl32.MCHT.TITLEMONTH: + case ComCtl32.MCHT.TITLEYEAR: + return GetCalendarChildAccessibleObject(_calendarIndex, CalendarChildType.CalendarHeader); + + case ComCtl32.MCHT.CALENDARDAY: + case ComCtl32.MCHT.CALENDARWEEKNUM: + case ComCtl32.MCHT.CALENDARDATE: + // Get calendar body's child. + CalendarBodyAccessibleObject calendarBodyAccessibleObject = (CalendarBodyAccessibleObject)GetCalendarChildAccessibleObject(_calendarIndex, CalendarChildType.CalendarBody); + return calendarBodyAccessibleObject.GetFromPoint(hitTestInfo); + + case ComCtl32.MCHT.TODAYLINK: + return GetCalendarChildAccessibleObject(_calendarIndex, CalendarChildType.TodayLink); + } + + return base.ElementProviderFromPoint(x, y); + } + + internal override UnsafeNativeMethods.IRawElementProviderFragment FragmentNavigate(UnsafeNativeMethods.NavigateDirection direction) + { + switch (direction) + { + case UnsafeNativeMethods.NavigateDirection.FirstChild: + return GetCalendarChildAccessibleObject(_calendarIndex, CalendarChildType.PreviousButton); + case UnsafeNativeMethods.NavigateDirection.LastChild: + return _owner.ShowTodayCircle + ? GetCalendarChildAccessibleObject(_calendarIndex, CalendarChildType.TodayLink) + : GetCalendarChildAccessibleObject(_calendarIndex, CalendarChildType.CalendarBody); + } + + return base.FragmentNavigate(direction); + } + + internal override UnsafeNativeMethods.IRawElementProviderFragment GetFocus() => _focused; + + public override AccessibleObject GetFocused() => _focused; + + public ComCtl32.MCHITTESTINFO GetHitTestInfo(int xScreen, int yScreen) + { + ComCtl32.MCHITTESTINFO hitTestInfo = new ComCtl32.MCHITTESTINFO(); + hitTestInfo.cbSize = (int)Marshal.SizeOf(hitTestInfo); + hitTestInfo.pt = new POINT(); + hitTestInfo.st = new Kernel32.SYSTEMTIME(); + + // NativeMethods.GetCursorPos(out Point pt); + Point point = new Point(xScreen, yScreen); + NativeMethods.MapWindowPoints(IntPtr.Zero, Handle, ref point, 1); + hitTestInfo.pt.x = point.X; + hitTestInfo.pt.y = point.Y; + + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), (int)ComCtl32.MCM.HITTEST, 0, ref hitTestInfo); + + return hitTestInfo; + } + + public CalendarChildAccessibleObject GetCalendarChildAccessibleObject(int calendarIndex, CalendarChildType calendarChildType, AccessibleObject parentAccessibleObject = null, int index = -1) => + calendarChildType switch + { + CalendarChildType.PreviousButton => new CalendarPreviousButtonAccessibleObject(this, _calendarIndex), + CalendarChildType.NextButton => new CalendarNextButtonAccessibleObject(this, _calendarIndex), + CalendarChildType.CalendarHeader => new CalendarHeaderAccessibleObject(this, _calendarIndex), + CalendarChildType.CalendarBody => new CalendarBodyAccessibleObject(this, _calendarIndex), + CalendarChildType.CalendarRow => GetCalendarRow(calendarIndex, parentAccessibleObject, index), + CalendarChildType.CalendarCell => GetCalendarCell(calendarIndex, parentAccessibleObject, index), + CalendarChildType.TodayLink => new CalendarTodayLinkAccessibleObject(this, (int)CalendarChildType.TodayLink, calendarChildType), + _ => null + }; + + public string GetCalendarChildName(int calendarIndex, CalendarChildType calendarChildType, AccessibleObject parentAccessibleObject = null, int index = -1) + { + switch (calendarChildType) + { + case CalendarChildType.CalendarHeader: + GetCalendarGridInfoText(ComCtl32.MCGIP.CALENDARHEADER, calendarIndex, 0, 0, out string text); + return text; + case CalendarChildType.TodayLink: + return string.Format(SR.MonthCalendarTodayButtonAccessibleName, _owner.TodayDate.ToShortDateString()); + }; + + return string.Empty; + } + + private CalendarCellAccessibleObject GetCalendarCell(int calendarIndex, AccessibleObject parentAccessibleObject, int columnIndex) + { + if (columnIndex < 0 || + columnIndex >= MAX_DAYS || + columnIndex >= ColumnCount) + { + return null; + } + + CalendarRowAccessibleObject parentRowAccessibleObject = (CalendarRowAccessibleObject)parentAccessibleObject; + int rowIndex = parentRowAccessibleObject.RowIndex; + bool getNameResult = GetCalendarGridInfoText(ComCtl32.MCGIP.CALENDARCELL, calendarIndex, rowIndex, columnIndex, out string text); + bool getDateResult = GetCalendarGridInfo(ComCtl32.MCGIF.DATE, ComCtl32.MCGIP.CALENDARCELL, + calendarIndex, + rowIndex, + columnIndex, + out RECT rectangle, + out Kernel32.SYSTEMTIME systemEndDate, + out Kernel32.SYSTEMTIME systemStartDate); + + DateTime endDate = DateTimePicker.SysTimeToDateTime(systemEndDate).Date; + DateTime startDate = DateTimePicker.SysTimeToDateTime(systemStartDate).Date; + + if (getNameResult && !string.IsNullOrEmpty(text)) + { + string cellName = GetCalendarCellName(endDate, startDate, text, rowIndex == -1); + + // The cell is present on the calendar, so create accessible object for it. + return new CalendarCellAccessibleObject(this, calendarIndex, parentAccessibleObject, rowIndex, columnIndex, cellName); + } + + return null; + } + + private string GetCalendarCellName(DateTime endDate, DateTime startDate, string defaultName, bool headerCell) + { + if (_owner.mcCurView == NativeMethods.MONTCALENDAR_VIEW_MODE.MCMV_MONTH) + { + if (headerCell) + { + return startDate.ToString("dddd"); + } + + return startDate.ToString("dddd, MMMM dd, yyyy"); + } + else if (_owner.mcCurView == NativeMethods.MONTCALENDAR_VIEW_MODE.MCMV_YEAR) + { + return startDate.ToString("MMMM yyyy"); + } + + return defaultName; + } + + private CalendarRowAccessibleObject GetCalendarRow(int calendarIndex, AccessibleObject parentAccessibleObject, int rowIndex) + { + if ((HasHeaderRow ? rowIndex < -1 : rowIndex < 0) || + rowIndex >= RowCount) + { + return null; + } + + // Search name for the first cell in the row. + bool success = GetCalendarGridInfo( + ComCtl32.MCGIF.DATE, + ComCtl32.MCGIP.CALENDARCELL, + calendarIndex, + rowIndex, + 0, + out RECT calendarPartRectangle, + out Kernel32.SYSTEMTIME endDate, + out Kernel32.SYSTEMTIME startDate); + + if (!success) + { + // Not able to get cell date for the row. + return null; + } + + SelectionRange cellsRange = _owner.GetDisplayRange(false); + + if (cellsRange.Start > DateTimePicker.SysTimeToDateTime(endDate) || cellsRange.End < DateTimePicker.SysTimeToDateTime(startDate)) + { + // Do not create row if the row's first cell is out of the current calendar's view range. + return null; + } + + return new CalendarRowAccessibleObject(this, calendarIndex, (CalendarBodyAccessibleObject)parentAccessibleObject, rowIndex); + } + + private bool GetCalendarGridInfo( + ComCtl32.MCGIF dwFlags, + ComCtl32.MCGIP dwPart, + int calendarIndex, + int row, + int column, + out RECT rectangle, + out Kernel32.SYSTEMTIME endDate, + out Kernel32.SYSTEMTIME startDate) + { + Debug.Assert( + (dwFlags & ~(ComCtl32.MCGIF.DATE | ComCtl32.MCGIF.RECT)) == 0, + "GetCalendarGridInfo() should be used only to obtain Date and Rect," + + "dwFlags has flag bits other that MCGIF_DATE and MCGIF_RECT"); + + ComCtl32.MCGRIDINFO gridInfo = new ComCtl32.MCGRIDINFO(); + gridInfo.dwFlags = dwFlags; + gridInfo.cbSize = (uint)Marshal.SizeOf(gridInfo); + gridInfo.dwPart = dwPart; + gridInfo.iCalendar = calendarIndex; + gridInfo.iCol = column; + gridInfo.iRow = row; + bool result; + + try + { + result = GetCalendarGridInfo(ref gridInfo); + rectangle = gridInfo.rc; + endDate = gridInfo.stEnd; + startDate = gridInfo.stStart; + } + catch + { + rectangle = new RECT(); + endDate = new Kernel32.SYSTEMTIME(); + startDate = new Kernel32.SYSTEMTIME(); + result = false; + } + + return result; + } + + private bool GetCalendarGridInfo(ref ComCtl32.MCGRIDINFO gridInfo) + { + // Do not use this if gridInfo.dwFlags contains MCGIF_NAME; + // use GetCalendarGridInfoText() instead. + Debug.Assert( + (gridInfo.dwFlags & ComCtl32.MCGIF.NAME) == 0, + "Param dwFlags contains MCGIF_NAME, use GetCalendarGridInfoText() to retrieve the text of a calendar part."); + + gridInfo.dwFlags &= ~ComCtl32.MCGIF.NAME; + + return _owner.SendMessage((int)ComCtl32.MCM.GETCALENDARGRIDINFO, 0, ref gridInfo) != IntPtr.Zero; + } + + private bool GetCalendarGridInfoText(ComCtl32.MCGIP dwPart, int calendarIndex, int row, int column, out string text) + { + const int nameLength = 128; + + ComCtl32.MCGRIDINFO gridInfo = new ComCtl32.MCGRIDINFO(); + gridInfo.cbSize = (uint)Marshal.SizeOf(gridInfo); + gridInfo.dwPart = dwPart; + gridInfo.iCalendar = calendarIndex; + gridInfo.iCol = column; + gridInfo.iRow = row; + gridInfo.pszName = new string('\0', nameLength + 2); + gridInfo.cchName = (uint)gridInfo.pszName.Length - 1; + + bool result = GetCalendarGridInfoText(ref gridInfo); + text = gridInfo.pszName; + + return result; + } + + // Use to retrieve MCGIF_NAME only. + private bool GetCalendarGridInfoText(ref ComCtl32.MCGRIDINFO gridInfo) + { + Debug.Assert( + gridInfo.dwFlags == 0, + "gridInfo.dwFlags should be 0 when calling GetCalendarGridInfoText"); + + gridInfo.dwFlags = ComCtl32.MCGIF.NAME; + + return _owner.SendMessage((int)ComCtl32.MCM.GETCALENDARGRIDINFO, 0, ref gridInfo) != IntPtr.Zero; + } + + public bool GetCalendarPartRectangle(int calendarIndex, ComCtl32.MCGIP dwPart, int row, int column, out RECT calendarPartRectangle) + { + bool success = GetCalendarGridInfo( + ComCtl32.MCGIF.RECT, + dwPart, + calendarIndex, + row, + column, + out calendarPartRectangle, + out Kernel32.SYSTEMTIME endDate, out Kernel32.SYSTEMTIME startDate); + + if (success) + { + success = UnsafeNativeMethods.MapWindowPoints(new HandleRef(this, Owner.Handle), new HandleRef(null, IntPtr.Zero), ref calendarPartRectangle, 2) != 0; + } + + if (!success) + { + calendarPartRectangle = new RECT(); + } + + return success; + } + + internal override object GetPropertyValue(int propertyID) => + propertyID switch + { + NativeMethods.UIA_ControlTypePropertyId => ControlType, + NativeMethods.UIA_NamePropertyId => Name, + NativeMethods.UIA_IsGridPatternAvailablePropertyId => true, + NativeMethods.UIA_IsTablePatternAvailablePropertyId => true, + NativeMethods.UIA_IsLegacyIAccessiblePatternAvailablePropertyId => true, + _ => base.GetPropertyValue(propertyID) + }; + + internal override bool IsPatternSupported(int patternId) => + patternId switch + { + var p when + p == NativeMethods.UIA_ValuePatternId || + p == NativeMethods.UIA_GridPatternId || + p == NativeMethods.UIA_TablePatternId || + p == NativeMethods.UIA_LegacyIAccessiblePatternId => true, + _ => base.IsPatternSupported(patternId) + }; + + public void RaiseMouseClick(int x, int y) + { + POINT previousPosition = new POINT(); + bool setOldCursorPos = UnsafeNativeMethods.GetPhysicalCursorPos(ref previousPosition); + + bool mouseSwapped = User32.GetSystemMetrics(User32.SystemMetric.SM_SWAPBUTTON) != 0; + + SendMouseInput(x, y, User32.MOUSEEVENTF.MOVE | User32.MOUSEEVENTF.ABSOLUTE); + SendMouseInput(0, 0, mouseSwapped ? User32.MOUSEEVENTF.RIGHTDOWN : User32.MOUSEEVENTF.LEFTDOWN); + SendMouseInput(0, 0, mouseSwapped ? User32.MOUSEEVENTF.RIGHTUP : User32.MOUSEEVENTF.LEFTUP); + + Threading.Thread.Sleep(50); + + // Set back the mouse position where it was. + if (setOldCursorPos) + { + SendMouseInput(previousPosition.x, previousPosition.y, User32.MOUSEEVENTF.MOVE | User32.MOUSEEVENTF.ABSOLUTE); + } + } + + private void SendMouseInput(int x, int y, User32.MOUSEEVENTF flags) + { + if ((flags & User32.MOUSEEVENTF.ABSOLUTE) != 0) + { + int vscreenWidth = User32.GetSystemMetrics(User32.SystemMetric.SM_CXVIRTUALSCREEN); + int vscreenHeight = User32.GetSystemMetrics(User32.SystemMetric.SM_CYVIRTUALSCREEN); + int vscreenLeft = User32.GetSystemMetrics(User32.SystemMetric.SM_XVIRTUALSCREEN); + int vscreenTop = User32.GetSystemMetrics(User32.SystemMetric.SM_YVIRTUALSCREEN); + + const int DesktopNormilizedMax = 65536; + + // Absolute input requires that input is in 'normalized' coords - with the entire + // desktop being (0,0)...(65535,65536). Need to convert input x,y coords to this + // first. + // + // In this normalized world, any pixel on the screen corresponds to a block of values + // of normalized coords - eg. on a 1024x768 screen, + // y pixel 0 corresponds to range 0 to 85.333, + // y pixel 1 corresponds to range 85.333 to 170.666, + // y pixel 2 correpsonds to range 170.666 to 256 - and so on. + // Doing basic scaling math - (x-top)*65536/Width - gets us the start of the range. + // However, because int math is used, this can end up being rounded into the wrong + // pixel. For example, if we wanted pixel 1, we'd get 85.333, but that comes out as + // 85 as an int, which falls into pixel 0's range - and that's where the pointer goes. + // To avoid this, we add on half-a-"screen pixel"'s worth of normalized coords - to + // push us into the middle of any given pixel's range - that's the 65536/(Width*2) + // part of the formula. So now pixel 1 maps to 85+42 = 127 - which is comfortably + // in the middle of that pixel's block. + // The key ting here is that unlike points in coordinate geometry, pixels take up + // space, so are often better treated like rectangles - and if you want to target + // a particular pixel, target its rectangle's midpoint, not its edge. + x = ((x - vscreenLeft) * DesktopNormilizedMax) / vscreenWidth + DesktopNormilizedMax / (vscreenWidth * 2); + y = ((y - vscreenTop) * DesktopNormilizedMax) / vscreenHeight + DesktopNormilizedMax / (vscreenHeight * 2); + + flags |= User32.MOUSEEVENTF.VIRTUALDESK; + } + + NativeMethods.INPUT mouseInput = new NativeMethods.INPUT(); + mouseInput.type = NativeMethods.INPUT_MOUSE; + mouseInput.inputUnion.mi.dx = x; + mouseInput.inputUnion.mi.dy = y; + mouseInput.inputUnion.mi.mouseData = 0; + mouseInput.inputUnion.mi.dwFlags = (int)flags; + mouseInput.inputUnion.mi.time = 0; + mouseInput.inputUnion.mi.dwExtraInfo = new IntPtr(0); + + UnsafeNativeMethods.SendInput(1, ref mouseInput, Marshal.SizeOf(mouseInput)); + } + + public void RaiseAutomationEventForChild(int automationEventId, DateTime selectionStart, DateTime selectionEnd) + { + AccessibleObject calendarChildAccessibleObject = GetCalendarChildAccessibleObject(selectionStart, selectionEnd); + if (calendarChildAccessibleObject != null) + { + calendarChildAccessibleObject.RaiseAutomationEvent(automationEventId); + + if (automationEventId == NativeMethods.UIA_AutomationFocusChangedEventId) + { + _focused = calendarChildAccessibleObject; + } + } + } + + private AccessibleObject GetCalendarChildAccessibleObject(DateTime selectionStart, DateTime selectionEnd) + { + int columnCount = ColumnCount; + + AccessibleObject bodyAccessibleObject = GetCalendarChildAccessibleObject(_calendarIndex, CalendarChildType.CalendarBody); + for (int row = 0; row < RowCount; row++) + { + AccessibleObject rowAccessibleObject = GetCalendarChildAccessibleObject(_calendarIndex, CalendarChildType.CalendarRow, bodyAccessibleObject, row); + for (int column = 0; column < columnCount; column++) + { + bool success = GetCalendarGridInfo( + ComCtl32.MCGIF.DATE, + ComCtl32.MCGIP.CALENDARCELL, + _calendarIndex, + row, + column, + out RECT calendarPartRectangle, + out Kernel32.SYSTEMTIME systemEndDate, + out Kernel32.SYSTEMTIME systemStartDate); + + if (!success) + { + continue; + } + + AccessibleObject cellAccessibleObject = GetCalendarChildAccessibleObject(_calendarIndex, CalendarChildType.CalendarCell, rowAccessibleObject, column); + if (cellAccessibleObject == null) + { + continue; + } + + DateTime endDate = DateTimePicker.SysTimeToDateTime(systemEndDate); + DateTime startDate = DateTimePicker.SysTimeToDateTime(systemStartDate); + + if (DateTime.Compare(selectionEnd, endDate) <= 0 && + DateTime.Compare(selectionStart, startDate) >= 0) + { + return cellAccessibleObject; + } + } + } + + return null; + } + + internal override UnsafeNativeMethods.IRawElementProviderSimple[] GetRowHeaders() => null; + + internal override UnsafeNativeMethods.IRawElementProviderSimple[] GetColumnHeaderItems() + { + if (!HasHeaderRow) + { + return null; + } + + UnsafeNativeMethods.IRawElementProviderSimple[] headers = + new UnsafeNativeMethods.IRawElementProviderSimple[MonthCalendarAccessibleObject.MAX_DAYS]; + AccessibleObject headerRowAccessibleObject = GetCalendarChildAccessibleObject(_calendarIndex, CalendarChildType.CalendarRow, this, -1); + for (int columnIndex = 0; columnIndex < MonthCalendarAccessibleObject.MAX_DAYS; columnIndex++) + { + headers[columnIndex] = GetCalendarChildAccessibleObject(_calendarIndex, CalendarChildType.CalendarCell, headerRowAccessibleObject, columnIndex); + } + + return headers; + } + + internal override UnsafeNativeMethods.IRawElementProviderSimple GetItem(int row, int column) + { + AccessibleObject rowAccessibleObject = GetCalendarChildAccessibleObject(_calendarIndex, CalendarChildType.CalendarRow, this, row); + + if (rowAccessibleObject == null) + { + return null; + } + + return GetCalendarChildAccessibleObject(_calendarIndex, CalendarChildType.CalendarCell, rowAccessibleObject, column); + } + } + } +} diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/MonthCalendar.cs b/src/System.Windows.Forms/src/System/Windows/Forms/MonthCalendar.cs index 21e332d9b0c..0e0db38f6c3 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/MonthCalendar.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/MonthCalendar.cs @@ -10,8 +10,9 @@ using System.Windows.Forms.Internal; using System.Windows.Forms.Layout; using Microsoft.Win32; -using ArrayList = System.Collections.ArrayList; + using static Interop; +using ArrayList = System.Collections.ArrayList; namespace System.Windows.Forms { @@ -62,7 +63,7 @@ namespace System.Windows.Forms Designer("System.Windows.Forms.Design.MonthCalendarDesigner, " + AssemblyRef.SystemDesign), SRDescription(nameof(SR.DescriptionMonthCalendar)) ] - public class MonthCalendar : Control + public partial class MonthCalendar : Control { const long DAYS_TO_1601 = 548229; const long DAYS_TO_10000 = 3615900; @@ -365,20 +366,20 @@ protected override CreateParams CreateParams { CreateParams cp = base.CreateParams; cp.ClassName = NativeMethods.WC_MONTHCAL; - cp.Style |= NativeMethods.MCS_MULTISELECT | NativeMethods.MCS_DAYSTATE; + cp.Style |= (int)ComCtl32.MCS.MULTISELECT | (int)ComCtl32.MCS.DAYSTATE; if (!showToday) { - cp.Style |= NativeMethods.MCS_NOTODAY; + cp.Style |= (int)ComCtl32.MCS.NOTODAY; } if (!showTodayCircle) { - cp.Style |= NativeMethods.MCS_NOTODAYCIRCLE; + cp.Style |= (int)ComCtl32.MCS.NOTODAYCIRCLE; } if (showWeekNumbers) { - cp.Style |= NativeMethods.MCS_WEEKNUMBERS; + cp.Style |= (int)ComCtl32.MCS.WEEKNUMBERS; } if (RightToLeft == RightToLeft.Yes && RightToLeftLayout == true) @@ -466,7 +467,7 @@ public Day FirstDayOfWeek } else { - SendMessage(NativeMethods.MCM_SETFIRSTDAYOFWEEK, 0, (int)value); + SendMessage((int)ComCtl32.MCM.SETFIRSTDAYOFWEEK, 0, (int)value); } } } @@ -569,7 +570,7 @@ public int MaxSelectionCount { if (IsHandleCreated) { - if (unchecked((int)(long)SendMessage(NativeMethods.MCM_SETMAXSELCOUNT, value, 0)) == 0) + if (unchecked((int)(long)SendMessage((int)ComCtl32.MCM.SETMAXSELCOUNT, value, 0)) == 0) { throw new ArgumentException(string.Format(SR.MonthCalendarMaxSelCount, value.ToString("D")), nameof(value)); } @@ -757,7 +758,7 @@ public int ScrollChange if (IsHandleCreated) { - SendMessage(NativeMethods.MCM_SETMONTHDELTA, value, 0); + SendMessage((int)ComCtl32.MCM.SETMONTHDELTA, value, 0); } scrollChange = value; } @@ -982,7 +983,7 @@ public Size SingleMonthSize if (IsHandleCreated) { - if (unchecked((int)(long)SendMessage(NativeMethods.MCM_GETMINREQRECT, 0, ref rect)) == 0) + if (unchecked((int)(long)SendMessage((int)ComCtl32.MCM.GETMINREQRECT, 0, ref rect)) == 0) { throw new InvalidOperationException(SR.InvalidSingleMonthSize); } @@ -1015,6 +1016,8 @@ public Size SingleMonthSize } } + internal override bool SupportsUiaProviders => true; + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never), Bindable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public override string Text { @@ -1055,8 +1058,8 @@ public DateTime TodayDate if (IsHandleCreated) { - NativeMethods.SYSTEMTIME st = new NativeMethods.SYSTEMTIME(); - int res = (int)UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.MCM_GETTODAY, 0, st); + Kernel32.SYSTEMTIME st = new Kernel32.SYSTEMTIME(); + int res = (int)UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), (int)ComCtl32.MCM.GETTODAY, 0, ref st); Debug.Assert(res != 0, "MCM_GETTODAY failed"); return DateTimePicker.SysTimeToDateTime(st).Date; } @@ -1131,7 +1134,7 @@ public Color TitleBackColor "value")); } titleBackColor = value; - SetControlColor(NativeMethods.MCSC_TITLEBK, value); + SetControlColor(ComCtl32.MCSC.TITLEBK, value); } } @@ -1157,7 +1160,7 @@ public Color TitleForeColor "value")); } titleForeColor = value; - SetControlColor(NativeMethods.MCSC_TITLETEXT, value); + SetControlColor(ComCtl32.MCSC.TITLETEXT, value); } } @@ -1183,7 +1186,7 @@ public Color TrailingForeColor "value")); } trailingForeColor = value; - SetControlColor(NativeMethods.MCSC_TRAILINGTEXT, value); + SetControlColor(ComCtl32.MCSC.TRAILINGTEXT, value); } } @@ -1398,31 +1401,31 @@ public SelectionRange GetDisplayRange(bool visible) /// private HitArea GetHitArea(int hit) { - switch (hit) + switch ((ComCtl32.MCHT)hit) { - case NativeMethods.MCHT_TITLEBK: + case ComCtl32.MCHT.TITLEBK: return HitArea.TitleBackground; - case NativeMethods.MCHT_TITLEMONTH: + case ComCtl32.MCHT.TITLEMONTH: return HitArea.TitleMonth; - case NativeMethods.MCHT_TITLEYEAR: + case ComCtl32.MCHT.TITLEYEAR: return HitArea.TitleYear; - case NativeMethods.MCHT_TITLEBTNNEXT: + case ComCtl32.MCHT.TITLEBTNNEXT: return HitArea.NextMonthButton; - case NativeMethods.MCHT_TITLEBTNPREV: + case ComCtl32.MCHT.TITLEBTNPREV: return HitArea.PrevMonthButton; - case NativeMethods.MCHT_CALENDARBK: + case ComCtl32.MCHT.CALENDARBK: return HitArea.CalendarBackground; - case NativeMethods.MCHT_CALENDARDATE: + case ComCtl32.MCHT.CALENDARDATE: return HitArea.Date; - case NativeMethods.MCHT_CALENDARDATENEXT: + case ComCtl32.MCHT.CALENDARDATENEXT: return HitArea.NextMonthDate; - case NativeMethods.MCHT_CALENDARDATEPREV: + case ComCtl32.MCHT.CALENDARDATEPREV: return HitArea.PrevMonthDate; - case NativeMethods.MCHT_CALENDARDAY: + case ComCtl32.MCHT.CALENDARDAY: return HitArea.DayOfWeek; - case NativeMethods.MCHT_CALENDARWEEKNUM: + case ComCtl32.MCHT.CALENDARWEEKNUM: return HitArea.WeekNumbers; - case NativeMethods.MCHT_TODAYLINK: + case ComCtl32.MCHT.TODAYLINK: return HitArea.TodayLink; default: return HitArea.Nowhere; @@ -1439,9 +1442,10 @@ private Size GetMinReqRect() /// /// Used internally to get the minimum size needed to display the - /// MonthCalendar. This is needed because - /// NativeMethods.MCM_GETMINREQRECT returns an incorrect value if showToday - /// is set to false. If updateRows is true, then the + /// MonthCalendar. This is needed because + /// ComCtl32.MCM.GETMINREQRECT + /// returns an incorrect value if showToday + /// is set to false. If updateRows is true, then the /// number of rows will be updated according to height. /// private Size GetMinReqRect(int newDimensionLength, bool updateRows, bool updateCols) @@ -1487,7 +1491,7 @@ private Size GetMinReqRect(int newDimensionLength, bool updateRows, bool updateC // if (IsHandleCreated) { - int maxTodayWidth = unchecked((int)(long)SendMessage(NativeMethods.MCM_GETMAXTODAYWIDTH, 0, 0)); + int maxTodayWidth = unchecked((int)(long)SendMessage((int)ComCtl32.MCM.GETMAXTODAYWIDTH, 0, 0)); if (maxTodayWidth > minSize.Width) { minSize.Width = maxTodayWidth; @@ -1505,9 +1509,9 @@ private SelectionRange GetMonthRange(int flag) { NativeMethods.SYSTEMTIMEARRAY sa = new NativeMethods.SYSTEMTIMEARRAY(); SelectionRange range = new SelectionRange(); - UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.MCM_GETMONTHRANGE, flag, sa); + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), (int)ComCtl32.MCM.GETMONTHRANGE, flag, sa); - NativeMethods.SYSTEMTIME st = new NativeMethods.SYSTEMTIME + Kernel32.SYSTEMTIME st = new Kernel32.SYSTEMTIME { wYear = sa.wYear1, wMonth = sa.wMonth1, @@ -1551,35 +1555,39 @@ private int GetPreferredWidth(int width, bool updateCols) /// public HitTestInfo HitTest(int x, int y) { - NativeMethods.MCHITTESTINFO mchi = new NativeMethods.MCHITTESTINFO + ComCtl32.MCHITTESTINFO mchi = new ComCtl32.MCHITTESTINFO { - pt_x = x, - pt_y = y, - cbSize = Marshal.SizeOf() + pt = new POINT + { + x = x, + y = y + }, + st = new Kernel32.SYSTEMTIME(), + cbSize = Marshal.SizeOf() }; - UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.MCM_HITTEST, 0, mchi); + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), (int)ComCtl32.MCM.HITTEST, 0, ref mchi); // If the hit area has an associated valid date, get it // HitArea hitArea = GetHitArea(mchi.uHit); if (HitTestInfo.HitAreaHasValidDateTime(hitArea)) { - NativeMethods.SYSTEMTIME sys = new NativeMethods.SYSTEMTIME + Kernel32.SYSTEMTIME sys = new Kernel32.SYSTEMTIME { - wYear = mchi.st_wYear, - wMonth = mchi.st_wMonth, - wDayOfWeek = mchi.st_wDayOfWeek, - wDay = mchi.st_wDay, - wHour = mchi.st_wHour, - wMinute = mchi.st_wMinute, - wSecond = mchi.st_wSecond, - wMilliseconds = mchi.st_wMilliseconds + wYear = mchi.st.wYear, + wMonth = mchi.st.wMonth, + wDayOfWeek = mchi.st.wDayOfWeek, + wDay = mchi.st.wDay, + wHour = mchi.st.wHour, + wMinute = mchi.st.wMinute, + wSecond = mchi.st.wSecond, + wMilliseconds = mchi.st.wMilliseconds }; - return new HitTestInfo(new Point(mchi.pt_x, mchi.pt_y), hitArea, DateTimePicker.SysTimeToDateTime(sys)); + return new HitTestInfo(new Point(mchi.pt.x, mchi.pt.y), hitArea, DateTimePicker.SysTimeToDateTime(sys)); } else { - return new HitTestInfo(new Point(mchi.pt_x, mchi.pt_y), hitArea); + return new HitTestInfo(new Point(mchi.pt.x, mchi.pt.y), hitArea); } } @@ -1622,21 +1630,21 @@ protected override void OnHandleCreated(EventArgs e) SetSelRange(selectionStart, selectionEnd); if (maxSelectionCount != DEFAULT_MAX_SELECTION_COUNT) { - SendMessage(NativeMethods.MCM_SETMAXSELCOUNT, maxSelectionCount, 0); + SendMessage((int)ComCtl32.MCM.SETMAXSELCOUNT, maxSelectionCount, 0); } AdjustSize(); if (todayDateSet) { - NativeMethods.SYSTEMTIME st = DateTimePicker.DateTimeToSysTime(todayDate); - UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.MCM_SETTODAY, 0, st); + Kernel32.SYSTEMTIME st = DateTimePicker.DateTimeToSysTime(todayDate); + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), (int)ComCtl32.MCM.SETTODAY, 0, ref st); } - SetControlColor(NativeMethods.MCSC_TEXT, ForeColor); - SetControlColor(NativeMethods.MCSC_MONTHBK, BackColor); - SetControlColor(NativeMethods.MCSC_TITLEBK, titleBackColor); - SetControlColor(NativeMethods.MCSC_TITLETEXT, titleForeColor); - SetControlColor(NativeMethods.MCSC_TRAILINGTEXT, trailingForeColor); + SetControlColor(ComCtl32.MCSC.TEXT, ForeColor); + SetControlColor(ComCtl32.MCSC.MONTHBK, BackColor); + SetControlColor(ComCtl32.MCSC.TITLEBK, titleBackColor); + SetControlColor(ComCtl32.MCSC.TITLETEXT, titleForeColor); + SetControlColor(ComCtl32.MCSC.TRAILINGTEXT, trailingForeColor); int firstDay; if (firstDayOfWeek == Day.Default) @@ -1647,12 +1655,12 @@ protected override void OnHandleCreated(EventArgs e) { firstDay = (int)firstDayOfWeek; } - SendMessage(NativeMethods.MCM_SETFIRSTDAYOFWEEK, 0, firstDay); + SendMessage((int)ComCtl32.MCM.SETFIRSTDAYOFWEEK, 0, firstDay); SetRange(); if (scrollChange != DEFAULT_SCROLL_CHANGE) { - SendMessage(NativeMethods.MCM_SETMONTHDELTA, scrollChange, 0); + SendMessage((int)ComCtl32.MCM.SETMONTHDELTA, scrollChange, 0); } SystemEvents.UserPreferenceChanged += new UserPreferenceChangedEventHandler(MarshaledUserPreferenceChanged); @@ -1684,6 +1692,13 @@ protected virtual void OnDateSelected(DateRangeEventArgs drevent) onDateSelected?.Invoke(this, drevent); } + protected override void OnGotFocus(EventArgs e) + { + base.OnGotFocus(e); + + AccessibilityObject.RaiseAutomationEvent(NativeMethods.UIA_AutomationFocusChangedEventId); + } + protected override void OnFontChanged(EventArgs e) { base.OnFontChanged(e); @@ -1693,13 +1708,13 @@ protected override void OnFontChanged(EventArgs e) protected override void OnForeColorChanged(EventArgs e) { base.OnForeColorChanged(e); - SetControlColor(NativeMethods.MCSC_TEXT, ForeColor); + SetControlColor(ComCtl32.MCSC.TEXT, ForeColor); } protected override void OnBackColorChanged(EventArgs e) { base.OnBackColorChanged(e); - SetControlColor(NativeMethods.MCSC_MONTHBK, BackColor); + SetControlColor(ComCtl32.MCSC.MONTHBK, BackColor); } [EditorBrowsable(EditorBrowsableState.Advanced)] @@ -1938,6 +1953,13 @@ private IntPtr RequestBuffer(int reqSize) return mdsBuffer; } + /// + /// Sends a Win32 message to this control. If the control does not yet + /// have a handle, it will be created. + /// + private IntPtr SendMessage(int msg, int wparam, ref ComCtl32.MCGRIDINFO lparam) => + ComCtl32.SendMessage(new HandleRef(this, Handle), msg, wparam, ref lparam); + /// /// Overrides Control.SetBoundsCore to enforce auto-sizing. /// @@ -1974,11 +1996,11 @@ protected override void SetBoundsCore(int x, int y, int width, int height, Bound /// /// If the handle has been created, this applies the color to the control /// - private void SetControlColor(int colorIndex, Color value) + private void SetControlColor(ComCtl32.MCSC colorIndex, Color value) { if (IsHandleCreated) { - SendMessage(NativeMethods.MCM_SETCOLOR, colorIndex, ColorTranslator.ToWin32(value)); + SendMessage((int)ComCtl32.MCM.SETCOLOR, (int)colorIndex, ColorTranslator.ToWin32(value)); } } @@ -2020,7 +2042,7 @@ private void SetRange(DateTime minDate, DateTime maxDate) NativeMethods.SYSTEMTIMEARRAY sa = new NativeMethods.SYSTEMTIMEARRAY(); flag |= NativeMethods.GDTR_MIN | NativeMethods.GDTR_MAX; - NativeMethods.SYSTEMTIME sys = DateTimePicker.DateTimeToSysTime(minDate); + Kernel32.SYSTEMTIME sys = DateTimePicker.DateTimeToSysTime(minDate); sa.wYear1 = sys.wYear; sa.wMonth1 = sys.wMonth; sa.wDayOfWeek1 = sys.wDayOfWeek; @@ -2031,7 +2053,7 @@ private void SetRange(DateTime minDate, DateTime maxDate) sa.wDayOfWeek2 = sys.wDayOfWeek; sa.wDay2 = sys.wDay; - if ((int)UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.MCM_SETRANGE, flag, sa) == 0) + if ((int)UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), (int)ComCtl32.MCM.SETRANGE, flag, sa) == 0) { throw new InvalidOperationException(string.Format(SR.MonthCalendarRange, minDate.ToShortDateString(), maxDate.ToShortDateString())); } @@ -2173,7 +2195,7 @@ private void SetSelRange(DateTime lower, DateTime upper) { NativeMethods.SYSTEMTIMEARRAY sa = new NativeMethods.SYSTEMTIMEARRAY(); - NativeMethods.SYSTEMTIME sys = DateTimePicker.DateTimeToSysTime(lower); + Kernel32.SYSTEMTIME sys = DateTimePicker.DateTimeToSysTime(lower); sa.wYear1 = sys.wYear; sa.wMonth1 = sys.wMonth; sa.wDayOfWeek1 = sys.wDayOfWeek; @@ -2183,7 +2205,7 @@ private void SetSelRange(DateTime lower, DateTime upper) sa.wMonth2 = sys.wMonth; sa.wDayOfWeek2 = sys.wDayOfWeek; sa.wDay2 = sys.wDay; - UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.MCM_SETSELRANGE, 0, sa); + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), (int)ComCtl32.MCM.SETSELRANGE, 0, sa); } if (changed) @@ -2286,12 +2308,16 @@ private void UpdateTodayDate() { if (IsHandleCreated) { - NativeMethods.SYSTEMTIME st = null; + if (todayDateSet) { - st = DateTimePicker.DateTimeToSysTime(todayDate); + Kernel32.SYSTEMTIME st = DateTimePicker.DateTimeToSysTime(todayDate); + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), (int)ComCtl32.MCM.SETTODAY, 0, ref st); + } + else + { + UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), (int)ComCtl32.MCM.SETTODAY, 0, IntPtr.Zero); } - UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.MCM_SETTODAY, 0, st); } } @@ -2328,6 +2354,9 @@ private void WmDateChanged(ref Message m) AccessibilityNotifyClients(AccessibleEvents.NameChange, -1); AccessibilityNotifyClients(AccessibleEvents.ValueChange, -1); + MonthCalendarAccessibleObject calendarAccessibleObject = (MonthCalendarAccessibleObject)AccessibilityObject; + calendarAccessibleObject.RaiseAutomationEventForChild(NativeMethods.UIA_AutomationFocusChangedEventId, selectionStart, selectionEnd); + //subhag if (start.Ticks < minDate.Ticks || end.Ticks < minDate.Ticks) { @@ -2629,159 +2658,5 @@ public enum HitArea /// TodayLink = 12, } - - [ComVisible(true)] - internal class MonthCalendarAccessibleObject : ControlAccessibleObject - { - private readonly MonthCalendar calendar; - - public MonthCalendarAccessibleObject(Control owner) - : base(owner) - { - calendar = owner as MonthCalendar; - } - - public override AccessibleRole Role - { - get - { - if (calendar != null) - { - AccessibleRole role = calendar.AccessibleRole; - if (role != AccessibleRole.Default) - { - return role; - } - } - return AccessibleRole.Table; - } - } - - public override string Help - { - get - { - var help = base.Help; - if (help != null) - { - return help; - } - else - { - if (calendar != null) - { - return calendar.GetType().Name + "(" + calendar.GetType().BaseType.Name + ")"; - } - } - return string.Empty; - } - } - - public override string Name - { - get - { - string name = base.Name; - if (name != null) - { - return name; - } - - if (calendar != null) - { - - if (calendar.mcCurView == NativeMethods.MONTCALENDAR_VIEW_MODE.MCMV_MONTH) - { - if (System.DateTime.Equals(calendar.SelectionStart.Date, calendar.SelectionEnd.Date)) - { - name = string.Format(SR.MonthCalendarSingleDateSelected, calendar.SelectionStart.ToLongDateString()); - } - else - { - name = string.Format(SR.MonthCalendarRangeSelected, calendar.SelectionStart.ToLongDateString(), calendar.SelectionEnd.ToLongDateString()); - } - } - else if (calendar.mcCurView == NativeMethods.MONTCALENDAR_VIEW_MODE.MCMV_YEAR) - { - if (System.DateTime.Equals(calendar.SelectionStart.Month, calendar.SelectionEnd.Month)) - { - name = string.Format(SR.MonthCalendarSingleDateSelected, calendar.SelectionStart.ToString("y")); - } - else - { - name = string.Format(SR.MonthCalendarRangeSelected, calendar.SelectionStart.ToString("y"), calendar.SelectionEnd.ToString("y")); - } - } - else if (calendar.mcCurView == NativeMethods.MONTCALENDAR_VIEW_MODE.MCMV_DECADE) - { - if (System.DateTime.Equals(calendar.SelectionStart.Year, calendar.SelectionEnd.Year)) - { - name = string.Format(SR.MonthCalendarSingleYearSelected, calendar.SelectionStart.ToString("yyyy")); - } - else - { - name = string.Format(SR.MonthCalendarYearRangeSelected, calendar.SelectionStart.ToString("yyyy"), calendar.SelectionEnd.ToString("yyyy")); - } - } - else if (calendar.mcCurView == NativeMethods.MONTCALENDAR_VIEW_MODE.MCMV_CENTURY) - { - name = string.Format(SR.MonthCalendarSingleDecadeSelected, calendar.SelectionStart.ToString("yyyy")); - } - } - return name; - } - } - - public override string Value - { - get - { - var value = string.Empty; - try - { - if (calendar != null) - { - if (calendar.mcCurView == NativeMethods.MONTCALENDAR_VIEW_MODE.MCMV_MONTH) - { - if (System.DateTime.Equals(calendar.SelectionStart.Date, calendar.SelectionEnd.Date)) - { - value = calendar.SelectionStart.ToLongDateString(); - } - else - { - value = string.Format("{0} - {1}", calendar.SelectionStart.ToLongDateString(), calendar.SelectionEnd.ToLongDateString()); - } - } - else if (calendar.mcCurView == NativeMethods.MONTCALENDAR_VIEW_MODE.MCMV_YEAR) - { - if (System.DateTime.Equals(calendar.SelectionStart.Month, calendar.SelectionEnd.Month)) - { - value = calendar.SelectionStart.ToString("y"); - } - else - { - value = string.Format("{0} - {1}", calendar.SelectionStart.ToString("y"), calendar.SelectionEnd.ToString("y")); - } - } - else - { - value = string.Format("{0} - {1}", calendar.SelectionRange.Start.ToString(), calendar.SelectionRange.End.ToString()); - } - } - } - catch - { - value = base.Value; - } - return value; - } - set - { - base.Value = value; - } - } - } - } // end class MonthCalendar } -