Blog

posts by Fons Sonnemans

XAML StandardUICommand.Kind

0 Comments
By Fons Sonnemans, 27-nov-2018

I'm in the process of learning the new Windows SDK Preview. This SDK has made it possible to set the Kind property of a StandardUICommand. The StandardUICommand was added in the 1809 SDK (17763) but the StandardUICommandKind property was readonly which made it impossible to use from XAML. You could only set the Kind using a constructor parameter in code. Now you can define the StandardUICommand in XAML inside your AppBarButton, MenuFlyoutItem and MenuBarItem.

Demo

I have created a small demo page with a CommandBar and a DropDownButton (also new in the 1809 SDK). The CommandBar has 2 AppBarButton controls (Undo & Redo). The Command property of these buttons are set to a StandardUICommand with the Kind values 'Undo' and 'Redo'. The DropDownButton has a flyout menu with 3 FlyoutMenuItems in it. The Commands of these items are also set to a StandardUICommands. The StandardUICommand will set the Icon, Label/Text, Tooltip and KeyboardAccelerator of the button/item.

The possible value for Kind are:

  • None
  • Cut
  • Copy
  • Paste
  • SelectAll
  • Delete
  • Share
  • Save
  • Open
  • Close
  • Pause
  • Play
  • Stop
  • Forward
  • Backward
  • Undo
  • Redo

The XAML of the page

<Page x:Class="App3.MainPage"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:local="using:App3"
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
      mc:Ignorable="d"
      Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="1*" />
        </Grid.RowDefinitions>
        <CommandBar DefaultLabelPosition="Right">
            <AppBarButton>
                <AppBarButton.Command>
                    <StandardUICommand Kind="Undo" />
                </AppBarButton.Command>
            </AppBarButton>
            <AppBarButton>
                <AppBarButton.Command>
                    <StandardUICommand Kind="Redo" />
                </AppBarButton.Command>
            </AppBarButton>
        </CommandBar>


        <DropDownButton Content="File" Margin="8" 
                                      Grid.Row="1" VerticalAlignment="Top">
            <DropDownButton.Flyout>
                <MenuFlyout>
                    <MenuFlyoutItem>
                        <MenuFlyoutItem.Command>
                            <StandardUICommand Kind="Open" />
                        </MenuFlyoutItem.Command>
                    </MenuFlyoutItem>
                    <MenuFlyoutItem>
                        <MenuFlyoutItem.Command>
                            <StandardUICommand Kind="Save" />
                        </MenuFlyoutItem.Command>
                    </MenuFlyoutItem>
                    <MenuFlyoutItem>
                        <MenuFlyoutItem.Command>
                            <StandardUICommand Kind="Delete" />
                        </MenuFlyoutItem.Command>
                    </MenuFlyoutItem>
                </MenuFlyout>
            </DropDownButton.Flyout>
        </DropDownButton>
    </Grid>
</Page>

Closure

It is great that the Kind property can now be set. This saves me a lot of work setting the Icon, Label/Text, Tooltip and KeyboardAccelerator of AppBarButtons and MenuItems manually.

Fons

READ MORE

XAML ComboBox IsEditable and Text Properties

0 Comments
By Fons Sonnemans, 12-sep-2018

I'm in the process of learning the new Windows SDK Preview. Today's subject is the new IsEditable and Text properties of the ComboBox control.

Demo

I have created a small demo page with two editable ComboBox controls. The first one is filled with some ComboBoxItems, it's Text is set to 'Hello World !' which not exists as ComboBoxItem. The second is databound to some sample data (Products). There are three TextBlock controls below each ComboBox. The Text of the first TextBox is databound to the SelectedIndex of the ComboBox. The second to the SelectedItem using a custom function in the MainPage. The third to the Text property of the ComboBox.

As you expect you can now edit the text inside the ComboBox. The SelectedIndex is set to -1 once you type a custom text. The third TextBox which is databound to the Text is updated on a TextChanged of the Text, not only on lostfocus (like SelectedItem).

<StackPanel HorizontalAlignment="Center"
            VerticalAlignment="Center"
            Width="300">
    
    <ComboBox x:Name="comboBox1"
              Header="ComboBox 1"
              IsEditable="True"
              Text="Hello World !"
              HorizontalAlignment="Stretch">
        <ComboBoxItem Content="A" />
        <ComboBoxItem Content="B" />
        <ComboBoxItem Content="C" />
        <ComboBoxItem Content="D" />
        <ComboBoxItem Content="E" />
    </ComboBox>
    <TextBlock Text="{x:Bind comboBox1.SelectedIndex, Mode=OneWay}" />
    <TextBlock Text="{x:Bind SelectedComboBoxItem(comboBox1.SelectedItem), Mode=OneWay}" />
    <TextBlock Text="{x:Bind comboBox1.Text, Mode=OneWay}" />

    <ComboBox x:Name="comboBox2"
              Margin="0,20,0,0"
              Header="ComboBox 2"
              IsEditable="True"
              ItemsSource="{x:Bind SampleData}"
              DisplayMemberPath="ProductName"
              HorizontalAlignment="Stretch"/>
    <TextBlock Text="{x:Bind comboBox2.SelectedIndex, Mode=OneWay}" />
    <TextBlock Text="{x:Bind SelectedProduct(comboBox2.SelectedItem), Mode=OneWay}" />
    <TextBlock Text="{x:Bind comboBox2.Text, Mode=OneWay}" />

