Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix ListView is in virtual mode #3963

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
// 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.Collections.Generic;
using System.Drawing;
using static Interop;

Expand Down Expand Up @@ -30,7 +29,7 @@ private bool OwnerHasDefaultGroup
{
get
{
if (!_owningListView.IsHandleCreated || !_owningListView.ShowGroups)
if (!_owningListView.IsHandleCreated || !_owningListView.ShowGroups || _owningListView.VirtualMode)
{
return false;
}
Expand Down Expand Up @@ -266,10 +265,10 @@ internal override UiaCore.IRawElementProviderSimple[] GetSelection()
return Array.Empty<UiaCore.IRawElementProviderSimple>();
}

UiaCore.IRawElementProviderSimple[] selectedItemProviders = new UiaCore.IRawElementProviderSimple[_owningListView.SelectedItems.Count];
UiaCore.IRawElementProviderSimple[] selectedItemProviders = new UiaCore.IRawElementProviderSimple[_owningListView.SelectedIndices.Count];
for (int i = 0; i < selectedItemProviders.Length; i++)
{
selectedItemProviders[i] = _owningListView.SelectedItems[i].AccessibilityObject;
selectedItemProviders[i] = _owningListView.Items[_owningListView.SelectedIndices[i]].AccessibilityObject;
}

return selectedItemProviders;
Expand Down
49 changes: 31 additions & 18 deletions src/System.Windows.Forms/src/System/Windows/Forms/ListView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2669,7 +2669,7 @@ unsafe void CustomDraw(ref Message m)
if (OwnerDraw)
{
using Graphics g = nmcd->nmcd.hdc.CreateGraphics();
DrawListViewItemEventArgs e = new DrawListViewItemEventArgs(
DrawListViewItemEventArgs e = new DrawListViewItemEventArgs(
g,
Items[(int)nmcd->nmcd.dwItemSpec],
itemBounds,
Expand Down Expand Up @@ -4862,10 +4862,15 @@ protected virtual void OnSelectedIndexChanged(EventArgs e)
{
((EventHandler)Events[EVENT_SELECTEDINDEXCHANGED])?.Invoke(this, e);

if (SelectedItems.Count > 0 && SelectedItems[0].Focused)
if (SelectedIndices.Count == 0)
{
AccessibleObject accessibilityObject = SelectedItems[0].AccessibilityObject;
accessibilityObject?.RaiseAutomationEvent(UiaCore.UIA.AutomationFocusChangedEventId);
return;
}

ListViewItem firstSelectedItem = Items[SelectedIndices[0]];
if (firstSelectedItem.Focused)
{
firstSelectedItem.AccessibilityObject.RaiseAutomationEvent(UiaCore.UIA.AutomationFocusChangedEventId);
}
}

Expand Down Expand Up @@ -6560,26 +6565,29 @@ private unsafe void WmReflectNotify(ref Message m)
break;

case (int)LVN.KEYDOWN:
if (Groups.Count > 0)
if (GroupsEnabled)
{
NMLVKEYDOWN* lvkd = (NMLVKEYDOWN*)m.LParam;
if (lvkd->wVKey == (short)Keys.Down
&& SelectedItems.Count > 0
&& SelectedItems[0].AccessibilityObject.FragmentNavigate(UiaCore.NavigateDirection.NextSibling) is null)
if ((lvkd->wVKey == (short)Keys.Down || lvkd->wVKey == (short)Keys.Up) && SelectedItems.Count > 0)
{
ListViewGroupAccessibleObject groupAccObj = (ListViewGroupAccessibleObject)SelectedItems[0].AccessibilityObject.FragmentNavigate(UiaCore.NavigateDirection.Parent);
ListViewGroupAccessibleObject nextGroupAccObj = (ListViewGroupAccessibleObject)groupAccObj.FragmentNavigate(UiaCore.NavigateDirection.NextSibling);
nextGroupAccObj?.SetFocus();
}
AccessibleObject accessibleObject = SelectedItems[0].AccessibilityObject;
if (lvkd->wVKey == (short)Keys.Down
&& accessibleObject.FragmentNavigate(UiaCore.NavigateDirection.NextSibling) is null)
{
ListViewGroupAccessibleObject groupAccObj = (ListViewGroupAccessibleObject)accessibleObject.FragmentNavigate(UiaCore.NavigateDirection.Parent);
ListViewGroupAccessibleObject nextGroupAccObj = (ListViewGroupAccessibleObject)groupAccObj.FragmentNavigate(UiaCore.NavigateDirection.NextSibling);
nextGroupAccObj?.SetFocus();
}

if (lvkd->wVKey == (short)Keys.Up
&& SelectedItems.Count > 0
&& SelectedItems[0].AccessibilityObject.FragmentNavigate(UiaCore.NavigateDirection.PreviousSibling) is null)
{
ListViewGroupAccessibleObject groupAccObj = (ListViewGroupAccessibleObject)SelectedItems[0].AccessibilityObject.FragmentNavigate(UiaCore.NavigateDirection.Parent);
groupAccObj?.SetFocus();
if (lvkd->wVKey == (short)Keys.Up
&& accessibleObject.FragmentNavigate(UiaCore.NavigateDirection.PreviousSibling) is null)
{
ListViewGroupAccessibleObject groupAccObj = (ListViewGroupAccessibleObject)accessibleObject.FragmentNavigate(UiaCore.NavigateDirection.Parent);
groupAccObj?.SetFocus();
}
}
}

if (CheckBoxes)
{
NMLVKEYDOWN* lvkd = (NMLVKEYDOWN*)m.LParam;
Expand Down Expand Up @@ -7668,6 +7676,11 @@ public bool IsReadOnly

public bool Contains(int selectedIndex)
{
if (selectedIndex < 0 || selectedIndex >= owner.Items.Count)
{
return false;
}

return owner.Items[selectedIndex].Selected;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,13 @@ internal class ListViewGroupAccessibleObject : AccessibleObject
public ListViewGroupAccessibleObject(ListViewGroup owningGroup, bool owningGroupIsDefault)
{
_owningGroup = owningGroup ?? throw new ArgumentNullException(nameof(owningGroup));
_owningListView = owningGroup.ListView ?? throw new InvalidOperationException(nameof(owningGroup.ListView));

// Using item from group for getting of ListView is a workaround for https://github.com/dotnet/winforms/issues/4019
_owningListView = owningGroup.ListView
?? (owningGroup.Items.Count > 0 && _owningGroup.Items[0].ListView != null
? _owningGroup.Items[0].ListView
: throw new InvalidOperationException(nameof(owningGroup.ListView)));

_owningGroupIsDefault = owningGroupIsDefault;
}

Expand All @@ -30,7 +36,7 @@ public override Rectangle Bounds
{
get
{
if (!_owningListView.IsHandleCreated)
if (!_owningListView.IsHandleCreated || _owningListView.VirtualMode)
{
return Rectangle.Empty;
}
Expand All @@ -57,9 +63,9 @@ public override string DefaultAction
=> SR.AccessibleActionDoubleClick;

internal override UiaCore.ExpandCollapseState ExpandCollapseState
=> _owningGroup.CollapsedState == ListViewGroupCollapsedState.Expanded
? UiaCore.ExpandCollapseState.Expanded
: UiaCore.ExpandCollapseState.Collapsed;
=> _owningGroup.CollapsedState == ListViewGroupCollapsedState.Collapsed
? UiaCore.ExpandCollapseState.Collapsed
: UiaCore.ExpandCollapseState.Expanded;

public override string Name
=> _owningGroup.Header;
Expand Down Expand Up @@ -114,7 +120,7 @@ private bool Focused

private bool GetNativeFocus()
{
if (!_owningListView.IsHandleCreated)
if (!_owningListView.IsHandleCreated || _owningListView.VirtualMode)
{
return false;
}
Expand Down Expand Up @@ -144,6 +150,11 @@ private bool GetNativeFocus()

internal override UiaCore.IRawElementProviderFragment? FragmentNavigate(UiaCore.NavigateDirection direction)
{
if (!_owningListView.IsHandleCreated || _owningListView.VirtualMode)
{
return null;
}

switch (direction)
{
case UiaCore.NavigateDirection.Parent:
Expand All @@ -160,6 +171,7 @@ private bool GetNativeFocus()
}

return null;

case UiaCore.NavigateDirection.LastChild:
childCount = GetChildCount();
if (childCount > 0)
Expand All @@ -168,13 +180,19 @@ private bool GetNativeFocus()
}

return null;

default:
return null;
}
}

public override AccessibleObject? GetChild(int index)
{
if (!_owningListView.IsHandleCreated || _owningListView.VirtualMode)
{
return null;
}

if (!_owningGroupIsDefault)
{
if (index < 0 || index >= _owningGroup.Items.Count)
Expand All @@ -198,6 +216,11 @@ private bool GetNativeFocus()

private int GetChildIndex(AccessibleObject child)
{
if (!_owningListView.IsHandleCreated || _owningListView.VirtualMode)
{
return -1;
}

int childCount = GetChildCount();
for (int i = 0; i < childCount; i++)
{
Expand All @@ -213,6 +236,11 @@ private int GetChildIndex(AccessibleObject child)

internal AccessibleObject? GetNextChild(AccessibleObject currentChild)
{
if (!_owningListView.IsHandleCreated || _owningListView.VirtualMode)
{
return null;
}

int currentChildIndex = GetChildIndex(currentChild);
if (currentChildIndex == -1)
{
Expand All @@ -230,6 +258,11 @@ private int GetChildIndex(AccessibleObject child)

internal AccessibleObject? GetPreviousChild(AccessibleObject currentChild)
{
if (!_owningListView.IsHandleCreated || _owningListView.VirtualMode)
{
return null;
}

int currentChildIndex = GetChildIndex(currentChild);
if (currentChildIndex <= 0)
{
Expand All @@ -241,6 +274,11 @@ private int GetChildIndex(AccessibleObject child)

public override int GetChildCount()
{
if (!_owningListView.IsHandleCreated || _owningListView.VirtualMode)
{
return -1;
}

if (_owningGroupIsDefault)
{
int count = 0;
Expand Down Expand Up @@ -273,6 +311,11 @@ internal override bool IsPatternSupported(UiaCore.UIA patternId)

internal override unsafe void SetFocus()
{
if (!_owningListView.IsHandleCreated || _owningListView.VirtualMode)
{
return;
}

_owningListView.FocusedGroup = _owningGroup;

RaiseAutomationEvent(UiaCore.UIA.AutomationFocusChangedEventId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ internal class ListViewItemAccessibleObject : AccessibleObject
public ListViewItemAccessibleObject(ListViewItem owningItem, ListViewGroup? owningGroup)
{
_owningItem = owningItem ?? throw new ArgumentNullException(nameof(owningItem));
_owningListView = owningItem.ListView ?? throw new InvalidOperationException(nameof(owningItem.ListView));
_owningListView = owningItem.ListView ?? owningGroup?.ListView ?? throw new InvalidOperationException(nameof(owningItem.ListView));
Copy link
Member

Choose a reason for hiding this comment

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

Suspected bug, tracked across #3987 and #4001

_owningGroup = owningGroup;
_systemIAccessible = _owningListView.AccessibilityObject.GetSystemIAccessibleInternal();
}
Expand All @@ -32,7 +32,9 @@ private string AutomationId
=> string.Format("{0}-{1}", typeof(ListViewItem).Name, CurrentIndex);

public override Rectangle Bounds
=> new Rectangle(
=> _owningGroup?.CollapsedState == ListViewGroupCollapsedState.Collapsed
? Rectangle.Empty
: new Rectangle(
_owningListView.AccessibilityObject.Bounds.X + _owningItem.Bounds.X,
_owningListView.AccessibilityObject.Bounds.Y + _owningItem.Bounds.Y,
_owningItem.Bounds.Width,
Expand Down Expand Up @@ -78,7 +80,7 @@ public override AccessibleStates State
{
AccessibleStates state = AccessibleStates.Selectable | AccessibleStates.Focusable | AccessibleStates.MultiSelectable;

if (_owningListView.SelectedItems.Contains(_owningItem))
if (_owningListView.SelectedIndices.Contains(_owningItem.Index))
{
return state |= AccessibleStates.Selected | AccessibleStates.Focused;
}
Expand Down Expand Up @@ -232,10 +234,7 @@ internal override void RemoveFromSelection()
internal override UiaCore.IRawElementProviderSimple ItemSelectionContainer
=> _owningListView.AccessibilityObject;

internal override void ScrollIntoView()
SergeySmirnov-Akvelon marked this conversation as resolved.
Show resolved Hide resolved
{
_owningItem.EnsureVisible();
}
internal override void ScrollIntoView() => _owningItem.EnsureVisible();

internal unsafe override void SelectItem()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ public ListViewItem(ListViewSubItem[] subItems, string imageKey, ListViewGroup g
Group = group;
}

internal AccessibleObject AccessibilityObject
internal virtual AccessibleObject AccessibilityObject
{
get
{
Expand Down
Loading