Skip to content

Commit c8bd154

Browse files
author
reunion-maestro-bot
committed
Syncing content from committish f7a64ff105945fb702b0709dd193f886a1eca128
1 parent a7cc340 commit c8bd154

File tree

55 files changed

+1265
-485
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+1265
-485
lines changed

Directory.Build.props

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,19 @@
2121
<ShouldUnsetParentConfigurationAndPlatform>false</ShouldUnsetParentConfigurationAndPlatform>
2222
</PropertyGroup>
2323

24+
<!-- Import LKG VC Toolset, if enabled (LkgVcTools* env vars set)-->
25+
<Import Project="$(NugetPackageDirectory)\$(LkgVcToolsName)\$(LkgVcToolsVersion)\build\native\$(LkgVcToolsName).props"
26+
Condition="'$(MSBuildProjectExtension)' == '.vcxproj' and '$(LkgVcToolsName)$(LkgVcToolsVersion)' != '' and
27+
Exists('$(NugetPackageDirectory)\$(LkgVcToolsName)\$(LkgVcToolsVersion)\build\native\$(LkgVcToolsName).props')" />
28+
<Target Name="LkgEnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild"
29+
Condition="'$(MSBuildProjectExtension)' == '.vcxproj' and '$(LkgVcToolsName)$(LkgVcToolsVersion)' != ''">
30+
<PropertyGroup>
31+
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
32+
</PropertyGroup>
33+
<Error Condition="!Exists('$(NugetPackageDirectory)\$(LkgVcToolsName)\$(LkgVcToolsVersion)\build\native\$(LkgVcToolsName).props')"
34+
Text="$([System.String]::Format('$(ErrorText)', '$(NugetPackageDirectory)\$(LkgVcToolsName)\$(LkgVcToolsVersion)\build\native\$(LkgVcToolsName).props'))" />
35+
</Target>
36+
2437
<!--
2538
Every project in this repository should include the root Directory.Build.props file. This allows us to create a
2639
single place where we can share common build infrastructure, without creating MSBuild duplicate import errors.

