Skip to content

Commit f1bf543

Browse files
author
reunion-maestro-bot
committed
Syncing content from committish 4e2e9e3b88c957dc7b253be9db4366f44f5068ed
1 parent 978ab63 commit f1bf543

38 files changed

Lines changed: 1456 additions & 113 deletions

src/controls/dev/Generated/TitleBar.properties.cpp

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,12 @@ namespace winrt::Microsoft::UI::Xaml::Controls
1313

1414
#include "TitleBar.g.cpp"
1515

16+
GlobalDependencyProperty TitleBarProperties::s_AutoRefreshDragRegionsProperty{ nullptr };
1617
GlobalDependencyProperty TitleBarProperties::s_ContentProperty{ nullptr };
1718
GlobalDependencyProperty TitleBarProperties::s_IconSourceProperty{ nullptr };
1819
GlobalDependencyProperty TitleBarProperties::s_IsBackButtonEnabledProperty{ nullptr };
1920
GlobalDependencyProperty TitleBarProperties::s_IsBackButtonVisibleProperty{ nullptr };
21+
GlobalDependencyProperty TitleBarProperties::s_IsDragRegionProperty{ nullptr };
2022
GlobalDependencyProperty TitleBarProperties::s_IsPaneToggleButtonVisibleProperty{ nullptr };
2123
GlobalDependencyProperty TitleBarProperties::s_LeftHeaderProperty{ nullptr };
2224
GlobalDependencyProperty TitleBarProperties::s_RightHeaderProperty{ nullptr };
@@ -33,6 +35,17 @@ TitleBarProperties::TitleBarProperties()
3335

