Proper usage of Viewbox for WPF kiosk touch screen applications - wpf

Is there some kind of "best practices" manual for creating proper GUI for kiosk touch screens? These applications need to have consistent look and feel across different screen resolutions and more importantly screen ratios (since everything is rendered as vectors so screen resolution and DPI shouldn't be an issue with WPF).
Take for example this screenshot where I tried to create simple keyboard for touch screens. I've used UniformGrid so that each button gets cell of equal size:
Here is the code for this:
<TabItem Header="Test 1">
<ItemsControl ItemsSource="{Binding KeyboardItems}" SnapsToDevicePixels="True">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Viewbox Margin="5">
<Button Content="{Binding}"></Button>
</Viewbox>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="8" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</TabItem>
Notice that all buttons are sized to content which makes them non-stretchable so each button has its own size... This is how Viewbox scales its content and of course this kind of GUI is out of question. This is not the keyboard I want to use on some kiosk application, so the next better version is following:
<TabItem Header="Test 2">
<ItemsControl ItemsSource="{Binding KeyboardItems}" SnapsToDevicePixels="True">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Margin="5">
<Viewbox>
<TextBlock Text="{Binding}" />
</Viewbox>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="8" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</TabItem>
Now this is a bit better as Viewbox is now wrapping the content rather than the whole button. Notice however that because we are now wrapping the button's content rather the whole button the button's border is now not scaled. I want this to be scaled too, not just the content. In the first example we had this but the overall look of the GUI was horrible.
Also notice that in this version I've set Margin to the Button and in the first version on the Viewbox. This means that in the first version margin will scale too (I want that!) while in the second version it will be constant for any screen size. So for really big screens the white space between buttons will become relatively smaller though they are absolutely of constant size (not what I want!).
Here is the code for generating keyboard buttons:
public partial class MainWindow : Window
{
public List<string> KeyboardItems { get; set; }
public MainWindow()
{
KeyboardItems = new List<string>();
for (char c = 'A'; c <= 'Z'; c++)
{
KeyboardItems.Add(c.ToString());
}
KeyboardItems.Add("Space");
DataContext = this;
InitializeComponent();
}
}
Problems like this are all around development of WPF touch screen kiosks so I'd like to hear some ideas and solutions you came about while dealing with scaling issues.

You didn't show us Test 3, which I thought might be this:
<TabItem Header="Test 3">
<Viewbox>
<ItemsControl ItemsSource="{Binding KeyboardItems}" SnapsToDevicePixels="True">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Margin="5">
<TextBlock Text="{Binding}" />
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="8" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</Viewbox>
</TabItem>
Does that have the desired effect?

Related

How to make the items in a WPF ListBox wrap horizontally and vertically

I want to show a list of thumbnails, and allow the user to choose one. A ListBox seemed an obvious choice, setting the template to include the thumbnail and description.
The following code does this correctly...
<ListBox Margin="5"
Name="BackgroundsLb">
<ListBox.ItemTemplate>
<DataTemplate>
<Border Margin="5"
BorderBrush="Black"
BorderThickness="1">
<StackPanel Margin="5">
<Image Source="{Binding Path=Data, Converter={StaticResource BytesToImageVC}}"
Width="150"
HorizontalAlignment="Center" />
<TextBlock Text="{Binding Path=Description}"
HorizontalAlignment="Center" />
</StackPanel>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
However, this displays the thumbnails vertically, one per row, as is normal for a ListBox. Given that the thumbnails are only 150 pixels wide, I would like to show them in something more like a grid, but (ideally) in a way so that the number of columns adapts to the size of the window.
I tried replacing the ListBox with an ItemsControl, adding in the following...
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Vertical" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
...and it displayed exactly how I wanted it, but the ItemsControl does not allow selection, so is no use for my purpose.
Is there a way to achieve a flexible, selectable display that fills the horizontal space, and breaks onto a new row according to the size of the window?
Thanks
You can use an ItemsPanelTemplate in a ListBox just the same as you are using one in the ItemsControl. The difference I think you're seeing is that ListBox uses scroll bars by default rather than wrapping the content. Basically the content is allowed to grow forever, so you never get the wrap. Instead you get a scrollbar. The good news is you can disable this behavior. The following should give you a horizontal wrap, where new rows are created as needed.
<ListBox ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>

Reuse of element in xaml (win store app)

In my xy.xaml file which is representing LayoutAwarePage I have this two elements
<StackPanel x:Name="LeftCommands" Orientation="Horizontal" Grid.Column="2" Margin="0,0,100,0" HorizontalAlignment="Right">
<Button x:Name="BragButton" HorizontalAlignment="Left"/>
</StackPanel>
and
<Page.TopAppBar>
<AppBar x:Name="PageAppBar" Padding="10,0,10,0" Height="120" Opacity="0.98">
<ItemsControl x:Name="groupTopMenuBar" Margin="116,0,40,0">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Margin="0,0,0,0">
<Button Click="UpperMenu_Click"
Style="{StaticResource TextPrimaryButtonStyle}"
Height="Auto" Margin="20,0,20,0"
CommandParameter="{Binding Group.MenuItemID}">
<StackPanel>
<Image Source="{Binding Group.IconURL}" Width="40"
Height="40" VerticalAlignment="Top" Stretch="UniformToFill" />
<TextBlock Text="{Binding Group.Name}" HorizontalAlignment="Left"
VerticalAlignment="Bottom"
Foreground="{StaticResource ListViewItemOverlayForegroundThemeBrush}"
Style="{StaticResource TitleTextStyle}" FontSize="16" />
</StackPanel>
</Button>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</AppBar>
</Page.TopAppBar>
I need to use these elements in every page of my application and I do not want to write this pieces of code in every .xaml file of application, so I want to ask if there is any way how to do this.
Thank you.
You want to share your AppBar across multiple pages. Unfortunately, in Windows Store apps it is not possible using referencing AppBar defined in App.xaml using StaticResource as is the case of Windows Phone.
There are 2 ways how to do it:
Create another Frame inside your main page and do all navigation in this frame. Check this MSDN article.
Create UserControl with contents (buttons) of your AppBar, add TopAppBar on every page and set its content to that UserControl. This approach is advised in this StackOverflow answer.
I might see little problem with page navigation. If you want to navigate the hosting frame from the UserControl, store the Frame instance which is created in the App.xaml.cs OnActivated method to some static property of App class. For example public static Frame RootFrame { get; private set; } and set it by App.RootFrame = new Frame(). To navigate from the code behind your UserControl just call something like this: App.RootFrame.Navigate().
This approach was advised by Filip Skakun here on StackOverflow.

Looking for a WPF multicolumn list with scaling

I would like to create a multi-column list of checkboxes, but here's the catch - as I resize the window I would like everything to scale accordingly, including text size. I've been trying to make use of a WrapPanel and ViewBox but can't get the XAML right. Are these controls the best option or should I be using a ListBox (note I don't need selection functionality or scrollbars)? Any suggestions or examples on how I could achieve this would be much appreciated. I'm using MVVM and the list will be data bound, if that makes a difference.
BTW since starting WPF I've been struggling to understand which controls size to their children and which size to their parent. Are there any good sites, cheat sheets, or whatever summarising the behaviour of each control?
If you have a variable number of child elements, you could put a UniformGrid into a ViewBox.
If the child elements have to be visualized by a DataTemplate, you would have to use an ItemsControl with the ItemsPanel property set to such a UniformGrid:
<Viewbox Stretch="Uniform">
<ItemsControl ItemsSource="{Binding Items}" Width="400" Height="200">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="4"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Background="AliceBlue">
<CheckBox Content="{Binding Label}" IsChecked="{Binding IsChecked}"
HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Viewbox>

