How do I write a custom picker control in UWP? - xaml

I have been fine-combing the web for any guidance, discussions or experience on this and I think I can safely say there’s nothing to be found.
We are developing a set of controls for UWP which we plan to open source and make available for free. One of the controls we are building is a TimeSpanPicker control, which will essentially look and behave like the TimePicker control, but instead of being limited to a time of day (i.e. 24 hour interval) it will allow the user to edit an arbitrary TimeSpan.
From what I have been able to piece together from the visible metadata of the Windows Runtime API, using the built-in TimePicker control for reference, I am realizing the following types of components are involved:
The TimePicker control itself which inherits from Control
The TimePickerFlyout class which inherits from PickerFlyoutBase
The TimePickerFlyoutPresenter control, which inherits from Control
I realize I need to mimic this pattern and write these three components for our picker control, but I can find no information about how these pieces fit together, and from the API surfaces alone I don’t think it’s possible to figure it out.
Specifically, the primary things I’d like to understand are:
How is the TimePickerFlyout incorporated into the TimePicker class? I can see no reference to a flyout anywhere within the default template of the picker control.
What role does the TimePickerFlyoutPresenter control play, and how is it incorporated into the TimePickerFlyout class? The TimePickerFlyout class has no template – so how does it instantiate and communicate with the TimePickerFlyoutPresenter control?
What are the basic steps to mimic this pattern?
What is the intended use of the ShouldShowConfirmationButtons and OnConfirmed virtual methods on PickerFlyoutBase? When I override them in my concrete implementation, they are never called.
I’d be very thankful for any guidance!

I had the same prob, but after work around, I did the following to change the TimePickerFlyout's background color and the size of the buttons on its footer and other style as I needed.
Navigate to C:\Program Files (x86)\Windows Kits\10\DesignTime\CommonConfiguration\Neutral\UAP\10.0.10240.0\Generic
(Note this may change depending on your SDK version)
Open the generic.xaml file
Copy the TargetType="TimePickerFlyoutPresenter" section to your App.xaml, and make changes whatever you want, All the Flyouts will be changed accordingly.
OR
Copy this style and put it in App.xaml.
<!-- Default style for Windows.UI.Xaml.Controls.TimePickerFlyoutPresenter -->
<Style TargetType="TimePickerFlyoutPresenter">
<Setter Property="Width" Value="242" />
<Setter Property="MinWidth" Value="242" />
<Setter Property="MaxHeight" Value="0" />
<Setter Property="FontFamily" Value="{ThemeResource ContentControlThemeFontFamily}" />
<Setter Property="FontWeight" Value="Normal" />
<Setter Property="IsTabStop" Value="False" />
<Setter Property="VerticalAlignment" Value="Stretch" />
<Setter Property="Background" Value="{ThemeResource SystemControlBackgroundChromeMediumLowBrush}" />
<Setter Property="AutomationProperties.AutomationId" Value="TimePickerFlyoutPresenter" />
<Setter Property="BorderBrush" Value="{ThemeResource SystemControlForegroundTransparentBrush}" />
<Setter Property="BorderThickness" Value="{ThemeResource DateTimeFlyoutBorderThickness}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TimePickerFlyoutPresenter">
<Border x:Name="Background"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
MaxHeight="396">
<Grid x:Name="ContentPanel">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="44" />
</Grid.RowDefinitions>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" x:Name="FirstPickerHostColumn" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" x:Name="SecondPickerHostColumn" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" x:Name="ThirdPickerHostColumn" />
</Grid.ColumnDefinitions>
<Rectangle x:Name="HighlightRect" Fill="{ThemeResource SystemControlHighlightListAccentLowBrush}" Grid.Column="0" Grid.ColumnSpan="5" VerticalAlignment="Center" Height="44" />
<Border x:Name="FirstPickerHost" Grid.Column="0" />
<Rectangle x:Name="FirstPickerSpacing" Fill="{ThemeResource SystemControlForegroundBaseLowBrush}" HorizontalAlignment="Center" Width="2" Grid.Column="1" />
<Border x:Name="SecondPickerHost" Grid.Column="2" />
<Rectangle x:Name="SecondPickerSpacing" Fill="{ThemeResource SystemControlForegroundBaseLowBrush}" HorizontalAlignment="Center" Width="2" Grid.Column="3" />
<Border x:Name="ThirdPickerHost" Grid.Column="4" />
</Grid>
<Grid Grid.Row="1" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Rectangle Height="2" VerticalAlignment="Top" Fill="{ThemeResource SystemControlForegroundBaseLowBrush}" Grid.ColumnSpan="2" />
<Button x:Name="AcceptButton" Grid.Column="0" Content="" FontFamily="{ThemeResource SymbolThemeFontFamily}" FontSize="16" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Style="{StaticResource DateTimePickerFlyoutButtonStyle}" Margin="0,2,0,0" />
<Button x:Name="DismissButton" Grid.Column="1" Content="" FontFamily="{ThemeResource SymbolThemeFontFamily}" FontSize="16" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Style="{StaticResource DateTimePickerFlyoutButtonStyle}" Margin="0,2,0,0" />
</Grid>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

