Skip to content
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
173 changes: 147 additions & 26 deletions XamlToolkit.Labs.WinUI/DataTable/DataColumn.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#include "pch.h"
#include "pch.h"
#include "winrt_module_imports.h"
#include "DataColumn.h"
#if __has_include("DataColumn.g.cpp")
Expand All @@ -9,13 +9,13 @@

namespace winrt::XamlToolkit::Labs::WinUI::implementation
{
winrt::GridLength DataColumn::DesiredWidth() const
{
winrt::GridLength DataColumn::DesiredWidth() const
{
return winrt::unbox_value<winrt::GridLength>(GetValue(DesiredWidthProperty()));
}

void DataColumn::DesiredWidth(winrt::GridLength value)
{
void DataColumn::DesiredWidth(winrt::GridLength value)
{
SetValue(DesiredWidthProperty(), winrt::box_value(value));
}

Expand All @@ -26,13 +26,29 @@ namespace winrt::XamlToolkit::Labs::WinUI::implementation
winrt::xaml_typename<class_type>(),
winrt::PropertyMetadata(winrt::box_value(winrt::GridLengthHelper::Auto()), &DataColumn::DesiredWidth_PropertyChanged ));

bool DataColumn::CanResize() const
{
return winrt::unbox_value<bool>(GetValue(CanResizeProperty()));
winrt::Microsoft::UI::Xaml::Style DataColumn::ColumnSizerStyle() const
{
return GetValue(ColumnSizerStyleProperty).try_as<winrt::Microsoft::UI::Xaml::Style>();
}

void DataColumn::ColumnSizerStyle(winrt::Microsoft::UI::Xaml::Style const& value)
{
SetValue(ColumnSizerStyleProperty, value);
}

const wil::single_threaded_property<DependencyProperty> DataColumn::ColumnSizerStyleProperty =
DependencyProperty::Register(L"ColumnSizerStyle",
winrt::xaml_typename<winrt::Microsoft::UI::Xaml::Style>(),
winrt::xaml_typename<class_type>(),
PropertyMetadata(nullptr));

bool DataColumn::CanResize() const
{
return winrt::unbox_value<bool>(GetValue(CanResizeProperty()));
}

void DataColumn::CanResize(bool value)
{
void DataColumn::CanResize(bool value)
{
SetValue(CanResizeProperty(), winrt::box_value(value));
}

Expand All @@ -50,23 +66,55 @@ namespace winrt::XamlToolkit::Labs::WinUI::implementation

winrt::GridLength DataColumn::CurrentWidth() const
{
return _currentWidth;
return DesiredWidth();
}

double DataColumn::ActualColumnWidth() const
{
return _actualColumnWidth;
}

void DataColumn::SetCurrentWidth(double value)
{
// A user resize resolves Auto/Star columns to pixels. Write that result
// back to the dependency property so XAML, bindings, persistence, and
// the layout engine all observe the same authoritative value.
_isInternalResizeUpdate = true;
try
{
DesiredWidth(GridLengthHelper::FromPixels(value));
}
catch (...)
{
_isInternalResizeUpdate = false;
throw;
}
_isInternalResizeUpdate = false;
}

void DataColumn::SetActualColumnWidth(double value)
{
_actualColumnWidth = value;
}

void DataColumn::OnApplyTemplate()
{
if (_columnSizer)
{
_columnSizer.TargetControl(nullptr);
_columnSizerManipulationStartedRevoker.revoke();
_columnSizerManipulationDeltaRevoker.revoke();
_columnSizerManipulationCompletedRevoker.revoke();
_columnSizerManipulationCompletedRevoker.revoke();
}

_columnSizer = GetTemplateChild(PartColumnSizer).try_as<winrt::XamlToolkit::WinUI::Controls::ContentSizer>();

if (_columnSizer)
{
_columnSizer.TargetControl(*this);
// Keep ContentSizer's native manipulation recognizer, but explicitly
// disable its automatic FrameworkElement.Width assignment.
_columnSizer.TargetControl(nullptr);
_columnSizerManipulationStartedRevoker = _columnSizer.ManipulationStarted(winrt::auto_revoke, { this, &DataColumn::ColumnSizer_ManipulationStarted });
_columnSizerManipulationDeltaRevoker = _columnSizer.ManipulationDelta(winrt::auto_revoke, { this, &DataColumn::ColumnSizer_ManipulationDelta });
_columnSizerManipulationCompletedRevoker = _columnSizer.ManipulationCompleted(winrt::auto_revoke, { this, &DataColumn::ColumnSizer_ManipulationCompleted });
}
Expand All @@ -80,36 +128,109 @@ namespace winrt::XamlToolkit::Labs::WinUI::implementation
base_type::OnApplyTemplate();
}

void DataColumn::ColumnSizer_ManipulationDelta([[maybe_unused]] winrt::IInspectable const& sender, [[maybe_unused]] winrt::ManipulationDeltaRoutedEventArgs const& e)
void DataColumn::ColumnSizer_ManipulationStarted([[maybe_unused]] winrt::Windows::Foundation::IInspectable const& sender, [[maybe_unused]] ManipulationStartedRoutedEventArgs const& e)
{
ColumnResizedByUserSizer();
auto parent = _parent.get();
if (parent == nullptr)
{
parent = winrt::XamlToolkit::WinUI::DependencyObjectEx::FindAscendant<winrt::XamlToolkit::Labs::WinUI::DataTable>(*this);
if (parent != nullptr)
{
_parent = winrt::make_weak(parent);
}
}

if (parent == nullptr)
{
return;
}

auto parentImpl = winrt::get_self<winrt::XamlToolkit::Labs::WinUI::implementation::DataTable>(parent);
_resizeStartWidth = parentImpl->BeginColumnResize(*this);
_isManipulationResizing = true;
}

void DataColumn::ColumnSizer_ManipulationCompleted([[maybe_unused]] winrt::IInspectable const& sender, [[maybe_unused]] winrt::ManipulationCompletedRoutedEventArgs const& e)
void DataColumn::ColumnSizer_ManipulationDelta([[maybe_unused]] winrt::Windows::Foundation::IInspectable const& sender, winrt::ManipulationDeltaRoutedEventArgs const& e)
{
ColumnResizedByUserSizer();
auto parent = _parent.get();
if (!_isManipulationResizing || parent == nullptr || _columnSizer == nullptr)
{
return;
}

const auto dragIncrement = _columnSizer.DragIncrement();
auto horizontalChange = std::trunc(e.Cumulative().Translation.X / dragIncrement) * dragIncrement;
if (FlowDirection() == winrt::FlowDirection::RightToLeft)
{
horizontalChange *= -1;
}

auto width = _resizeStartWidth + horizontalChange;

// PART_ColumnSizer is hosted inside this column. If an interactive
// resize is allowed to reduce the entire column below the sizer's own
// width, the parent clips the sizer and the user cannot drag it back.
// Keep this clamp local to pointer resizing so setting DesiredWidth to
// zero programmatically retains its existing meaning.
const auto interactiveMinWidth = std::max<double>(MinWidth(), _columnSizer.ActualWidth());
width = std::max<double>(width, interactiveMinWidth);
if (std::isfinite(MaxWidth()))
{
width = std::min<double>(width, MaxWidth());
}

const auto currentWidth = DesiredWidth();
if (currentWidth.GridUnitType != GridUnitType::Pixel || currentWidth.Value != width)
{
SetCurrentWidth(width);
}
}

void DataColumn::ColumnResizedByUserSizer()
void DataColumn::ColumnSizer_ManipulationCompleted([[maybe_unused]] winrt::Windows::Foundation::IInspectable const& sender, [[maybe_unused]] winrt::ManipulationCompletedRoutedEventArgs const& e)
{
// Update our internal representation to be our size now as a fixed value.
_currentWidth = winrt::GridLength(ActualWidth());
_isManipulationResizing = false;

// Notify the rest of the table to update
if (auto parent = _parent.get())
{
auto parentImpl = winrt::get_self<winrt::XamlToolkit::Labs::WinUI::implementation::DataTable>(parent);
parentImpl->ColumnResized();
// Ensure the final persisted width receives a complete layout pass
// even when the last manipulation delta was coalesced.
winrt::get_self<winrt::XamlToolkit::Labs::WinUI::implementation::DataTable>(parent)->ColumnWidthChanged();
}
}

void DataColumn::ApplyDesiredWidth([[maybe_unused]] GridLength const& value)
{
if (!_isInternalResizeUpdate)
{
InvalidateMeasure();
}

auto parent = _parent.get();
if (parent == nullptr)
{
parent = winrt::XamlToolkit::WinUI::DependencyObjectEx::FindAscendant<winrt::XamlToolkit::Labs::WinUI::DataTable>(*this);
if (parent == nullptr)
{
return;
}

_parent = winrt::make_weak(parent);
}

auto parentImpl = winrt::get_self<winrt::XamlToolkit::Labs::WinUI::implementation::DataTable>(parent);
// A changed pixel width is a new measure constraint. TextBlock decides
// whether TextTrimming is necessary during Measure, so arranging rows
// alone would retain an ellipsis computed for the previous width.
parentImpl->ColumnWidthChanged();
}

void DataColumn::DesiredWidth_PropertyChanged(winrt::DependencyObject const& d, [[maybe_unused]] winrt::DependencyPropertyChangedEventArgs const& e)
{
// If the developer updates the size of the column, update our internal copy
// The dependency property is the single source of truth. Its callback
// only schedules the required layout work.
if (auto col = d.try_as<winrt::XamlToolkit::Labs::WinUI::DataColumn>())
{
auto colImpl = winrt::get_self<winrt::XamlToolkit::Labs::WinUI::implementation::DataColumn>(col);
colImpl->_currentWidth = col.DesiredWidth();
winrt::get_self<winrt::XamlToolkit::Labs::WinUI::implementation::DataColumn>(col)->ApplyDesiredWidth(col.DesiredWidth());
}
}
}
40 changes: 28 additions & 12 deletions XamlToolkit.Labs.WinUI/DataTable/DataColumn.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#pragma once
#pragma once

#include "DataColumn.g.h"

Expand All @@ -9,16 +9,16 @@

namespace winrt
{
using namespace Windows::Foundation;
using namespace Microsoft::UI::Xaml;
using namespace Microsoft::UI::Xaml::Input;
using namespace Windows::Foundation;
using namespace Microsoft::UI::Xaml;
using namespace Microsoft::UI::Xaml::Input;
}

namespace winrt::XamlToolkit::Labs::WinUI::implementation
{
struct DataColumn : DataColumnT<DataColumn>
{
static constexpr auto PartColumnSizer = L"PART_ColumnSizer";
static constexpr auto PartColumnSizer = L"PART_ColumnSizer";

DataColumn();

Expand All @@ -32,31 +32,47 @@ namespace winrt::XamlToolkit::Labs::WinUI::implementation

static const wil::single_threaded_property<winrt::DependencyProperty> DesiredWidthProperty;

winrt::Microsoft::UI::Xaml::Style ColumnSizerStyle() const;
void ColumnSizerStyle(winrt::Microsoft::UI::Xaml::Style const& value);

static const wil::single_threaded_property<DependencyProperty> ColumnSizerStyleProperty;

wil::single_threaded_rw_property<double> MaxChildDesiredWidth;

winrt::GridLength CurrentWidth() const;

double ActualColumnWidth() const;

void SetCurrentWidth(double value);

void SetActualColumnWidth(double value);

void OnApplyTemplate();

void ColumnSizer_ManipulationDelta(winrt::IInspectable const& sender, winrt::ManipulationDeltaRoutedEventArgs const& e);
void ColumnSizer_ManipulationStarted(winrt::Windows::Foundation::IInspectable const& sender, winrt::ManipulationStartedRoutedEventArgs const& e);

void ColumnSizer_ManipulationCompleted(winrt::IInspectable const& sender, winrt::ManipulationCompletedRoutedEventArgs const& e);
void ColumnSizer_ManipulationDelta(winrt::Windows::Foundation::IInspectable const& sender, winrt::ManipulationDeltaRoutedEventArgs const& e);

void ColumnResizedByUserSizer();
void ColumnSizer_ManipulationCompleted(winrt::IInspectable const& sender, winrt::ManipulationCompletedRoutedEventArgs const& e);

private:
static void DesiredWidth_PropertyChanged(winrt::DependencyObject const& d, winrt::DependencyPropertyChangedEventArgs const& e);

static inline winrt::GridLength StarLength = winrt::GridLength(1, winrt::GridUnitType::Star);
void ApplyDesiredWidth(GridLength const& value);

winrt::GridLength _currentWidth;
double _actualColumnWidth{ 0 };
bool _isInternalResizeUpdate{ false };

winrt::XamlToolkit::WinUI::Controls::ContentSizer _columnSizer{ nullptr };

winrt::weak_ref<winrt::XamlToolkit::Labs::WinUI::DataTable> _parent;

winrt::UIElement::ManipulationDelta_revoker _columnSizerManipulationDeltaRevoker;
winrt::UIElement::ManipulationCompleted_revoker _columnSizerManipulationCompletedRevoker;
winrt::UIElement::ManipulationStarted_revoker _columnSizerManipulationStartedRevoker;
winrt::UIElement::ManipulationDelta_revoker _columnSizerManipulationDeltaRevoker;
winrt::UIElement::ManipulationCompleted_revoker _columnSizerManipulationCompletedRevoker;

bool _isManipulationResizing{ false };
double _resizeStartWidth{ 0 };
};
}

Expand Down
8 changes: 7 additions & 1 deletion XamlToolkit.Labs.WinUI/DataTable/DataColumn.idl
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace XamlToolkit.Labs.WinUI
namespace XamlToolkit.Labs.WinUI
{
[default_interface]
runtimeclass DataColumn : Microsoft.UI.Xaml.Controls.ContentControl
Expand All @@ -12,5 +12,11 @@ namespace XamlToolkit.Labs.WinUI
Microsoft.UI.Xaml.GridLength DesiredWidth;

static Microsoft.UI.Xaml.DependencyProperty DesiredWidthProperty{ get; };

Microsoft.UI.Xaml.Style ColumnSizerStyle;

static Microsoft.UI.Xaml.DependencyProperty ColumnSizerStyleProperty{ get; };

Double ActualColumnWidth{ get; };
}
}
29 changes: 20 additions & 9 deletions XamlToolkit.Labs.WinUI/DataTable/DataColumn.xaml
Original file line number Diff line number Diff line change
@@ -1,13 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8" ?>
<!-- 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. -->
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:labs="using:XamlToolkit.Labs.WinUI"
xmlns:controls="using:XamlToolkit.WinUI.Controls"
xmlns:converters="using:XamlToolkit.WinUI.Converters">
xmlns:converters="using:XamlToolkit.WinUI.Converters"
xmlns:labs="using:XamlToolkit.Labs.WinUI">

<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="ms-appx:///XamlToolkit.WinUI.Controls/Sizers/SizerBase.xaml" />
</ResourceDictionary.MergedDictionaries>

<converters:BoolToVisibilityConverter x:Key="DataColumnBoolToVisibilityConverter" />

<Style x:Key="DefaultDataColumnSizerStyle"
BasedOn="{StaticResource DefaultContentSizerStyle}"
TargetType="controls:ContentSizer">
<Setter Property="Width" Value="8" />
<Setter Property="MinWidth" Value="4" />
<Setter Property="Margin" Value="0" />
<Setter Property="Padding" Value="0" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="Foreground" Value="{ThemeResource ControlStrokeColorSecondaryBrush}" />
</Style>

<!-- Implicitly applied default style -->
<Style BasedOn="{StaticResource DefaultDataColumnStyle}"
TargetType="labs:DataColumn" />
Expand All @@ -21,6 +36,7 @@
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="Foreground" Value="{ThemeResource TextFillColorSecondaryBrush}" />
<Setter Property="IsTabStop" Value="False" />
<Setter Property="ColumnSizerStyle" Value="{StaticResource DefaultDataColumnSizerStyle}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="labs:DataColumn">
Expand All @@ -38,12 +54,7 @@
Foreground="{TemplateBinding Foreground}" />
<controls:ContentSizer x:Name="PART_ColumnSizer"
Grid.Column="1"
Width="8"
MinWidth="4"
Margin="0"
Padding="0"
Background="Transparent"
Foreground="{ThemeResource ControlStrokeColorSecondaryBrush}"
Style="{TemplateBinding ColumnSizerStyle}"
Visibility="{Binding CanResize, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource DataColumnBoolToVisibilityConverter}}">
<controls:ContentSizer.Resources>
<x:Double x:Key="SizerBaseThumbWidth">2</x:Double>
Expand Down
Loading