Skip to content

Commit 92c24d3

Browse files
committed
Fixes #7154 #13672 Allows DataGridView to start row/column on index 1 on Narrator
1 parent e9badd9 commit 92c24d3

File tree

5 files changed

+71
-14
lines changed

5 files changed

+71
-14
lines changed

src/System.Windows.Forms.Primitives/src/System/LocalAppContextSwitches/LocalAppContextSwitches.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,14 @@ internal static partial class LocalAppContextSwitches
2020
internal const string ServicePointManagerCheckCrlSwitchName = "System.Windows.Forms.ServicePointManagerCheckCrl";
2121
internal const string TrackBarModernRenderingSwitchName = "System.Windows.Forms.TrackBarModernRendering";
2222
private const string DoNotCatchUnhandledExceptionsSwitchName = "System.Windows.Forms.DoNotCatchUnhandledExceptions";
23+
internal const string DataGridViewUIAStartRowCountAtZeroSwitchName = "System.Windows.Forms.DataGridViewUIAStartRowCountAtZero";
2324

2425
private static int s_scaleTopLevelFormMinMaxSizeForDpi;
2526
private static int s_anchorLayoutV2;
2627
private static int s_servicePointManagerCheckCrl;
2728
private static int s_trackBarModernRendering;
2829
private static int s_doNotCatchUnhandledExceptions;
30+
private static int s_dataGridViewUIAStartRowCountAtZero;
2931

3032
private static FrameworkName? s_targetFrameworkName;
3133

@@ -151,4 +153,12 @@ public static bool ServicePointManagerCheckCrl
151153
[MethodImpl(MethodImplOptions.AggressiveInlining)]
152154
get => GetCachedSwitchValue(ServicePointManagerCheckCrlSwitchName, ref s_servicePointManagerCheckCrl);
153155
}
156+
157+
public static bool DataGridViewUIAStartRowCountAtZero
158+
{
159+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
160+
get => GetCachedSwitchValue(DataGridViewUIAStartRowCountAtZeroSwitchName, ref s_dataGridViewUIAStartRowCountAtZero);
161+
}
162+
163+
internal static void SetDataGridViewUIAStartRowCountAtZero(bool value) => s_dataGridViewUIAStartRowCountAtZero = value ? 1 : 0;
154164
}

src/System.Windows.Forms/src/System/Windows/Forms/DataGridViewCell.DataGridViewCellAccessibleObject.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
using System.ComponentModel;
55
using System.Drawing;
6+
using System.Windows.Forms.Primitives;
67
using static Interop;
78

89
namespace System.Windows.Forms;
@@ -55,9 +56,9 @@ public override string? Name
5556

5657
int rowIndex = _owner.DataGridView is null
5758
? -1
58-
: _owner.DataGridView.Rows.GetVisibleIndex(_owner.OwningRow);
59+
: _owner.DataGridView.Rows.GetVisibleIndex(_owner.OwningRow) + RowStartIndex;
5960

60-
string name = string.Format(SR.DataGridView_AccDataGridViewCellName, _owner.OwningColumn.HeaderText, rowIndex);
61+
string name = string.Format(SR.DataGridView_AccDataGridViewCellName, _owner.OwningColumn.HeaderText, rowIndex).Trim();
6162

