UWP Composition API, uwpcompositionapi
If you have read UWP Jenkins + NuGet + MSBuild, teach you how to create automatic UWP Build and App store packages.
For VS2017, you need to update the configuration. For more information, click here at the end of the article.
I have previously written an article about FlexGrid for locking columns. If you have not read this article, you can read it first.
Put it first
The background for creating a new control is that the SDK is upgraded to 14393, And the Composition API has changed accordingly.
This has a major impact on us:
10586:
In SDK 10586,ElementCompositionPreview.GetElementVisual
The Visual returned by the method is only controlled by the caller. Use the corresponding VisualUIElement
The operations are purely incremental effects on the xaml. This is because the returnedVisual
(At the underlying layer) YesUIElement
As a sub-item of the root Visual.
14393:
In version 14332 and later versions,ElementCompositionPreview.GetElementVisual
The returned Visual method is the same as that of the XAML layout operation. This means that, unlike the update in NovemberUIElement
Operations performed by the element will definitely change the XAML layout.
Because both the caller and The XAML are operating on the sameVisual
, XAML may overwrite the value assigned by the caller. The following are attributes that may be set in XAML:
- Offset
- Size
- Opacity
- TransformMatrix
- Clip
- CompositeMode
The rules for updating an interactive Visual attribute in XAML are as follows:
In general, in the past, Visual was absolutely controlled by me, and now XAML also affects the final value of Visual together.
In this way, the baby is not happy, directly upgrade the previous project to 14393, FlexGrid various problems.
Let's create a New FlexGird by ourselves with the feeling that we are not as good as ourselves.
First, let's take a look at the composition of the entire New FlexGird. The entire control is a ListView, with the Header (Column Header and locked row) placed in the TopHeader of the ListView ScrollViewer.
The template for the entire New FlexGird is as follows.
<ControlTemplate TargetType="local:NewFlexGrid"> <Border x:Name="RootBorder" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}"> <ScrollViewer x:Name="ScrollViewer" Style="{StaticResource FlexGridScrollViewerStyle}" AutomationProperties.AccessibilityView="Raw" BringIntoViewOnFocusChange="{TemplateBinding ScrollViewer.BringIntoViewOnFocusChange}" HorizontalScrollMode="{TemplateBinding ScrollViewer.HorizontalScrollMode}" HorizontalScrollBarVisibility="{TemplateBinding ScrollViewer.HorizontalScrollBarVisibility}" IsHorizontalRailEnabled="{TemplateBinding ScrollViewer.IsHorizontalRailEnabled}" IsHorizontalScrollChainingEnabled="{TemplateBinding ScrollViewer.IsHorizontalScrollChainingEnabled}" IsVerticalScrollChainingEnabled="{TemplateBinding ScrollViewer.IsVerticalScrollChainingEnabled}" IsVerticalRailEnabled="{TemplateBinding ScrollViewer.IsVerticalRailEnabled}" IsDeferredScrollingEnabled="{TemplateBinding ScrollViewer.IsDeferredScrollingEnabled}" TabNavigation="{TemplateBinding TabNavigation}" VerticalScrollBarVisibility="{TemplateBinding ScrollViewer.VerticalScrollBarVisibility}" VerticalScrollMode="{TemplateBinding ScrollViewer.VerticalScrollMode}" ZoomMode="{TemplateBinding ScrollViewer.ZoomMode}"> <ScrollViewer.TopHeader> <StackPanel Orientation="Vertical" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <local:NewFlexGridColumnHeader x:Name="ColumnHeader" FrozenCount="{TemplateBinding ColumnHeaderFrozenCount}" SelectionMode="None" IsItemClickEnabled="True" Style="{StaticResource NoScrollViewerListViewStyle}" ItemsSource="{TemplateBinding ColumnsHeaderItemsSource}" ItemTemplate="{TemplateBinding ColumnsHeaderItemTemplate}"> <local:NewFlexGridColumnHeader.ItemsPanel> <ItemsPanelTemplate> <StackPanel Orientation="Horizontal"/> </ItemsPanelTemplate> </local:NewFlexGridColumnHeader.ItemsPanel> </local:NewFlexGridColumnHeader> <local:NewFlexGridFrozenRows x:Name="FrozenRows" ItemTemplate="{TemplateBinding ItemTemplate}" ItemsSource="{TemplateBinding FrozenRowsItemsSource}" IsItemClickEnabled="True" SelectionMode="None" Style="{StaticResource NoScrollViewerListViewStyle}" ItemContainerStyle="{TemplateBinding ItemContainerStyle}"> <local:NewFlexGridFrozenRows.ItemsPanel> <ItemsPanelTemplate> <StackPanel Orientation="Vertical"/> </ItemsPanelTemplate> </local:NewFlexGridFrozenRows.ItemsPanel> </local:NewFlexGridFrozenRows> </StackPanel> </ScrollViewer.TopHeader> <ItemsPresenter HorizontalAlignment="Left" VerticalAlignment="Top" FooterTransitions="{TemplateBinding FooterTransitions}" FooterTemplate="{TemplateBinding FooterTemplate}" Footer="{TemplateBinding Footer}" Padding="{TemplateBinding Padding}"/> <!--HeaderTemplate="{TemplateBinding HeaderTemplate}" Header="{TemplateBinding Header}" HeaderTransitions="{TemplateBinding HeaderTransitions}"--> </ScrollViewer> </Border> </ControlTemplate>
When the ScrollViewer element and the New FlexGird Loaded event are obtained, we need to prepare the Composition element.
private void PrepareCompositionAnimation() { if (_scrollViewer != null) { if (_scrollerViewerManipulation == null) { _scrollerViewerManipulation = ElementCompositionPreview.GetScrollViewerManipulationPropertySet(_scrollViewer); } if (_offsetXAnimation == null) { _offsetXAnimation = _scrollerViewerManipulation.Compositor.CreateExpressionAnimation("-min(0,ScrollManipulation.Translation.X)"); _offsetXAnimation.SetReferenceParameter("ScrollManipulation", _scrollerViewerManipulation); _columnsHeader._offsetXAnimation = _offsetXAnimation; _frozenRows._offsetXAnimation = _offsetXAnimation; } } }
I have read the articles related to Composition several times before and should know what this is. If you do not know it, please read the UWP Composition API-PullToRefresh first.
NewFlexGridColumnHeader is a horizontal ListView with no ScrollViewer. It is associated through the ColumnHeaderFrozenCount/ColumnsHeaderItemsSource/ColumnsHeaderItemTemplate attribute in New FlexGird.
In the NewFlexGridColumnHeader's PrepareContainerForItemOverride method, we use the _ offsetXAnimation prepared earlier to let the qualified Column headers execute the animation.
protected override void PrepareContainerForItemOverride(DependencyObject element, object item) { base.PrepareContainerForItemOverride(element, item); int index = this.IndexFromContainer(element); if (index > -1 && index < FrozenCount && _offsetXAnimation != null) { Canvas.SetZIndex((element as UIElement), 10); var _frozenContentVisual = ElementCompositionPreview.GetElementVisual(element as UIElement); _frozenContentVisual.StartAnimation("Offset.X", _offsetXAnimation); } }
NewFlexGridFrozenRows is a vertical ListView, which also has no ScrollViewer and is used to store locked rows. It is associated by attributes such as FrozenRowsItemsSource, ItemTemplate, and ItemContainerStyle in New FlexGird.
In the NewFlexGridFrozenRows PrepareContainerForItemOverride method, we mainly register the NewFlexGridFrozenRows_Loaded event,
protected override void PrepareContainerForItemOverride(DependencyObject element, object item) { base.PrepareContainerForItemOverride(element, item); var flexGridItem = element as ListViewItem; flexGridItem.RightTapped -= FlexGridItem_RightTapped; flexGridItem.Holding -= FlexGridItem_Holding; flexGridItem.RightTapped += FlexGridItem_RightTapped; flexGridItem.Holding += FlexGridItem_Holding; flexGridItem.Loaded += NewFlexGridFrozenRows_Loaded; }
When the Item is Loaded, we define the _ offsetXAnimation we have prepared by defining an attachment attribute to know which element needs to be animated by Frozen.
private void NewFlexGridFrozenRows_Loaded(object sender, RoutedEventArgs e) { (sender as ListViewItem).Loaded -= NewFlexGridFrozenRows_Loaded; var templateRoot = (sender as ListViewItem).ContentTemplateRoot; var child = templateRoot.GetAllChildren(); var _frozenContent = child.Where(x => FlexGridItemFrozenContent.GetIsFrozenContent(x)); if (_frozenContent != null && _offsetXAnimation != null) { foreach (var item in _frozenContent) { var _frozenContentVisual = ElementCompositionPreview.GetElementVisual(item); _frozenContentVisual.StartAnimation("Offset.X", _offsetXAnimation); } } }
The New FlexGird and NewFlexGridFrozenRows have the same operations.
In the Unloaded event, we need to release some resources to prevent memory leaks and release all the Composition resources at the right time (see the Dispose method)
private void NewFlexGrid_Unloaded(object sender, RoutedEventArgs e) { if (_offsetXAnimation != null) { _offsetXAnimation.Dispose(); _offsetXAnimation = null; } //don't dispose at this moment,some page NavigationCacheMode is required //you must dispose it at page back. //if (_scrollerViewerManipulation != null) //{ // _scrollerViewerManipulation.Dispose(); // _scrollerViewerManipulation = null; //} }
public void Dispose() { if (_offsetXAnimation != null) { _offsetXAnimation.Dispose(); _offsetXAnimation = null; } if (_scrollerViewerManipulation != null) { _scrollerViewerManipulation.Dispose(); _scrollerViewerManipulation = null; } }
Define the locked column (blue part) in our ItemTemplate of New FlexGird)
<DataTemplate x:Key="WideScreenItemTemplate"> <Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch" > <Grid.ColumnDefinitions> <ColumnDefinition Width="110"/> <ColumnDefinition Width="110" /> <ColumnDefinition Width="110" /> <ColumnDefinition Width="110" /> <ColumnDefinition Width="110" /> <ColumnDefinition Width="110" /> <ColumnDefinition Width="110" /> </Grid.ColumnDefinitions> <Grid Background="Green" Width="110" flexgrid:FlexGridItemFrozenContent.IsFrozenContent="True"> <TextBlock Text="{Binding Age}" /> </Grid> <TextBlock Text="{Binding Name}" Grid.Column="1"/> <TextBlock Text="{Binding IsMale}" Grid.Column="2"/> <Grid Background="Yellow" Width="110" Grid.Column="3" > <TextBlock Text="{Binding Age}" /> </Grid> <TextBlock Text="{Binding Name}" Grid.Column="4"/> <TextBlock Text="{Binding IsMale}" Grid.Column="5"/> <TextBlock Text="{Binding Name}" Grid.Column="6"/> </Grid> </DataTemplate>
OK. The row and column are locked when running.
During use, some children's shoes may find that there are some other problems.
1. The locked column or row cannot cover the moving part because it is transparent Background. The solution is to add Background = "{ThemeResource ApplicationPageBackgroundThemeBrush}" to the column element to be locked }"
2. the Pointer over style, because 1 contains an opaque color, this section blocks the ListViewitem PointerOver color, the solution is
Rewrite the style of ListviewItem, which can be found in Microsoft documents. There are two types of ListViewItem templates.
Here we will rewrite the 2nd templates and move the PointerOverBorder element in the template to the ContentBorder (that is, move it to the end of ContentBorder in Xaml). The specific template
See NewFlexGridItemStyle
3. When Pointer press goes down, the front-end of the frozen column will display the blocked content. The solution is:
<Border HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" flexgrid:FlexGridItemFrozenContent.IsFrozenContent="True"> <StackPanel Margin="-15,0,0,0" Padding="15,4,0,4" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" VerticalAlignment="Center"> <TextBlock Text="{x:Bind Name, Mode=OneWay}"}" TextTrimming="CharacterEllipsis"/> </StackPanel> </Border>
It will be used to block the color block behind the content. Here is StackPanel, and a negative X (-15) Margin is added. Then you may ask why it is not added to Border. According to the update in SDK 14393, if the content as Visual has the initial location attribute, this will affect the final value of Visual. You can try adding flexgrid: FlexGridItemFrozenContent. IsFrozenContent = "True" elements with attribute settings that affect the position, which will render the final animation invalid.
I don't know if there will be any other changes in Creators Update. It seems that there is a new API. After I have studied it, I will send it to you.
Finally, open source is helpful: New FlexGird. You can use it ..