3436
void TitleBarProperties::EnsureProperties()
3537
{
38+
if (!s_AutoRefreshDragRegionsProperty)
39+
{
40+
s_AutoRefreshDragRegionsProperty =
41+
InitializeDependencyProperty(
42+
L"AutoRefreshDragRegions",
43+
winrt::name_of<bool>(),
44+
winrt::name_of<winrt::TitleBar>(),
45+
false /* isAttached */,
46+
ValueHelper<bool>::BoxValueIfNecessary(false),
47+
winrt::PropertyChangedCallback(&OnAutoRefreshDragRegionsPropertyChanged));
48+
}
3649
if (!s_ContentProperty)
3750
{
3851
s_ContentProperty =
@@ -77,6 +90,17 @@ void TitleBarProperties::EnsureProperties()
7790
ValueHelper<bool>::BoxValueIfNecessary(false),
7891
winrt::PropertyChangedCallback(&OnIsBackButtonVisiblePropertyChanged));
7992
}
93+
if (!s_IsDragRegionProperty)
94+
{
95+
s_IsDragRegionProperty =
96+
InitializeDependencyProperty(
97+
L"IsDragRegion",
98+
winrt::name_of<winrt::IReference<bool>>(),
99+
winrt::name_of<winrt::TitleBar>(),
100+
true /* isAttached */,
101+
ValueHelper<winrt::IReference<bool>>::BoxedDefaultValue(),
102+
&TitleBar::OnIsDragRegionPropertyChanged);
103+
}
80104
if (!s_IsPaneToggleButtonVisibleProperty)
81105
{
82106
s_IsPaneToggleButtonVisibleProperty =
@@ -147,10 +171,12 @@ void TitleBarProperties::EnsureProperties()
147171

148172
void TitleBarProperties::ClearProperties()
149173
{
174+
s_AutoRefreshDragRegionsProperty = nullptr;
150175
s_ContentProperty = nullptr;
151176
s_IconSourceProperty = nullptr;
152177
s_IsBackButtonEnabledProperty = nullptr;
153178
s_IsBackButtonVisibleProperty = nullptr;
179+
s_IsDragRegionProperty = nullptr;
154180
s_IsPaneToggleButtonVisibleProperty = nullptr;
155181
s_LeftHeaderProperty = nullptr;
156182
s_RightHeaderProperty = nullptr;
@@ -159,6 +185,14 @@ void TitleBarProperties::ClearProperties()
159185
s_TitleProperty = nullptr;
160186
}
161187

188+
void TitleBarProperties::OnAutoRefreshDragRegionsPropertyChanged(
189+
winrt::DependencyObject const& sender,
190+
winrt::DependencyPropertyChangedEventArgs const& args)
191+
{
192+
auto owner = sender.as<winrt::TitleBar>();
193+
winrt::get_self<TitleBar>(owner)->OnPropertyChanged(args);
194+
}
195+
162196
void TitleBarProperties::OnContentPropertyChanged(
163197
winrt::DependencyObject const& sender,
164198
winrt::DependencyPropertyChangedEventArgs const& args)
@@ -239,6 +273,19 @@ void TitleBarProperties::OnTitlePropertyChanged(
239273
winrt::get_self<TitleBar>(owner)->OnPropertyChanged(args);
240274
}
241275

276+
void TitleBarProperties::AutoRefreshDragRegions(bool value)
277+
{
278+
[[gsl::suppress(con)]]
279+
{
280+
static_cast<TitleBar*>(this)->SetValue(s_AutoRefreshDragRegionsProperty, ValueHelper<bool>::BoxValueIfNecessary(value));
281+
}
282+
}
283+
284+
bool TitleBarProperties::AutoRefreshDragRegions()
285+
{
286+
return ValueHelper<bool>::CastOrUnbox(static_cast<TitleBar*>(this)->GetValue(s_AutoRefreshDragRegionsProperty));
287+
}
288+
242289
void TitleBarProperties::Content(winrt::UIElement const& value)
243290
{
244291
[[gsl::suppress(con)]]
@@ -291,6 +338,17 @@ bool TitleBarProperties::IsBackButtonVisible()
291338
return ValueHelper<bool>::CastOrUnbox(static_cast<TitleBar*>(this)->GetValue(s_IsBackButtonVisibleProperty));
292339
}
293340

341+
342+
void TitleBarProperties::SetIsDragRegion(winrt::UIElement const& target, winrt::IReference<bool> const& value)
343+
{
344+
target.SetValue(s_IsDragRegionProperty, ValueHelper<winrt::IReference<bool>>::BoxValueIfNecessary(value));
345+
}
346+
347+
winrt::IReference<bool> TitleBarProperties::GetIsDragRegion(winrt::UIElement const& target)
348+
{
349+
return ValueHelper<winrt::IReference<bool>>::CastOrUnbox(target.GetValue(s_IsDragRegionProperty));
350+
}
351+
294352
void TitleBarProperties::IsPaneToggleButtonVisible(bool value)
295353
{
296354
[[gsl::suppress(con)]]

src/controls/dev/Generated/TitleBar.properties.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ class TitleBarProperties
99
public:
1010
TitleBarProperties();
1111

12+
void AutoRefreshDragRegions(bool value);
13+
bool AutoRefreshDragRegions();
14+
1215
void Content(winrt::UIElement const& value);
1316
winrt::UIElement Content();
1417

@@ -21,6 +24,9 @@ class TitleBarProperties
2124
void IsBackButtonVisible(bool value);
2225
bool IsBackButtonVisible();
2326

27+
static void SetIsDragRegion(winrt::UIElement const& target, winrt::IReference<bool> const& value);
28+
static winrt::IReference<bool> GetIsDragRegion(winrt::UIElement const& target);
29+
2430
void IsPaneToggleButtonVisible(bool value);
2531
bool IsPaneToggleButtonVisible();
2632

@@ -39,21 +45,25 @@ class TitleBarProperties
3945
void Title(winrt::hstring const& value);
4046
winrt::hstring Title();
4147

48+
static winrt::DependencyProperty AutoRefreshDragRegionsProperty() { return s_AutoRefreshDragRegionsProperty; }
4249
static winrt::DependencyProperty ContentProperty() { return s_ContentProperty; }
4350
static winrt::DependencyProperty IconSourceProperty() { return s_IconSourceProperty; }
4451
static winrt::DependencyProperty IsBackButtonEnabledProperty() { return s_IsBackButtonEnabledProperty; }
4552
static winrt::DependencyProperty IsBackButtonVisibleProperty() { return s_IsBackButtonVisibleProperty; }
53+
static winrt::DependencyProperty IsDragRegionProperty() { return s_IsDragRegionProperty; }
4654
static winrt::DependencyProperty IsPaneToggleButtonVisibleProperty() { return s_IsPaneToggleButtonVisibleProperty; }
4755
static winrt::DependencyProperty LeftHeaderProperty() { return s_LeftHeaderProperty; }
4856
static winrt::DependencyProperty RightHeaderProperty() { return s_RightHeaderProperty; }
4957
static winrt::DependencyProperty SubtitleProperty() { return s_SubtitleProperty; }
5058
static winrt::DependencyProperty TemplateSettingsProperty() { return s_TemplateSettingsProperty; }
5159
static winrt::DependencyProperty TitleProperty() { return s_TitleProperty; }
5260

61+
static GlobalDependencyProperty s_AutoRefreshDragRegionsProperty;
5362
static GlobalDependencyProperty s_ContentProperty;
5463
static GlobalDependencyProperty s_IconSourceProperty;
5564
static GlobalDependencyProperty s_IsBackButtonEnabledProperty;
5665
static GlobalDependencyProperty s_IsBackButtonVisibleProperty;
66+
static GlobalDependencyProperty s_IsDragRegionProperty;
5767
static GlobalDependencyProperty s_IsPaneToggleButtonVisibleProperty;
5868
static GlobalDependencyProperty s_LeftHeaderProperty;
5969
static GlobalDependencyProperty s_RightHeaderProperty;
@@ -72,6 +82,10 @@ class TitleBarProperties
7282
static void EnsureProperties();
7383
static void ClearProperties();
7484

85+
static void OnAutoRefreshDragRegionsPropertyChanged(
86+
winrt::DependencyObject const& sender,
87+
winrt::DependencyPropertyChangedEventArgs const& args);
88+
7589
static void OnContentPropertyChanged(
7690
winrt::DependencyObject const& sender,
7791
winrt::DependencyPropertyChangedEventArgs const& args);

src/controls/dev/Repeater/APITests/FlowLayoutTests.cs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1232,6 +1232,42 @@ public void ValidateIntersectionWithRealizationWindow()
12321232
}
12331233
}
12341234