controls/dev/AnnotatedScrollBar/AnnotatedScrollBar.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,12 @@ void AnnotatedScrollBar::QueueLayoutLabels(unsigned int millisecondWait)
302302
auto weakThis = get_weak();
303303
auto runLayoutLabelsAction = [weakThis]()
304304
{
305+
if (winrt::WindowsXamlManager::GetForCurrentThread() == nullptr)
306+
{
307+
// Exit early if Xaml core has already shut down.
308+
return;
309+
}
310+
305311
if (auto strongThis = weakThis.get())
306312
{
307313
strongThis->m_labelsDebounce.clear();
@@ -315,7 +321,7 @@ void AnnotatedScrollBar::QueueLayoutLabels(unsigned int millisecondWait)
315321

316322
void AnnotatedScrollBar::LayoutLabels()
317323
{
318-
auto labelsGrid = m_labelsGrid.get();
324+
auto labelsGrid = m_labelsGrid.safe_get();
319325
if (!labelsGrid)
320326
{
321327
return;

controls/dev/Collections/Vector.h

Lines changed: 94 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,15 @@
66
#include "VectorChangedEventArgs.h"
77
#include <algorithm>
88

9-
// Nearly all Vector need to set DependencyObjectBase flag
9+
// Nearly all Vector need to set DependencyObjectBase flag
1010
// to make DependencyObject as ComposableBase
1111
// Otherwise we may hit memory leak when interact with .net.
1212
// One exception is AcrylicBrush::CreateAcrylicBrushWorker
1313
// because we know we don't have memory leak.
1414
enum class VectorFlag {
1515
None = 0, // not Observable, not Bindable and not DependencyObjectBase
16-
Observable = 1,
17-
DependencyObjectBase = 2,
16+
Observable = 1,
17+
DependencyObjectBase = 2,
1818
Bindable = 4,
1919
NoTrackerRef = 8
2020
};
@@ -121,7 +121,7 @@ struct ObservableTraits<T, true, isBindable>
121121
winrt::BindableVectorChangedEventHandler,
122122
winrt::VectorChangedEventHandler<T>
123123
>::type;
124-
124+
125125
using EventToken = typename winrt::event_token;
126126

127127
static void RaiseEvent(EventSource* e, SenderType sender, winrt::CollectionChange collectionChange, uint32_t index)
@@ -223,6 +223,53 @@ struct VectorInnerImpl
223223
return false;
224224
}
225225

226+
bool IndexOf_OptimizedForRemove(typename T_type const& value, uint32_t& index)
227+
{
228+
index = 0;
229+
230+
if (m_optimizedIndexOf_StartNextSearchAt >= m_vector.size())
231+
{
232+
if (m_vector.size() == 0)
233+
{
234+
m_optimizedIndexOf_StartNextSearchAt = 0;
235+
}
236+
else
237+
{
238+
m_optimizedIndexOf_StartNextSearchAt = static_cast<unsigned int>(m_vector.size() - 1);
239+
}
240+
}
241+
242+
const auto& searchFor = wrap(value);
243+
244+
auto it = std::find(m_vector.begin() + m_optimizedIndexOf_StartNextSearchAt, m_vector.end(), searchFor);
245+
bool found = (it != m_vector.end());
246+
if (!found && m_optimizedIndexOf_StartNextSearchAt > 0)
247+
{
248+
// Note: if std::find fails, it returns the second iterator and not always the end of the vector.
249+
it = std::find(m_vector.begin(), m_vector.begin() + m_optimizedIndexOf_StartNextSearchAt, searchFor);
250+
found = (it != m_vector.begin() + m_optimizedIndexOf_StartNextSearchAt);
251+
}
252+
253+
if (found)
254+
{
255+
index = (uint32_t)(it - m_vector.begin());
256+
if (index > 0)
257+
{
258+
// Next time we search start near this index. We assume that the remove is being processed from the end
259+
// of the vector towards index 0, so the next item being removed is probably at "index - 1". Start the
260+
// next search there.
261+
m_optimizedIndexOf_StartNextSearchAt = index - 1;
262+
}
263+
else
264+
{
265+
m_optimizedIndexOf_StartNextSearchAt = index;
266+
}
267+
return true;
268+
}
269+
270+
return false;
271+
}
272+
226273
uint32_t GetMany(uint32_t const startIndex, winrt::array_view<T_type> values)
227274
{
228275
if (startIndex >= m_vector.size())
@@ -255,7 +302,7 @@ struct VectorInnerImpl
255302
{
256303
throw winrt::hresult_out_of_bounds();
257304
}
258-
305+
259306
}
260307

261308
void RemoveAt(uint32_t const index)
@@ -313,6 +360,35 @@ struct VectorInnerImpl
313360

314361
std::vector<T_Storage> m_vector;
315362
ITrackerHandleManager* m_trackerHandleManager{ nullptr };
363+
364+
//
365+
// IndexOf optimization for scenarios where lots of items are removed from a large vector, such as unchecking a
366+
// selected subtree in a large TreeView
367+
//
368+
// e.g. A TreeView has a single root with 10 children, each with 10,000 children inside them. The entire TreeView
369+
// is selected, and the user unchecks one of the 10 first-level children. The SelectedItems vector of the TreeView
370+
// has all 100,000 (roughly - it includes the parent nodes too) items, and we're going to remove 10,000. TreeView's
371+
// deselection logic makes sure an item is selected before removing it from the SelectedItems vector, and it does
372+
// this with an IndexOf. So naively, unchecking one top-level child is going to run 10,000 IndexOf operations into
373+
// a vector of 100,000. If the child being unchecked is near the end of the TreeView, then its entries are near the
374+
// end of the vector of 100,000, and each IndexOf will have to search linearly to the end of the vector, making the
375+
// operation extremely slow.
376+
//
377+
// Instead, we optimize by starting the search not at index 0 but partway through the list, specifically near where
378+
// we found a previous hit. The assumption is that all 10,000 items being unchecked were checked when the top-level
379+
// child was checked, so they're all next to each other in the SelectedItems vector, and we can find them quickly
380+
// if we start near where we found the previous hit.
381+
//
382+
// Note that it's _near_ the previous hit and not _at_ the previous hit because of another optimization. Removing
383+
// 10,000 items means shuffling the vector 10,000 times, which is also a slow operation. The last item being removed
384+
// is going to be shuffled up 9,999 times only to be deleted in the end. To avoid wasted shuffling, we iterate the
385+
// child collection backwards when deselecting. But this also means IndexOf can't start the next search at the same
386+
// index but rather at that index - 1. In this example, item 89,999 was just removed, and next we're going to remove
387+
// item 89,998. Starting the next search at index 89,999 actualy has the worst performance, because it guarantees that
388+
// we're going to search linearly from 89,999 to the end of the vector, then wrap around at the beginning and search
389+
// through every item in the vector before finding our hit at the last possible index 89,998.
390+
//
391+
unsigned int m_optimizedIndexOf_StartNextSearchAt = 0;
316392
};
317393

318394
// Vector Inner implementation with Observable function
@@ -341,7 +417,7 @@ struct ObservableVectorInnerImpl:
341417
Traits::RaiseEvent(m_pIVectorExternal->GetVectorEventSource(), sender, collectionChange, index);
342418
}
343419
}
344-
420+
345421
winrt::event_token AddEventHandler(EventHandler const& handler)
346422
{
347423
return Traits::AddEventHandler(m_pIVectorExternal->GetVectorEventSource(), handler);
@@ -389,7 +465,7 @@ struct ComposableBasePointersImplTType
389465
using WinRTBaseFactoryInterface = typename winrt::IDependencyObjectFactory;
390466
};
391467