6263
if (_owner.OwningColumn.SortMode != DataGridViewColumnSortMode.NotSortable)
6364
{
@@ -113,6 +114,8 @@ private AccessibleObject? ParentPrivate
113114

114115
public override AccessibleRole Role => AccessibleRole.Cell;
115116

117+
private static int RowStartIndex => LocalAppContextSwitches.DataGridViewUIAStartRowCountAtZero ? 0 : 1;
118+
116119
public override AccessibleStates State
117120
{
118121
get

src/System.Windows.Forms/src/System/Windows/Forms/DataGridViewRow.DataGridViewRowAccessibleObject.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System.Drawing;
55
using System.Globalization;
66
using System.Text;
7+
using System.Windows.Forms.Primitives;
78
using static Interop;
89

910
namespace System.Windows.Forms;
@@ -94,8 +95,7 @@ public override string Name
9495
}
9596

9697
int index = _owningDataGridViewRow is { Visible: true, DataGridView: { } }
97-
? _owningDataGridViewRow.DataGridView.Rows.GetVisibleIndex(_owningDataGridViewRow)
98-
: -1;
98+
? _owningDataGridViewRow.DataGridView.Rows.GetVisibleIndex(_owningDataGridViewRow) + RowStartIndex : -1;
9999

100100
return string.Format(SR.DataGridView_AccRowName, index.ToString(CultureInfo.CurrentCulture));
101101
}
@@ -132,6 +132,8 @@ private AccessibleObject? ParentPrivate
132132

133133
public override AccessibleRole Role => AccessibleRole.Row;
134134

135+
private static int RowStartIndex => LocalAppContextSwitches.DataGridViewUIAStartRowCountAtZero ? 0 : 1;
136+
135137
internal override int[] RuntimeId
136138
=> _runtimeId ??= new int[]
137139
{

src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/DataGridViewCellAccessibleObjectTests.cs

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System.Drawing;
5+
using System.Windows.Forms.Primitives;
56
using Moq;
67
using Moq.Protected;
78
using static Interop;
@@ -207,6 +208,7 @@ public void DataGridViewCellAccessibleObject_Name_ReturnExpected_IfDataGridViewN
207208
Assert.Equal(expected, accessibleObject.Name);
208209
}
209210

211+
// Whether UIA row indexing is 1-based or 0-based, is controlled by the DataGridViewUIAStartRowCountAtZero switch
210212
[WinFormsFact]
211213
public void DataGridViewCellAccessibleObject_Name_ReturnExpected()
212214
{
@@ -218,12 +220,13 @@ public void DataGridViewCellAccessibleObject_Name_ReturnExpected()
218220
dataGridView.Rows.Add("3");
219221

220222
AccessibleObject accessibleObject = dataGridView.Rows[2].Cells[0].AccessibilityObject;
221-
string expected = string.Format(SR.DataGridView_AccDataGridViewCellName, column.HeaderText, 2);
223+
string expected = string.Format(SR.DataGridView_AccDataGridViewCellName, column.HeaderText, 3);
222224

223225
Assert.Equal(expected, accessibleObject.Name);
224226
Assert.False(dataGridView.IsHandleCreated);
225227
}
226228