</StackPanel>

In the MainPage class there is a SampleData property which contains some Product objects. It also contains the methods SelectedComboBoxItem() and SelectedProduct(). These methods are used for databinding the SelectedItem to the Text of the TextBlock. The SelectedComboBoxItem() shows the Content of the selected ComboBoxItem or the entered text. The SelectedProduct() shows the ProductName of the selected product or the entered text.

public sealed partial class MainPage : Page {

    internal List<Product> SampleData { get; } = new List<Product>() {
        new Product("Car", 20000),
        new Product("Bike", 800),
        new Product("Laptop", 400),
    };

    public MainPage() {
        this.InitializeComponent();
    }

    private string SelectedComboBoxItem(object selectedItem) {
        if (selectedItem is ComboBoxItem p) {
            return p.Content?.ToString();
        }
        return selectedItem?.ToString();
    }

    private string SelectedProduct(object selectedItem) {
        if (selectedItem is Product p) {
            return p.ProductName;
        }
        return selectedItem?.ToString();
    }
}

internal class Product {

    public string ProductName { get; set; }
    public decimal UnitPrice { get; set; }
    public Product(string productName, decimal unitPrice) {
        this.ProductName = productName;
        this.UnitPrice = unitPrice;
    }

}

TextBoxStyle

The ComboBox has also a new TextBoxStyle property. You can use it to define a custom style for the TextBox inside an editable ComboBox. I hoped that I could use it to remove the spell checking but unfortunately that doesn't work.

<ComboBox x:Name="comboBox1"
          Header="ComboBox 1"
          IsEditable="True"
          Text="Hello World !"
          HorizontalAlignment="Stretch">
    <ComboBox.TextBoxStyle>
        <Style TargetType="TextBox"
                BasedOn="{StaticResource ComboBoxTextBoxStyle}">
            <Setter Property="IsSpellCheckEnabled"
                    Value="False" />
        </Style>
    </ComboBox.TextBoxStyle>
    <ComboBoxItem Content="A" />
    <ComboBoxItem Content="B" />
    <ComboBoxItem Content="C" />
    <ComboBoxItem Content="D" />
    <ComboBoxItem Content="E" />
</ComboBox>

As you can see there is still a red squiggly line if you make a typo although I have set IsSpellCheckEnabled to False.

Closure

The IsEditable is a small but important new feature. WPF already had edtable ComboBoxes. In Windows Forms the ComboBox has a DropDownStyle which you can set to DropDown. Now you can have the same thing in UWP.

Fons

READ MORE

XAML new Description property for Input-Box controls

0 Comments
By Fons Sonnemans, 04-sep-2018

I'm in the process of learning the new Windows SDK Preview. Today's subject is the new Description property which is added to the TextBox, PasswordBox, RichEditBox, AutoSuggestBox and ComboBox input controls.

Demo

I have created a small demo page with all the input controls which have a Description. The Description is shown below the control in a gray color.

The XAML is very simple. The Description is of the type object. The PasswordBox in this example has a TextBlock with two Runs (one Bold) in it as the Description. It also supports DataBinding. The Description of the ComboBox is databound to the SelectedIndex.

<StackPanel HorizontalAlignment="Center"
            VerticalAlignment="Center"
            Width="300"
            Spacing="8">
    <TextBox Header="TextBox"
                Text="Hello World!"
                Description="TextBox Description" />
    <PasswordBox Header="PasswordBox"
                    Password="secret">
        <PasswordBox.Description>
            <TextBlock>
        <Run Text="PasswordBox"
                FontWeight="Bold" />
        <Run Text="Description" />
            </TextBlock>
        </PasswordBox.Description>
    </PasswordBox>
    <RichEditBox Header="RichEditBox"
                    Description="RichEditBox Description" />
    <AutoSuggestBox Header="AutoSuggestBox"
                    Description="AutoSuggestBox Description" />
    <ComboBox Header="ComboBox"
                x:Name="comboBoxDemo"
                Description="{x:Bind comboBoxDemo.SelectedIndex, Mode=OneWay}"
                SelectedIndex="2"
                HorizontalAlignment="Stretch">
        <ComboBoxItem Content="A" />
        <ComboBoxItem Content="B" />
        <ComboBoxItem Content="C" />
        <ComboBoxItem Content="D" />
    </ComboBox>
</StackPanel>

Description color

You can easily change the color of the Description by defining a Brush Resource with the Key SystemControlDescriptionTextForegroundBrush. I added one to the StackPanel with the color Red.