1235+
[TestMethod]
1236+
public void ValidateUniformGridWithAvailableWidthNarrowerThanOneItem()
1237+
{
1238+
// Watson #61810783: when a UniformGridLayout receives a finite available minor
1239+
// size that is narrower than one item's minor stride, GetItemsPerLine used to
1240+
// return 0, causing a divide-by-zero in GetMajorSize during anchor evaluation.
1241+
// Verify that the layout pass completes without crashing and produces a sane
1242+
// (>= 1 item per line) layout instead.
1243+
RunOnUIThread.Execute(() =>
1244+
{
1245+
var repeater = new ItemsRepeater()
1246+
{
1247+
ItemsSource = Enumerable.Range(0, 6).Select(i => i.ToString()).ToList(),
1248+
Layout = new UniformGridLayout()
1249+
{
1250+
Orientation = Orientation.Horizontal,
1251+
MinItemWidth = 100,
1252+
MinItemHeight = 50
1253+
},
1254+
};
1255+
1256+
// Constrain the repeater to a width narrower than MinItemWidth.
1257+
var host = new Grid() { Width = 5, Height = 200 };
1258+
host.Children.Add(repeater);
1259+
1260+
Content = host;
1261+
Content.UpdateLayout();
1262+
1263+
// The fix should produce a non-crashing layout. We don't validate exact
1264+
// dimensions because the policy when a single item is wider than the
1265+
// available width is "still lay out one item per line", which produces a
1266+
// non-zero major extent.
1267+
Verify.IsTrue(repeater.DesiredSize.Height >= 0);
1268+
});
1269+
}
1270+
12351271
[TestMethod]
12361272
public void ValidateUniformGridWithNoItems()
12371273
{

src/controls/dev/Repeater/APITests/RepeaterTests.cs

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -755,5 +755,132 @@ public void BringIntoViewOfExistingItemsDoesNotChangeScrollOffset()
755755
});
756756
}
757757