229+
// Whether UIA row indexing is 1-based or 0-based, is controlled by the DataGridViewUIAStartRowCountAtZero switch
227230
[WinFormsFact]
228231
public void DataGridViewCellAccessibleObject_Name_ReturnExpected_IfOneRowHidden()
229232
{
@@ -236,12 +239,13 @@ public void DataGridViewCellAccessibleObject_Name_ReturnExpected_IfOneRowHidden(
236239
dataGridView.Rows[0].Visible = false;
237240

238241
AccessibleObject accessibleObject = dataGridView.Rows[2].Cells[0].AccessibilityObject;
239-
string expected = string.Format(SR.DataGridView_AccDataGridViewCellName, column.HeaderText, 1);
242+
string expected = string.Format(SR.DataGridView_AccDataGridViewCellName, column.HeaderText, 2);
240243

241244
Assert.Equal(expected, accessibleObject.Name);
242245
Assert.False(dataGridView.IsHandleCreated);
243246
}
244247

248+
// Whether UIA row indexing is 1-based or 0-based, is controlled by the DataGridViewUIAStartRowCountAtZero switch
245249
[WinFormsFact]
246250
public void DataGridViewCellAccessibleObject_Name_ReturnExpected_IfTwoRowsHidden()
247251
{
@@ -255,7 +259,7 @@ public void DataGridViewCellAccessibleObject_Name_ReturnExpected_IfTwoRowsHidden
255259
dataGridView.Rows[1].Visible = false;
256260

257261
AccessibleObject accessibleObject = dataGridView.Rows[2].Cells[0].AccessibilityObject;
258-
string expected = string.Format(SR.DataGridView_AccDataGridViewCellName, column.HeaderText, 0);
262+
string expected = string.Format(SR.DataGridView_AccDataGridViewCellName, column.HeaderText, 1);
259263

260264
Assert.Equal(expected, accessibleObject.Name);
261265
Assert.False(dataGridView.IsHandleCreated);
@@ -1432,6 +1436,23 @@ private DataGridView CreateDataGridView(int columnCount, bool createControl = tr
14321436
return dataGridView;
14331437
}
14341438

1439+
// Unit test for https://github.com/dotnet/winforms/issues/7154
1440+
[WinFormsFact]
1441+
public void DataGridView_SwitchConfigured_AdjustsCellRowStartIndices()
1442+
{
1443+
LocalAppContextSwitches.SetDataGridViewUIAStartRowCountAtZero(true);
1444+
1445+
using DataGridView dataGridView = new();
1446+
dataGridView.Columns.Add(new DataGridViewTextBoxColumn());
1447+
dataGridView.Rows.Add(new DataGridViewRow());
1448+
1449+
Assert.Equal($"{string.Format(SR.DataGridView_AccRowName, 0)}, Not sorted.", dataGridView.Rows[0].Cells[0].AccessibilityObject.Name);
1450+
1451+
LocalAppContextSwitches.SetDataGridViewUIAStartRowCountAtZero(false);
1452+
1453+
Assert.Equal($"{string.Format(SR.DataGridView_AccRowName, 1)}, Not sorted.", dataGridView.Rows[0].Cells[0].AccessibilityObject.Name);
1454+
}
1455+
14351456
private class SubDataGridViewCell : DataGridViewCell
14361457
{
14371458
}

src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/DataGridViewRowAccessibleObjectTests.cs

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System.Drawing;
5+
using System.Windows.Forms.Primitives;
56
using static Interop;
67

78
namespace System.Windows.Forms.Tests;
@@ -71,6 +72,7 @@ public void DataGridViewRowAccessibleObject_Name_Get_ReturnsExpected_IfDataGridV
7172
Assert.Equal(string.Format(SR.DataGridView_AccRowName, -1), accessibilityObject.Name);
7273
}
7374

75+
// Whether UIA row indexing is 1-based or 0-based, is controlled by the DataGridViewUIAStartRowCountAtZero switch
7476
[Fact]
7577
public void DataGridViewRowAccessibleObject_Name_Get_ReturnsExpected()
7678
{
@@ -84,9 +86,9 @@ public void DataGridViewRowAccessibleObject_Name_Get_ReturnsExpected()
8486
AccessibleObject accessibleObject2 = dataGridView.Rows[1].AccessibilityObject;
8587
AccessibleObject accessibleObject3 = dataGridView.Rows[2].AccessibilityObject;
8688

87-
Assert.Equal(string.Format(SR.DataGridView_AccRowName, 0), accessibleObject1.Name);
88-
Assert.Equal(string.Format(SR.DataGridView_AccRowName, 1), accessibleObject2.Name);
89-
Assert.Equal(string.Format(SR.DataGridView_AccRowName, 2), accessibleObject3.Name);
89+
Assert.Equal(string.Format(SR.DataGridView_AccRowName, 1), accessibleObject1.Name);
90+
Assert.Equal(string.Format(SR.DataGridView_AccRowName, 2), accessibleObject2.Name);
91+
Assert.Equal(string.Format(SR.DataGridView_AccRowName, 3), accessibleObject3.Name);
9092
Assert.False(dataGridView.IsHandleCreated);
9193
}
9294

@@ -110,6 +112,7 @@ public void DataGridViewRowAccessibleObject_Name_Get_ReturnsExpected_IfFirstRowH
110112
Assert.False(dataGridView.IsHandleCreated);
111113
}
112114