<StackPanel HorizontalAlignment="Center"
            VerticalAlignment="Center"
            Width="300"
            Spacing="8">
    <StackPanel.Resources>
        <SolidColorBrush x:Key="SystemControlDescriptionTextForegroundBrush"
                         Color="Red" />
    </StackPanel.Resources>
    <TextBox Header="TextBox"
             Text="Hello World!"
             Description="TextBox Description" />

The Descriptions are now Red.

Closure

The Description is a small but nice new addition. Nothing you couldn't do yourself with a normal TextBlock but this is easier.

Fons

READ MORE

XAML MenuBar

0 Comments
By Fons Sonnemans, 27-aug-2018

I'm in the process of learning the new Windows SDK Preview. Today's subject is the new MenuBar control. You can now create menus with submenus in your UWP apps. Before you could use the Menu control from the Windows Community Toolkit.

Demo

I have created a small demo page which displays the following menu.

The XAML contains the new MenuBar control which contains two MenuBarItem elements. Each MenuBarItem contains MenuFlyoutItem, MenuFlyoutSeparator or MenuFlyoutSubItem elements. 

<Page x:Class="MenuBarDemo.MainPage"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:local="using:MenuBarDemo"
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
      mc:Ignorable="d"
      RequestedTheme="Light"
      Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <Grid>
        <MenuBar VerticalAlignment="Top">
            <MenuBarItem Title="File"
                         AccessKey="F">
                <MenuFlyoutItem Text="Open"
                                Icon="OpenFile"
                                Click="MenuFlyoutOpen_Click">
                    <MenuFlyoutItem.KeyboardAccelerators>
                        <KeyboardAccelerator Key="O"
                                             Modifiers="Control" />
                    </MenuFlyoutItem.KeyboardAccelerators>
                </MenuFlyoutItem>
                <MenuFlyoutSeparator />
                <MenuFlyoutItem Text="Save"
                                Icon="Save"
                                Command="{x:Bind SaveCommand}">
                    <MenuFlyoutItem.KeyboardAccelerators>
                        <KeyboardAccelerator Key="S"
                                             Modifiers="Control" />
                    </MenuFlyoutItem.KeyboardAccelerators>
                </MenuFlyoutItem>
                <MenuFlyoutSubItem Text="Demo">
                    <MenuFlyoutItem Text="A"
                                    Command="{x:Bind DemoCommand}"
                                    CommandParameter="{Binding Text, 
                                        RelativeSource={RelativeSource Self}, 
                                        Mode=OneTime}" />
                    <MenuFlyoutItem Text="B"
                                    Command="{x:Bind DemoCommand}"
                                    CommandParameter="{Binding Text, 
                                        RelativeSource={RelativeSource Self}, 
                                        Mode=OneTime}" />
                    <MenuFlyoutItem Text="C"
                                    Command="{x:Bind DemoCommand}"
                                    CommandParameter="{Binding Text, 
                                        RelativeSource={RelativeSource Self}, 
                                        Mode=OneTime}" />
                </MenuFlyoutSubItem>
            </MenuBarItem>
            <MenuBarItem Title="Help"
                         AccessKey="H">
                <MenuFlyoutItem Text="About"
                                Click="MenuFlyoutItemAbout_Click" />
            </MenuBarItem>
        </MenuBar>
    </Grid>
</Page>

The C# code with the Click handlers and the Commands looks like this.

public sealed partial class MainPage : Page {
    public ICommand SaveCommand { get; }
    public ICommand DemoCommand { get; }

    public MainPage() {
        this.InitializeComponent();
        this.SaveCommand = new RelayCommand(OnSave);
        this.DemoCommand = new RelayCommand<string>(OnDemo);
    }

    private async void OnDemo(string text) {
        await new MessageDialog($"Demo {text}").ShowAsync();
    }

    private async void OnSave() {
        await new MessageDialog("Save").ShowAsync();
    }

    private async void MenuFlyoutOpen_Click(object sender, RoutedEventArgs e) {
        await new MessageDialog("Open").ShowAsync();
    }

    private async void MenuFlyoutItemAbout_Click(object sender, RoutedEventArgs e) {
        await new MessageDialog("About").ShowAsync();
    }
}

AccessKey and KeyboardAccelerators

The MenuBarItem have an AccessKey assigned. So if you press the Alt key on your keyboard you get the following output. Alt+F will open the File menu, Alt+H will open the Help menu.

The Open and Save MenuFlyoutItem have a KeyboardAccelerator. Ctrl+O will execute the Open menu, Ctrl+S will execute the Save menu.

Closure

The MenuBar is a great new addition. It will help me writing nice LOB applications. You can download this sample project from this GitHub repository.

Fons

READ MORE

XAML Control.CornerRadius

0 Comments
By Fons Sonnemans, 22-aug-2018

I'm in the process of learning the new Windows SDK Preview. Yesterday I wrote a blog about the new AppBarElementContainer. Today's subject is the new CornerRadius property of the Control class. Until now most input controls where rectangular. Now they can have rounded corners.

Demo

