mirror of
https://github.com/microsoft/microsoft-ui-xaml.git
synced 2026-10-28 16:35:19 +08:00
Review round 3. Attribution matched the last app HRESULT against a control-wide counter, so a nested callback could overwrite it and an unrelated exception with the same HRESULT could match. App code's exceptions are now tagged where TableView calls it (CallerError, and CallerOutOfMemory for an allocation failure, which stays a std::bad_alloc), and the catch that decides an outcome reads the type. SelectionModel calls TableView back across the ABI, which drops the C++ type, so the call into it tags the exception again by its error object. Column code is TableView's unless app code ran it: the editing hooks are internal, so only a CellEditingTemplate's content (while the editor is prepared) is the app's there, and only an app-derived column class owns its display factory and sort member path. Expansion is always tracked and its catch reads the tag; only a request that changes one group or node runs a test. A filter over a large source no longer drops into the small bucket: the large flag counts the source before the filter, and the metrics and TableView_Instance carry both counts. Initialize closes as unloaded_before_render when the table is unloaded with rows and a body viewport before any row was laid out. An app filter predicate, sort or group key selector or custom comparer that throws is still contained, and now writes one recoverable TableView_Error per operation with FaultCount. Internal pipeline builds fail when Tabular falls back to the TiP stub. All versions start at 1, since none has shipped. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 53d996ee-1cb6-468a-bfc9-e11af03b1f80
71 lines
3.3 KiB
C++
71 lines
3.3 KiB
C++
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
// Licensed under the MIT License. See LICENSE in the project root for license information.
|
|
|
|
#include "pch.h"
|
|
#include "common.h"
|
|
#include "TableView.h"
|
|
#include "TableViewSource.h"
|
|
|
|
namespace
|
|
{
|
|
uint32_t ColumnKindOf(winrt::TableViewColumn const& column)
|
|
{
|
|
uint32_t kinds{};
|
|
if (column.try_as<winrt::TableViewTemplateColumn>()) { kinds |= TableViewTelemetry::ColumnKind::Template; }
|
|
else if (column.try_as<winrt::TableViewTextColumn>()) { kinds |= TableViewTelemetry::ColumnKind::Text; }
|
|
if (TableViewDetails::IsAppDerivedColumn(column)) { kinds |= TableViewTelemetry::ColumnKind::AppDerived; }
|
|
return kinds;
|
|
}
|
|
}
|
|
|
|
// TableView_Instance's two sample points: Loaded and Unloaded. Reads TableView's own state; no app code runs beyond
|
|
// the columns' QueryInterface and class name, and an app-supplied Columns vector.
|
|
void TableView::SampleInstanceTelemetry() noexcept
|
|
{
|
|
if (!m_telemetrySession.active || !TableViewTelemetry::ShouldSample()) { return; }
|
|
try
|
|
{
|
|
TableViewTelemetry::Sample sample;
|
|
sample.sourceKinds = m_telemetrySourceKinds;
|
|
sample.rightToLeft = FlowDirection() == winrt::FlowDirection::RightToLeft;
|
|
sample.templateIncomplete = m_templateIncomplete;
|
|
bool anySortable{};
|
|
bool anyEditable{};
|
|
if (auto const columns = Columns())
|
|
{
|
|
for (auto const& column : columns)
|
|
{
|
|
if (!column) { continue; }
|
|
sample.columnKinds |= ColumnKindOf(column);
|
|
anySortable = anySortable || column.CanSort();
|
|
anyEditable = anyEditable || !column.IsReadOnly();
|
|
}
|
|
}
|
|
if (CanUserSortColumns() && anySortable) { sample.enabledFeatures |= TableViewTelemetry::Feature::Sorting; }
|
|
if (!IsReadOnly() && anyEditable) { sample.enabledFeatures |= TableViewTelemetry::Feature::Editing; }
|
|
if (auto const source = m_activeSource.get())
|
|
{
|
|
auto const* const sourceImpl = winrt::get_self<::TableViewSource>(source);
|
|
if (sourceImpl->HasFilter()) { sample.enabledFeatures |= TableViewTelemetry::Feature::Filtering; }
|
|
if (sourceImpl->HasGroupBy()) { sample.enabledFeatures |= TableViewTelemetry::Feature::Grouping; }
|
|
if (sourceImpl->HasParentBy()) { sample.enabledFeatures |= TableViewTelemetry::Feature::Hierarchy; }
|
|
if (sourceImpl->IsLiveShaping()) { sample.enabledFeatures |= TableViewTelemetry::Feature::LiveShaping; }
|
|
sample.sourceRows = sourceImpl->SourceRowCount();
|
|
sample.dataRows = sourceImpl->DataRowCount();
|
|
if (sample.dataRows == 0)
|
|
{
|
|
TableViewTelemetry::NoteContent(m_telemetrySession, TableViewTelemetry::ContentShown::Empty);
|
|
}
|
|
}
|
|
if (auto const repeater = m_rowsRepeater.get())
|
|
{
|
|
// A cleared container stays the repeater's child in its recycle pool, so this is the containers created.
|
|
sample.peakRealizedRows = static_cast<uint32_t>(winrt::VisualTreeHelper::GetChildrenCount(repeater));
|
|
}
|
|
TableViewTelemetry::AddSample(m_telemetrySession, sample);
|
|
}
|
|
catch (...)
|
|
{
|
|
// Telemetry never throws into the app; a sample that could not be read leaves the session as it was.
|
|
}
|
|
} |