So, since a couple of folks asked me to follow up on this, I was actually eventually able to figure all this stuff out, with some help from Jim Walker (MSFT). It's been a long time coming, but I now decided to revisit this and finally finish those TimeSpanPicker and TimeSpanEditor controls I was working on.
https://github.com/IDeliverable/UwpControls
Run the TestHost project to see both controls in action.
The repository contains a few other controls as well; the part containing the TimeSpanPicker and TimeSpanEditor controls is here:
https://github.com/IDeliverable/UwpControls/tree/master/src/IDeliverable.Controls.Uwp.TimeSpanPicker
The code should hopefully serve as a really good sample for anyone interested in how to build a custom picker control, but the controls should also be useful for anyone needing to add TimeSpan editing functionality to their app. I really went to town with these controls, sweating the details and non-obvious things like accessibility, support for multiple input modes (touch, keyboard, mouse), system theme compliance, full templating support, etc.
Documentation for these two controls is work in progress at the moment.
I plan on packaging these controls up as NuGet packages soon, but for now, you'll have to consume them as source code. I'm also going to check if there's any interest incorporating them into the Window Community Toolkit.

Related

UWP-CPP/Winrt Set Corner radius of the ListViewItem when hovered/selected

I am planning to show a list of objects using ListView. According to the design the rectangle around the ListViewItem should be rounded at the corners. Have tried multiple ways to achieve the same but couldn't find a solution.
<ListView
x:Name="ObjectList"
ItemsSource="{x:Bind ObjectViewModel.Objects}"
SelectionChanged="ListViewButtonClick"
MaxWidth ="{StaticResource ColumnMaximumWidth}"
VerticalAlignment = "Center"
HorizontalContentAlignment = "Stretch"
ScrollViewer.HorizontalScrollBarVisibility ="Disabled"
SelectionMode ="Single" />
<ListView.Resources>
<SolidColorBrush x:Key="ListViewItemBackgroundSelected" Color="Green" />
<SolidColorBrush x:Key="ListViewItemBackgroundSelectedPointerOver" Color="Green" />
</ListView.Resources>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="Margin" Value="0,0,0,30" />
</Style>
</ListView.ItemContainerStyle>
<ItemsControl.ItemTemplate>
<DataTemplate x:DataType="local:ObjectModel">
<Border
BorderBrush="Red"
BorderThickness="3"
CornerRadius="5">
<Grid MinHeight="66" CornerRadius="8">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<FontIcon
FontSize="17"
Glyph=""
Style="{StaticResource FontIconStyle1}" />
<TextBlock
Grid.Column="1"
Style="{StaticResource AddBluetoothLabelTextStyle}"
Text="{x:Bind ObjectName, Mode=OneWay}" />
</Grid>
</Border>
</ItemsControl.ItemTemplate>
</ListView>
As shown in the picture the corners of the selected/hovered items is not rounded. Can you please help how this can be achieved. TIA
You need to change the VisualState to set CornerRadius of ListViewItem in style of ListViewItem.
Please check the following steps:
Open generic.xaml file, find a Style whose TargetType is ListViewItem and Key is ListViewItemRevealStyle. Copy the style into your Page.Resources.
Delete the x:Key property of the style so that the style could be used in all ListViewItem in the current Page.
Find a VisualState whose name is Selected, add the following code:
<VisualState x:Name="Selected">
<VisualState.Setters>
<Setter Target="Root.CornerRadius" Value="10" />
</VisualState.Setters>
</VisualState>
Find VisualStates whose names are PointerOver, PointerOverSelected, PointerOverPressed, PointerOverPressed, add the following code seperately:
<Setter Target="Root.CornerRadius" Value="10" /> <!--Add this code-->
Delete the ListView.ItemContainerStyle statement, which conflicts with the ListViewItem’s style.
Note, it is better if you could follow the above steps first to set corner radius in the style of ListViewItem then add other style or settings later, which could ensure settings in the style of ListViewItem work.