I have created a small demo page in which I placed a few input controls in a StackPanel. For all controls I have set the CornerRadius to 8.

<Page x:Class="App2.MainPage"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:local="using:App2"
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
      mc:Ignorable="d"
      Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <Grid>
        <StackPanel HorizontalAlignment="Center"
                    VerticalAlignment="Center"
                    Spacing="8">
            <Button Content="Hello"
                    HorizontalAlignment="Stretch"
                    CornerRadius="8"
                    FontSize="20"
                    Margin="0,0,0,0"
                    VerticalAlignment="Center" />
            <TextBox Text="World"
                     CornerRadius="8" />
            <PasswordBox CornerRadius="8" />
            <ComboBox SelectedIndex="0"
                      HorizontalAlignment="Stretch"
                      CornerRadius="8">
                <ComboBoxItem Content="A" />
                <ComboBoxItem Content="B" />
                <ComboBoxItem Content="C" />
            </ComboBox>
            <TimePicker CornerRadius="8" />
        </StackPanel>
    </Grid>
</Page>

this results in the following output. The Button, TextBox, PasswordBox and ComboBox have rounded corners. Unfortunately the TimePicker does not. I hope it will be fixed before it released.

The focus rectangle of a Button is also still rectangular. I expect that will stay like this.

Closure

The CornerRadius is a nice new feature. I will maybe use it a one of my games. Probably not in a LOB app.

Fons

READ MORE

XAML AppBarElementContainer

0 Comments
By Fons Sonnemans, 21-aug-2018

I have been playing with the new Windows Insider Preview SDK build 17733. It contains a new control named AppBarElementContainer. This control allows you to add other controls then the AppBarButton, AppBarSeparator and AppBarToggleButton to a CommandBar or the "depricated" AppBar.

Demo

AppBarElementContainer is a container control so you can place any control (which fit inside the limited space) into it. In the next example I used for this demo a ComboBox and a Slider.

<Page x:Class="App1.MainPage"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:local="using:App1"
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
      mc:Ignorable="d"
      Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

    <Grid>
        <CommandBar VerticalAlignment="Top">
            <CommandBar.PrimaryCommands>
                <AppBarButton Icon="Save"
                              Label="Save" />
                <AppBarElementContainer>
                    <ComboBox Width="200"
                              Margin="0,4,0,0"
                              SelectedIndex="0">
                        <ComboBoxItem Content="A" />
                        <ComboBoxItem Content="B" />
                        <ComboBoxItem Content="C" />
                    </ComboBox>
                </AppBarElementContainer>
                <AppBarElementContainer>
                    <Slider Width="200"
                            Margin="4" />
                </AppBarElementContainer>
                <AppBarButton Icon="Delete"
                              Label="Delete"
                              LabelPosition="Collapsed" />
            </CommandBar.PrimaryCommands>
            <CommandBar.SecondaryCommands>
                <AppBarButton Icon="Undo"
                              Label="Undo" />
                <AppBarButton Icon="Redo"
                              Label="Redo" />
            </CommandBar.SecondaryCommands>
            <CommandBar.Content>
                <TextBlock Text="Hello World"
                           Margin="8" />
            </CommandBar.Content>
        </CommandBar>
    </Grid>
</Page>

The app looks like this

When there is not enough space to show all primary commands they are automatically moved to the secondary commands. This also works for the AppBarElementContainer. If you want to add multiple controls to a CommandBar, don't put them in a horizontal StackPanel inside a AppBarElementContainer. Place each control in it's own AppBarElementContainer. Just like I did.

Closure

I really like the new AppBarElementContainer. I will use it a lot in my new apps running on Windows 10 (1809).

Fons

READ MORE

XAML Repeater Control

1 Comments
By Fons Sonnemans, 02-aug-2018

On Monday, the Windows Developer team announced the preview release of the Windows UI Library (WinUI). The WinUI NuGet packages contain new and popular UWP XAML controls and features which are backward-compatible on a range of Windows 10 versions, from the latest insider flights down to the Anniversary Update (1607). Windows developers will no longer need to wait for their users to adopt the latest Windows 10 release in order to provide some of the rich features provided by these packages.

Read the get started article or use this quick step-by-step guide.

Microsoft also published a Sample app on GitHub named XamlUiBasics. The dev branch already contains demos of the new SplitButton, ToggleSplitButton. DropDownButton and the Repeater control. There is not much WinUI documentation available yet so we have to figure out how it works using the sample code.

Repeater Control

The Repeater control is similar to the ItemsControl we already have. You set or databind the ItemsSource to a collection. You also have to specify the Layout and ViewGenerator properties.

The Layout property can be a StackLayout, FlowLayout, GridLayout or your custom layout. Layouts replace the ItemsPanel property of the ItemsControl. Layouts support virtualization which is very important if you have large datasets and the repeater is in a ScrollViewer. Only the visible items are painted which improves the performance a lot.

