Jump to: navigation, search

XAML Tips


Revision as of 14:04, 28 November 2019 by LGE (Talk | contribs)

Functionality related

Attachable behaviors

It's quite often that you need to attach behaviors to certain XAML elements. For example, on a Grid, you want to attach a behavior which executes a command upon a Tapped event, or you want to execute a command when a certain property on a UBIK object changes.

Notice that in the following examples, "Interactivity" and "Core" are both namespaces and you have to make sure that they are defined at the root of your XAMLs:

<DataTemplate
   ...
   xmlns:Core="using:Microsoft.Xaml.Interactions.Core"
   xmlns:Interactivity="using:Microsoft.Xaml.Interactivity">
    ...
</DataTemplate>

Event Triggered

With an EventTriggerBehavior, you can react on changes/events of UI Elements:

<Grid>
    <Interactivity:Interaction.Behaviors>
        <Core:EventTriggerBehavior EventName="Tapped">
            <Core:InvokeCommandAction Command="{Binding NavigateToChildrenCommand}" />
        </Core:EventTriggerBehavior>
    </Interactivity:Interaction.Behaviors>
</Grid>

Data Triggered

If you want to react on changes of the underlying data (ViewModel), you can use DataTriggerBehavior instead. The following example, when used in the UBIKSplashArea template, automatically navigates to the root objects once the login process is finished and the user was successfully authenticated:

<Grid>
   <Interactivity:Interaction.Behaviors>
       <Core:DataTriggerBehavior Binding="{Binding IsLoggedIn}" Value="True">
           <Core:InvokeCommandAction Command="{Binding NavigateToRootPageCommand}" />
       </Core:DataTriggerBehavior>
    </Interactivity:Interaction.Behaviors>
</Grid>


Fit mode

If you are using the standard app without XAML customizings, the fit mode feature should work out of the box. However, when using a FlipView for displaying documents in your customized XAMLs, you must make sure to include the following binding to the FlipView's ItemTemplate so that the fit modes are properly applied.

<DataTemplate
   ...
   xmlns:hs="using:UBIK.WinX.HotSpotting.Document">
    ...
    <Grid.Resources>
        ...
        <DataTemplate x:Key="FlipDocItemTemplate">
            ...
            <hs:Document
               ...
               FitMode="{Binding DocumentViewModel.FitMode}" />
        </DataTemplate>
    </Grid.Resources>
</DataTemplate>

Content creation

To directly create an object on a child of the current object, you can define a Button as follows. The method "Item.IsTypeCreationAllowed" used in the expression gets the uid of the type that should be created below a child, if a child does not allow the creation of that type underneath it, the child will be hidden in the selection dialog. To actually create the object, the "CreateChildItemCommand" needs to be passed a KeyValueList with two parameters: The Parent-key is the ContentViewModel of the child underneath the object should be created, the Type-key is the type of object which should be created--this should match the uid passed to the "Item.IsTypeCreationAllowed" method.

<x:String x:Key="PlantMap">Item.IsTypeCreationAllowed(&quot;21fc990a-d064-4bee-8d48-3293351f827a&quot;)</x:String>
<cv:ListCollectionView x:Key="PlantMapView" Expression="{StaticResource PlantMap}" ItemsSource="{Binding Children.Items}" />

<AppBarButton>
<AppBarButton.Flyout>
  <Flyout Placement="Full">
        <ListView ItemsSource="{Binding Source={StaticResource PlantMapView}}">
          <ListView.ItemTemplate>
                <DataTemplate>
                <Button Content="{Binding Header}" Command="{Binding CreateChildItemCommand}" x:Name="CreateButton" Tag="{Binding}">
                        <Button.CommandParameter>
                          <uc:KeyValueList>
                                <uc:KeyValueParameter Key="Parent" Value="{Binding Tag, ElementName=CreateButton}"/>
                                <uc:KeyValueParameter Key="Type" Value="21fc990a-d064-4bee-8d48-3293351f827a"/>
                          </uc:KeyValueList>
                        </Button.CommandParameter>
                  </Button>
                </DataTemplate>
          </ListView.ItemTemplate>
        </ListView>
  </Flyout>
</AppBarButton.Flyout>
</AppBarButton>

Disable FilloutCriteria