Segmented control Xamarin Forms customisation

I would like to create the segmented control in the image below.
What i currently have or attempted using is the library in the following link : https://github.com/1iveowl/Plugin.SegmentedControl
How ever as you can see the final result ends up being a horizontal segmented UI, which is what I do not want.
I have checked the documentation of the plugin to see if there is a way of changing the orientation and it seems that is the current limitation of the plugin
<control:SegmentedControl
x:Name="SegmentedGenderControl"
TintColor="#F2EBF9"
SelectedTextColor="#6F1AC1"
TextColor="Black"
DisabledColor="White"
BorderColor="#6F1AC1"
BorderWidth="1.0"
FontSize="Medium"
Margin="8,8,8,8">
<control:SegmentedControl.Children >
<control:SegmentedControlOption Text="Male"/>
<control:SegmentedControlOption Text="Female"/>
<control:SegmentedControlOption Text="Female"/>
</control:SegmentedControl.Children>
</control:SegmentedControl>
The second alternative that I have thought about is using a grid with 3 rows :
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
</Grid>
And then manually handle the selection based on the selection. Is there a simpler or plugin that is available to the public that is not the one above that I can use ?
So I finally managed to solve this problem as suggested by an external party through the use of a collection view
See the code that follows :
<CollectionView
HeightRequest="250"
x:Name="OptionsCollectionView"
ItemsSource="{Binding SelectionOptions}"
VerticalOptions="Start"
SelectionMode="Single">
<CollectionView.ItemTemplate>
<DataTemplate>
<yummy:PancakeView
x:Name="optionPancake"
Padding="20">
<yummy:PancakeView.Border>
<yummy:Border
Color="{StaticResource CollectionViewBorderColor}"
Thickness="2" />
</yummy:PancakeView.Border>
<StackLayout
Orientation="Horizontal">
<Label
x:Name="optionLabel"
Text="{Binding Option}"
FontSize="15"
FontFamily="EuclidCircularASemibold"
TextColor="{StaticResource SubHeadingColor}"
FontAttributes="Bold" />
</StackLayout>
</yummy:PancakeView>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
The code that follows is for styling the visual state group, when an item is selected:
<Style TargetType="yummy:PancakeView">
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup>
<VisualState Name="Normal"/>
<VisualState Name="Selected">
<VisualState.Setters>
<Setter
Property="BackgroundColor"
Value="{StaticResource FrameSelectedColor}"/>
<Setter
Property="yummy:PancakeView.Border"
Value="{yummy:BorderMarkup Color={StaticResource SelectedLabelColor}, Thickness='2'}"/>
<Setter TargetName="optionLabel"
Property="Label.TextColor"
Value="{StaticResource SelectedLabelColor}"/>
<Setter Property="CornerRadius"
Value="5"/>
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>

How Can I Reuse an Icon in Resources in UWP? In WPF, I'd use x:Shared=false

