Skip to content

Issue 6530 automation peer for context menu support groups #7335

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

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
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 @@ -521,7 +521,7 @@ override protected void SetFocusCore()
throw new InvalidOperationException(SR.SetFocusFailed);
}

private UIElement _owner;
private readonly UIElement _owner;
private SynchronizedInputAdaptor _synchronizedInputPattern;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -482,6 +482,7 @@
<Compile Include="System\Windows\Automation\Peers\ListViewAutomationPeer.cs" />
<Compile Include="System\Windows\Automation\Peers\MediaElementAutomationPeer.cs" />
<Compile Include="System\Windows\Automation\Peers\MenuAutomationPeer.cs" />
<Compile Include="System\Windows\Automation\Peers\MenuItemDataAutomationPeer.cs" />
<Compile Include="System\Windows\Automation\Peers\MenuItemAutomationPeer.cs" />
<Compile Include="System\Windows\Automation\Peers\NavigationWindowAutomationPeer.cs" />
<Compile Include="System\Windows\Automation\Peers\PasswordBoxAutomationPeer.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
namespace System.Windows.Automation.Peers
{
///
public class ContextMenuAutomationPeer : FrameworkElementAutomationPeer
public class ContextMenuAutomationPeer : ItemsControlAutomationPeer
{
///
public ContextMenuAutomationPeer(ContextMenu owner): base(owner)
Expand All @@ -32,6 +32,11 @@ protected override bool IsContentElementCore()
{
return false;
}

protected override ItemAutomationPeer CreateItemAutomationPeer(object item)
{
return new MenuItemDataAutomationPeer(item, this);
}
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
namespace System.Windows.Automation.Peers
{
///
public class MenuAutomationPeer : FrameworkElementAutomationPeer
public class MenuAutomationPeer : ItemsControlAutomationPeer
{
///
public MenuAutomationPeer(Menu owner): base(owner)
Expand All @@ -32,6 +32,11 @@ protected override bool IsContentElementCore()
{
return false;
}

protected override ItemAutomationPeer CreateItemAutomationPeer(object item)
{
return new MenuItemDataAutomationPeer(item, this);
}
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,18 @@
namespace System.Windows.Automation.Peers
{
///
public class MenuItemAutomationPeer : FrameworkElementAutomationPeer, IExpandCollapseProvider, IInvokeProvider, IToggleProvider
public class MenuItemAutomationPeer : ItemsControlAutomationPeer, IExpandCollapseProvider, IInvokeProvider, IToggleProvider
{
///
public MenuItemAutomationPeer(MenuItem owner): base(owner)
{
}

override protected ItemAutomationPeer CreateItemAutomationPeer(object item)
{
return new MenuItemDataAutomationPeer(item, this);
}

///
override protected string GetClassNameCore()
{
Expand Down Expand Up @@ -160,16 +165,14 @@ override protected string GetAccessKeyCore()
///
protected override List<AutomationPeer> GetChildrenCore()
Copy link
Contributor

Choose a reason for hiding this comment

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

The existing implementation is not happy with base.GetChildrenCore() because the submenu items are not part of the same visual tree, as per the comment. You are now changing the base to a different peer, yet you still do not use ItemsControlAutomationPeer.GetChildrenCore(). Is the reasoning different?

Copy link
Contributor

@miloush miloush May 8, 2023

Choose a reason for hiding this comment

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

I am especially concerned about the grouping vs non-grouping cases in ItemsControlAutomationPeer, it using GroupItemAutomationPeer, the compatibility switch and the note about virtualization. Is none of that any concern here?

{
List<AutomationPeer> children = base.GetChildrenCore();

if (ExpandCollapseState.Expanded == ((IExpandCollapseProvider)this).ExpandCollapseState)
{
ItemsControl owner = (ItemsControl)Owner;
ItemCollection items = owner.Items;

if (items.Count > 0)
{
children = new List<AutomationPeer>(items.Count);
List<AutomationPeer> children = new List<AutomationPeer>(items.Count);
for (int i = 0; i < items.Count; i++)
{
UIElement uiElement = owner.ItemContainerGenerator.ContainerFromIndex(i) as UIElement;
Expand All @@ -182,12 +185,58 @@ protected override List<AutomationPeer> GetChildrenCore()
children.Add(peer);
}
}

return children;
}
}

return GetChildrenFromVisualTree();
Copy link
Contributor

Choose a reason for hiding this comment

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

This changes the current behavior, correct? Before the PR, the children contained the visual tree and children. After the PR, it contains visual tree or children.

Copy link
Contributor

@miloush miloush May 8, 2023

Choose a reason for hiding this comment

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

If that is a desired change, it should be explained and mentioned in the risks.

Copy link
Author

@RonHeck RonHeck Jun 21, 2023

Choose a reason for hiding this comment

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

I tried to create the same behavior like all other controls that support grouping. I have to compare to automation of similar itemcontrols behavior again. I'm a bit out of topic since my last changes.

}

private List<AutomationPeer> GetChildrenFromVisualTree()
{
List<AutomationPeer> children = null;

Iterate(Owner, ref children);
return children;
}

private static void AddPeerToList(AutomationPeer peer, ref List<AutomationPeer> children)
{
if (children == null)
children = new List<AutomationPeer>();

children.Add(peer);
}

private static void Iterate(DependencyObject parent, ref List<AutomationPeer> children)
{
if (parent != null)
{
AutomationPeer peer = null;
int count = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < count; i++)
{
DependencyObject child = VisualTreeHelper.GetChild(parent, i);

if (child is UIElement uiElement
&& (peer = CreatePeerForElement(uiElement)) != null)
{
AddPeerToList(peer, ref children);
}
else if (child is UIElement3D uiElemenet3D
&& (peer = UIElement3DAutomationPeer.CreatePeerForElement(uiElemenet3D)) != null)
{
AddPeerToList(peer, ref children);
}
else
{
Iterate(child, ref children);
}
}
}
}

///
void IExpandCollapseProvider.Expand()
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using System.Windows.Automation.Provider;


namespace System.Windows.Automation.Peers
{
public class MenuItemDataAutomationPeer : ItemAutomationPeer, IExpandCollapseProvider, IInvokeProvider, IToggleProvider
{
#region Constructors

public MenuItemDataAutomationPeer(object item, ItemsControlAutomationPeer itemsControlPeer)
: base(item, itemsControlPeer)
{
}

#endregion

#region AutomationPeer overrides

protected override string GetClassNameCore()
{
AutomationPeer wrapperPeer = GetWrapperPeer();
if (wrapperPeer != null)
{
return wrapperPeer.GetClassName();
}

return "MenuItem";
}

protected override AutomationControlType GetAutomationControlTypeCore()
{
AutomationPeer wrapperPeer = GetWrapperPeer();
if (wrapperPeer != null)
{
return wrapperPeer.GetAutomationControlType();
}

return AutomationControlType.MenuItem;
}

public override object GetPattern(PatternInterface patternInterface)
{
switch (patternInterface)
{
case PatternInterface.ExpandCollapse:
case PatternInterface.Invoke:
case PatternInterface.Toggle:
return this;
}

return base.GetPattern(patternInterface);
}

private void EnsureEnabled()
{

FrameworkElementAutomationPeer itemsControllerPeer = GetItemsControlAutomationPeer();
if (!itemsControllerPeer.IsEnabled())
{
throw new ElementNotEnabledException();
}
}

#endregion

#region IExpandCollapseProvider Members

void IExpandCollapseProvider.Expand()
{
MenuItemAutomationPeer wrapperPeer = GetWrapperPeer() as MenuItemAutomationPeer;
if (wrapperPeer != null)
{
((IExpandCollapseProvider)wrapperPeer).Expand();
}
ThrowElementNotAvailableException();
}

void IExpandCollapseProvider.Collapse()
{
MenuItemAutomationPeer wrapperPeer = GetWrapperPeer() as MenuItemAutomationPeer;
if (wrapperPeer != null)
{
((IExpandCollapseProvider)wrapperPeer).Collapse();
}
ThrowElementNotAvailableException();
}

ExpandCollapseState IExpandCollapseProvider.ExpandCollapseState
{
get
{
MenuItemAutomationPeer wrapperPeer = GetWrapperPeer() as MenuItemAutomationPeer;
if (wrapperPeer != null)
{
return ((IExpandCollapseProvider)wrapperPeer).ExpandCollapseState;
}
ThrowElementNotAvailableException();
return ExpandCollapseState.LeafNode;
}
}

#endregion

#region IInvokeProvider Members

void IInvokeProvider.Invoke()
{
// check if enabled
EnsureEnabled();

MenuItemAutomationPeer wrapperPeer = GetWrapperPeer() as MenuItemAutomationPeer;
if (wrapperPeer != null)
{
((IInvokeProvider)wrapperPeer).Invoke();
}
else
{
ThrowElementNotAvailableException();
}
}

#endregion

#region IToggleProvider Members

void IToggleProvider.Toggle()
{
MenuItemAutomationPeer wrapperPeer = GetWrapperPeer() as MenuItemAutomationPeer;
if (wrapperPeer != null)
{
((IToggleProvider)wrapperPeer).Toggle();
}
else
{
ThrowElementNotAvailableException();
}
}

ToggleState IToggleProvider.ToggleState
{
get
{
MenuItemAutomationPeer wrapperPeer = GetWrapperPeer() as MenuItemAutomationPeer;
if (wrapperPeer != null)
{
return ((IToggleProvider)wrapperPeer).ToggleState;
}
ThrowElementNotAvailableException();
return ToggleState.Indeterminate;
}
}

#endregion
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2439,12 +2439,13 @@ public abstract partial class ContentTextAutomationPeer : System.Windows.Automat
protected ContentTextAutomationPeer(System.Windows.FrameworkContentElement owner) : base (default(System.Windows.FrameworkContentElement)) { }
public virtual void RaiseActiveTextPositionChangedEvent(System.Windows.Documents.TextPointer rangeStart, System.Windows.Documents.TextPointer rangeEnd) { }
}
public partial class ContextMenuAutomationPeer : System.Windows.Automation.Peers.FrameworkElementAutomationPeer
public partial class ContextMenuAutomationPeer : System.Windows.Automation.Peers.ItemsControlAutomationPeer
{
public ContextMenuAutomationPeer(System.Windows.Controls.ContextMenu owner) : base (default(System.Windows.FrameworkElement)) { }
public ContextMenuAutomationPeer(System.Windows.Controls.ContextMenu owner) : base (default(System.Windows.Controls.ItemsControl)) { }
protected override System.Windows.Automation.Peers.AutomationControlType GetAutomationControlTypeCore() { throw null; }
protected override string GetClassNameCore() { throw null; }
protected override bool IsContentElementCore() { throw null; }
protected override System.Windows.Automation.Peers.ItemAutomationPeer CreateItemAutomationPeer(object item) { throw null; }
}
public sealed partial class DataGridAutomationPeer : System.Windows.Automation.Peers.ItemsControlAutomationPeer, System.Windows.Automation.Provider.IGridProvider, System.Windows.Automation.Provider.ISelectionProvider, System.Windows.Automation.Provider.ITableProvider
{
Expand Down Expand Up @@ -2971,18 +2972,32 @@ public MediaElementAutomationPeer(System.Windows.Controls.MediaElement owner) :
protected override System.Windows.Automation.Peers.AutomationControlType GetAutomationControlTypeCore() { throw null; }
protected override string GetClassNameCore() { throw null; }
}
public partial class MenuAutomationPeer : System.Windows.Automation.Peers.FrameworkElementAutomationPeer
public partial class MenuAutomationPeer : System.Windows.Automation.Peers.ItemsControlAutomationPeer
{
public MenuAutomationPeer(System.Windows.Controls.Menu owner) : base (default(System.Windows.FrameworkElement)) { }
public MenuAutomationPeer(System.Windows.Controls.Menu owner) : base (default(System.Windows.Controls.ItemsControl)) { }
protected override System.Windows.Automation.Peers.AutomationControlType GetAutomationControlTypeCore() { throw null; }
protected override string GetClassNameCore() { throw null; }
protected override bool IsContentElementCore() { throw null; }
override protected System.Windows.Automation.Peers.ItemAutomationPeer CreateItemAutomationPeer(object item) { throw null; }
}
public partial class MenuItemDataAutomationPeer : System.Windows.Automation.Peers.ItemAutomationPeer, System.Windows.Automation.Provider.IExpandCollapseProvider, System.Windows.Automation.Provider.IInvokeProvider, System.Windows.Automation.Provider.IToggleProvider
{
public MenuItemDataAutomationPeer(object item, ItemsControlAutomationPeer itemsControlPeer) : base(default(object), default(System.Windows.Automation.Peers.ItemsControlAutomationPeer)) { }
System.Windows.Automation.ExpandCollapseState System.Windows.Automation.Provider.IExpandCollapseProvider.ExpandCollapseState { get { throw null; } }
System.Windows.Automation.ToggleState System.Windows.Automation.Provider.IToggleProvider.ToggleState { get { throw null; } }
protected override string GetClassNameCore() { throw null; }
protected override AutomationControlType GetAutomationControlTypeCore() { throw null; }
void System.Windows.Automation.Provider.IExpandCollapseProvider.Expand() { }
void System.Windows.Automation.Provider.IExpandCollapseProvider.Collapse() { }
void System.Windows.Automation.Provider.IInvokeProvider.Invoke() { throw null; }
void System.Windows.Automation.Provider.IToggleProvider.Toggle() { throw null; }
}
public partial class MenuItemAutomationPeer : System.Windows.Automation.Peers.FrameworkElementAutomationPeer, System.Windows.Automation.Provider.IExpandCollapseProvider, System.Windows.Automation.Provider.IInvokeProvider, System.Windows.Automation.Provider.IToggleProvider
public partial class MenuItemAutomationPeer : System.Windows.Automation.Peers.ItemsControlAutomationPeer, System.Windows.Automation.Provider.IExpandCollapseProvider, System.Windows.Automation.Provider.IInvokeProvider, System.Windows.Automation.Provider.IToggleProvider
{
public MenuItemAutomationPeer(System.Windows.Controls.MenuItem owner) : base (default(System.Windows.FrameworkElement)) { }
public MenuItemAutomationPeer(System.Windows.Controls.MenuItem owner) : base (default(System.Windows.Controls.ItemsControl)) { }
System.Windows.Automation.ExpandCollapseState System.Windows.Automation.Provider.IExpandCollapseProvider.ExpandCollapseState { get { throw null; } }
System.Windows.Automation.ToggleState System.Windows.Automation.Provider.IToggleProvider.ToggleState { get { throw null; } }
override protected System.Windows.Automation.Peers.ItemAutomationPeer CreateItemAutomationPeer(object item) { throw null; }
protected override string GetAccessKeyCore() { throw null; }
protected override System.Windows.Automation.Peers.AutomationControlType GetAutomationControlTypeCore() { throw null; }
protected override System.Collections.Generic.List<System.Windows.Automation.Peers.AutomationPeer> GetChildrenCore() { throw null; }
Expand Down
Loading