diff --git a/src/AutomationPeers/TableViewAutomationPeer.cs b/src/AutomationPeers/TableViewAutomationPeer.cs index 84e46213..7bd5b0c9 100644 --- a/src/AutomationPeers/TableViewAutomationPeer.cs +++ b/src/AutomationPeers/TableViewAutomationPeer.cs @@ -37,7 +37,7 @@ protected override AutomationControlType GetAutomationControlTypeCore() /// protected override string GetLocalizedControlTypeCore() { - return "table view"; + return TableViewLocalizedStrings.TableViewControlType; } /// diff --git a/src/AutomationPeers/TableViewCellAutomationPeer.cs b/src/AutomationPeers/TableViewCellAutomationPeer.cs index b4e2b7f6..5beae9fc 100644 --- a/src/AutomationPeers/TableViewCellAutomationPeer.cs +++ b/src/AutomationPeers/TableViewCellAutomationPeer.cs @@ -42,7 +42,7 @@ protected override AutomationControlType GetAutomationControlTypeCore() /// protected override string GetLocalizedControlTypeCore() { - return "cell"; + return TableViewLocalizedStrings.CellControlType; } /// @@ -71,7 +71,7 @@ protected override string GetNameCore() : string.Empty; var rowIndex = _owner.Row?.Index ?? -1; - var rowDisplay = rowIndex >= 0 ? $"Row {rowIndex + 1}" : string.Empty; + var rowDisplay = rowIndex >= 0 ? TableViewLocalizedStrings.FormatRowNumber(rowIndex + 1) : string.Empty; var cellValue = _owner.Column is TableViewTemplateColumn ? string.Empty diff --git a/src/AutomationPeers/TableViewColumnHeaderAutomationPeer.cs b/src/AutomationPeers/TableViewColumnHeaderAutomationPeer.cs index 4b13c231..0ec8fe6e 100644 --- a/src/AutomationPeers/TableViewColumnHeaderAutomationPeer.cs +++ b/src/AutomationPeers/TableViewColumnHeaderAutomationPeer.cs @@ -38,7 +38,7 @@ protected override AutomationControlType GetAutomationControlTypeCore() /// protected override string GetLocalizedControlTypeCore() { - return "column header"; + return TableViewLocalizedStrings.ColumnHeaderControlType; } /// diff --git a/src/AutomationPeers/TableViewRowAutomationPeer.cs b/src/AutomationPeers/TableViewRowAutomationPeer.cs index 6a0b95f5..9662aa62 100644 --- a/src/AutomationPeers/TableViewRowAutomationPeer.cs +++ b/src/AutomationPeers/TableViewRowAutomationPeer.cs @@ -44,7 +44,7 @@ protected override string GetNameCore() } var index = _owner.Index; - return index >= 0 ? $"Row {index + 1}" : "Row"; + return index >= 0 ? TableViewLocalizedStrings.FormatRowNumber(index + 1) : TableViewLocalizedStrings.Row; } /// diff --git a/src/AutomationPeers/TableViewRowHeaderAutomationPeer.cs b/src/AutomationPeers/TableViewRowHeaderAutomationPeer.cs index aed2b94f..6e13188c 100644 --- a/src/AutomationPeers/TableViewRowHeaderAutomationPeer.cs +++ b/src/AutomationPeers/TableViewRowHeaderAutomationPeer.cs @@ -35,7 +35,7 @@ protected override AutomationControlType GetAutomationControlTypeCore() /// protected override string GetLocalizedControlTypeCore() { - return "row header"; + return TableViewLocalizedStrings.RowHeaderControlType; } /// @@ -77,6 +77,6 @@ protected override string GetNameCore() // Fall back to the row index var rowIndex = _owner.TableViewRow?.Index ?? -1; - return rowIndex >= 0 ? $"Row {rowIndex + 1}" : base.GetNameCore(); + return rowIndex >= 0 ? TableViewLocalizedStrings.FormatRowNumber(rowIndex + 1) : base.GetNameCore(); } } diff --git a/src/Strings/de-DE/WinUI.TableView.resw b/src/Strings/de-DE/WinUI.TableView.resw index d868a18a..c3a87287 100644 --- a/src/Strings/de-DE/WinUI.TableView.resw +++ b/src/Strings/de-DE/WinUI.TableView.resw @@ -204,4 +204,28 @@ Gruppen nach Wert sortieren + + Tabellenansicht + UI Automation localized control type of the TableView. + + + Spaltenkopf + UI Automation localized control type of a column header. + + + Zeilenkopf + UI Automation localized control type of a row header. + + + Zelle + UI Automation localized control type of a cell. + + + Zeile {0} + UI Automation name of a row; {0} is the one-based row number. + + + Zeile + UI Automation name of a row whose index is not known. + diff --git a/src/Strings/en-US/WinUI.TableView.resw b/src/Strings/en-US/WinUI.TableView.resw index aee6f2e4..9434ffd5 100644 --- a/src/Strings/en-US/WinUI.TableView.resw +++ b/src/Strings/en-US/WinUI.TableView.resw @@ -204,4 +204,28 @@ Sort Groups by Value + + table view + UI Automation localized control type of the TableView. + + + column header + UI Automation localized control type of a column header. + + + row header + UI Automation localized control type of a row header. + + + cell + UI Automation localized control type of a cell. + + + Row {0} + UI Automation name of a row; {0} is the one-based row number. + + + Row + UI Automation name of a row whose index is not known. + \ No newline at end of file diff --git a/src/Strings/es-ES/WinUI.TableView.resw b/src/Strings/es-ES/WinUI.TableView.resw index 01fc0e8b..68708bf3 100644 --- a/src/Strings/es-ES/WinUI.TableView.resw +++ b/src/Strings/es-ES/WinUI.TableView.resw @@ -204,4 +204,28 @@ Ordenar grupos por valor + + vista de tabla + UI Automation localized control type of the TableView. + + + encabezado de columna + UI Automation localized control type of a column header. + + + encabezado de fila + UI Automation localized control type of a row header. + + + celda + UI Automation localized control type of a cell. + + + Fila {0} + UI Automation name of a row; {0} is the one-based row number. + + + Fila + UI Automation name of a row whose index is not known. + diff --git a/src/Strings/fa-IR/WinUI.TableView.resw b/src/Strings/fa-IR/WinUI.TableView.resw index 47a5b9d2..eeced4ce 100644 --- a/src/Strings/fa-IR/WinUI.TableView.resw +++ b/src/Strings/fa-IR/WinUI.TableView.resw @@ -204,4 +204,28 @@ مرتب‌ سازی گروه‌ ها بر اساس مقدار + + نمای جدول + UI Automation localized control type of the TableView. + + + سرستون + UI Automation localized control type of a column header. + + + سرسطر + UI Automation localized control type of a row header. + + + خانه + UI Automation localized control type of a cell. + + + سطر {0} + UI Automation name of a row; {0} is the one-based row number. + + + سطر + UI Automation name of a row whose index is not known. + \ No newline at end of file diff --git a/src/Strings/ja-JP/WinUI.TableView.resw b/src/Strings/ja-JP/WinUI.TableView.resw index d3d55d16..2d73c1e1 100644 --- a/src/Strings/ja-JP/WinUI.TableView.resw +++ b/src/Strings/ja-JP/WinUI.TableView.resw @@ -214,4 +214,28 @@ グループを値で並べ替え + + テーブル ビュー + UI Automation localized control type of the TableView. + + + 列ヘッダー + UI Automation localized control type of a column header. + + + 行ヘッダー + UI Automation localized control type of a row header. + + + セル + UI Automation localized control type of a cell. + + + 行 {0} + UI Automation name of a row; {0} is the one-based row number. + + + 行 + UI Automation name of a row whose index is not known. + diff --git a/src/Strings/pl-PL/WinUI.TableView.resw b/src/Strings/pl-PL/WinUI.TableView.resw index 4b5e09cd..98c899fe 100644 --- a/src/Strings/pl-PL/WinUI.TableView.resw +++ b/src/Strings/pl-PL/WinUI.TableView.resw @@ -204,5 +204,29 @@ Sortuj grupy według wartości + + widok tabeli + UI Automation localized control type of the TableView. + + + nagłówek kolumny + UI Automation localized control type of a column header. + + + nagłówek wiersza + UI Automation localized control type of a row header. + + + komórka + UI Automation localized control type of a cell. + + + Wiersz {0} + UI Automation name of a row; {0} is the one-based row number. + + + Wiersz + UI Automation name of a row whose index is not known. + diff --git a/src/Strings/pt-BR/WinUI.TableView.resw b/src/Strings/pt-BR/WinUI.TableView.resw index eb574cd7..f318d064 100644 --- a/src/Strings/pt-BR/WinUI.TableView.resw +++ b/src/Strings/pt-BR/WinUI.TableView.resw @@ -204,4 +204,28 @@ Ordenar grupos por valor + + exibição de tabela + UI Automation localized control type of the TableView. + + + cabeçalho de coluna + UI Automation localized control type of a column header. + + + cabeçalho de linha + UI Automation localized control type of a row header. + + + célula + UI Automation localized control type of a cell. + + + Linha {0} + UI Automation name of a row; {0} is the one-based row number. + + + Linha + UI Automation name of a row whose index is not known. + diff --git a/src/Strings/ru-RU/WinUI.TableView.resw b/src/Strings/ru-RU/WinUI.TableView.resw index 545c0450..55bc1056 100644 --- a/src/Strings/ru-RU/WinUI.TableView.resw +++ b/src/Strings/ru-RU/WinUI.TableView.resw @@ -204,4 +204,28 @@ Сортировать группы по значению + + представление таблицы + UI Automation localized control type of the TableView. + + + заголовок столбца + UI Automation localized control type of a column header. + + + заголовок строки + UI Automation localized control type of a row header. + + + ячейка + UI Automation localized control type of a cell. + + + Строка {0} + UI Automation name of a row; {0} is the one-based row number. + + + Строка + UI Automation name of a row whose index is not known. + diff --git a/src/Strings/sk-SK/WinUI.TableView.resw b/src/Strings/sk-SK/WinUI.TableView.resw index b4623e9b..2104ab4c 100644 --- a/src/Strings/sk-SK/WinUI.TableView.resw +++ b/src/Strings/sk-SK/WinUI.TableView.resw @@ -204,4 +204,28 @@ Zoradiť Skupiny Podľa Hodnoty + + zobrazenie tabuľky + UI Automation localized control type of the TableView. + + + hlavička stĺpca + UI Automation localized control type of a column header. + + + hlavička riadka + UI Automation localized control type of a row header. + + + bunka + UI Automation localized control type of a cell. + + + Riadok {0} + UI Automation name of a row; {0} is the one-based row number. + + + Riadok + UI Automation name of a row whose index is not known. + diff --git a/src/Strings/zh-CN/WinUI.TableView.resw b/src/Strings/zh-CN/WinUI.TableView.resw index 80cc95a9..d21158c8 100644 --- a/src/Strings/zh-CN/WinUI.TableView.resw +++ b/src/Strings/zh-CN/WinUI.TableView.resw @@ -204,4 +204,28 @@ 按值排序分组 + + 表格视图 + UI Automation localized control type of the TableView. + + + 列标题 + UI Automation localized control type of a column header. + + + 行标题 + UI Automation localized control type of a row header. + + + 单元格 + UI Automation localized control type of a cell. + + + 第 {0} 行 + UI Automation name of a row; {0} is the one-based row number. + + + 行 + UI Automation name of a row whose index is not known. + diff --git a/src/Strings/zh-TW/WinUI.TableView.resw b/src/Strings/zh-TW/WinUI.TableView.resw index 591cbf65..25269a14 100644 --- a/src/Strings/zh-TW/WinUI.TableView.resw +++ b/src/Strings/zh-TW/WinUI.TableView.resw @@ -204,4 +204,28 @@ 依值排序群組 + + 表格檢視 + UI Automation localized control type of the TableView. + + + 欄標題 + UI Automation localized control type of a column header. + + + 列標題 + UI Automation localized control type of a row header. + + + 儲存格 + UI Automation localized control type of a cell. + + + 第 {0} 列 + UI Automation name of a row; {0} is the one-based row number. + + + 列 + UI Automation name of a row whose index is not known. + diff --git a/src/TableViewLocalizedStrings.cs b/src/TableViewLocalizedStrings.cs index a4925678..133423e6 100644 --- a/src/TableViewLocalizedStrings.cs +++ b/src/TableViewLocalizedStrings.cs @@ -1,5 +1,6 @@ using Microsoft.Windows.ApplicationModel.Resources; using System; +using System.Globalization; namespace WinUI.TableView; @@ -47,6 +48,12 @@ static TableViewLocalizedStrings() UngroupAll = GetValue(nameof(UngroupAll)); SortGroupsByCount = GetValue(nameof(SortGroupsByCount)); SortGroupsByValue = GetValue(nameof(SortGroupsByValue)); + TableViewControlType = GetValue(nameof(TableViewControlType)); + ColumnHeaderControlType = GetValue(nameof(ColumnHeaderControlType)); + RowHeaderControlType = GetValue(nameof(RowHeaderControlType)); + CellControlType = GetValue(nameof(CellControlType)); + RowNumber = GetValue(nameof(RowNumber)); + Row = GetValue(nameof(Row)); } private static string GetValue(string name) @@ -101,4 +108,18 @@ private static string GetValue(string name) public static string UngroupAll { get; set; } public static string SortGroupsByCount { get; set; } public static string SortGroupsByValue { get; set; } + public static string TableViewControlType { get; set; } + public static string ColumnHeaderControlType { get; set; } + public static string RowHeaderControlType { get; set; } + public static string CellControlType { get; set; } + public static string RowNumber { get; set; } + public static string Row { get; set; } + + /// + /// Formats the UI Automation name of a row from its one-based number, e.g. "Row 3". + /// + public static string FormatRowNumber(int oneBasedRowNumber) + { + return string.Format(CultureInfo.CurrentCulture, RowNumber, oneBasedRowNumber); + } } diff --git a/tests/TableViewAutomationPeerTests.cs b/tests/TableViewAutomationPeerTests.cs index 150185b0..59fc938f 100644 --- a/tests/TableViewAutomationPeerTests.cs +++ b/tests/TableViewAutomationPeerTests.cs @@ -1,9 +1,12 @@ +using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Automation; using Microsoft.UI.Xaml.Automation.Peers; using Microsoft.UI.Xaml.Automation.Provider; using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Data; using Microsoft.VisualStudio.TestTools.UnitTesting; using Microsoft.VisualStudio.TestTools.UnitTesting.AppContainer; +using System.Threading.Tasks; using WinUI.TableView.AutomationPeers; namespace WinUI.TableView.Tests; @@ -423,4 +426,168 @@ public void TableViewRowHeader_OnCreateAutomationPeer_ReturnsTableViewRowHeaderA Assert.IsInstanceOfType(peer, typeof(TableViewRowHeaderAutomationPeer)); } + + // ─── Localized automation text ────────────────────────────────────────── + // Each test swaps the resource value for a sentinel, so it fails on a peer that returns an + // English literal even when the test runs under en-US, where the literal and the resource agree. + + [UITestMethod] + public void TableViewAutomationPeer_LocalizedControlType_ComesFromResources() + { + AssertUsesResource(v => TableViewLocalizedStrings.TableViewControlType = v, () => TableViewLocalizedStrings.TableViewControlType, + () => new TableViewAutomationPeer(new TableView()).GetLocalizedControlType()); + } + + [UITestMethod] + public void TableViewColumnHeaderAutomationPeer_LocalizedControlType_ComesFromResources() + { + AssertUsesResource(v => TableViewLocalizedStrings.ColumnHeaderControlType = v, () => TableViewLocalizedStrings.ColumnHeaderControlType, + () => new TableViewColumnHeaderAutomationPeer(new TableViewColumnHeader()).GetLocalizedControlType()); + } + + [UITestMethod] + public void TableViewRowHeaderAutomationPeer_LocalizedControlType_ComesFromResources() + { + AssertUsesResource(v => TableViewLocalizedStrings.RowHeaderControlType = v, () => TableViewLocalizedStrings.RowHeaderControlType, + () => new TableViewRowHeaderAutomationPeer(new TableViewRowHeader()).GetLocalizedControlType()); + } + + [UITestMethod] + public void TableViewCellAutomationPeer_LocalizedControlType_ComesFromResources() + { + AssertUsesResource(v => TableViewLocalizedStrings.CellControlType = v, () => TableViewLocalizedStrings.CellControlType, + () => new TableViewCellAutomationPeer(new TableViewCell()).GetLocalizedControlType()); + } + + [UITestMethod] + public void TableViewRowAutomationPeer_NameWithoutIndex_ComesFromResources() + { + // Row.Index returns -1 when not in a list, so the name is the bare "Row" resource. + AssertUsesResource(v => TableViewLocalizedStrings.Row = v, () => TableViewLocalizedStrings.Row, + () => new TableViewRowAutomationPeer(new TableViewRow()).GetName()); + } + + [UITestMethod] + public void FormatRowNumber_UsesTheRowNumberResource() + { + var original = TableViewLocalizedStrings.RowNumber; + try + { + TableViewLocalizedStrings.RowNumber = "R#{0}"; + Assert.AreEqual("R#3", TableViewLocalizedStrings.FormatRowNumber(3)); + } + finally + { + TableViewLocalizedStrings.RowNumber = original; + } + + StringAssert.Contains(TableViewLocalizedStrings.FormatRowNumber(3), "3"); + } + + [UITestMethod] + public void AutomationResources_AreNotEmpty() + { + Assert.IsFalse(string.IsNullOrEmpty(TableViewLocalizedStrings.TableViewControlType)); + Assert.IsFalse(string.IsNullOrEmpty(TableViewLocalizedStrings.ColumnHeaderControlType)); + Assert.IsFalse(string.IsNullOrEmpty(TableViewLocalizedStrings.RowHeaderControlType)); + Assert.IsFalse(string.IsNullOrEmpty(TableViewLocalizedStrings.CellControlType)); + Assert.IsFalse(string.IsNullOrEmpty(TableViewLocalizedStrings.Row)); + StringAssert.Contains(TableViewLocalizedStrings.RowNumber, "{0}"); + } + + // The indexed names ("Row {n}") are only composed for a row that has an index, i.e. one realized in a + // loaded TableView. Each test swaps RowNumber for a sentinel and reads the peer, so it fails on a peer + // that composes the name from an English literal instead of FormatRowNumber. + + [UITestMethod] + public async Task TableViewRowAutomationPeer_IndexedName_ComesFromResources() + { + var row = await RealizeSecondRowAsync(); + + AssertUsesRowNumberResource(() => new TableViewRowAutomationPeer(row).GetName(), expected: "R#2"); + } + + [UITestMethod] + public async Task TableViewRowHeaderAutomationPeer_IndexedName_ComesFromResources() + { + var row = await RealizeSecondRowAsync(); + var rowHeader = new TableViewRowHeader { TableViewRow = row }; + + AssertUsesRowNumberResource(() => new TableViewRowHeaderAutomationPeer(rowHeader).GetName(), expected: "R#2"); + } + + [UITestMethod] + public async Task TableViewCellAutomationPeer_IndexedName_ComesFromResources() + { + var row = await RealizeSecondRowAsync(); + Assert.IsTrue(row.Cells.Count > 0, "the realized row has no cells"); + var cell = row.Cells[0]; + + // The cell's name also carries the column header and the value; only its row part is under test. + AssertUsesRowNumberResource(() => new TableViewCellAutomationPeer(cell).GetName(), expected: "R#2", whole: false); + } + + private static void AssertUsesRowNumberResource(Func read, string expected, bool whole = true) + { + var original = TableViewLocalizedStrings.RowNumber; + try + { + TableViewLocalizedStrings.RowNumber = "R#{0}"; + var name = read(); + if (whole) + { + Assert.AreEqual(expected, name); + } + else + { + StringAssert.Contains(name, expected); + } + } + finally + { + TableViewLocalizedStrings.RowNumber = original; + } + } + + private static async Task RealizeSecondRowAsync() + { + var tableView = new TableView { AutoGenerateColumns = false }; + tableView.Columns.Add(new TableViewTextColumn + { + Header = "Name", + Binding = new Binding { Path = new PropertyPath(nameof(IndexedNameItem.Name)) } + }); + tableView.ItemsSource = new[] + { + new IndexedNameItem { Name = "A" }, + new IndexedNameItem { Name = "B" }, + }; + + await UnitTestApp.Current.MainWindow.LoadTestContentAsync(tableView); + + var row = tableView.ContainerFromIndex(1) as TableViewRow; + Assert.IsNotNull(row, "the second row was not realized"); + Assert.AreEqual(1, row.Index, "the realized row reports no index, so no indexed name would be composed"); + return row; + } + + private sealed class IndexedNameItem + { + public string Name { get; set; } = string.Empty; + } + + private static void AssertUsesResource(Action set, Func get, Func read) + { + const string sentinel = "localized-sentinel"; + var original = get(); + try + { + set(sentinel); + Assert.AreEqual(sentinel, read()); + } + finally + { + set(original); + } + } }