I am trying to create a button Style that I can use for a "Lookup" button throughout my UWP app. However, the icon only appears on the first button on the screen. I tried this solution using templates, but it is not working for me. Thanks for the help.
Code:
<Page.Resources>
<ControlTemplate x:Key="FindSymbolTemplate">
<SymbolIcon Symbol="Find" Foreground="White" />
</ControlTemplate>
<Style TargetType="Button" x:Key="LookupButton">
<Setter Property="Content">
<Setter.Value>
<ContentControl Template="{StaticResource FindSymbolTemplate}" />
</Setter.Value>
</Setter>
</Style>
</Page.Resources>
....
<Button x:Name="tourNumLookup"
Style="{StaticResource LookupButton}"
Grid.Column="1"
Margin="10,0"
VerticalAlignment="Center" />
....
<Button x:Name="customerIdLookup"
Style="{StaticResource LookupButton}"
VerticalAlignment="Center"
Grid.Column="1"
Grid.Row="1"
Margin="10,0" />
The two buttons in the UI. Only the first has the SymbolIcon content.
#Romasz's solution absolutely works, but what if you want a lightly different Foreground on the SymbolIcon inside another Button?
Here's a potentially more flexible way that I normally go with.
First let's create a base Style that holds some default values for all the icons.
<Style x:Key="Style-Icon-Base"
TargetType="ContentControl">
<!-- If you don't specify the Foreground, it will use its ancestor's -->
<!--<Setter Property="Foreground"
Value="White" />-->
<Setter Property="HorizontalContentAlignment"
Value="Center" />
<Setter Property="VerticalContentAlignment"
Value="Center" />
<Setter Property="Width"
Value="20" />
<Setter Property="Height"
Value="20" />
<Setter Property="Padding"
Value="0" />
</Style>
Then we create a new icon Style which inherits from the one above. Note within the ControlTemplate I have used TemplateBinding to make property values dynamic. TemplateBinding isn't available inside a DataTemplate.
<Style x:Key="Style-Icon-Find"
BasedOn="{StaticResource Style-Icon-Base}"
TargetType="ContentControl">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ContentControl">
<!--
'cause you cannot change the size of the SymbolIcon, we insert a Viewbox here,
otherwise you don't need it.
-->
<Viewbox Margin="{TemplateBinding Padding}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}">
<SymbolIcon Symbol="Find"
Foreground="{TemplateBinding Foreground}" />
</Viewbox>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
This way you have created a highly reusable icon Style, to use it, have a look at the following Buttons:
<StackPanel Orientation="Horizontal"
HorizontalAlignment="Center">
<Button Margin="4"
Padding="8"
BorderBrush="LightBlue">
<ContentControl Width="36"
Height="36"
Foreground="DarkCyan"
Style="{StaticResource Style-Icon-Find}" />
</Button>
<!-- Note how I defined the Foreground at the Button level and it flows down to the icon -->
<Button Foreground="DarkGoldenrod"
Margin="4">
<StackPanel Orientation="Horizontal">
<ContentControl Style="{StaticResource Style-Icon-Find}"
Width="16"
Height="16" />
<TextBlock Text="Search"
VerticalAlignment="Center"
Margin="8,0,0,0" />
</StackPanel>
</Button>
<Button Margin="4"
Padding="4">
<ContentControl Style="{StaticResource Style-Icon-Find}" />
</Button>
</StackPanel>
And they look like:
Generally UI elements can be used once (or saying different - have only one parent) - this is probably why it only works for the first button in your case. One solution may be to define DataTemplate and use it as ContentTemplate, so each button creates its own icon:
<Page.Resources>
<DataTemplate x:Key="FindTemplate">
<SymbolIcon Symbol="Find" Foreground="White" />
</DataTemplate>
</Page.Resources>
...
<Button x:Name="tourNumLookup" ContentTemplate="{StaticResource FindTemplate}"
Grid.Column="1" Margin="10,0" VerticalAlignment="Center" />
<Button x:Name="customerIdLookup" ContentTemplate="{StaticResource FindTemplate}"
VerticalAlignment="Center" Grid.Column="1" Grid.Row="1" Margin="10,0" />
You don't need to create ControlTemplate to reuse the icon. You can simply put this SymbolIcon to the resource dictionary and use as StaticResource for the buttons' Content.
<Page.Resources>
<SymbolIcon x:Key="FindSymbol" Symbol="Find" Foreground="White" />
</Page.Resources>
<Button x:Name="tourNumLookup"
Content="{StaticResource FindSymbol}"
Grid.Column="1"
Margin="10,0"
VerticalAlignment="Center" />
<Button x:Name="customerIdLookup"
Content="{StaticResource FindSymbol}"
VerticalAlignment="Center"
Grid.Column="1"
Grid.Row="1"
Margin="10,0" />
UPDATE
BTW this is possibly a bug in the UWP platform, because I tried the following code and only the first Button rendered the icon at desing time and none of the at runtime.
<Page.Resources>
<SymbolIcon x:Key="FindSymbol" Symbol="Find" Foreground="White" />
<Style TargetType="Button" x:Key="LookupButton">
<Setter Property="Content" Value="{StaticResource FindSymbol}"/>
</Style>
</Page.Resources>
<StackPanel>
<Button x:Name="tourNumLookup"
Style="{StaticResource LookupButton}"
Margin="10,0"
VerticalAlignment="Center" />
<Button x:Name="customerIdLookup"
Style="{StaticResource LookupButton}"
VerticalAlignment="Center"
Margin="10,0" />
</StackPanel>
I tried to assign the Setter's Value directly but I got the same result. And also tried with FontIcon.