115+
// Whether UIA row indexing is 1-based or 0-based, is controlled by the DataGridViewUIAStartRowCountAtZero switch
113116
[Fact]
114117
public void DataGridViewRowAccessibleObject_Name_Get_ReturnsExpected_IfSecondRowHidden()
115118
{
@@ -125,11 +128,12 @@ public void DataGridViewRowAccessibleObject_Name_Get_ReturnsExpected_IfSecondRow
125128
AccessibleObject accessibleObject3 = dataGridView.Rows[2].AccessibilityObject;
126129

127130
Assert.Equal(string.Format(SR.DataGridView_AccRowName, 0), accessibleObject1.Name);
128-
Assert.Equal(string.Format(SR.DataGridView_AccRowName, -1), accessibleObject2.Name);
129-
Assert.Equal(string.Format(SR.DataGridView_AccRowName, 1), accessibleObject3.Name);
131+
Assert.Equal(string.Format(SR.DataGridView_AccRowName, 1), accessibleObject2.Name);
132+
Assert.Equal(string.Format(SR.DataGridView_AccRowName, 2), accessibleObject3.Name);
130133
Assert.False(dataGridView.IsHandleCreated);
131134
}
132135

136+
// Whether UIA row indexing is 1-based or 0-based, is controlled by the DataGridViewUIAStartRowCountAtZero switch
133137
[Fact]
134138
public void DataGridViewRowAccessibleObject_Name_Get_ReturnsExpected_IfLastRowHidden()
135139
{
@@ -144,8 +148,8 @@ public void DataGridViewRowAccessibleObject_Name_Get_ReturnsExpected_IfLastRowHi
144148
AccessibleObject accessibleObject2 = dataGridView.Rows[1].AccessibilityObject;
145149
AccessibleObject accessibleObject3 = dataGridView.Rows[2].AccessibilityObject;
146150

147-
Assert.Equal(string.Format(SR.DataGridView_AccRowName, 0), accessibleObject1.Name);
148-
Assert.Equal(string.Format(SR.DataGridView_AccRowName, 1), accessibleObject2.Name);
151+
Assert.Equal(string.Format(SR.DataGridView_AccRowName, 1), accessibleObject1.Name);
152+
Assert.Equal(string.Format(SR.DataGridView_AccRowName, 2), accessibleObject2.Name);
149153
Assert.Equal(string.Format(SR.DataGridView_AccRowName, -1), accessibleObject3.Name);
150154
Assert.False(dataGridView.IsHandleCreated);
151155
}
@@ -2364,6 +2368,23 @@ public void DataGridViewRowAccessibleObject_GetChildCount_ReturnsZero_IfRowHeade
23642368
Assert.False(dataGridView.IsHandleCreated);
23652369
}
23662370

2371+
// Unit test for https://github.com/dotnet/winforms/issues/7154
2372+
[WinFormsFact]
2373+
public void DataGridView_SwitchConfigured_AdjustsRowStartIndices()
2374+
{
2375+
LocalAppContextSwitches.SetDataGridViewUIAStartRowCountAtZero(true);
2376+
2377+
using DataGridView dataGridView = new();
2378+
dataGridView.Columns.Add(new DataGridViewTextBoxColumn());
2379+
dataGridView.Rows.Add(new DataGridViewRow());
2380+
2381+
Assert.Equal(string.Format(SR.DataGridView_AccRowName, 0), dataGridView.Rows[0].AccessibilityObject.Name);
2382+
2383+
LocalAppContextSwitches.SetDataGridViewUIAStartRowCountAtZero(false);
2384+
2385+
Assert.Equal(string.Format(SR.DataGridView_AccRowName, 1), dataGridView.Rows[0].AccessibilityObject.Name);
2386+
}
2387+
23672388
[WinFormsFact]
23682389
public void DataGridViewRowAccessibleObject_GetPropertyValue_ValueValuePropertyId_ReturnsExpected()
23692390
{

0 commit comments

Comments
 (0)