diff --git a/src/System.Windows.Forms.Primitives/src/Interop/UiaCore/Interop.IMultipleViewProvider.cs b/src/System.Windows.Forms.Primitives/src/Interop/UiaCore/Interop.IMultipleViewProvider.cs
new file mode 100644
index 00000000000..4435be67558
--- /dev/null
+++ b/src/System.Windows.Forms.Primitives/src/Interop/UiaCore/Interop.IMultipleViewProvider.cs
@@ -0,0 +1,28 @@
+// 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 UiaCore
+ {
+ [ComImport]
+ [Guid("6278cab1-b556-4a1a-b4e0-418acc523201")]
+ [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ public interface IMultipleViewProvider
+ {
+ string GetViewName(int viewId);
+
+ void SetCurrentView(int viewId);
+
+ int CurrentView
+ {
+ get;
+ }
+
+ int[] GetSupportedViews();
+ }
+ }
+}
diff --git a/src/System.Windows.Forms/src/Resources/SR.resx b/src/System.Windows.Forms/src/Resources/SR.resx
index d7e3457f7c3..71699358051 100644
--- a/src/System.Windows.Forms/src/Resources/SR.resx
+++ b/src/System.Windows.Forms/src/Resources/SR.resx
@@ -6617,4 +6617,7 @@ Stack trace where the illegal operation occurred was:
Error provider
+
+ Double Click
+
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 f91297a75de..1d52f63d18b 100644
--- a/src/System.Windows.Forms/src/Resources/xlf/SR.cs.xlf
+++ b/src/System.Windows.Forms/src/Resources/xlf/SR.cs.xlf
@@ -192,6 +192,11 @@
Sbalit
+
+ Double Click
+ Double Click
+
+
Expand
Rozbalit
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 322931e55d4..fc7e7633cae 100644
--- a/src/System.Windows.Forms/src/Resources/xlf/SR.de.xlf
+++ b/src/System.Windows.Forms/src/Resources/xlf/SR.de.xlf
@@ -192,6 +192,11 @@
Zuklappen
+
+ Double Click
+ Double Click
+
+
Expand
Aufklappen
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 ac9527cdab8..6f91f9a81cf 100644
--- a/src/System.Windows.Forms/src/Resources/xlf/SR.es.xlf
+++ b/src/System.Windows.Forms/src/Resources/xlf/SR.es.xlf
@@ -192,6 +192,11 @@
Contraer
+
+ Double Click
+ Double Click
+
+
Expand
Expandir
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 27540207724..1ae841ef38c 100644
--- a/src/System.Windows.Forms/src/Resources/xlf/SR.fr.xlf
+++ b/src/System.Windows.Forms/src/Resources/xlf/SR.fr.xlf
@@ -192,6 +192,11 @@
Réduire
+
+ Double Click
+ Double Click
+
+
Expand
Développer
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 2b5dec53659..a71f51d790e 100644
--- a/src/System.Windows.Forms/src/Resources/xlf/SR.it.xlf
+++ b/src/System.Windows.Forms/src/Resources/xlf/SR.it.xlf
@@ -192,6 +192,11 @@
Comprimi
+
+ Double Click
+ Double Click
+
+
Expand
Espandi
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 5b6938e6658..d8f63271592 100644
--- a/src/System.Windows.Forms/src/Resources/xlf/SR.ja.xlf
+++ b/src/System.Windows.Forms/src/Resources/xlf/SR.ja.xlf
@@ -192,6 +192,11 @@
折りたたむ
+
+ Double Click
+ Double Click
+
+
Expand
展開
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 b310740138a..ab43687653a 100644
--- a/src/System.Windows.Forms/src/Resources/xlf/SR.ko.xlf
+++ b/src/System.Windows.Forms/src/Resources/xlf/SR.ko.xlf
@@ -192,6 +192,11 @@
축소
+
+ Double Click
+ Double Click
+
+
Expand
확장
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 76f9b8707b7..eb2fc03121a 100644
--- a/src/System.Windows.Forms/src/Resources/xlf/SR.pl.xlf
+++ b/src/System.Windows.Forms/src/Resources/xlf/SR.pl.xlf
@@ -192,6 +192,11 @@
Zwiń
+
+ Double Click
+ Double Click
+
+
Expand
Rozwiń
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 cb4a7a81eb7..9cd1fc54a41 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
@@ -192,6 +192,11 @@
Recolher
+
+ Double Click
+ Double Click
+
+
Expand
Expandir
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 e48568a4fc4..cb79c03ef2d 100644
--- a/src/System.Windows.Forms/src/Resources/xlf/SR.ru.xlf
+++ b/src/System.Windows.Forms/src/Resources/xlf/SR.ru.xlf
@@ -192,6 +192,11 @@
Свернуть
+
+ Double Click
+ Double Click
+
+
Expand
Развертывание
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 bee44d905bc..43536f16706 100644
--- a/src/System.Windows.Forms/src/Resources/xlf/SR.tr.xlf
+++ b/src/System.Windows.Forms/src/Resources/xlf/SR.tr.xlf
@@ -192,6 +192,11 @@
Daralt
+
+ Double Click
+ Double Click
+
+
Expand
Genişlet
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 505654e52a7..9be7a575394 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
@@ -192,6 +192,11 @@
折叠
+
+ Double Click
+ Double Click
+
+
Expand
展开
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 7f912da3b35..2b56d9daf84 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
@@ -192,6 +192,11 @@
摺疊
+
+ Double Click
+ Double Click
+
+
Expand
展開
diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/AccessibleObject.cs b/src/System.Windows.Forms/src/System/Windows/Forms/AccessibleObject.cs
index 6135993d47f..b68d98585ea 100644
--- a/src/System.Windows.Forms/src/System/Windows/Forms/AccessibleObject.cs
+++ b/src/System.Windows.Forms/src/System/Windows/Forms/AccessibleObject.cs
@@ -45,7 +45,8 @@ public class AccessibleObject :
UiaCore.ISelectionProvider,
UiaCore.ISelectionItemProvider,
UiaCore.IRawElementProviderHwndOverride,
- UiaCore.IScrollItemProvider
+ UiaCore.IScrollItemProvider,
+ UiaCore.IMultipleViewProvider
{
///
/// Specifies the interface used by this .
@@ -680,6 +681,16 @@ internal virtual void SetValue(string newValue)
internal virtual UiaCore.IRawElementProviderSimple GetOverrideProviderForHwnd(IntPtr hwnd) => null;
+ internal virtual int GetMultiViewProviderCurrentView() => 0;
+
+ internal virtual int[] GetMultiViewProviderSupportedViews() => new int[0];
+
+ internal virtual string GetMultiViewProviderViewName(int viewId) => null;
+
+ internal virtual void SetMultiViewProviderCurrentView(int viewId)
+ {
+ }
+
internal virtual void SetValue(double newValue)
{
}
@@ -2182,6 +2193,14 @@ object IReflect.InvokeMember(string name, BindingFlags invokeAttr, Binder binder
UiaCore.IRawElementProviderSimple UiaCore.IRawElementProviderHwndOverride.GetOverrideProviderForHwnd(IntPtr hwnd)
=> GetOverrideProviderForHwnd(hwnd);
+ int UiaCore.IMultipleViewProvider.CurrentView => GetMultiViewProviderCurrentView();
+
+ int[] UiaCore.IMultipleViewProvider.GetSupportedViews() => GetMultiViewProviderSupportedViews();
+
+ string UiaCore.IMultipleViewProvider.GetViewName(int viewId) => GetMultiViewProviderViewName(viewId);
+
+ void UiaCore.IMultipleViewProvider.SetCurrentView(int viewId) => SetMultiViewProviderCurrentView(viewId);
+
BOOL UiaCore.IRangeValueProvider.IsReadOnly => IsReadOnly ? BOOL.TRUE : BOOL.FALSE;
double UiaCore.IRangeValueProvider.LargeChange => LargeChange;
@@ -2541,7 +2560,8 @@ internal sealed class InternalAccessibleObject :
UiaCore.ISelectionProvider,
UiaCore.ISelectionItemProvider,
UiaCore.IScrollItemProvider,
- UiaCore.IRawElementProviderHwndOverride
+ UiaCore.IRawElementProviderHwndOverride,
+ UiaCore.IMultipleViewProvider
{
private IAccessible publicIAccessible; // AccessibleObject as IAccessible
private readonly OleAut32.IEnumVariant publicIEnumVariant; // AccessibleObject as IEnumVariant
@@ -2569,6 +2589,7 @@ internal sealed class InternalAccessibleObject :
private readonly UiaCore.ISelectionItemProvider publicISelectionItemProvider; // AccessibleObject as ISelectionItemProvider
private readonly UiaCore.IScrollItemProvider publicIScrollItemProvider; // AccessibleObject as IScrollItemProvider
private readonly UiaCore.IRawElementProviderHwndOverride publicIRawElementProviderHwndOverride; // AccessibleObject as IRawElementProviderHwndOverride
+ private readonly UiaCore.IMultipleViewProvider publicIMultiViewProvider; // AccessibleObject as IMultipleViewProvider
///
/// Create a new wrapper.
@@ -2599,6 +2620,7 @@ internal InternalAccessibleObject(AccessibleObject accessibleImplemention)
publicISelectionItemProvider = (UiaCore.ISelectionItemProvider)accessibleImplemention;
publicIScrollItemProvider = (UiaCore.IScrollItemProvider)accessibleImplemention;
publicIRawElementProviderHwndOverride = (UiaCore.IRawElementProviderHwndOverride)accessibleImplemention;
+ publicIMultiViewProvider = (UiaCore.IMultipleViewProvider)accessibleImplemention;
// Note: Deliberately not holding onto AccessibleObject to enforce all access through the interfaces
}
@@ -2846,6 +2868,10 @@ object UiaCore.IRawElementProviderSimple.GetPatternProvider(UiaCore.UIA patternI
{
return (UiaCore.IScrollItemProvider)this;
}
+ else if (patternId == UiaCore.UIA.MultipleViewPatternId)
+ {
+ return (UiaCore.IMultipleViewProvider)this;
+ }
else
{
return null;
@@ -3043,5 +3069,17 @@ UiaCore.IRawElementProviderSimple UiaCore.ISelectionItemProvider.SelectionContai
/// Return the provider for the specified component, or null if the component is not being overridden.
UiaCore.IRawElementProviderSimple UiaCore.IRawElementProviderHwndOverride.GetOverrideProviderForHwnd(IntPtr hwnd)
=> publicIRawElementProviderHwndOverride.GetOverrideProviderForHwnd(hwnd);
+
+ int UiaCore.IMultipleViewProvider.CurrentView
+ => publicIMultiViewProvider.CurrentView;
+
+ int[] UiaCore.IMultipleViewProvider.GetSupportedViews()
+ => publicIMultiViewProvider.GetSupportedViews();
+
+ string UiaCore.IMultipleViewProvider.GetViewName(int viewId)
+ => publicIMultiViewProvider.GetViewName(viewId);
+
+ void UiaCore.IMultipleViewProvider.SetCurrentView(int viewId)
+ => publicIMultiViewProvider.SetCurrentView(viewId);
}
}
diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/Button.ButtonAccessibleObject.cs b/src/System.Windows.Forms/src/System/Windows/Forms/Button.ButtonAccessibleObject.cs
new file mode 100644
index 00000000000..473732fcb44
--- /dev/null
+++ b/src/System.Windows.Forms/src/System/Windows/Forms/Button.ButtonAccessibleObject.cs
@@ -0,0 +1,43 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading.Tasks;
+using static System.Windows.Forms.Control;
+using static Interop;
+
+namespace System.Windows.Forms
+{
+ public partial class Button
+ {
+ [ComVisible(true)]
+ internal class ButtonAccessibleObject : ControlAccessibleObject
+ {
+ internal ButtonAccessibleObject(Button owner) : base(owner)
+ {
+ }
+
+ private Button OwningButton => Owner as Button;
+
+ internal override object GetPropertyValue(UiaCore.UIA propertyID)
+ {
+ switch (propertyID)
+ {
+ case UiaCore.UIA.NamePropertyId:
+ return Name;
+ case UiaCore.UIA.AutomationIdPropertyId:
+ return OwningButton.IsHandleCreated ? OwningButton.Name : String.Empty;
+ case UiaCore.UIA.ControlTypePropertyId:
+ return UiaCore.UIA.ButtonControlTypeId;
+ case UiaCore.UIA.IsKeyboardFocusablePropertyId:
+ // This is necessary for compatibility with MSAA proxy:
+ // IsKeyboardFocusable = true regardless the control is enabled/disabled.
+ return true;
+ }
+
+ return base.GetPropertyValue(propertyID);
+ }
+ }
+ }
+}
diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/Button.cs b/src/System.Windows.Forms/src/System/Windows/Forms/Button.cs
index 76e89f76731..15470bef4f3 100644
--- a/src/System.Windows.Forms/src/System/Windows/Forms/Button.cs
+++ b/src/System.Windows.Forms/src/System/Windows/Forms/Button.cs
@@ -22,7 +22,7 @@ namespace System.Windows.Forms
[ClassInterface(ClassInterfaceType.AutoDispatch)]
[SRDescription(nameof(SR.DescriptionButton))]
[Designer("System.Windows.Forms.Design.ButtonBaseDesigner, " + AssemblyRef.SystemDesign)]
- public class Button : ButtonBase, IButtonControl
+ public partial class Button : ButtonBase, IButtonControl
{
///
/// The dialog result that will be sent to the parent dialog form when
@@ -387,5 +387,8 @@ protected override void WndProc(ref Message m)
break;
}
}
+
+ protected override AccessibleObject CreateAccessibilityInstance()
+ => new ButtonAccessibleObject(this);
}
}
diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/ButtonBase.cs b/src/System.Windows.Forms/src/System/Windows/Forms/ButtonBase.cs
index 7ea984f6899..634088cfdfa 100644
--- a/src/System.Windows.Forms/src/System/Windows/Forms/ButtonBase.cs
+++ b/src/System.Windows.Forms/src/System/Windows/Forms/ButtonBase.cs
@@ -753,11 +753,6 @@ private void Animate(bool animate)
}
}
- protected override AccessibleObject CreateAccessibilityInstance()
- {
- return new ButtonBaseAccessibleObject(this);
- }
-
private void DetachImageList(object sender, EventArgs e)
{
ImageList = null;
@@ -1327,34 +1322,5 @@ protected override void WndProc(ref Message m)
}
}
}
-
- [ComVisible(true)]
- public class ButtonBaseAccessibleObject : ControlAccessibleObject
- {
- public ButtonBaseAccessibleObject(Control owner) : base(owner)
- {
- }
-
- public override void DoDefaultAction()
- {
- ((ButtonBase)Owner).OnClick(EventArgs.Empty);
- }
-
- public override AccessibleStates State
- {
- get
- {
- AccessibleStates state = base.State;
-
- ButtonBase owner = (ButtonBase)Owner;
- if (owner.OwnerDraw && owner.MouseIsDown)
- {
- state |= AccessibleStates.Pressed;
- }
-
- return state;
- }
- }
- }
}
}
diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/CheckBox.CheckBoxAccessibleObject.cs b/src/System.Windows.Forms/src/System/Windows/Forms/CheckBox.CheckBoxAccessibleObject.cs
new file mode 100644
index 00000000000..04a28bddd5c
--- /dev/null
+++ b/src/System.Windows.Forms/src/System/Windows/Forms/CheckBox.CheckBoxAccessibleObject.cs
@@ -0,0 +1,84 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading.Tasks;
+using static System.Windows.Forms.Control;
+using static Interop;
+
+namespace System.Windows.Forms
+{
+ public partial class CheckBox
+ {
+ [ComVisible(true)]
+ internal class CheckBoxAccessibleObject : ControlAccessibleObject
+ {
+ internal CheckBoxAccessibleObject(CheckBox owner) : base(owner)
+ {
+ }
+
+ private CheckBox OwningCheckBox => Owner as CheckBox;
+
+ internal override bool IsPatternSupported(UiaCore.UIA patternId)
+ {
+ if (patternId == UiaCore.UIA.TogglePatternId)
+ {
+ return true;
+ }
+
+ return base.IsPatternSupported(patternId);
+ }
+
+ internal override object GetPropertyValue(UiaCore.UIA propertyID)
+ {
+ switch (propertyID)
+ {
+ case UiaCore.UIA.NamePropertyId:
+ return Name;
+ case UiaCore.UIA.AutomationIdPropertyId:
+ return OwningCheckBox.IsHandleCreated ? OwningCheckBox.Name : String.Empty;
+ case UiaCore.UIA.IsTogglePatternAvailablePropertyId:
+ return IsPatternSupported(UiaCore.UIA.TogglePatternId);
+ case UiaCore.UIA.ControlTypePropertyId:
+ return UiaCore.UIA.CheckBoxControlTypeId;
+ case UiaCore.UIA.IsKeyboardFocusablePropertyId:
+ // This is necessary for compatibility with MSAA proxy:
+ // IsKeyboardFocusable = true regardless the control is enabled/disabled.
+ return true;
+ }
+
+ return base.GetPropertyValue(propertyID);
+ }
+
+ public override string DefaultAction
+ {
+ get
+ {
+ string defaultAction = Owner.AccessibleDefaultActionDescription;
+ if (defaultAction != null)
+ {
+ return defaultAction;
+ }
+
+ if (OwningCheckBox.Checked)
+ {
+ return SR.AccessibleActionUncheck;
+ }
+ else
+ {
+ return SR.AccessibleActionCheck;
+ }
+ }
+ }
+
+ internal override UiaCore.ToggleState ToggleState
+ {
+ get
+ {
+ return OwningCheckBox.Checked ? UiaCore.ToggleState.On : UiaCore.ToggleState.Off;
+ }
+ }
+ }
+ }
+}
diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/CheckBox.cs b/src/System.Windows.Forms/src/System/Windows/Forms/CheckBox.cs
index d930a0888da..8b6380ca201 100644
--- a/src/System.Windows.Forms/src/System/Windows/Forms/CheckBox.cs
+++ b/src/System.Windows.Forms/src/System/Windows/Forms/CheckBox.cs
@@ -25,7 +25,7 @@ namespace System.Windows.Forms
[DefaultBindingProperty(nameof(CheckState))]
[ToolboxItem("System.Windows.Forms.Design.AutoSizeToolboxItem," + AssemblyRef.SystemDesign)]
[SRDescription(nameof(SR.DescriptionCheckBox))]
- public class CheckBox : ButtonBase
+ public partial class CheckBox : ButtonBase
{
private static readonly object EVENT_CHECKEDCHANGED = new object();
private static readonly object EVENT_CHECKSTATECHANGED = new object();
@@ -534,7 +534,6 @@ protected override void OnClick(EventArgs e)
if (threeState)
{
CheckState = CheckState.Indeterminate;
-
// If the check box is clicked as a result of AccObj::DoDefaultAction
// then the native check box does not fire OBJ_STATE_CHANGE event when going to Indeterminate state.
// So the WinForms layer fires the OBJ_STATE_CHANGE event.
@@ -647,85 +646,5 @@ public override string ToString()
int checkState = (int)CheckState;
return s + ", CheckState: " + checkState.ToString(CultureInfo.InvariantCulture);
}
-
- [ComVisible(true)]
- public class CheckBoxAccessibleObject : ButtonBaseAccessibleObject
- {
- public CheckBoxAccessibleObject(Control owner) : base(owner)
- {
- }
-
- public override string DefaultAction
- {
- get
- {
- string defaultAction = Owner.AccessibleDefaultActionDescription;
- if (defaultAction != null)
- {
- return defaultAction;
- }
-
- if (((CheckBox)Owner).Checked)
- {
- return SR.AccessibleActionUncheck;
- }
- else
- {
- return SR.AccessibleActionCheck;
- }
- }
- }
-
- public override AccessibleRole Role
- {
- get
- {
- AccessibleRole role = Owner.AccessibleRole;
- if (role != AccessibleRole.Default)
- {
- return role;
- }
- return AccessibleRole.CheckButton;
- }
- }
-
- public override AccessibleStates State
- {
- get
- {
- switch (((CheckBox)Owner).CheckState)
- {
- case CheckState.Checked:
- return AccessibleStates.Checked | base.State;
- case CheckState.Indeterminate:
- return AccessibleStates.Indeterminate | base.State;
- }
-
- return base.State;
- }
- }
-
- public override void DoDefaultAction()
- {
- CheckBox cb = Owner as CheckBox;
-
- if (cb != null)
- {
- cb.AccObjDoDefaultAction = true;
- }
-
- try
- {
- base.DoDefaultAction();
- }
- finally
- {
- if (cb != null)
- {
- cb.AccObjDoDefaultAction = false;
- }
- }
- }
- }
}
}
diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/Control.ControlAccessibleObject.cs b/src/System.Windows.Forms/src/System/Windows/Forms/Control.ControlAccessibleObject.cs
index b1bb5395939..2d10ec3effa 100644
--- a/src/System.Windows.Forms/src/System/Windows/Forms/Control.ControlAccessibleObject.cs
+++ b/src/System.Windows.Forms/src/System/Windows/Forms/Control.ControlAccessibleObject.cs
@@ -26,6 +26,8 @@ public class ControlAccessibleObject : AccessibleObject
private IntPtr _handle = IntPtr.Zero; // Associated window handle (if any)
private int[] _runtimeId = null; // Used by UIAutomation
+ internal event EventHandler RequestingNavigationFragment;
+
public ControlAccessibleObject(Control ownerControl)
{
Owner = ownerControl ?? throw new ArgumentNullException(nameof(ownerControl));
@@ -368,6 +370,167 @@ public override AccessibleRole Role
}
}
+ internal class RequestingNavigationFragmentEventArgs : EventArgs
+ {
+ public RequestingNavigationFragmentEventArgs(UiaCore.NavigateDirection navigationDirection)
+ {
+ NavigationDirection = navigationDirection;
+ }
+
+ public UiaCore.IRawElementProviderFragment RequestingNavigationFragment { get; set; }
+
+ public UiaCore.NavigateDirection NavigationDirection { get; private set; }
+ }
+
+ internal override UiaCore.IRawElementProviderFragment FragmentNavigate(UiaCore.NavigateDirection direction)
+ {
+ switch (direction)
+ {
+ case UiaCore.NavigateDirection.Parent:
+ case UiaCore.NavigateDirection.NextSibling:
+ case UiaCore.NavigateDirection.PreviousSibling:
+ var eventArgs = new RequestingNavigationFragmentEventArgs(direction);
+ RequestingNavigationFragment?.Invoke(this, eventArgs);
+ return eventArgs.RequestingNavigationFragment;
+ default:
+ return base.FragmentNavigate(direction);
+ }
+ }
+
+ internal AccessibleObject GetNextChildInTabOrder(AccessibleObject currentChild)
+ {
+ var currentChildControlAccessibleObject = currentChild as ControlAccessibleObject;
+ if (currentChildControlAccessibleObject == null || currentChildControlAccessibleObject.Owner == null)
+ {
+ return null;
+ }
+
+ return GetNextChildInTabOrder(currentChildControlAccessibleObject.Owner, true);
+ }
+
+ internal AccessibleObject GetPreviousChildInTabOrder(AccessibleObject currentChild)
+ {
+ var currentChildControlAccessibleObject = currentChild as ControlAccessibleObject;
+ if (currentChildControlAccessibleObject == null || currentChildControlAccessibleObject.Owner == null)
+ {
+ return null;
+ }
+
+ return GetNextChildInTabOrder(currentChildControlAccessibleObject.Owner, true);
+ }
+
+ private static bool HaveSameTabIndex(ControlCollection controls)
+ {
+ if (controls.Count == 0 || controls.Count == 1)
+ {
+ return true;
+ }
+
+ int firstTabIndex = controls[0]._tabIndex;
+ for(int i = 1; i < controls.Count; i++)
+ {
+ if (controls[i]._tabIndex != firstTabIndex)
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ internal AccessibleObject GetNextChildInTabOrder(Control currentChild, bool forward)
+ {
+ if (Owner == null || !Owner.IsHandleCreated)
+ {
+ return null;
+ }
+
+ ControlCollection childControls = (ControlCollection)Owner.Properties.GetObject(s_controlsCollectionProperty);
+ if (childControls == null && childControls.Count == 0)
+ {
+ return null;
+ }
+
+ Control nextChild = null;
+ if (forward)
+ {
+ for (int i = 0; i < childControls.Count; i++)
+ {
+ if (currentChild == null)
+ {
+ if (i < childControls.Count &&
+ (nextChild == null || nextChild.TabIndex >= childControls[i].TabIndex))
+ {
+ nextChild = childControls[i];
+ }
+
+ continue;
+ }
+
+ if (childControls[i].Name == currentChild.Name)
+ {
+ continue;
+ }
+
+ if (i < childControls.Count - 1 &&
+ currentChild.Name == childControls[i + 1].Name &&
+ currentChild.TabIndex == childControls[i].TabIndex)
+ {
+ nextChild = childControls[i];
+ break;
+ }
+
+ if ((childControls[i].TabIndex > currentChild.TabIndex) &&
+ (nextChild == null || nextChild.TabIndex > childControls[i].TabIndex))
+ {
+ nextChild = childControls[i];
+ }
+ }
+ }
+ else
+ {
+ for (int i = childControls.Count - 1; i >= 0; i--)
+ {
+ if (currentChild == null)
+ {
+ if (i >= 0 &&
+ (nextChild == null || nextChild.TabIndex <= childControls[i].TabIndex))
+ {
+ nextChild = childControls[i];
+ }
+
+ continue;
+ }
+
+ if (childControls[i].Name == currentChild.Name)
+ {
+ continue;
+ }
+
+ if (i > 0 &&
+ currentChild.Name == childControls[i - 1].Name &&
+ currentChild.TabIndex == childControls[i].TabIndex)
+ {
+ nextChild = childControls[i];
+ break;
+ }
+
+ if ((childControls[i].TabIndex < currentChild.TabIndex) &&
+ (nextChild == null || nextChild.TabIndex <= childControls[i].TabIndex))
+ {
+ nextChild = childControls[i];
+ }
+ }
+ }
+
+ if (nextChild != null)
+ {
+ return nextChild.AccessibilityObject;
+ }
+
+ return null;
+ }
+
public override int GetHelpTopic(out string fileName)
{
int topic = 0;
@@ -437,8 +600,7 @@ public override bool RaiseLiveRegionChanged()
return RaiseAutomationEvent(UiaCore.UIA.LiveRegionChangedEventId);
}
- internal override bool IsIAccessibleExSupported()
- => Owner is IAutomationLiveRegion ? true : base.IsIAccessibleExSupported();
+ internal override bool IsIAccessibleExSupported() => true;
internal override object GetPropertyValue(UiaCore.UIA propertyID)
{
diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/Control.cs b/src/System.Windows.Forms/src/System/Windows/Forms/Control.cs
index 4bd4464e465..b3f65dee115 100644
--- a/src/System.Windows.Forms/src/System/Windows/Forms/Control.cs
+++ b/src/System.Windows.Forms/src/System/Windows/Forms/Control.cs
@@ -5924,6 +5924,10 @@ private ArrayList GetChildWindowsTabOrderList()
return holders;
}
+ internal Control FirstChildControlInTabOrder => GetFirstChildControlInTabOrder(true);
+
+ internal Control LastChildControlInTabOrder => GetFirstChildControlInTabOrder(false);
+
internal virtual Control GetFirstChildControlInTabOrder(bool forward)
{
ControlCollection ctlControls = (ControlCollection)Properties.GetObject(s_controlsCollectionProperty);
@@ -13364,7 +13368,7 @@ internal virtual bool SupportsUiaProviders
{
get
{
- return false;
+ return true;
}
}
diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/Form.AccessibleObject.cs b/src/System.Windows.Forms/src/System/Windows/Forms/Form.AccessibleObject.cs
index ea1908f73c8..1b0e2afc20e 100644
--- a/src/System.Windows.Forms/src/System/Windows/Forms/Form.AccessibleObject.cs
+++ b/src/System.Windows.Forms/src/System/Windows/Forms/Form.AccessibleObject.cs
@@ -29,6 +29,19 @@ internal FormAccessibleObject(Form owner) : base(owner)
internal override Rectangle BoundingRectangle => _owner.Bounds;
+ internal override UiaCore.IRawElementProviderFragment FragmentNavigate(UiaCore.NavigateDirection direction)
+ {
+ switch (direction)
+ {
+ case UiaCore.NavigateDirection.FirstChild:
+ return GetNextChildInTabOrder(null, true);
+ case UiaCore.NavigateDirection.LastChild:
+ return GetNextChildInTabOrder(null, false);
+ default:
+ return null;
+ }
+ }
+
internal override bool IsIAccessibleExSupported()
{
if (_owner != null)
diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/Form.cs b/src/System.Windows.Forms/src/System/Windows/Forms/Form.cs
index f8e98eb6e5e..d5d37c4b331 100644
--- a/src/System.Windows.Forms/src/System/Windows/Forms/Form.cs
+++ b/src/System.Windows.Forms/src/System/Windows/Forms/Form.cs
@@ -3865,6 +3865,50 @@ protected virtual void OnClosed(EventArgs e)
((EventHandler)Events[EVENT_CLOSED])?.Invoke(this, e);
}
+ protected override void OnControlAdded(ControlEventArgs e)
+ {
+ base.OnControlAdded(e);
+
+ ControlAccessibleObject controlAccessibleObject = e.Control.AccessibilityObject as ControlAccessibleObject;
+ if (controlAccessibleObject != null)
+ {
+ controlAccessibleObject.RequestingNavigationFragment += ControlAccessibleObject_RequestingNavigationFragment;
+ }
+ }
+
+ protected override void OnControlRemoved(ControlEventArgs e)
+ {
+ base.OnControlRemoved(e);
+
+ ControlAccessibleObject controlAccessibleObject = e.Control.AccessibilityObject as ControlAccessibleObject;
+ if (controlAccessibleObject != null)
+ {
+ controlAccessibleObject.RequestingNavigationFragment -= ControlAccessibleObject_RequestingNavigationFragment;
+ }
+ }
+
+ private void ControlAccessibleObject_RequestingNavigationFragment(object sender, ControlAccessibleObject.RequestingNavigationFragmentEventArgs e)
+ {
+ switch (e.NavigationDirection)
+ {
+ case UiaCore.NavigateDirection.Parent:
+ e.RequestingNavigationFragment = AccessibilityObject;
+ break;
+ case UiaCore.NavigateDirection.NextSibling:
+ case UiaCore.NavigateDirection.PreviousSibling:
+ bool forward = e.NavigationDirection == UiaCore.NavigateDirection.NextSibling;
+ var controlAccessibleObject = AccessibilityObject as ControlAccessibleObject;
+ var senderControlAccessibleObject = sender as ControlAccessibleObject;
+ if (controlAccessibleObject != null && senderControlAccessibleObject != null)
+ {
+ var nextControlAccessibleObject = controlAccessibleObject.GetNextChildInTabOrder(senderControlAccessibleObject.Owner, forward);
+ e.RequestingNavigationFragment = nextControlAccessibleObject;
+ }
+
+ break;
+ }
+ }
+
///
/// The Closing event is fired before the form is closed.
///
diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/GroupBox.GroupBoxAccessibleObject.cs b/src/System.Windows.Forms/src/System/Windows/Forms/GroupBox.GroupBoxAccessibleObject.cs
new file mode 100644
index 00000000000..9056337389b
--- /dev/null
+++ b/src/System.Windows.Forms/src/System/Windows/Forms/GroupBox.GroupBoxAccessibleObject.cs
@@ -0,0 +1,40 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading.Tasks;
+using static Interop;
+
+namespace System.Windows.Forms
+{
+ public partial class GroupBox
+ {
+ [ComVisible(true)]
+ internal class GroupBoxAccessibleObject : ControlAccessibleObject
+ {
+ internal GroupBoxAccessibleObject(GroupBox owner) : base(owner)
+ {
+ }
+
+ private GroupBox OwningGroupBox => Owner as GroupBox;
+
+ internal override object GetPropertyValue(UiaCore.UIA propertyID)
+ {
+ switch (propertyID)
+ {
+ case UiaCore.UIA.NamePropertyId:
+ return Name;
+ case UiaCore.UIA.ControlTypePropertyId:
+ return UiaCore.UIA.GroupControlTypeId;
+ case UiaCore.UIA.AutomationIdPropertyId:
+ return OwningGroupBox.IsHandleCreated ? OwningGroupBox.Name : String.Empty;
+ case UiaCore.UIA.IsKeyboardFocusablePropertyId:
+ return true;
+ }
+
+ return base.GetPropertyValue(propertyID);
+ }
+ }
+ }
+}
diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/GroupBox.cs b/src/System.Windows.Forms/src/System/Windows/Forms/GroupBox.cs
index a90069afd20..472d840af33 100644
--- a/src/System.Windows.Forms/src/System/Windows/Forms/GroupBox.cs
+++ b/src/System.Windows.Forms/src/System/Windows/Forms/GroupBox.cs
@@ -25,7 +25,7 @@ namespace System.Windows.Forms
[DefaultProperty(nameof(Text))]
[Designer("System.Windows.Forms.Design.GroupBoxDesigner, " + AssemblyRef.SystemDesign)]
[SRDescription(nameof(SR.DescriptionGroupBox))]
- public class GroupBox : Control
+ public partial class GroupBox : Control
{
int fontHeight = -1;
Font cachedFont;
@@ -753,41 +753,5 @@ protected override AccessibleObject CreateAccessibilityInstance()
{
return new GroupBoxAccessibleObject(this);
}
-
- [ComVisible(true)]
- internal class GroupBoxAccessibleObject : ControlAccessibleObject
- {
- internal GroupBoxAccessibleObject(GroupBox owner) : base(owner)
- {
- }
-
- public override AccessibleRole Role
- {
- get
- {
- AccessibleRole role = Owner.AccessibleRole;
- if (role != AccessibleRole.Default)
- {
- return role;
- }
- return AccessibleRole.Grouping;
- }
- }
-
- internal override bool IsIAccessibleExSupported() => true;
-
- internal override object GetPropertyValue(UiaCore.UIA propertyID)
- {
- switch (propertyID)
- {
- case UiaCore.UIA.ControlTypePropertyId:
- return UiaCore.UIA.GroupControlTypeId;
- case UiaCore.UIA.IsKeyboardFocusablePropertyId:
- return true;
- }
-
- return base.GetPropertyValue(propertyID);
- }
- }
}
}
diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/Label.LabelAccessibleObject.cs b/src/System.Windows.Forms/src/System/Windows/Forms/Label.LabelAccessibleObject.cs
new file mode 100644
index 00000000000..acb1f0e2a0b
--- /dev/null
+++ b/src/System.Windows.Forms/src/System/Windows/Forms/Label.LabelAccessibleObject.cs
@@ -0,0 +1,38 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading.Tasks;
+using static Interop;
+
+namespace System.Windows.Forms
+{
+ public partial class Label
+ {
+ [ComVisible(true)]
+ internal class LabelAccessibleObject : ControlAccessibleObject
+ {
+ public LabelAccessibleObject(Label owner) : base(owner)
+ {
+ }
+
+ private Label OwningLabel => Owner as Label;
+
+ internal override object GetPropertyValue(UiaCore.UIA propertyID)
+ {
+ switch (propertyID)
+ {
+ case UiaCore.UIA.NamePropertyId:
+ return Name;
+ case UiaCore.UIA.AutomationIdPropertyId:
+ return OwningLabel.IsHandleCreated ? OwningLabel.Name : String.Empty;
+ case UiaCore.UIA.ControlTypePropertyId:
+ return UiaCore.UIA.TextControlTypeId;
+ }
+
+ return base.GetPropertyValue(propertyID);
+ }
+ }
+ }
+}
diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/Label.cs b/src/System.Windows.Forms/src/System/Windows/Forms/Label.cs
index 0771878fa96..88ab5b6bd53 100644
--- a/src/System.Windows.Forms/src/System/Windows/Forms/Label.cs
+++ b/src/System.Windows.Forms/src/System/Windows/Forms/Label.cs
@@ -28,7 +28,7 @@ namespace System.Windows.Forms
[ToolboxItem("System.Windows.Forms.Design.AutoSizeToolboxItem," + AssemblyRef.SystemDesign)]
[SRDescription(nameof(SR.DescriptionLabel))]
// If not for FormatControl, we could inherit from ButtonBase and get foreground images for free.
- public class Label : Control, IAutomationLiveRegion
+ public partial class Label : Control, IAutomationLiveRegion
{
private static readonly object EVENT_TEXTALIGNCHANGED = new object();
@@ -1643,95 +1643,62 @@ protected override void WndProc(ref Message m)
}
}
- [ComVisible(true)]
- internal class LabelAccessibleObject : ControlAccessibleObject
+ ///
+ /// Override ImageList.Indexer to support Label's ImageList semantics.
+ ///
+ internal class LabelImageIndexer : ImageList.Indexer
{
- public LabelAccessibleObject(Label owner) : base(owner)
+ private readonly Label owner;
+ private bool useIntegerIndex = true;
+
+ public LabelImageIndexer(Label owner)
{
+ this.owner = owner;
}
- public override AccessibleRole Role
+ public override ImageList ImageList
{
- get
- {
- AccessibleRole role = Owner.AccessibleRole;
- if (role != AccessibleRole.Default)
- {
- return role;
- }
- return AccessibleRole.StaticText;
- }
+ get { return owner?.ImageList; }
+ set { Debug.Assert(false, "Setting the image list in this class is not supported"); }
}
- internal override bool IsIAccessibleExSupported() => true;
-
- internal override object GetPropertyValue(UiaCore.UIA propertyID)
+ public override string Key
{
- if (propertyID == UiaCore.UIA.ControlTypePropertyId)
+ get => base.Key;
+ set
{
- return UiaCore.UIA.TextControlTypeId;
+ base.Key = value;
+ useIntegerIndex = false;
}
-
- return base.GetPropertyValue(propertyID);
}
- }
- }
- ///
- /// Override ImageList.Indexer to support Label's ImageList semantics.
- ///
- internal class LabelImageIndexer : ImageList.Indexer
- {
- private readonly Label owner;
- private bool useIntegerIndex = true;
-
- public LabelImageIndexer(Label owner)
- {
- this.owner = owner;
- }
-
- public override ImageList ImageList
- {
- get { return owner?.ImageList; }
- set{ Debug.Assert(false, "Setting the image list in this class is not supported"); }
- }
-
- public override string Key
- {
- get => base.Key;
- set
+ public override int Index
{
- base.Key = value;
- useIntegerIndex = false;
- }
- }
-
- public override int Index
- {
- get => base.Index;
- set
- {
- base.Index = value;
- useIntegerIndex = true;
+ get => base.Index;
+ set
+ {
+ base.Index = value;
+ useIntegerIndex = true;
+ }
}
- }
- public override int ActualIndex
- {
- get
+ public override int ActualIndex
{
- if (useIntegerIndex)
- {
- // The behavior of label is to return the last item in the Images collection
- // if the index is currently set higher than the count.
- return (Index < ImageList.Images.Count) ? Index : ImageList.Images.Count - 1;
- }
- else if (ImageList != null)
+ get
{
- return ImageList.Images.IndexOfKey(Key);
- }
+ if (useIntegerIndex)
+ {
+ // The behavior of label is to return the last item in the Images collection
+ // if the index is currently set higher than the count.
+ return (Index < ImageList.Images.Count) ? Index : ImageList.Images.Count - 1;
+ }
+ else if (ImageList != null)
+ {
+ return ImageList.Images.IndexOfKey(Key);
+ }
- return -1;
+ return -1;
+ }
}
}
}
diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/ListView.ListViewAccessibleObject.cs b/src/System.Windows.Forms/src/System/Windows/Forms/ListView.ListViewAccessibleObject.cs
new file mode 100644
index 00000000000..0ff7c09bdb7
--- /dev/null
+++ b/src/System.Windows.Forms/src/System/Windows/Forms/ListView.ListViewAccessibleObject.cs
@@ -0,0 +1,260 @@
+// 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.
+
+#nullable disable
+
+using System.Collections.Generic;
+using System.Drawing;
+using System.Linq;
+using System.Runtime.InteropServices;
+using Accessibility;
+using static Interop;
+using static Interop.UiaCore;
+
+namespace System.Windows.Forms
+{
+ public partial class ListView
+ {
+ [ComVisible(true)]
+ internal class ListViewAccessibleObject : ControlAccessibleObject
+ {
+ private readonly ListView owner;
+ private readonly Dictionary _itemAccessibleObjects;
+ private readonly IAccessible _systemIAccessible;
+
+ internal ListViewAccessibleObject(ListView owner) : base(owner)
+ {
+ this.owner = owner;
+ _itemAccessibleObjects = new Dictionary();
+ _systemIAccessible = GetSystemIAccessibleInternal();
+ }
+
+ internal override UiaCore.IRawElementProviderFragmentRoot FragmentRoot => this;
+
+ private ListView OwningListView => Owner as ListView;
+
+ internal override bool IsPatternSupported(UIA patternId)
+ {
+ if (patternId == UIA.SelectionPatternId ||
+ patternId == UIA.MultipleViewPatternId ||
+ patternId == UIA.LegacyIAccessiblePatternId ||
+ (patternId == UIA.GridPatternId && OwningListView.View == View.Details) ||
+ (patternId == UIA.TablePatternId && OwningListView.View == View.Details))
+ {
+ return true;
+ }
+
+ return base.IsPatternSupported(patternId);
+ }
+
+ internal override IRawElementProviderFragment FragmentNavigate(UiaCore.NavigateDirection direction)
+ {
+ int childCount = OwningListView.Items.Count;
+
+ if (childCount == 0)
+ {
+ return null;
+ }
+
+ switch (direction)
+ {
+ case NavigateDirection.FirstChild:
+ return GetChild(0);
+ case NavigateDirection.LastChild:
+ return GetChild(childCount - 1);
+ default:
+ return base.FragmentNavigate(direction);
+ }
+ }
+
+ internal override bool CanSelectMultiple => true;
+
+ internal override object GetPropertyValue(UIA propertyID)
+ {
+ switch (propertyID)
+ {
+ case UIA.NamePropertyId:
+ return Name;
+ case UIA.AutomationIdPropertyId:
+ return OwningListView.IsHandleCreated ? OwningListView.Name : string.Empty;
+ case UIA.RuntimeIdPropertyId:
+ return RuntimeId;
+ case UIA.ControlTypePropertyId:
+ return UIA.ListControlTypeId;
+ case UIA.IsMultipleViewPatternAvailablePropertyId:
+ return IsPatternSupported(UIA.MultipleViewPatternId);
+ case UIA.ItemStatusPropertyId:
+ return GetItemStatus();
+ }
+
+ return base.GetPropertyValue(propertyID);
+ }
+
+ internal override int[] RuntimeId
+ {
+ get
+ {
+ var runtimeId = new int[2];
+ runtimeId[0] = 0x2a;
+ runtimeId[1] = (int)(long)OwningListView.Handle;
+ return runtimeId;
+ }
+ }
+
+ private string GetItemStatus()
+ {
+ switch (owner.Sorting)
+ {
+ case SortOrder.None:
+ return SR.NotSortedAccessibleStatus;
+ case SortOrder.Ascending:
+ return SR.SortedAscendingAccessibleStatus;
+ case SortOrder.Descending:
+ return SR.SortedDescendingAccessibleStatus;
+ }
+
+ return null;
+ }
+
+ public override AccessibleObject GetChild(int index)
+ {
+ if (index < 0 || index >= OwningListView.Items.Count)
+ {
+ return null;
+ }
+
+ ListViewItem item = OwningListView.Items[index];
+ if (!_itemAccessibleObjects.ContainsKey(item))
+ {
+ _itemAccessibleObjects.Add(item, new ListViewItemAccessibleObject(OwningListView, item, this));
+ }
+
+ return _itemAccessibleObjects[item];
+ }
+
+ public override int GetChildCount()
+ {
+ return OwningListView.IsHandleCreated ? OwningListView.Items.Count : 0;
+ }
+
+ public Dictionary ItemAccessibleObjects
+ {
+ get
+ {
+ return _itemAccessibleObjects;
+ }
+ }
+
+ internal override IRawElementProviderSimple[] GetSelection()
+ {
+ List selectedItemProviders = new List();
+
+ if (!OwningListView.IsHandleCreated)
+ {
+ return selectedItemProviders.ToArray();
+ }
+
+ var selectedItems = OwningListView.SelectedItems;
+ foreach (var selectedItem in selectedItems)
+ {
+ selectedItemProviders.Add(_itemAccessibleObjects[(ListViewItem)selectedItem] as IRawElementProviderSimple);
+ }
+
+ return selectedItemProviders.ToArray();
+ }
+
+ internal override IRawElementProviderFragment ElementProviderFromPoint(double x, double y)
+ {
+ return HitTest((int)x, (int)y);
+ }
+
+ public override AccessibleObject HitTest(int x, int y)
+ {
+ Point pt = owner.PointToClient(new Point(x, y));
+ ListViewHitTestInfo hti = owner.HitTest(pt.X, pt.Y);
+ if (!_itemAccessibleObjects.ContainsKey(hti.Item))
+ {
+ _itemAccessibleObjects.Add(hti.Item, new ListViewItemAccessibleObject(OwningListView, hti.Item, this));
+ }
+ return ItemAccessibleObjects[hti.Item].SubItemAccessibleObjects[hti.SubItem];
+ }
+
+ internal override int ColumnCount
+ {
+ get
+ {
+ int columnCount = 0;
+
+ var items = OwningListView.Items;
+ if (items.Count > 0)
+ {
+ columnCount = items[0].SubItems.Count;
+ }
+
+ return columnCount;
+ }
+ }
+
+ internal override int RowCount => OwningListView.Items.Count;
+
+ internal override IRawElementProviderSimple[] GetRowHeaders() => null;
+
+ internal override IRawElementProviderSimple[] GetColumnHeaders()
+ {
+ var columnHeaders = new List();
+ foreach (var columnHeader in OwningListView.Columns)
+ {
+ columnHeaders.Add(new ListViewColumnAccessibleObject(columnHeader as ColumnHeader));
+ }
+
+ return columnHeaders.ToArray();
+ }
+
+ internal override RowOrColumnMajor RowOrColumnMajor => RowOrColumnMajor.RowMajor;
+
+ internal override int GetMultiViewProviderCurrentView()
+ {
+ return (int)OwningListView.View;
+ }
+
+ internal override int[] GetMultiViewProviderSupportedViews()
+ {
+ var allViews = Enum.GetValues(typeof(View));
+ var allViewIds = new List();
+ foreach (var view in allViews)
+ {
+ allViewIds.Add((int)view);
+ }
+
+ return allViewIds.ToArray();
+ }
+
+ internal override string GetMultiViewProviderViewName(int viewId)
+ {
+ var allViews = Enum.GetValues(typeof(View));
+ foreach (var view in allViews)
+ {
+ if ((int)view == viewId)
+ {
+ return view.ToString();
+ }
+ }
+
+ return null;
+ }
+
+ internal override void SetMultiViewProviderCurrentView(int viewId)
+ {
+ var allViews = Enum.GetValues(typeof(View));
+ foreach (var view in allViews)
+ {
+ if ((int)view == viewId)
+ {
+ OwningListView.View = (View)view;
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/ListView.ListViewColumnAccessibleObject.cs b/src/System.Windows.Forms/src/System/Windows/Forms/ListView.ListViewColumnAccessibleObject.cs
new file mode 100644
index 00000000000..b8b23b8aa1c
--- /dev/null
+++ b/src/System.Windows.Forms/src/System/Windows/Forms/ListView.ListViewColumnAccessibleObject.cs
@@ -0,0 +1,36 @@
+// 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.
+
+#nullable disable
+
+using static Interop.UiaCore;
+
+namespace System.Windows.Forms
+{
+ public partial class ListView
+ {
+ internal class ListViewColumnAccessibleObject : AccessibleObject
+ {
+ private ColumnHeader _columnHeader;
+
+ public ListViewColumnAccessibleObject(ColumnHeader columnHeader)
+ {
+ _columnHeader = columnHeader;
+ }
+
+ internal override object GetPropertyValue(UIA propertyID)
+ {
+ switch (propertyID)
+ {
+ case UIA.ControlTypePropertyId:
+ return UIA.HeaderItemControlTypeId;
+ case UIA.NamePropertyId:
+ return _columnHeader?.Text ?? string.Empty;
+ default:
+ return base.GetPropertyValue(propertyID);
+ }
+ }
+ }
+ }
+}
diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/ListView.ListViewItemAccessibleObject.cs b/src/System.Windows.Forms/src/System/Windows/Forms/ListView.ListViewItemAccessibleObject.cs
new file mode 100644
index 00000000000..267b2142524
--- /dev/null
+++ b/src/System.Windows.Forms/src/System/Windows/Forms/ListView.ListViewItemAccessibleObject.cs
@@ -0,0 +1,355 @@
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading.Tasks;
+using Accessibility;
+using static Interop;
+using static Interop.UiaCore;
+
+namespace System.Windows.Forms
+{
+ public partial class ListView
+ {
+ ///
+ /// ListBox control accessible object with UI Automation provider functionality.
+ /// This inherits from the base ListBoxExAccessibleObject and ListBoxAccessibleObject
+ /// to have all base functionality.
+ ///
+ [ComVisible(true)]
+ internal class ListViewItemAccessibleObject : AccessibleObject
+ {
+ private readonly ListViewItem _itemEntry;
+ private readonly ListViewAccessibleObject _owningAccessibleObject;
+ private readonly Dictionary _subItemAccessibleObjects;
+ private readonly ListView _owningListView;
+ private readonly IAccessible _systemIAccessible;
+
+
+ public ListViewItemAccessibleObject(ListView owningListView, object itemEntry, ListViewAccessibleObject owningAccessibleObject)
+ {
+ _owningListView = owningListView;
+ _itemEntry = (ListViewItem)itemEntry;
+ _owningAccessibleObject = owningAccessibleObject;
+ _subItemAccessibleObjects = new Dictionary();
+ _systemIAccessible = owningAccessibleObject.GetSystemIAccessibleInternal();
+ FillSubItemAccessibleObjectsCollection();
+ }
+
+ public override Rectangle Bounds
+ {
+ get
+ {
+ return new Rectangle(
+ _owningListView.AccessibilityObject.Bounds.X + _itemEntry.Bounds.X,
+ _owningListView.AccessibilityObject.Bounds.Y + _itemEntry.Bounds.Y,
+ _itemEntry.Bounds.Width,
+ _itemEntry.Bounds.Height);
+ }
+ }
+
+ private string AutomationId
+ {
+ get
+ {
+ return string.Format("{0}-{1}", typeof(ListViewItem).Name, CurrentIndex);
+ }
+ }
+
+ private int CurrentIndex => _owningListView.Items.IndexOf(_itemEntry);
+
+ internal override UiaCore.IRawElementProviderFragmentRoot FragmentRoot => _owningAccessibleObject;
+
+ internal override bool IsItemSelected
+ {
+ get
+ {
+ return (State & AccessibleStates.Selected) != 0;
+ }
+ }
+
+ ///
+ /// Gets or sets the accessible name.
+ ///
+ public override string Name
+ {
+ get
+ {
+ return _itemEntry.Text;
+ }
+ set => base.Name = value;
+ }
+
+ ///
+ /// Gets the accessible role.
+ ///
+ public override AccessibleRole Role => AccessibleRole.ListItem;
+
+ ///
+ /// Gets the accessible state.
+ ///
+ public override AccessibleStates State
+ {
+ get
+ {
+ AccessibleStates state = AccessibleStates.Selectable | AccessibleStates.Focusable | AccessibleStates.MultiSelectable;
+
+ if (_owningListView.SelectedItems.Contains(_itemEntry))
+ {
+ return state |= AccessibleStates.Selected | AccessibleStates.Focused;
+ }
+
+ return state |= (AccessibleStates)(_systemIAccessible.get_accState(GetChildId()));
+ }
+ }
+
+ internal override void AddToSelection()
+ {
+ SelectItem();
+ }
+
+ public override string DefaultAction => SR.AccessibleActionDoubleClick;
+
+ public override void DoDefaultAction()
+ {
+ SetFocus();
+ }
+
+ internal override IRawElementProviderFragment FragmentNavigate(NavigateDirection direction)
+ {
+ int firstItemIndex = 0;
+ int lastItemIndex = _owningListView.Items.Count - 1;
+ int currentIndex = CurrentIndex;
+
+ switch (direction)
+ {
+ case NavigateDirection.Parent:
+ return _owningListView.AccessibilityObject;
+ case NavigateDirection.NextSibling:
+ var listViewAccessibilityObject = _owningListView.AccessibilityObject;
+ if (listViewAccessibilityObject != null)
+ {
+ int itemsCount = listViewAccessibilityObject.GetChildCount();
+ int nextItemIndex = currentIndex + 1;
+ if (itemsCount > nextItemIndex)
+ {
+ return listViewAccessibilityObject.GetChild(nextItemIndex);
+ }
+ }
+
+ break;
+ case NavigateDirection.PreviousSibling:
+ listViewAccessibilityObject = _owningListView.AccessibilityObject;
+ if (listViewAccessibilityObject != null)
+ {
+ int previousItemIndex = currentIndex - 1;
+ if (previousItemIndex >= 0)
+ {
+ return listViewAccessibilityObject.GetChild(previousItemIndex);
+ }
+ }
+
+ break;
+ case NavigateDirection.FirstChild:
+ if (_itemEntry.SubItems.Count > 0)
+ {
+ return GetChild(firstItemIndex);
+ }
+
+ return null;
+ case NavigateDirection.LastChild:
+ if (_itemEntry.SubItems.Count > 0)
+ {
+ return GetChild(lastItemIndex);
+ }
+
+ return null;
+ }
+
+ return null;
+ }
+
+ private void FillSubItemAccessibleObjectsCollection()
+ {
+ foreach (var item in _itemEntry.SubItems)
+ {
+ _subItemAccessibleObjects.Add((ListViewItem.ListViewSubItem)item, new ListViewSubItemAccessibleObject(_owningListView, _itemEntry, (ListViewItem.ListViewSubItem)item));
+ }
+ }
+
+ public override AccessibleObject GetChild(int index)
+ {
+ if (index < 0 || index >= _itemEntry.SubItems.Count)
+ {
+ return null;
+ }
+
+ ListViewItem.ListViewSubItem item = _itemEntry.SubItems[index];
+
+ return _subItemAccessibleObjects[item];
+ }
+
+ internal override int[] RuntimeId
+ {
+ get
+ {
+ var owningListViewRuntimeId = _owningListView.AccessibilityObject.RuntimeId;
+
+ var runtimeId = new int[4];
+ runtimeId[0] = owningListViewRuntimeId[0];
+ runtimeId[1] = owningListViewRuntimeId[1];
+ runtimeId[2] = 4; // Win32-like const.
+ runtimeId[3] = CurrentIndex;
+ return runtimeId;
+ }
+ }
+
+ public Dictionary SubItemAccessibleObjects
+ {
+ get
+ {
+ return _subItemAccessibleObjects;
+ }
+ }
+
+ internal override object GetPropertyValue(UIA propertyID)
+ {
+ switch (propertyID)
+ {
+ case UIA.RuntimeIdPropertyId:
+ return RuntimeId;
+ case UIA.AutomationIdPropertyId:
+ return AutomationId;
+ case UIA.BoundingRectanglePropertyId:
+ return Bounds;
+ case UIA.FrameworkIdPropertyId:
+ return "WinForm";
+ case UIA.ControlTypePropertyId:
+ return UIA.ListItemControlTypeId;
+ case UIA.NamePropertyId:
+ return Name;
+ case UIA.HasKeyboardFocusPropertyId:
+ return _owningListView.Focused && _owningListView.FocusedItem == _itemEntry;
+ case UIA.IsKeyboardFocusablePropertyId:
+ return (State & AccessibleStates.Focusable) == AccessibleStates.Focusable;
+ case UIA.IsEnabledPropertyId:
+ return _owningListView.Enabled;
+ case UIA.IsOffscreenPropertyId:
+ return (State & AccessibleStates.Offscreen) == AccessibleStates.Offscreen;
+ case UIA.NativeWindowHandlePropertyId:
+ return _owningListView.Handle;
+ case UIA.IsSelectionItemPatternAvailablePropertyId:
+ return IsPatternSupported(UIA.SelectionItemPatternId);
+ case UIA.IsScrollItemPatternAvailablePropertyId:
+ return IsPatternSupported(UIA.ScrollItemPatternId);
+ case UIA.IsInvokePatternAvailablePropertyId:
+ return IsPatternSupported(UIA.InvokePatternId);
+ default:
+ return base.GetPropertyValue(propertyID);
+ }
+ }
+
+ ///
+ /// Indicates whether specified pattern is supported.
+ ///
+ /// The pattern ID.
+ /// True if specified
+ internal override bool IsPatternSupported(UIA patternId)
+ {
+ if (patternId == UIA.ScrollItemPatternId ||
+ patternId == UIA.LegacyIAccessiblePatternId ||
+ patternId == UIA.SelectionItemPatternId ||
+ patternId == UIA.InvokePatternId)
+ {
+ return true;
+ }
+
+ return base.IsPatternSupported(patternId);
+ }
+
+ internal override void RemoveFromSelection()
+ {
+ // Do nothing, C++ implementation returns UIA_E_INVALIDOPERATION 0x80131509
+ }
+
+ internal override IRawElementProviderSimple ItemSelectionContainer => _owningListView.AccessibilityObject;
+
+ internal override void ScrollIntoView()
+ {
+ int currentIndex = CurrentIndex;
+
+ if (_owningListView.SelectedItems == null) //no items selected
+ {
+ User32.SendMessageW(_owningListView, (User32.WM)User32.LB.SETCARETINDEX, (IntPtr)currentIndex);
+ return;
+ }
+
+ int firstVisibleIndex = User32.SendMessageW(_owningListView, (User32.WM)User32.LB.GETTOPINDEX).ToInt32();
+ if (currentIndex < firstVisibleIndex)
+ {
+ User32.SendMessageW(_owningListView, (User32.WM)User32.LB.SETTOPINDEX, (IntPtr)currentIndex);
+ return;
+ }
+
+ int itemsHeightSum = 0;
+ int visibleItemsCount = 0;
+ int listBoxHeight = _owningListView.ClientRectangle.Height;
+ int itemsCount = _owningListView.Items.Count;
+
+ for (int i = firstVisibleIndex; i < itemsCount; i++)
+ {
+ int itemHeight = User32.SendMessageW(_owningListView, (User32.WM)User32.LB.GETITEMHEIGHT, (IntPtr)i).ToInt32();
+
+ if ((itemsHeightSum += itemHeight) <= listBoxHeight)
+ {
+ continue;
+ }
+
+ int lastVisibleIndex = i - 1; // - 1 because last "i" index is invisible
+ visibleItemsCount = lastVisibleIndex - firstVisibleIndex + 1; // + 1 because array indexes begin with 0
+
+ if (currentIndex > lastVisibleIndex)
+ {
+ User32.SendMessageW(_owningListView, (User32.WM)User32.LB.SETTOPINDEX, (IntPtr)(currentIndex - visibleItemsCount + 1));
+ }
+
+ break;
+ }
+ }
+
+ internal unsafe override void SelectItem()
+ {
+ _owningListView.selectedIndexCollection.Add(CurrentIndex);
+
+ User32.InvalidateRect(new HandleRef(this, _owningListView.Handle), null, BOOL.FALSE);
+ RaiseAutomationEvent(UIA.AutomationFocusChangedEventId);
+ RaiseAutomationEvent(UIA.SelectionItem_ElementSelectedEventId);
+ }
+
+ internal override void SetFocus()
+ {
+ RaiseAutomationEvent(UIA.AutomationFocusChangedEventId);
+ SelectItem();
+ }
+
+ public override void Select(AccessibleSelection flags)
+ {
+ try
+ {
+ _systemIAccessible.accSelect((int)flags, GetChildId());
+ }
+ catch (ArgumentException)
+ {
+ // In Everett, the ListBox accessible children did not have any selection capability.
+ // In Whidbey, they delegate the selection capability to OLEACC.
+ // However, OLEACC does not deal w/ several Selection flags: ExtendSelection, AddSelection, RemoveSelection.
+ // OLEACC instead throws an ArgumentException.
+ // Since Whidbey API's should not throw an exception in places where Everett API's did not, we catch
+ // the ArgumentException and fail silently.
+ }
+ }
+ }
+ }
+}
diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/ListView.ListViewSubItemAccessibleObject.cs b/src/System.Windows.Forms/src/System/Windows/Forms/ListView.ListViewSubItemAccessibleObject.cs
new file mode 100644
index 00000000000..494d488a2d2
--- /dev/null
+++ b/src/System.Windows.Forms/src/System/Windows/Forms/ListView.ListViewSubItemAccessibleObject.cs
@@ -0,0 +1,204 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Drawing;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading.Tasks;
+using static Interop;
+using static Interop.UiaCore;
+
+namespace System.Windows.Forms
+{
+ public partial class ListView
+ {
+ [ComVisible(true)]
+ internal class ListViewSubItemAccessibleObject : AccessibleObject
+ {
+ private ListView _owningListView;
+ private ListViewItem _owningItem;
+ private ListViewItem.ListViewSubItem _owningSubItem;
+
+ public ListViewSubItemAccessibleObject(ListView owningListView, ListViewItem owningItem, ListViewItem.ListViewSubItem owningSubItem)
+ {
+ _owningListView = owningListView ?? throw new ArgumentNullException(nameof(owningListView));
+ _owningItem = owningItem ?? throw new ArgumentNullException(nameof(owningItem));
+ _owningSubItem = owningSubItem ?? throw new ArgumentNullException(nameof(owningSubItem));
+ }
+
+ internal override IRawElementProviderFragmentRoot FragmentRoot
+ {
+ get
+ {
+ return _owningListView.AccessibilityObject;
+ }
+ }
+
+ public override Rectangle Bounds
+ {
+ get
+ {
+ return new Rectangle(
+ _owningListView.AccessibilityObject.Bounds.X + _owningSubItem.Bounds.X,
+ _owningListView.AccessibilityObject.Bounds.Y + _owningSubItem.Bounds.Y,
+ _owningListView.Columns[GetCurrentIndex()].Width,
+ _owningSubItem.Bounds.Height);
+ }
+ }
+
+ internal override IRawElementProviderFragment FragmentNavigate(NavigateDirection direction)
+ {
+ switch (direction)
+ {
+ case NavigateDirection.Parent:
+ return ParentFragment;
+ case NavigateDirection.NextSibling:
+ int nextSubItemIndex = GetCurrentIndex() + 1;
+ if (_owningItem.SubItems.Count > nextSubItemIndex)
+ {
+ var subItemCollection = (ParentFragment as ListViewItemAccessibleObject).SubItemAccessibleObjects;
+ var nextSubItem = _owningItem.SubItems[nextSubItemIndex];
+ return subItemCollection[nextSubItem] as IRawElementProviderFragment;
+ }
+
+ break;
+ case NavigateDirection.PreviousSibling:
+ int previousSubItemIndex = GetCurrentIndex() - 1;
+ if (previousSubItemIndex >= 0)
+ {
+ var subItemCollection = (ParentFragment as ListViewItemAccessibleObject).SubItemAccessibleObjects;
+ var previousSubItem = _owningItem.SubItems[previousSubItemIndex];
+ return subItemCollection[previousSubItem] as IRawElementProviderFragment;
+ }
+
+ break;
+ }
+
+ return base.FragmentNavigate(direction);
+ }
+
+ ///
+ /// Gets or sets the accessible name.
+ ///
+ public override string Name
+ {
+ get
+ {
+ return _owningSubItem.Text;
+ }
+ set => base.Name = value;
+ }
+
+ internal override int[] RuntimeId
+ {
+ get
+ {
+ var itemCollection = (_owningListView.AccessibilityObject as ListViewAccessibleObject).ItemAccessibleObjects;
+ var itemUiaProvider = itemCollection[_owningItem] as IRawElementProviderFragment;
+ var owningItemRuntimeId = itemUiaProvider.GetRuntimeId();
+
+ var runtimeId = new int[5];
+ runtimeId[0] = owningItemRuntimeId[0];
+ runtimeId[1] = owningItemRuntimeId[1];
+ runtimeId[2] = owningItemRuntimeId[2];
+ runtimeId[3] = owningItemRuntimeId[3];
+ runtimeId[4] = GetCurrentIndex();
+ return runtimeId;
+ }
+ }
+
+ internal override object GetPropertyValue(UIA propertyID)
+ {
+ switch (propertyID)
+ {
+ case UIA.ControlTypePropertyId:
+ return UIA.TextControlTypeId;
+ case UIA.NamePropertyId:
+ return Name;
+ case UIA.FrameworkIdPropertyId:
+ return "WinForm";
+ case UIA.ProcessIdPropertyId:
+ return Process.GetCurrentProcess().Id;
+ case UIA.AutomationIdPropertyId:
+ return AutomationId;
+ case UIA.RuntimeIdPropertyId:
+ return RuntimeId;
+ case UIA.HasKeyboardFocusPropertyId:
+ return _owningListView.Focused && _owningListView.FocusedItem == _owningItem;
+ case UIA.IsKeyboardFocusablePropertyId:
+ return (State & AccessibleStates.Focusable) == AccessibleStates.Focusable;
+ case UIA.IsEnabledPropertyId:
+ return _owningListView.Enabled;
+ case UIA.IsOffscreenPropertyId:
+ return (State & AccessibleStates.Offscreen) == AccessibleStates.Offscreen;
+ case UIA.BoundingRectanglePropertyId:
+ return Bounds;
+ case UIA.IsGridItemPatternAvailablePropertyId:
+ return IsPatternSupported(UIA.GridItemPatternId);
+ case UIA.IsTableItemPatternAvailablePropertyId:
+ return IsPatternSupported(UIA.TableItemPatternId);
+ default:
+ return base.GetPropertyValue(propertyID);
+ }
+ }
+
+ ///
+ /// Gets the accessible state.
+ ///
+ public override AccessibleStates State
+ {
+ get
+ {
+ return AccessibleStates.Focusable;
+ }
+ }
+
+ internal override IRawElementProviderSimple ContainingGrid => _owningListView.AccessibilityObject;
+
+ internal override int Row => _owningItem.Index;
+
+ internal override int Column => _owningItem.SubItems.IndexOf(_owningSubItem);
+
+ internal override IRawElementProviderSimple[] GetColumnHeaderItems()
+ {
+ var columnHeaders = new List();
+ columnHeaders.Add(new ListViewColumnAccessibleObject(_owningListView.Columns[Column] as ColumnHeader));
+
+ return columnHeaders.ToArray();
+ }
+
+ internal override bool IsPatternSupported(UIA patternId)
+ {
+ if(patternId == UIA.GridItemPatternId ||
+ patternId == UIA.TableItemPatternId)
+ {
+ return true;
+ }
+ return base.IsPatternSupported(patternId);
+ }
+
+ private string AutomationId
+ {
+ get
+ {
+ return string.Format("{0}-{1}", typeof(ListViewItem.ListViewSubItem).Name, GetCurrentIndex());
+ }
+ }
+
+ private IRawElementProviderFragment ParentFragment
+ {
+ get
+ {
+ var accessibleObject = _owningListView.AccessibilityObject as ListViewAccessibleObject;
+ return accessibleObject.ItemAccessibleObjects[_owningItem] as IRawElementProviderFragment;
+ }
+ }
+
+ private int GetCurrentIndex()
+ {
+ return _owningItem.SubItems.IndexOf(_owningSubItem);
+ }
+ }
+ }
+}
diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/ListView.cs b/src/System.Windows.Forms/src/System/Windows/Forms/ListView.cs
index aef7d3d4dac..8b04223f5dc 100644
--- a/src/System.Windows.Forms/src/System/Windows/Forms/ListView.cs
+++ b/src/System.Windows.Forms/src/System/Windows/Forms/ListView.cs
@@ -31,7 +31,7 @@ namespace System.Windows.Forms
[DefaultProperty(nameof(Items))]
[DefaultEvent(nameof(SelectedIndexChanged))]
[SRDescription(nameof(SR.DescriptionListView))]
- public class ListView : Control
+ public partial class ListView : Control
{
//members
private const int MASK_HITTESTFLAG = 0x00F7;
@@ -9461,43 +9461,5 @@ protected override AccessibleObject CreateAccessibilityInstance()
{
return new ListViewAccessibleObject(this);
}
-
- internal class ListViewAccessibleObject : ControlAccessibleObject
- {
- private readonly ListView owner;
-
- internal ListViewAccessibleObject(ListView owner) : base(owner)
- {
- this.owner = owner;
- }
-
- internal override bool IsIAccessibleExSupported()
- {
- if (owner != null)
- {
- return true;
- }
-
- return base.IsIAccessibleExSupported();
- }
-
- internal override object GetPropertyValue(UiaCore.UIA propertyID)
- {
- if (propertyID == UiaCore.UIA.ItemStatusPropertyId)
- {
- switch (owner.Sorting)
- {
- case SortOrder.None:
- return SR.NotSortedAccessibleStatus;
- case SortOrder.Ascending:
- return SR.SortedAscendingAccessibleStatus;
- case SortOrder.Descending:
- return SR.SortedDescendingAccessibleStatus;
- }
- }
-
- return base.GetPropertyValue(propertyID);
- }
- }
}
}
diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/PictureBox.PictureBoxAccessibleObject.cs b/src/System.Windows.Forms/src/System/Windows/Forms/PictureBox.PictureBoxAccessibleObject.cs
new file mode 100644
index 00000000000..f71fc74aa7e
--- /dev/null
+++ b/src/System.Windows.Forms/src/System/Windows/Forms/PictureBox.PictureBoxAccessibleObject.cs
@@ -0,0 +1,40 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading.Tasks;
+using static Interop;
+
+namespace System.Windows.Forms
+{
+ public partial class PictureBox
+ {
+ [ComVisible(true)]
+ internal class PictureBoxAccessibleObject : ControlAccessibleObject
+ {
+ internal PictureBoxAccessibleObject(PictureBox owner) : base(owner)
+ {
+ }
+
+ private PictureBox OwningPictureBox => Owner as PictureBox;
+
+ internal override object GetPropertyValue(UiaCore.UIA propertyID)
+ {
+ switch (propertyID)
+ {
+ case UiaCore.UIA.NamePropertyId:
+ return Name;
+ case UiaCore.UIA.ControlTypePropertyId:
+ return UiaCore.UIA.PaneControlTypeId;
+ case UiaCore.UIA.AutomationIdPropertyId:
+ return OwningPictureBox.IsHandleCreated ? OwningPictureBox.Name : String.Empty;
+ case UiaCore.UIA.IsKeyboardFocusablePropertyId:
+ return true;
+ }
+
+ return base.GetPropertyValue(propertyID);
+ }
+ }
+ }
+}
diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/PictureBox.cs b/src/System.Windows.Forms/src/System/Windows/Forms/PictureBox.cs
index 42b0fdd3230..00030f8fc53 100644
--- a/src/System.Windows.Forms/src/System/Windows/Forms/PictureBox.cs
+++ b/src/System.Windows.Forms/src/System/Windows/Forms/PictureBox.cs
@@ -29,7 +29,7 @@ namespace System.Windows.Forms
[Docking(DockingBehavior.Ask)]
[Designer("System.Windows.Forms.Design.PictureBoxDesigner, " + AssemblyRef.SystemDesign)]
[SRDescription(nameof(SR.DescriptionPictureBox))]
- public class PictureBox : Control, ISupportInitialize
+ public partial class PictureBox : Control, ISupportInitialize
{
///
/// The type of border this control will have.
@@ -1200,5 +1200,8 @@ private enum ImageInstallationType
ErrorOrInitial,
FromUrl
}
+
+ protected override AccessibleObject CreateAccessibilityInstance()
+ => new PictureBoxAccessibleObject(this);
}
}
diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/RadioButton.RadioButtonAccessibleObject.cs b/src/System.Windows.Forms/src/System/Windows/Forms/RadioButton.RadioButtonAccessibleObject.cs
new file mode 100644
index 00000000000..3cb99f7fdc2
--- /dev/null
+++ b/src/System.Windows.Forms/src/System/Windows/Forms/RadioButton.RadioButtonAccessibleObject.cs
@@ -0,0 +1,81 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading.Tasks;
+using static Interop;
+
+namespace System.Windows.Forms
+{
+ public partial class RadioButton
+ {
+ [ComVisible(true)]
+ public class RadioButtonAccessibleObject : ControlAccessibleObject
+ {
+ public RadioButtonAccessibleObject(RadioButton owner) : base(owner)
+ {
+ }
+
+ private RadioButton OwningRadioButton => Owner as RadioButton;
+
+ internal override bool IsPatternSupported(UiaCore.UIA patternId)
+ {
+ if (patternId == UiaCore.UIA.SelectionItemPatternId)
+ {
+ return true;
+ }
+
+ return base.IsPatternSupported(patternId);
+ }
+
+ internal override object GetPropertyValue(UiaCore.UIA propertyID)
+ {
+ switch (propertyID)
+ {
+ case UiaCore.UIA.NamePropertyId:
+ return Name;
+ case UiaCore.UIA.AutomationIdPropertyId:
+ return OwningRadioButton.IsHandleCreated ? OwningRadioButton.Name : String.Empty;
+ case UiaCore.UIA.IsSelectionItemPatternAvailablePropertyId:
+ return IsPatternSupported(UiaCore.UIA.SelectionItemPatternId);
+ case UiaCore.UIA.ControlTypePropertyId:
+ return UiaCore.UIA.RadioButtonControlTypeId;
+ case UiaCore.UIA.IsKeyboardFocusablePropertyId:
+ // This is necessary for compatibility with MSAA proxy:
+ // IsKeyboardFocusable = true regardless the control is enabled/disabled.
+ return true;
+ }
+
+ return base.GetPropertyValue(propertyID);
+ }
+
+ public override string DefaultAction
+ {
+ get
+ {
+ string defaultAction = Owner.AccessibleDefaultActionDescription;
+ if (defaultAction != null)
+ {
+ return defaultAction;
+ }
+
+ return SR.AccessibleActionCheck;
+ }
+ }
+
+ internal override bool IsItemSelected
+ {
+ get => OwningRadioButton.Checked;
+ }
+
+ public override void DoDefaultAction()
+ {
+ if (OwningRadioButton.IsHandleCreated)
+ {
+ OwningRadioButton.PerformClick();
+ }
+ }
+ }
+ }
+}
diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/RadioButton.cs b/src/System.Windows.Forms/src/System/Windows/Forms/RadioButton.cs
index a6249e42091..7b38cf66269 100644
--- a/src/System.Windows.Forms/src/System/Windows/Forms/RadioButton.cs
+++ b/src/System.Windows.Forms/src/System/Windows/Forms/RadioButton.cs
@@ -26,7 +26,7 @@ namespace System.Windows.Forms
[ToolboxItem("System.Windows.Forms.Design.AutoSizeToolboxItem," + AssemblyRef.SystemDesign)]
[Designer("System.Windows.Forms.Design.RadioButtonDesigner, " + AssemblyRef.SystemDesign)]
[SRDescription(nameof(SR.DescriptionRadioButton))]
- public class RadioButton : ButtonBase
+ public partial class RadioButton : ButtonBase
{
private static readonly object EVENT_CHECKEDCHANGED = new object();
private static readonly ContentAlignment anyRight = ContentAlignment.TopRight | ContentAlignment.MiddleRight | ContentAlignment.BottomRight;
@@ -602,57 +602,5 @@ public override string ToString()
string s = base.ToString();
return s + ", Checked: " + Checked.ToString();
}
-
- [ComVisible(true)]
- public class RadioButtonAccessibleObject : ButtonBaseAccessibleObject
- {
- public RadioButtonAccessibleObject(RadioButton owner) : base(owner)
- {
- }
-
- public override string DefaultAction
- {
- get
- {
- string defaultAction = Owner.AccessibleDefaultActionDescription;
- if (defaultAction != null)
- {
- return defaultAction;
- }
-
- return SR.AccessibleActionCheck;
- }
- }
-
- public override AccessibleRole Role
- {
- get
- {
- AccessibleRole role = Owner.AccessibleRole;
- if (role != AccessibleRole.Default)
- {
- return role;
- }
- return AccessibleRole.RadioButton;
- }
- }
-
- public override AccessibleStates State
- {
- get
- {
- if (((RadioButton)Owner).Checked)
- {
- return AccessibleStates.Checked | base.State;
- }
- return base.State;
- }
- }
-
- public override void DoDefaultAction()
- {
- ((RadioButton)Owner).PerformClick();
- }
- }
}
}
diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/Splitter.SplitterAccessibleObject.cs b/src/System.Windows.Forms/src/System/Windows/Forms/Splitter.SplitterAccessibleObject.cs
new file mode 100644
index 00000000000..43a9b7972d2
--- /dev/null
+++ b/src/System.Windows.Forms/src/System/Windows/Forms/Splitter.SplitterAccessibleObject.cs
@@ -0,0 +1,42 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading.Tasks;
+using static Interop;
+
+namespace System.Windows.Forms
+{
+ public partial class Splitter
+ {
+ [ComVisible(true)]
+ internal class SplitterAccessibleObject : ControlAccessibleObject
+ {
+ internal SplitterAccessibleObject(Splitter owner) : base(owner)
+ {
+ }
+
+ private Splitter OwningSplitter => Owner as Splitter;
+
+ internal override object GetPropertyValue(UiaCore.UIA propertyID)
+ {
+ switch (propertyID)
+ {
+ case UiaCore.UIA.NamePropertyId:
+ return Name;
+ case UiaCore.UIA.AutomationIdPropertyId:
+ return OwningSplitter.IsHandleCreated ? OwningSplitter.Name : String.Empty;
+ case UiaCore.UIA.ControlTypePropertyId:
+ return UiaCore.UIA.PaneControlTypeId;
+ case UiaCore.UIA.IsKeyboardFocusablePropertyId:
+ // This is necessary for compatibility with MSAA proxy:
+ // IsKeyboardFocusable = true regardless the control is enabled/disabled.
+ return true;
+ }
+
+ return base.GetPropertyValue(propertyID);
+ }
+ }
+ }
+}
diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/Splitter.cs b/src/System.Windows.Forms/src/System/Windows/Forms/Splitter.cs
index 6238fe1230a..a049d488b7d 100644
--- a/src/System.Windows.Forms/src/System/Windows/Forms/Splitter.cs
+++ b/src/System.Windows.Forms/src/System/Windows/Forms/Splitter.cs
@@ -24,7 +24,7 @@ namespace System.Windows.Forms
[DefaultProperty(nameof(Dock))]
[SRDescription(nameof(SR.DescriptionSplitter))]
[Designer("System.Windows.Forms.Design.SplitterDesigner, " + AssemblyRef.SystemDesign)]
- public class Splitter : Control
+ public partial class Splitter : Control
{
private const int DRAW_START = 1;
private const int DRAW_MOVE = 2;
@@ -971,6 +971,9 @@ public override string ToString()
return s + ", MinExtra: " + MinExtra.ToString(CultureInfo.CurrentCulture) + ", MinSize: " + MinSize.ToString(CultureInfo.CurrentCulture);
}
+ protected override AccessibleObject CreateAccessibilityInstance()
+ => new SplitterAccessibleObject(this);
+
///
/// Return value holder...
///
diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/TextBox.TextBoxAccessibleObject.cs b/src/System.Windows.Forms/src/System/Windows/Forms/TextBox.TextBoxAccessibleObject.cs
new file mode 100644
index 00000000000..8117d7b19c4
--- /dev/null
+++ b/src/System.Windows.Forms/src/System/Windows/Forms/TextBox.TextBoxAccessibleObject.cs
@@ -0,0 +1,68 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading.Tasks;
+using static System.Windows.Forms.Control;
+using static Interop;
+
+namespace System.Windows.Forms
+{
+ public partial class TextBox
+ {
+ [ComVisible(true)]
+ internal class TextBoxAccessibleObject : ControlAccessibleObject
+ {
+ internal TextBoxAccessibleObject(TextBox owner) : base(owner)
+ {
+ }
+
+ public override string Name
+ {
+ get
+ {
+ string name = base.Name;
+ if (name == null)
+ {
+ name = String.Empty;
+ }
+
+ // Otherwise just return the default label string, minus any mnemonics
+ return name;
+ }
+ set => base.Name = value;
+ }
+
+ private TextBox OwningTextBox => Owner as TextBox;
+ internal override bool IsPatternSupported(UiaCore.UIA patternId)
+ {
+ if (patternId == UiaCore.UIA.ValuePatternId)
+ {
+ return true;
+ }
+
+ return base.IsPatternSupported(patternId);
+ }
+
+ internal override object GetPropertyValue(UiaCore.UIA propertyID)
+ {
+ switch (propertyID)
+ {
+ case UiaCore.UIA.NamePropertyId:
+ return Name;
+ case UiaCore.UIA.AutomationIdPropertyId:
+ return OwningTextBox.IsHandleCreated ? OwningTextBox.Name : String.Empty;
+ case UiaCore.UIA.ControlTypePropertyId:
+ return UiaCore.UIA.EditControlTypeId;
+ case UiaCore.UIA.IsKeyboardFocusablePropertyId:
+ // This is necessary for compatibility with MSAA proxy:
+ // IsKeyboardFocusable = true regardless the control is enabled/disabled.
+ return true;
+ }
+
+ return base.GetPropertyValue(propertyID);
+ }
+ }
+ }
+}
diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/TextBox.cs b/src/System.Windows.Forms/src/System/Windows/Forms/TextBox.cs
index e29ab89718e..362122085ca 100644
--- a/src/System.Windows.Forms/src/System/Windows/Forms/TextBox.cs
+++ b/src/System.Windows.Forms/src/System/Windows/Forms/TextBox.cs
@@ -21,7 +21,7 @@ namespace System.Windows.Forms
[ComVisible(true)]
[Designer("System.Windows.Forms.Design.TextBoxDesigner, " + AssemblyRef.SystemDesign)]
[SRDescription(nameof(SR.DescriptionTextBox))]
- public class TextBox : TextBoxBase
+ public partial class TextBox : TextBoxBase
{
private static readonly object EVENT_TEXTALIGNCHANGED = new object();
@@ -981,5 +981,8 @@ public TestAccessor(TextBox textBox)
public bool ShouldRenderPlaceHolderText(in Message m) => _textBox.ShouldRenderPlaceHolderText(m);
}
+
+ protected override AccessibleObject CreateAccessibilityInstance()
+ => new TextBoxAccessibleObject(this);
}
}