To enable/disable the automatic filtering of a query based on the ParentObject, there is the possibility to specify EnableFillOutCriteria--if it is not set, it defaults to false. Additionaly "SkipDialog" can be set to true, to not display a dialog.

<Grid x:Name="selectionGrid" Tag="{Binding MetaUID}">
  <Interactivity:Interaction.Behaviors>
         <Core:EventTriggerBehavior EventName="Tapped">
                            <Core:InvokeCommandAction Command="{Binding ElementName=ChildAreaGrid, Path=DataContext.AddTemplatableDataCommand}" >
                                <Core:InvokeCommandAction.CommandParameter>
                                    <uc:KeyValueList>
                                        <uc:KeyValueParameter Key="Uid" Value="{Binding Tag,ElementName=selectionGrid}"/>
                                        <uc:KeyValueParameter Key="EnableFillOutCriteria" Value="false"/>
                                        <uc:KeyValueParameter Key="SkipDialog" Value="false"/>
                                    </uc:KeyValueList>
                                </Core:InvokeCommandAction.CommandParameter>
                            </Core:InvokeCommandAction>
         </Core:EventTriggerBehavior>
  </Interactivity:Interaction.Behaviors>
</Grid>


Hotspotting

The hotspotting command is used for hotspotting as well as for annotating, to configure the button for hotspotting, the commandparameter "Mode" should be set to "HotSpotting", for annotating the "Mode" should be "Annotate". The parameter commit is optional, if set to true, the changes get automatically persisted when leaving the editing mode.

<AppBarToggleButton
                IsChecked="{Binding EditingAnnotation, Mode=TwoWay}"
                IsEnabled="{Binding IsAnnotatable}"
                Command="{Binding HotSpottingCommand}">
                <AppBarToggleButton.CommandParameter>
                        <uc:KeyValueList>
                                <uc:KeyValueParameter Key="Mode" Value="Annotate"/>
                                <uc:KeyValueParameter Key="Commit" Value="true"/>
                        </uc:KeyValueList>
                </AppBarToggleButton.CommandParameter>
</AppBarToggleButton>

Remember Scroll Position of ListView

The UBIK-Client does include a function to remember the position in a list (ListView) when navigating away from it. This function is only available when the list (ListView) has a unique name as a property (x:name). When browsing back to the previously visited list UBIK scrolls back to the last position. The function does not save scroll positions over different sessions. Implementing the function to remember the scroll position in a ListView one has to consider that the list elements (Children) could depend on a other UI-element. If the list elements do depend on a other UI-elemente, this element has to be created above the ListView in the XAML.

<DataTemplate xmlns:behaviors="using:UBIK.WinX.Behaviors">
  ...
     <ListView>
        <Interactivity:Interaction.Behaviors>
           <behaviors:FirstVisibleItemPersistenceBehavior FirstVisibleItems="{Binding ScrollItems}" />
        </Interactivity:Interaction.Behaviors>
     </ListView>
</DataTemplate>

MultiBinding

Very often we want to display some UI elements (e.g. a Grid) depending on whether multiple criteria are met. It's much easier to achieve this by using a MultiBindingBehavior like the following.

<DataTemplate
   xmlns:Interactivity="using:Microsoft.Xaml.Interactivity"
   xmlns:behaviors="using:UBIK.WinX.Behaviors">
    ...
    <Grid>
        <Interactivity:Interaction.Behaviors>
            <behaviors:MultiBindingBehavior Converter="{StaticResource VisLogicAndConverter}" PropertyName="Visibility">
                <behaviors:MultiBindingItem Value="{Binding MassEditViewModel, Converter={StaticResource NullToVisConverter}}" />
                <behaviors:MultiBindingItem Value="{Binding Documents.Items.Count, Converter={StaticResource ItemCountToVisConverter}}" />
            </behaviors:MultiBindingBehavior>
        </Interactivity:Interaction.Behaviors>
    </Grid>
</DataTemplate>