The ViewGenerator is a RecyclingViewGenerator which has a RecyclePool property and one or more (Data)Templates. The DataTemplate is used to visualize each item in the Repeater. Similar to the ItemTemplate property of the ItemsControl. The RecyclingViewGenerator also has a SelectTemplateKey event which you can use if you have multiple DataTemplates. The eventhandler should return the name (x:Name) of the DataTemplate to be used to visualize the item. This replaces the ItemTemplateSelector of the ItemsControl which if used would break the virtualization.

Demo Project

I have created a small sample project and published it on GitHub. My demo is heavily inspired by the RepeaterPage.xaml and RepeaterPage.xaml.cs from the XamlUIBasics sample. It uses the latest 17134 (1803) SDK so there is no need to upgrade to an Insiders Preview SDK.

The MainPage contains a Repeater inside a ScrollViewer

<ScrollViewer>
    <controls:Repeater x:Name="repeater"
                       ItemsSource="{x:Bind SampleDataItems}"
                       Layout="{StaticResource VerticalStackLayout}"
                       ViewGenerator="{StaticResource HorizontalElementGenerator}" />
</ScrollViewer>

This creates the following output in which each item is show in a vertical stack.

Repeater using StackLayout

The RecyclePool, StackLayout and RecyclingViewGenerator are resources inside the Page.

<controls:RecyclePool x:Key="RecyclePool1" />

<controls:StackLayout x:Name="VerticalStackLayout"
                      Orientation="Vertical"
                      ItemSpacing="8" />

<controls:RecyclingViewGenerator x:Key="HorizontalElementGenerator"
                                 RecyclePool="{StaticResource RecyclePool1}">
    <DataTemplate x:Key="hItem"
                  x:DataType="l:SampleData">
        <Grid Background="{ThemeResource SystemChromeLowColor}"
              HorizontalAlignment="Left"
              Width="{x:Bind Max}">
            <Rectangle Fill="{ThemeResource SystemAccentColor}"
                       Width="{x:Bind Value}"
                       Height="24"
                       HorizontalAlignment="Left" />
            <TextBlock Text="{x:Bind Value}"
                       VerticalAlignment="Center"
                       HorizontalAlignment="Left"
                       Margin="4,0" />
        </Grid>
    </DataTemplate>
</controls:RecyclingViewGenerator>

 The radio buttons can be used to switch to a different Layout and ViewGenerator.

FlowLayout

In the FlowLayout all items are shown next to each other and wrap to the next line if necessary. 

Repeater using FlowLayout

The RecyclePool, FlowLayout and RecyclingViewGenerator are resources inside the Page.

<controls:RecyclePool x:Key="RecyclePool2" />
        
<DataTemplate x:Key="FlowBarTemplate"
              x:DataType="l:SampleData">
    <Border Background="{ThemeResource SystemAccentColor}"
            Width="{x:Bind Value}"
            Height="24"
            VerticalAlignment="Top">
        <TextBlock Text="{x:Bind Value}"
                    VerticalAlignment="Center"
                    HorizontalAlignment="Center" />
    </Border>
</DataTemplate>

<controls:FlowLayout x:Name="FlowLayout"
                     Orientation="Horizontal"
                     MinItemSpacing="8"
                     LineSpacing="8" />

<controls:RecyclingViewGenerator x:Key="FlowElementGenerator"
                                 RecyclePool="{StaticResource RecyclePool2}">
    <StaticResource x:Key="fItem"
                    ResourceKey="FlowBarTemplate" />
</controls:RecyclingViewGenerator>

GridLayout

In the GridLayout all items are shown in a GridView. Rectanges are shown for values below 200 others are Ellipses.

Repeater using GridLayout

The RecyclePool, GridLayout and RecyclingViewGenerator are resources inside the Page.

<controls:RecyclePool x:Key="RecyclePool3" />

<controls:GridLayout x:Name="GridLayout"
                    MinItemSpacing="8"
                    LineSpacing="8" />

<controls:RecyclingViewGenerator x:Key="GridViewGenerator"
                                 RecyclePool="{StaticResource RecyclePool3}"
                                 SelectTemplateKey="GridViewGenerator_SelectTemplateKey">
    <DataTemplate x:Name="EllipseItem"
                    x:DataType="l:SampleData">
        <Grid>
            <Ellipse Fill="{ThemeResource SystemChromeLowColor}"
                     Height="{x:Bind MaxSize}"
                     Width="{x:Bind MaxSize}"
                     VerticalAlignment="Center"
                     HorizontalAlignment="Center" />
            <Ellipse Fill="{ThemeResource SystemAccentColor}"
                     Height="{x:Bind Size}"
                     Width="{x:Bind Size}"
                     VerticalAlignment="Center"
                     HorizontalAlignment="Center" />
            <TextBlock Text="{x:Bind Value}"
                       VerticalAlignment="Center"
                       HorizontalAlignment="Center" />
        </Grid>
    </DataTemplate>
    <DataTemplate x:Name="RectangleItem"
                  x:DataType="l:SampleData">
        <Grid>
            <Rectangle Fill="{ThemeResource SystemChromeLowColor}"
                       Height="{x:Bind MaxSize}"
                       Width="{x:Bind MaxSize}"
                       VerticalAlignment="Center"
                       HorizontalAlignment="Center" />
            <Rectangle Fill="{ThemeResource SystemAccentColor}"
                       Height="{x:Bind Size}"
                       Width="{x:Bind Size}"
                       VerticalAlignment="Center"
                       HorizontalAlignment="Center" />
            <TextBlock Text="{x:Bind Value}"
                       VerticalAlignment="Center"
                       HorizontalAlignment="Center" />
        </Grid>
    </DataTemplate>
