【问题标题】:How To Access GridView Cells using Winappdriver?如何使用 Winappdriver 访问 GridView 单元格?
【发布时间】:2020-10-17 21:11:09
【问题描述】:

我正在尝试使用 winappdriverWPF 项目中的 GridView 获取单元格值。

我对这一行有疑问:

 string name = row.FindElementByName("Name1").Text;

使用给定搜索无法在页面上找到元素 参数。

请检查我的以下代码:

 <Grid>
        <ListView Margin="10" Name="lvUsers" AutomationProperties.AutomationId="lvUsers">
                <ListView.View>
                <GridView x:Name="ListViewItem"  AutomationProperties.AutomationId="ListViewItem">
                        <GridViewColumn x:Name="Name1" AutomationProperties.Name="Name1" AutomationProperties.AutomationId="Name1" Header="Name" Width="120" DisplayMemberBinding="{Binding Name}" />
                        <GridViewColumn Header="Age" Width="50" DisplayMemberBinding="{Binding Age}" />
                        <GridViewColumn Header="Mail" Width="150" DisplayMemberBinding="{Binding Mail}" />
                    </GridView>
                </ListView.View>
            </ListView>
  </Grid>

 var listBox = session.FindElementByAccessibilityId("lvUsers");
            var comboBoxItems = listBox.FindElementsByClassName("ListViewItem");
             foreach (var row  in  comboBoxItems)
             {
                string name = row.FindElementByName("Name1").Text;
                if (name == "John Doe")
                {                     
                   findName = true;
                   break;
                }
         }
        Assert.AreEqual(findName, true);

【问题讨论】:

  • 可能不是您想听到的,但是...通常对 wpf 视图模型而不是视图进行自动化测试。遍历数据网格或列表视图以查找单元格的内容总是很痛苦。 gridviewcolumn 是一个抽象的东西。您不会在可视化树中找到它。我建议您下载 snoop(.net core 有不同的版本)并使用它来探索您在运行时在 UI 中实际获得的内容。
  • WinAppDrive 使用 UI 自动化。使用 Windows SDK 中的 inspect.exe 来检查 UI 自动化从正在运行的应用程序中“看到”了什么。 github.com/microsoft/WinAppDriver/blob/master/Docs/… 只有 UIElement 派生类可以支持 UI 自动化。 GridViewColumn 不是 UIElement

标签: c# wpf automated-tests ui-automation winappdriver


【解决方案1】:

您显然选择了错误的工具来完成您的任务。 自动化旨在与 UI 元素一起使用,但您需要任务的数据。 看看你的 DataGrid 的可视化树是什么样子的:

DataGrid 继承自 ItemsControl。在他的想象中,只有行。没有列。 可以从特定单元格中提取数据,但是非常困难,没有意义。

您需要创建一个普通的数据源。 要开始使用,请执行 INotifyPropertyChanged 的​​某种实现。 例如,这个:

/// <summary>Base class implementing INotifyPropertyChanged.</summary>
public abstract class BaseINPC : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    /// <summary>Called AFTER the property value changes.</summary>
    /// <param name="propertyName">The name of the property.
    /// In the property setter, the parameter is not specified. </param>
    public void RaisePropertyChanged([CallerMemberName] string propertyName = "")
        => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));

    /// <summary> A virtual method that defines changes in the value field of a property value. </summary>
    /// <typeparam name = "T"> Type of property value. </typeparam>
    /// <param name = "oldValue"> Reference to the field with the old value. </param>
    /// <param name = "newValue"> New value. </param>
    /// <param name = "propertyName"> The name of the property. If <see cref = "string.IsNullOrWhiteSpace (string)" />,
    /// then ArgumentNullException. </param> 
    /// <remarks> If the base method is not called in the derived class,
    /// then the value will not change.</remarks>
    protected virtual void Set<T>(ref T oldValue, T newValue, [CallerMemberName] string propertyName = "")
    {
        if (string.IsNullOrWhiteSpace(propertyName))
            throw new ArgumentNullException(nameof(propertyName));

        if ((oldValue == null && newValue != null) || (oldValue != null && !oldValue.Equals(newValue)))
            OnValueChange(ref oldValue, newValue, propertyName);
    }

    /// <summary> A virtual method that changes the value of a property. </summary>
    /// <typeparam name = "T"> Type of property value. </typeparam>
    /// <param name = "oldValue"> Reference to the property value field. </param>
    /// <param name = "newValue"> New value. </param>
    /// <param name = "propertyName"> The name of the property. </param>
    /// <remarks> If the base method is not called in the derived class,
    /// then the value will not change.</remarks>
    protected virtual void OnValueChange<T>(ref T oldValue, T newValue, string propertyName)
    {
        oldValue = newValue;
        RaisePropertyChanged(propertyName);
    }

}

您可以在此基础上为集合创建一个类型:

public class PersonVM : BaseINPC
{
    private string _name;
    private uint _age;
    private string _mail;

    public string Name { get => _name; set => Set(ref _name, value); }
    public uint Age { get => _age; set => Set(ref _age, value); }
    public string Mail { get => _mail; set => Set(ref _mail, value); }
}

以及带有集合的 ViewModel:

public class ViewModel
{
    public ObservableCollection<PersonVM> People { get; } 
        = new ObservableCollection<PersonVM>()
        {
            new PersonVM(){Name="Peter", Age=20, Mail="Peter@mail.com"},
            new PersonVM(){Name="Alex", Age=30, Mail="Alex@mail.com"},
            new PersonVM(){Name="Nina", Age=25, Mail="Nina@mail.com"},
        };
}

将其连接到 DataContext 窗口:

<Window.DataContext>
    <local:ViewModel/>
</Window.DataContext>
<Grid>
    <ListView Margin="10" ItemsSource="{Binding People}">
        <ListView.View>
            <GridView x:Name="ListViewItem" >
                <GridViewColumn x:Name="Name1" Header="Name" Width="120" DisplayMemberBinding="{Binding Name}" />
                <GridViewColumn Header="Age" Width="50" DisplayMemberBinding="{Binding Age}" />
                <GridViewColumn Header="Mail" Width="150" DisplayMemberBinding="{Binding Mail}" />
            </GridView>
        </ListView.View>
    </ListView>
</Grid>

现在您的任务被简化为在 People 集合中找到所需的项目。

【讨论】:

    【解决方案2】:

    如果您知道网格中单元格的确切位置(例如 x 行、y 列),请使用以下自定义代码。
    它对我有用,我必须得到第三行第二列的数字。网格有 6 列。

    var gridItemsCollection = grid.FindElementsByXPath("//ListItem/Text");
    List<int> allIds = HelperClass.GetColumnValuesFromGrid(gridItemsCollection, 6,2).ConvertAll(int.Parse);
    var myId = allIds[2];//3rd row. 3-1
    

    以下是函数定义。 (虽然不是完美的代码)

    public static List<string> GetColumnValuesFromGrid(IReadOnlyCollection<AppiumWebElement> gridItemsCollection, int gridColumns, int selectColumn)
        {
            List<string> list = new List<string>();
        List<string> selectList = new List<string>();
    
    int index = selectColumn - 1;
    if (index < 0 || gridItemsCollection.Count == 0)
    {
    return null;
    }
    
    foreach (var element in gridItemsCollection)
    {
        list.Add(element.Text);
    }            
    
    while (index < list.Count)
    {
        selectList.Add(list[index]);
        index += gridColumns;
    }
    
    return selectList;
    }
    

    我还必须获得最大数量。所以,我做了以下事情。

    allIds.Sort();
    allIds.Reverse();
    var maxId = allIds[0];
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多