Silverlight 4 - simple control template

<DataTemplate x:Key="dirtSimple">
<TextBlock Margin="10,0,0,0" Text="{Binding Path=CurrentBook.Published, StringFormat=d}"></TextBlock>
</DataTemplate>
<ControlTemplate x:Key="lbWrapPanelTemplate">
<StackPanel Orientation="Horizontal" Margin="2" Background="Aqua">
<ItemsPresenter></ItemsPresenter>
</StackPanel>
</ControlTemplate>
...
<ListBox Template="{StaticResource lbWrapPanelTemplate}" x:Name="bookListBox" Grid.Row="0" ItemsSource="{Binding Path=BookSource}" ItemTemplate="{StaticResource dirtSimple}" >
</ListBox>
The list box is displaying correctly, with a beautiful "Aqua" background, and each item is boringly displayed with just a date. For some reason though the items are not flowing horizontally. I originally tried it with the Silverlight Toolkit's WrapPanel, with the same problem, but I can't even get it to work with a built-in StackPanel, so I suspect I'm missing something.
Are you trying to get selection-based behavior that a ListBox provides? If not, use an ItemsControl (and supply an ItemsPanel as below).
The reason it's not going horizontal is the ItemsPresenter ultimately has its own panel it lays out items in. It's not inserting each item separately into your StackPanel (or WrapPanel), it's putting them in its own panel
What you want to do is specify a value for ItemsPanel like so:
<ListBox ItemTemplate="{StaticResource dirtSimple}">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>

How to space items evenly in a horizontal Silverlight Listbox

In Silverlight, I have a Vertical ListBox that has a Horizontal ListBox for each item. I want the items in the HorizontalListbox to space evenly across the width of the parent (Vertical) ListBox. How can I do this?
<ListBox x:Name="MachineListBox" Background="Green">
<ListBox.ItemTemplate>
<DataTemplate>
<ListBox ItemsSource="{Binding CoilList}" Background="Red" HorizontalAlignment="Stretch">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel
HorizontalAlignment="Stretch" />
</ItemsPanelTemplate >
</ListBox.ItemsPanel>
<ListBox.ItemTemplate >
<DataTemplate>
<TextBlock
Text="{Binding Coil}"
HorizontalAlignment="Stretch"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I'll take the liberty of suggesting you use a custom control of my own invention. It's called a ProportionalPanel and it does just what you need - spaces items evenly. You could use it for the ItemsPanel in the inner ListBox instead of the StackPanel. I also provide the source code, so you can tweak the logic any way you like. The relevant post on my blog is here.
I think the proportional sizing operator will do what you're looking for. Haven't tried it but it sounds like an option. Width="*" and Margin="2*".

Resources