The behavior makes sure the container Grid is set to Visibile only if the mass editing mode is not turned on (MassEditViewModel is null) and the context object has child document(s) (Documents.Items.Count is greater than 0. You can combine any number of binding results (MultiBindingItem) using the VisLogicAndConverter (the name should be self explanatory).

InvokeOnItemsCommand

Available on all ListViewModels, this command allows executing a specified command on a collection of list items. It can be used in combination with features such as mass editing and expression based collection filtering. Examples for both combinations are provided below.

IC Attention.pngThe command specified through the "Command" parameter is executed on list items and, therefore, must be available in the list item contexts (view models). If in doubt, the developer mode can be used to inspect if a command is available in a certain context.
IC Hint square.pngParameter "Command" and "SelectedItemsOnly" are specific to the InvokeOnItemsCommand. What other parameters to define or whether to define them at all depends on the type of command to be executed on the items.

Invoke on selected items

This example demonstrates how you can use the mass editing feature to select certain objects from the child list and then execute the SetPropertyValueAndValidateCommand for those selected.

  • The example code assumes that the child objects have an editable property called "VALUE" and tries to set 50 as their value;
  • You should insert the following code snippet into the default UBIKChildArea template;
  • If the parameter "SelectedItemsOnly" is missed or set to "False", the command will be executed on all child items;
  • To enable selection, click on the "Mass Edit" button below the property list.


  <AppBarButton
     xmlns:example="using:UBIK.WinX.Controls"
     Command="{Binding Children.InvokeOnItemsCommand}"
     Icon="AllApps"
     Label="Set to 50%"
     Style="{ThemeResource UBIKActionAppBarButton}">
      <AppBarButton.CommandParameter>
          <example:KeyValueList>
              <example:KeyValueParameter Key="Command" Value="SetPropertyValueAndValidateCommand" />
              <example:KeyValueParameter Key="SelectedItemsOnly" Value="True" />
              <example:KeyValueParameter Key="PropertyName" Value="VALUE" />
              <example:KeyValueParameter Key="PropertyValue" Value="50" />
          </example:KeyValueList>
      </AppBarButton.CommandParameter>
  </AppBarButton>

Invoke on filtered results

First, you need to setup a filtered list (ListCollectionView) in the Resources section of a UI element (e.g. Grid).

  • This list is only available/visible within that UI element (the Grid in this case);
  • The ItemsSource uses Children.Items. Use the developer mode if necessary to find out if this is available where you intend to define the list;
  • The example expression filters for any items that don't contain the text "EXAMPLE" in their Title texts. You can filter differently by altering the expression.


    <Grid xmlns:CV="using:UBIK.WinX.UI.CollectionView">
        <Grid.Resources>
            <CV:ListCollectionView
               x:Key="Filtered"
               Expression="!Item.Title.Contains(&quot;EXAMPLE&quot;)"
               ItemsSource="{Binding Children.Items}" />
        </Grid.Resources>
       ...
    </Grid>


With the filtered list configured, you can then insert the following code snippet to execute the SetPropertyValueAndValidateCommand for the filtered result items.

  • The example code assumes that the child objects have an editable property called "VALUE" and tries to set 50 as their value;
  • The "Filtered" refers to the ListCollectionView configured above.


  <AppBarButton
     xmlns:example="using:UBIK.WinX.Controls"
     Command="{Binding Source={StaticResource Filtered}, Path=ListViewModel.InvokeOnItemsCommand}"
     Icon="AllApps"
     Label="Set to 50"
     Style="{ThemeResource UBIKActionAppBarButton}">
      <AppBarButton.CommandParameter>
          <example:KeyValueList>
              <example:KeyValueParameter Key="Command" Value="SetPropertyValueAndValidateCommand" />
              <example:KeyValueParameter Key="PropertyName" Value="VALUE" />
              <example:KeyValueParameter Key="PropertyValue" Value="50" />
          </example:KeyValueList>
      </AppBarButton.CommandParameter>
  </AppBarButton>


Performance related

FlipView

When using the FlipView control in your XAML code, it's better to enable UI virtualization. The difference in performance gets more obvious as the number of items in the FlipView increases. Here's how to enable it.

<FlipView
   ...
   VirtualizingStackPanel.VirtualizationMode="Standard">
    <Flipview.ItemsPanel>
        <ItemsPanelTemplate>
            <VirtualizingStackPanel Orientation="Horizontal" />
        </ItemsPanelTemplate>
    </Flipview.ItemsPanel>
</FlipView>


VirtualizingStackPanel.VirtualizationMode offers two possibilities: Standard & Recycling. In case you are interested, here are their differences.