Assign the value of a dynamic element to another element

I have a ellipse with "width='auto'", and I want a circle, so it's not possible set "height='auto'" because if the user resize the window, the circle will be a ellipse. I've tried "Height='{Binding ElementName=TheLeft, Path=Width}'".
<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">
<Grid Background="White">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="3*" />
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="3*" />
</Grid.ColumnDefinitions>
<!--<Ellipse x:Name="TheLeft" Fill="Pink" Grid.Column="0" Height="{Binding ElementName=TheLeft, Path=Width}" Width="auto" VerticalAlignment="Bottom"/>-->
<!-- I've set Height="200" in the uncommented element -->
<Ellipse x:Name="TheLeft" Fill="Pink" Grid.Column="0" Height="200" Width="auto" VerticalAlignment="Bottom"/>
<Rectangle Fill="Red" Grid.Column="1" RadiusX="50" RadiusY="40"/>
<Ellipse Fill="Pink" Grid.Column="2" Height="200" Width="auto" VerticalAlignment="Bottom"/>
</Grid>
</Page>
First, I am going to definitely suggest you take out the inline styling, especially since you want the same values for many of the properties for the two Ellipses.
<Style x:Key="circularEllipse" TargetType="{x:Type Ellipse"}>
<Setter Property="Fill" Value="Pink"/>
<Setter Property="VerticalAlignment" Value="Bottom"/>
<Setter Property="Height" Value="200"/>
<Setter Property="Width" Value="{Binding Path=Height, RelativeSource={RelativeSource Self}}"/>
</Style>
And then when you code the ellipse - you just need to call this style and it can be used for both of them, saving you coding time and reducing the amount of code on your layout.
<Ellipse x:Name="TheLeft" Grid.Column="0" Style="{StaticResouce circularEllipse}"/>
So, if you need to change the size of the circle, you only have to change the property in one place. Also, if you need a variant, you can use the BasedOn option and not have to redo the entire code. Such as:
<Style x:Key="circularEllipse2" TargetType="{x:Type Ellipse}" BasedOn="{StaticResource circularEllipse}">
<Setter Property="Height" Value="100"/>
</Style>
This will pick up all of the other properties from the previous style, but change the height (or any other property you may need to change)

Fieldset in Winrt/XAML

I need to create something in WinRT/XAML similar to an HTML fielset. http://jsfiddle.net/Sf2Vy/
Basically, I have a border and there is some text on top of the border. Where the text covers the border, I need the border to not show under the text. The background behind the border isn't a solid color so I can't just set the background color of the text. The text length is variable also.
Is there an easy way to do this?
Yeah, so, the answer is no. There is no FieldSet.
Having said that, I think you could work out a similar effect simple enough. The code below shows you a solution that could easily be wrapped in a custom user control called fieldset.
<Grid Width="500" VerticalAlignment="Center">
<!-- top fieldset thing -->
<Grid VerticalAlignment="Top">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="35" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.Resources>
<Style TargetType="Border">
<Setter Property="VerticalAlignment" Value="Top" />
<Setter Property="BorderThickness" Value="0,5,0,0" />
<Setter Property="BorderBrush" Value="white" />
<Setter Property="Margin" Value="0,-2,0,0" />
</Style>
<Style TargetType="TextBlock">
<Setter Property="Margin" Value="10,-15,10,0" />
<Setter Property="FontSize" Value="30" />
</Style>
</Grid.Resources>
<Border Grid.Column="0" />
<TextBlock Grid.Column="1" Text="User Info" />
<Border Grid.Column="2" />
</Grid>
<!-- regular form fields -->
<Border BorderBrush="White" BorderThickness="5,0,5,5">
<StackPanel Margin="20">
<TextBox Header="Salutation" />
<TextBox Header="First Name" />
<TextBox Header="Middle Name" />
<TextBox Header="Last Name" />
<Button Margin="0,5,-3,0" HorizontalAlignment="Right">Save Data</Button>
</StackPanel>
</Border>
</Grid>
It looks something like this:
It's not 100% perfect - or, maybe... it is.
Best of luck!