</controls:RecyclingViewGenerator>

The SelectTemplateKey eventhandler is used select the EllipseItem DataTemplate or the RectangeItem DataTemplates which are defined inside the GridViewGenerator RecyclingViewGenerator. These DataTemplates have a x:Name and not a x:Key like the one in the HorizontalElementGenerator and FlowElementGenerator.

private void GridViewGenerator_SelectTemplateKey(RecyclingViewGenerator sender, Microsoft.UI.Xaml.Controls.SelectTemplateEventArgs args) {
    args.TemplateKey = ((SampleData)args.DataContext).Value >= 200 ? nameof(this.EllipseItem) : nameof(this.RectangleItem);
}

Closure

The new Repeater control looks very useful. There are still a few things which I'm missing or unknown.

  • How do you set ItemContainerTransitions? I excpect I will have to use Composition Animations.
  • How do you use the RecyclePool correctly? I created three pools, one for each RecyclingViewGenerator.
  • How do you implement a ScrollIntoView logic. It looks like you need to place the Repeater inside an ElementTracker for that. ElementTracker enables Repeater to coordinate with ScrollViewer down-level.

We will have to wait for more docs.

Fons

READ MORE

Xaml Diff - Generate Visual State Setters

0 Comments
By Fons Sonnemans, 23-mei-2018

I have used Visual States in XAML a lot. It all started in Silverlight, now I use it in my UWP apps. I often generate them in Blend for Visual Studio using recording. Blend used to generate Storyboards but with the current version generates Setters (UWP only). This is better, makes them easier to write and read. It is a bit buggy but I expect (hope) it will be fixed soon. A lot of developers are mistakenly not using Blend. They only use Visual Studio which doesn't support the great States feature of Blend. Writing the Visual States yourself can then be a lot of work.

To help those developers I have created an app called Xaml Diff. It generates the Visual State Setters using a diff analysis of your named elements in your XAML. It is free and you can download it from the Microsoft Store.

How to use

You start by designing or writing two versions of an XAML Page or UserControl. For this demo I have created a Page with a ToggleSwitch and two Rectangles. In the first version of the page the blue Rectangle is in Column 0 of my Grid and the red Rectangle in Column 1. Both Rectangles have an x:Name to allow them to be used in the Target property of the Setter.

<Page x:Class="App27.MainPage"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:local="using:App27"
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
      mc:Ignorable="d"
      Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

    <Grid Padding="12" ColumnSpacing="12" >
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <Grid.ChildrenTransitions>
            <TransitionCollection>
                <ReorderThemeTransition />
            </TransitionCollection>
        </Grid.ChildrenTransitions>
        
        <ToggleSwitch Header="State1"
                      x:Name="toggleState"
                      VerticalAlignment="Top" />

        <Rectangle x:Name="rectangleBlue"
                   Grid.Row="1"
                   StrokeThickness="4"
                   Stroke="Black"
                   Fill="Blue" />

        <Rectangle x:Name="rectangleRed"
                   Grid.Row="1"
                   Grid.Column="1"
                   Fill="Red" />

    </Grid>
</Page>

You Copy & Paste the XAML of the first version of the page into the Source textbox of Xaml Diff app. In the second version of my page I swapped the column of both rectangles. I also changed the StrokeThicknes of the blue rectangle to 12. This version will be converted to a Visual State.

<Page x:Class="App27.MainPage"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:local="using:App27"
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
      mc:Ignorable="d"
      Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

    <Grid Padding="12" ColumnSpacing="12" >

        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <Grid.ChildrenTransitions>
            <TransitionCollection>
                <ReorderThemeTransition />
            </TransitionCollection>
        </Grid.ChildrenTransitions>
        
        <ToggleSwitch Header="State1"
                      x:Name="toggleState"
                      VerticalAlignment="Top" />

        <Rectangle x:Name="rectangleBlue"
                   Grid.Row="1"
                   StrokeThickness="12"
                   Stroke="Black"
                   Grid.Column="1"
                   Fill="Blue" />

        <Rectangle x:Name="rectangleRed"
                   Grid.Row="1"
                   Fill="Red" />

    </Grid>
</Page>

You Copy & Paste the XAML of the second version of the page into the Destination textbox of Xaml Diff app. Now you can generate the Visual State Setters using the red Generate button. With the Swap button you can swap the Source & Destination. You can use the Copy button to copy it to the Clipboard.