392-
template <>
468+
template <>
393469
struct ComposableBasePointersImplTType<false>
394470
{
395471
using WinRTBase = typename winrt::IInspectable;
@@ -417,7 +493,7 @@ struct VectorOptionsBase: VectorInterfaceHelper<T, isBindable>, ComposableBasePo
417493

418494
template <typename T, bool isObservable, bool isBindable, bool isDependencyObjectBase, bool isNoTrackerRef>
419495
struct VectorOptions: VectorOptionsBase<T, isObservable, isBindable, isDependencyObjectBase, isNoTrackerRef>
420-
{
496+
{
421497
};
422498

423499
template <typename T, bool isObservable, bool isDependencyObjectBase, bool isNoTrackerRef>
@@ -532,7 +608,7 @@ struct VectorOptionsFromFlag :
532608
} \
533609
private:
534610

535-
// Implement IVectorOwner, also define the Inner Vector and Event Source
611+
// Implement IVectorOwner, also define the Inner Vector and Event Source
536612
#define Implement_Vector_External(Options) \
537613
private: \
538614
using VectorInnerType = ObservableVectorInnerImpl<##Options##>; \
@@ -552,22 +628,22 @@ struct VectorOptionsFromFlag :
552628
Implement_IVector_Modify_Functions(##Options##) \
553629
Implement_IIterator(##Options##) \
554630
Implement_IObservable(##Options##) \
555-
Implement_Vector_External(##Options##)
631+
Implement_Vector_External(##Options##)
556632

557633
// Implement all interfaces for IXXVector/IXXIterator/IXXObservable except those which will modify the vector
558634
// Like TreeViewNode, we need do additional work before Add/Remove/Modify the vector
559635
#define Implement_Vector_Read(Options) \
560636
Implement_IVector_Read_Functions(##Options##) \
561637
Implement_IIterator(##Options##) \
562638
Implement_IObservable(##Options##) \
563-
Implement_Vector_External(##Options##)
639+
Implement_Vector_External(##Options##)
564640

565641
// Implement all interfaces for IXXVector/IXXIterator/IXXObservable except those which will modify the vector
566642
// Like TreeViewNode, we need do additional work before Add/Remove/Modify the vector
567643
#define Implement_Vector_Read_NoObservable(Options) \
568644
Implement_IVector_Read_Functions(##Options##) \
569645
Implement_IIterator(##Options##) \
570-
Implement_Vector_External(##Options##)
646+
Implement_Vector_External(##Options##)
571647

572648

573649
template <typename T, bool isObservable, bool isBindable, bool isDependencyObjectBase, bool isNoTrackerRef, typename Options = VectorOptions<T, isObservable, isBindable, isDependencyObjectBase, isNoTrackerRef>>
@@ -602,8 +678,8 @@ class VectorBase :
602678
};
603679

604680

605-
template<typename T,
606-
int flags = MakeVectorParam<VectorFlag::Observable, VectorFlag::DependencyObjectBase>(),
681+
template<typename T,
682+
int flags = MakeVectorParam<VectorFlag::Observable, VectorFlag::DependencyObjectBase>(),
607683
typename Helper = VectorFlagHelper<flags>>
608684
class Vector :
609685
public VectorBase<T, Helper::isObservable, Helper::isBindable, Helper::isDependencyObjectBase, Helper::isNoTrackerRef>
@@ -613,18 +689,18 @@ class Vector :
613689
Vector(uint32_t capacity) : VectorBase<T, Helper::isObservable, Helper::isBindable, Helper::isDependencyObjectBase, Helper::isNoTrackerRef>(capacity) {}
614690

615691
// The same copy of data for NavigationView split into two parts in top navigationview. So two or more vectors are created to provide multiple datasource for controls.
616-
// InspectingDataSource is converting C# collections to Vector<winrt::IInspectable>. When GetAt(index) for things like string, a new IInspectable is always returned by C# projection.
692+
// InspectingDataSource is converting C# collections to Vector<winrt::IInspectable>. When GetAt(index) for things like string, a new IInspectable is always returned by C# projection.
617693
// ListView use indexOf for selection, so a copied/filtered view of C# collection doesn't work for SelectedItem(s) anymore because IInspectable comparsion always return false.
618-
// As a workaround, the copied/filtered vector requires others help to IndexOf the orignial C# collection.
694+
// As a workaround, the copied/filtered vector requires others help to IndexOf the orignial C# collection.
619695
// So the comparison is done by C# vector other than Inspectable directly comparision. Here is an example:
620-
// Raw data A is: Home-Apps-Music-Sports
696+
// Raw data A is: Home-Apps-Music-Sports
621697
// data is splitted two vectors: B and C. B includes Homes, and C includes Apps-Music-Sports
622698
// Music is the selected item. SplitDataSource is the class help to manage the raw data and provides splitted vectors to ListViews
623699
// ListView call C.indexOf("Music")
624700
// C ask SplitDataSource.IndexOf
625701
// SplitDataSource calls A.IndexOf (C# provided it)
626702
// SpiltDataSource help to convert the indexInRawData to indexInC
627-
// return index in C
703+
// return index in C
628704
Vector(std::function<int(typename T const& value)> indexOfFunction) : m_indexOfFunction(indexOfFunction)
629705
{
630706
if (m_indexOfFunction)

controls/dev/Materials/Acrylic/AcrylicBrush.cpp

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,14 @@ winrt::CompositionAnimation AcrylicBrush::MakeColorAnimation(const winrt::Color&
8181
{
8282
auto animation = compositor.CreateColorKeyFrameAnimation();
8383
animation.InsertKeyFrame(1.0, color);
84-
animation.Duration(duration.count() == 0 ? winrt::TimeSpan::duration(1 * 10000) : duration); // Zero duration KeyFrameAnimations not supported, use 1ms duration in that case.
84+
if (SharedHelpers::IsAnimationsEnabled())
85+
{
86+
animation.Duration(duration.count() == 0 ? winrt::TimeSpan::duration(1 * 10000) : duration); // Zero duration KeyFrameAnimations not supported, use 1ms duration in that case.
87+
}
88+
else
89+
{
90+
animation.Duration(1ms); // shortest allowed to minimize CPU use
91+
}
8592
return animation;
8693
}
8794

@@ -95,7 +102,7 @@ winrt::CompositionAnimation AcrylicBrush::MakeFloatAnimation(
95102
auto animation = compositor.CreateScalarKeyFrameAnimation();
96103
animation.InsertKeyFrame(0.0, fromValue);
97104
animation.InsertKeyFrame(1.0, toValue, easing);
98-
animation.Duration(duration);
105+
animation.Duration(SharedHelpers::IsAnimationsEnabled() ? duration : 1ms); // 1m is shortest allowed, minimize CPU use
99106
return animation;
100107
}
101108

controls/dev/Repeater/APITests/ItemTemplateTests.cs

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,67 @@ public void ValidateRecyclingElementFactoryWithSingleTemplate()
250250
});
251251
}
252252

253+
[TestMethod]
254+
public void ValidateDataTemplateWithElementNameBinding()
255+
{
256+
const int numItems = 5;
257+
ItemsRepeater itemsRepeater = null;
258+
259+
RunOnUIThread.Execute(() =>
260+
{
261+
var dataTemplate = XamlReader.Load(
262+
@"<DataTemplate xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'>
263+
<TextBlock Text='{Binding}' Tag='{Binding ElementName=siblingTextBlock, Path=Text}'/>
264+
</DataTemplate>") as DataTemplate;
265+
266+
var stackPanel = new StackPanel()
267+
{
268+
Name = "stackPanel",
269+
Width = 200
270+
};
271+
272+
var siblingTextBlock = new TextBlock()
273+
{
274+
Name = "siblingTextBlock",
275+
Text = "DataSource"
276+
};
277+
278+
itemsRepeater = new ItemsRepeater()
279+
{
280+
Name = "itemsRepeater",
281+
ItemsSource = Enumerable.Range(0, numItems).Select(i => i.ToString()),
282+
Layout = new StackLayout(),
283+
ItemTemplate = dataTemplate
284+
};
285+
286+
stackPanel.Children.Add(siblingTextBlock);
287+
stackPanel.Children.Add(itemsRepeater);
288+
289+
Content = stackPanel;
290+
291+
Content.UpdateLayout();
292+
});
293+
294+
IdleSynchronizer.Wait();
295+
296+
RunOnUIThread.Execute(() =>
297+
{
298+
Verify.AreEqual(numItems, VisualTreeHelper.GetChildrenCount(itemsRepeater));
299+
300+
for (int i = 0; i < numItems; i++)
301+
{
302+
var itemTextBlock = itemsRepeater.TryGetElement(i) as TextBlock;
303+
304+
Verify.IsNotNull(itemTextBlock);
305+
Verify.AreEqual(i.ToString(), itemTextBlock.Text);
306+
Verify.AreEqual("DataSource", itemTextBlock.Tag);
307+
}
308+
309+
itemsRepeater.ItemsSource = null;
310+
Content.UpdateLayout();
311+
});
312+
}
313+
253314
[TestMethod]
254315
[TestProperty("IsolationLevel", "Method")] // Task 28232821: DCPP Test: ItemsRepeater tests are running in isolation mode due to test instability.
255316
public void ValidateDataTemplateAsItemTemplate()
@@ -635,6 +696,7 @@ public void VerifyNullTemplateGivesMeaningfullError()
635696
Content = null;
636697
});
637698
}
699+
638700
private ItemsRepeaterScrollHost CreateAndInitializeRepeater(
639701
object itemsSource,
640702
Layout layout,

0 commit comments

Comments
 (0)