758+
// Verifies that an ItemsRepeater using a DataTemplate can be garbage
759+
// collected after elements are recycled to the RecyclePool and the
760+
// repeater is removed from the tree. The recycling step is critical —
761+
// it creates the RecyclePool on the DataTemplate and populates it,
762+
// which forms the reference cycle that must be breakable by the tracker.
763+
[TestMethod]
764+
public void VerifyRepeaterWithRecycledElementsDoesNotLeak()
765+
{
766+
WeakReference repeaterWeakRef = null;
767+
var data = new ObservableCollection<string>(
768+
Enumerable.Range(0, 5).Select(i => string.Format("Item #{0}", i)));
769+
770+
RunOnUIThread.Execute(() =>
771+
{
772+
var repeater = new ItemsRepeater()
773+
{
774+
ItemsSource = data,
775+
ItemTemplate = CreateDataTemplateWithContent(@"<TextBlock Text='{Binding}' Height='50' />"),
776+
};
777+
repeaterWeakRef = new WeakReference(repeater);
778+
Content = repeater;
779+
});
780+
781+
IdleSynchronizer.Wait();
782+
783+
RunOnUIThread.Execute(() =>
784+
{
785+
// Verify elements are realized, then clear the data source to
786+
// force all elements into the RecyclePool on the DataTemplate.
787+
Verify.IsNotNull(repeaterWeakRef.Target);
788+
var repeater = (ItemsRepeater)repeaterWeakRef.Target;
789+
Verify.IsNotNull(repeater.TryGetElement(0), "Element 0 should be realized.");
790+
data.Clear();
791+
});
792+
793+
IdleSynchronizer.Wait();
794+
795+
RunOnUIThread.Execute(() =>
796+
{
797+
// Remove from tree. The RecyclePool now holds recycled elements
798+
// that reference the DataTemplate and the repeater (as owner).
799+
Content = null;
800+
});
801+
802+
IdleSynchronizer.Wait();
803+
804+
// Force GC — the tracker_ref on ItemTemplateWrapper's DataTemplate
805+
// makes the reference visible to the XAML reference tracker, which
806+
// detects the RecyclePool cycle and breaks it during collection.
807+
for (int i = 0; i < 5 && repeaterWeakRef.IsAlive; i++)
808+
{
809+
GC.Collect();
810+
GC.WaitForPendingFinalizers();
811+
IdleSynchronizer.Wait();
812+
}
813+
814+
Verify.IsFalse(repeaterWeakRef.IsAlive,
815+
"ItemsRepeater should be collected after recycling elements and removal.");
816+
}
817+
818+
// Verifies that an ItemsRepeater using a RecyclingElementFactory (the
819+
// DataTemplateSelector equivalent) can be garbage collected after
820+
// elements are recycled and the repeater is removed from the tree.
821+
[TestMethod]
822+
public void VerifyRepeaterWithRecyclingElementFactoryDoesNotLeak()
823+
{
824+
WeakReference repeaterWeakRef = null;
825+
var data = new ObservableCollection<int>(Enumerable.Range(0, 6));
826+
827+
RunOnUIThread.Execute(() =>
828+
{
829+
var elementFactory = new RecyclingElementFactory()
830+
{
831+
RecyclePool = new RecyclePool(),
832+
};
833+
elementFactory.Templates["even"] = (DataTemplate)XamlReader.Load(
834+
@"<DataTemplate xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'>
835+
<TextBlock Text='even' Height='50' />
836+
</DataTemplate>");
837+
elementFactory.Templates["odd"] = (DataTemplate)XamlReader.Load(
838+
@"<DataTemplate xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'>
839+
<TextBlock Text='odd' Height='50' />
840+
</DataTemplate>");
841+
842+
elementFactory.SelectTemplateKey +=
843+
delegate (RecyclingElementFactory sender, SelectTemplateEventArgs args)
844+
{
845+
args.TemplateKey = ((int)args.DataContext % 2 == 0) ? "even" : "odd";
846+
};
847+
848+
var repeater = new ItemsRepeater()
849+
{
850+
ItemsSource = data,
851+
ItemTemplate = elementFactory,
852+
};
853+
repeaterWeakRef = new WeakReference(repeater);
854+
Content = repeater;
855+
});
856+
857+
IdleSynchronizer.Wait();
858+
859+
RunOnUIThread.Execute(() =>
860+
{
861+
// Clear items to force elements into the RecyclePool, then remove.
862+
data.Clear();
863+
});
864+
865+
IdleSynchronizer.Wait();
866+
867+
RunOnUIThread.Execute(() =>
868+
{
869+
Content = null;
870+
});
871+
872+
IdleSynchronizer.Wait();
873+
874+
for (int i = 0; i < 5 && repeaterWeakRef.IsAlive; i++)
875+
{
876+
GC.Collect();
877+
GC.WaitForPendingFinalizers();
878+
IdleSynchronizer.Wait();
879+
}
880+
881+
Verify.IsFalse(repeaterWeakRef.IsAlive,
882+
"ItemsRepeater with RecyclingElementFactory should be collected after recycling and removal.");
883+
}
884+
758885
}
759886
}

0 commit comments

Comments
 (0)