Using the Settings button, you have the options to include the Source in the Generated XAML. You can also add a VisualState.StateTrigger.

If you copy this generated XAML back into your IDE (Visual Studio or Blend) you only have to write the StateTrigger. In my case I data bind it to the ToggleSwitch.IsOn property, see line 15.

<VisualStateManager.VisualStateGroups>
    <VisualStateGroup x:Name="visualStateGroup1">
        <VisualState x:Name="visualState1">
            <VisualState.Setters>
                <Setter Target="rectangleBlue.(Rectangle.StrokeThickness)"
                        Value="12" />
                <Setter Target="rectangleBlue.(Grid.Column)"
                        Value="1" />
                <Setter Target="rectangleRed.(Grid.Column)"
                        Value="0" />
            </VisualState.Setters>
            <VisualState.StateTriggers>
                <StateTrigger IsActive="{x:Bind toggleState.IsOn, Mode=OneWay}" />
            </VisualState.StateTriggers>
        </VisualState>
    </VisualStateGroup>
</VisualStateManager.VisualStateGroups>

If you run the app and turn the ToggleSwitch on you will see the Rectangles swap column. This is animated because the Grid has a ReorderThemeTransition in the ChildrenTransitions.

Closure

I hope that the Xaml Diff can help you writing your Visual State Setters. It only works for UWP apps although Xamarin Forms 3.0 now has also Visual States support. Unfortunatly the implementation in Xamarin Forms is different. It uses Property and not Target in the Setters. Time/work for XAML Standard.

I'm open for suggestion to make this tool better. Use the Mail, Feedback or Rate buttons in the app.

Fons

READ MORE

HeaderTemplate in XAML

0 Comments
By Fons Sonnemans, 06-apr-2018

In a LOB application it is very common to have headers above an input controls (TextBox, ComboBox, Pickers, etc.) which also indicates that the data is required. This can easily be implemented in a XAML/UWP application using the HeaderTemplate property of the input controls.

Demo

In the following Page I have created a DataTemplate with the key RequiredHeaderTemplate. It contains StackPanel with 2 TextBlocks. The first is used to show the Header value. It is data bound to the Text of the TextBlock. The second is showing the '(required)' text in the accent color and a small font. The input controls in the page all have a header. The required ones also have the HeaderTemplate property set to the static resource RequiredHeaderTemplate.

<Page x:Class="HeaderTemplateDemo.MainPage"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:local="using:HeaderTemplateDemo"
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
      mc:Ignorable="d">
    <Page.Resources>

        <DataTemplate x:Key="RequiredHeaderTemplate">
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="{Binding}" />
                <TextBlock Text="(required)"
                           Foreground="{ThemeResource SystemControlForegroundAccentBrush}"
                           FontSize="12"
                           Margin="4,0,0,1"
                           VerticalAlignment="Bottom" />
            </StackPanel>
        </DataTemplate>

    </Page.Resources>

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <StackPanel Orientation="Vertical"
                    HorizontalAlignment="Center"
                    VerticalAlignment="Center"
                    Width="300">
            <TextBox HeaderTemplate="{StaticResource RequiredHeaderTemplate}"
                     Header="Name"
                     Margin="0,0,0,8"
                     InputScope="PersonalFullName" />
            <TextBox HeaderTemplate="{StaticResource RequiredHeaderTemplate}"
                     Header="Company"
                     Margin="0,0,0,8"
                     InputScope="PersonalFullName" />
            <TextBox Header="Email"
                     Margin="0,0,0,8"
                     InputScope="EmailNameOrAddress" />
            <ComboBox HeaderTemplate="{StaticResource RequiredHeaderTemplate}"
                      Header="Department"
                      HorizontalAlignment="Stretch"
                      Margin="0,0,0,8">
                <ComboBoxItem Content="Sales" />
                <ComboBoxItem Content="Production" />
                <ComboBoxItem Content="Marketing" />
            </ComboBox>
            <CalendarDatePicker Header="Start"
                                HeaderTemplate="{StaticResource RequiredHeaderTemplate}"
                                HorizontalAlignment="Stretch" />
        </StackPanel>
    </Grid>
</Page>

The results in the following output:

HeaderTemplate example

You can move the DataTemplate to a ResourceDictionary which makes it is available for multiple pages.

Closure

You can download the sample code using the download button below. I hope you like it.

Fons

Tags: Apps, XAML, UWP, Windows 10

READ MORE

Conditional XAML and x:Bind

0 Comments
By Fons Sonnemans, 19-mrt-2018

Recently I used Conditional XAML for the first time in one of my UWP apps. I wanted to use a ColorPicker control which is only available in the Fall Creators Update (version 1709, build 16299). The Microsoft documentation explains what Conditional XAML is really well.

Conditional XAML provides a way to use the ApiInformation.IsApiContractPresent method in XAML markup. This lets you set properties and instantiate objects in markup based on the presence of an API without needing to use code behind. It selectively parses elements or attributes to determine whether they will be available at runtime. Conditional statements are evaluated at runtime, and elements qualified with a conditional XAML tag are parsed if they evaluate to true; otherwise, they are ignored.

Conditional XAML is available starting with the Creators Update (version 1703, build 15063). To use conditional XAML, the Minimum Version of your Visual Studio project must be set to build 15063 (Creators Update) or later, and the Target Version be set to a later version than the Minimum. See Version adaptive apps for more info about configuring your Visual Studio project.

While testing my app I noticed that it can be a little tricky when you combine it with {x:Bind}. Let me explain this using a simple demo.

The Problem

To explain the problem, I have created an Universal App which uses the MVVM design pattern. I have a simple Employee model class and MainViewModel class.

class Employee {

    public string Name { get; set; }
    public double Salary { get; set; }

    public Employee(string name, double salary) {
        this.Name = name;
        this.Salary = salary;
    }

}

sealed class MainViewModel  {

    public static MainViewModel Current { get; } = new MainViewModel();

    public Employee Employee { get; } = new Employee("Fons", 2000);

    private MainViewModel() { // Singleton
    }

}

In my Page I use {x:Bind} to databind the Text of the TextBlock controls to the Name and Salary of the Employee from the MainViewModel.

<Page x:Class="ConditionalXamlAndCompiledBindingDemo.Views.Pages.MainPage"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:vm="using:ConditionalXamlAndCompiledBindingDemo.ViewModels"
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
      mc:Ignorable="d">

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
          Padding="8">

        <StackPanel>
            <TextBlock Text="{x:Bind vm:MainViewModel.Current.Employee.Name}" />
            <TextBlock Text="{x:Bind vm:MainViewModel.Current.Employee.Salary}" />
        </StackPanel>

    </Grid>
</Page>

I want the StackPanel only to be used on computers running the Fall Creators Update. So, I used Conditional XAML. I declared two conditional XAML namespaces at the top of my page: fallOrHigher and lowerThanFall. I used the IsApiContractPresent(ContractName, VersionNumber) and IsApiContractNotPresent(ContractName, VersionNumber) methods to check for the UniversalApiContract version 5 (Fall Creators Update). The StackPanel is prefixed with the fallOrHigher namespace.

<Page x:Class="ConditionalXamlAndCompiledBindingDemo.Views.Pages.MainPage"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:vm="using:ConditionalXamlAndCompiledBindingDemo.ViewModels"
      xmlns:fallOrHigher="http://schemas.microsoft.com/winfx/2006/xaml/presentation?IsApiContractPresent(Windows.Foundation.UniversalApiContract,5)"
      xmlns:lowerThanFall="http://schemas.microsoft.com/winfx/2006/xaml/presentation?IsApiContractNotPresent(Windows.Foundation.UniversalApiContract,5)"
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
      mc:Ignorable="d">

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
          Padding="8">

        <fallOrHigher:StackPanel>
            <TextBlock Text="{x:Bind vm:MainViewModel.Current.Employee.Name}" />
            <TextBlock Text="{x:Bind vm:MainViewModel.Current.Employee.Salary}" />
        </fallOrHigher:StackPanel>

    </Grid>
</Page>

When I tested the application it seemed to work fine, the StackPanel was visible. But you are tricked. You also have to test it on an older version of Windows (1703). To do/simulate this I changed the IsApiContractPresent() method of the fallOrHigher namespace into an IsApiContractNotPresent(). The application crashed with a NullReferenceException.

The Solution

Examining the exception I learned that it was caused by the generated code of the {x:Bind}.

System.NullReferenceException

The StackTrace shows it occured in the Set_Windows_UI_Xaml_Controls_TextBlock_Text() method which is automatically generated by the {x:Bind}. That's why it is called compiled binding. The solution is easy, don't generate this code. We can do this by adding the fallOrHigher namespace prefix in front of the databound properties of the inner controls of the StackPanel. Or we can add the prefix in front of the all inner controls which use {x:Bind}. This makes the {x:Bind} conditional and solves our problem.

<fallOrHigher:StackPanel>
    <TextBlock fallOrHigher:Text="{x:Bind vm:MainViewModel.Current.Employee.Salary}" />    
    <fallOrHigher:TextBlock Text="{x:Bind vm:MainViewModel.Current.Employee.Name}" />
</fallOrHigher:StackPanel>

When you test your app you won't get the exception and the StackPanel isn't there. Don't forget to change the IsApiContractNotPresent() method of the fallOrHigher namespace back into the IsApiContractPresent().

Closure

Testing Conditional XAML on correct use is very important. It's not difficult but easily overlooked. Conditional XAML is very powerful and I will use it a lot in my apps. The 1803 version of the SDK has some new properties and the TreeView control which I want to start using.

You can download the sample code using the download button below.

Fons

Tags: Apps, XAML, UWP, Windows 10

READ MORE

All postings/content on this blog are provided "AS IS" with no warranties, and confer no rights. All entries in this blog are my opinion and don't necessarily reflect the opinion of my employer or sponsors. The content on this site is licensed under a Creative Commons Attribution By license.