A C#/XAML FlipView Context Indicator for Windows 8 24 August 2012 Diederik-Krols Metro, WinRT This article presents a FlipView context indicator control for Windows 8 UI Style apps. FlipView is a Windows 8 items control that displays one item at a time. It allows traversing its collection of items through a horizontal flick gesture. There are some limitations in its usage. In an app, the maximum size of a flipview is the size of the page. If you make the FlipView wider than its container, you're just going to confuse your user (and probably also the runtime): does a horizontal flick triggers scrolling, or are we moving to the next (or previous) page ? This is one of the reasons why I personally prefer the single page GridView style over a FlipView. If you decide to stick to the FlipView, you're not only limited in the size of the data template. You'll also bump into another problem: how are you going to make it clear to your user that he can navigate to other items ? Well, the Guidelines for FlipView Controls prescribe the usage of a context indicator, but they don't specify how it should look like. The official FlipView Control Sample only has a HTML/JS version. There used to be XAML/C# sample for the Release Preview -my control templates are based on it. But that sample app seems to have been lost in RTM translation. So the best functional example of a FlipView Context Indicator can be found in the store: Here's how I built my control. Its design is very simple and so is the implementation. [Warning:spoiler in next statement] It is just a templated ListBox that is bound to the ItemsSource and SelectedItem of its associated FlipView. In Visual Studio, I created a new project of the type 'Windows Store Apps Class Library' and added a new 'Templated Control'. I made that control inherit from ListBox. In the Generic.xaml resource dictionary, I added the two styles from the (now disappeared) FlipView Control Sample: a style for the ListBox itself (just a rectangle), and a style for the ListBoxItem (another rectangle). That's a lot of XAML to just define two rectangles. I gave the control a dependency property -called FlipView- of the type ... FlipView: public static readonly DependencyProperty FlipviewProperty = DependencyProperty.Register("Flipview", typeof(FlipView), typeof(FlipViewIndicator), new PropertyMetadata(null, FlipView_Changed)); public FlipView Flipview { get { return (FlipView)GetValue(FlipviewProperty); } set { SetValue(FlipviewProperty, value); } } When the property is assigned or changed -e.g. through XAML- I set the ItemsSource of the context indicator to the ItemsSource of the FlipView. I also create a two-way binding to the SelectedItem. private static void FlipView_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e) { FlipViewIndicator that = d as FlipViewIndicator; FlipView flip = (e.NewValue as FlipView); that.ItemsSource = flip.ItemsSource; Binding binding = new Binding(); binding.Mode = BindingMode.TwoWay; binding.Source = flip; binding.Path = new PropertyPath("SelectedItem"); that.SetBinding(FlipViewIndicator.SelectedItemProperty, binding); } This way, the context indicator can be used to navigate through the items collection - just by tapping one of the small rectangles. That's why I made these rectangles wider than the ones in the Store. Here's how it looks like in the attached sample app. For the record: the flipview context indicator is the small bar in the lower left corner: This app is just a FlipView and Context Indicator on a list of Belgian Super Heroes. The description is in Dutch, which may look weird if you don't know the language. But I assure you: if you translate them in English, they will still look weird. The bottom line is that Belgian Super Heroes evolved from men in mini skirts in the 1970s, to men in fluorescent leather catsuits in the 2010s: O, I almost forgot: here's how to add the control on a page, and link it to a FlipView: <FlipView x:Name="heroFlipView" ItemsSource="{Binding Heroes}"> ... </FlipView> <controls:FlipViewIndicator Flipview="{Binding ElementName=heroFlipView}" VerticalAlignment="Bottom" HorizontalAlignment="Left" Margin="8" /> That's all there is. Here's the code of the control and sample app. It was written in Visual Studio 2012 Ultimate, for the Windows 8 RTM: U2UConsult.Win8.FlipViewIndicatorSample.zip (587.48 kb) Update: This is the very first version of the FlipViewIndicator. A newer version of this control is embedded in Tim Heuer's Callisto framework, were it will continue its life cycle. Enjoy!
Transforming SVG graphics to XAML Metro Icons 27 July 2012 Diederik-Krols Metro, WinRT This short article shows how to pimp your Windows 8 Metro apps with native XAML icons that are created from W3C Scalable Vector Graphics. SVG graphics are XML documents, and hence should be easily translatable into an other XML schema, such as XPS (the technology formerly known as ... Metro). Just like Metro XAML, XPS is a subset of XAML. Unfortunately its full object model is not implemented in Silverlight or Metro. In Windows, since the-OS-that-should-not-be-named, XPS is also used as print spooler format. This means that you don't even have to write an XSLT transformation to translate SVG to XAML: let the printer driver do the heavy lifting. Here's how it goes. Find a graphic Grab a designer, or navigate to your favorite source of royalty free images - such as The Noun Project. Download the resource, as SVG: Transform it to XPS Open the SVG file in your browser, and print to an XPS file through the Microsoft XPS Document Writer: Find XAML Payload The XPS document is actually a ZIP-file. So just add .zip to its name: The XPS document contains page layout, headers, footers, embedded fonts, and so on. It also contains all left-overs from the tools that created the SVG. Not all SGV graphics are lean and mean on the inside. Some of the XML reminds me to the tons of obsolete HTML that used to be generated by Microsoft FrontPage. Anyway, you just have to find the place where the payload -the graphic- is hidden. It's most probably part of the first page of the first document, so \Documents\1\Pages\1.fpage would be the first place to look: Bingo! Here's the XAML that we're looking for - the one with all the PATH elements: Clean up XAML If you just copy/paste that bunch of Canvas elements from the FixedPage into a Grid in a Metro app, the Visual Studio XAML Designer may already recognize it and start displaying the icon. The very first Canvas on the page is probably the page boundary, so you may delete it. The Glyphs elements can also be removed. The now remaining Path elements are most probably the translated SVG. All you need to do is throw these in a Viewbox, like this Planet SVG: <Viewbox> <Grid VerticalAlignment="Center" HorizontalAlignment="Center"> <Path Data="F 1 M 46.857,18.462 C 29.654,18.462 15.658,32.457 15.658,49.66 15.658,66.863 29.654,80.858 46.857,80.858 64.06,80.858 78.056,66.863 78.056,49.66 78.056,32.457 64.06,18.462 46.857,18.462 Z M 46.857,74.819 C 32.983,74.819 21.697,63.534 21.697,49.66 21.697,35.787 32.984,24.5 46.857,24.5 60.73,24.5 72.018,35.787 72.018,49.66 72.018,63.534 60.73,74.819 46.857,74.819 Z" Fill="Gold" /> <Path Data="F 1 M 80.782,42.622 C 81.339,44.794 81.26,47.228 81.091,49.701 89.277,54.928 90.683,58.083 90.625,58.6 90.613,58.619 89.058,60.471 80.095,59.816 75.472,59.477 69.955,58.53 64.001,57.09 64.001,57.109 64.003,57.125 64.003,57.144 64.003,59.188 63.462,61.105 62.524,62.772 68.841,64.302 74.719,65.31 79.666,65.673 89.301,66.377 94.692,64.71 96.152,60.574 98.181,54.833 90.748,48.331 80.782,42.622 Z" Fill="Gold" /> <Path Data="F 1 M 43.045,50.566 C 33.593,46.987 25.045,42.873 18.653,38.804 10.379,33.539 8.963,30.361 9.021,29.84 9.033,29.821 10.344,27.84 19.296,28.492 20.495,26.281 22.416,24.499 24.4,23.092 23.142,22.943 21.311,22.865 19.983,22.768 10.347,22.063 4.953,23.73 3.493,27.866 1.934,32.281 5.624,37.325 14.774,43.291 21.589,47.733 30.796,52.21 41.022,56.078 41.211,54.04 41.932,52.158 43.045,50.566 Z" Fill="Gold" /> <Path Data="M 44.509,57.291 C 44.509,52.9832 48.0012,49.491 52.309,49.491 L 52.309,49.491 C 56.6168,49.491 60.109,52.9832 60.109,57.291 L 60.109,57.291 C 60.109,61.5988 56.6168,65.091 52.309,65.091 L 52.309,65.091 C 48.0012,65.091 44.509,61.5988 44.509,57.291 Z" Fill="Goldenrod" /> </Grid> </Viewbox> Oh wait, there's an easier way If that's too cumbersome, you can also upload your SVG to Graphspe's on-line SVG to XAML Converter. That's what I did with Andrew Forrester's awesome Death Star graphic, also from the Noun Project: It directly returns the Canvas with the Path. Just drop it in a ViewBox: <Viewbox> <Path Fill="#FF000000" Data="F1 M50.844,1.04L50.844,1.04 50.844,1.04C50.741,1.039 50.64,1.03 50.54,1.023 50.35,1.009 50.159,0.997 49.965,0.997 22.863,0.997 0.813999999999993,23.045 0.813999999999993,50.146 0.813999999999993,77.248 22.863,99.295 49.965,99.295 50.156,99.295 50.344,99.283 50.531,99.27 50.633,99.263 50.735,99.254 50.838,99.253 50.84,99.253 50.842,99.253 50.845,99.253 77.461,98.778 99.115,76.748 99.115,50.146 99.115,23.54 77.461,1.511 50.844,1.04z M58.051,94.223C58.466,93.766,58.868,93.299,59.271,92.833L59.538,92.525C59.749,92.283 59.959,92.042 60.163,91.795 60.63,91.23 61.077,90.65 61.523,90.071L61.785,89.737C61.94,89.542 62.094,89.346 62.242,89.147 62.835,88.349 63.429,87.507 64.01,86.643L64.176,86.406C64.77,85.516 65.369,84.565 65.957,83.578 66.073,83.383 66.184,83.184 66.293,82.986L66.509,82.603C66.902,81.916 67.258,81.293 67.593,80.659 67.732,80.394 67.864,80.129 67.996,79.861L68.192,79.468C68.493,78.869 68.793,78.27 69.075,77.663 69.236,77.314 69.389,76.965 69.542,76.615L69.65,76.368C69.908,75.779 70.165,75.189 70.406,74.593 70.578,74.166 70.741,73.737 70.904,73.305 71.153,72.648 71.372,72.055 71.578,71.456 71.738,70.994 71.891,70.528 72.041,70.061 72.233,69.462 72.416,68.862 72.591,68.256 72.728,67.784 72.861,67.313 72.987,66.837 73.149,66.225 73.3,65.608 73.444,64.991L73.474,64.859C73.575,64.4289999999999 73.674,64.0009999999999 73.765,63.569 73.898,62.934 74.016,62.297 74.13,61.657L74.192,61.317C74.255,60.965 74.32,60.6139999999999 74.376,60.261 74.477,59.617 74.559,58.971 74.641,58.323L74.7,57.85C74.74,57.539 74.781,57.23 74.813,56.918 74.891,56.172 74.945,55.419 74.997,54.667L75.028,54.261C75.049,54.023 75.068,53.784 75.081,53.545 75.143,52.348 75.173,51.236 75.173,50.146 75.173,48.35 75.088,46.469 74.921,44.544 75.592,44.415 76.253,44.229 76.923,43.985 82.302,42.016 85.113,36.683 83.919,30.715 82.993,26.089 79.793,21.875 75.356,19.444 72.966,18.133 70.406,17.441 67.954,17.441 67.457,17.441 66.953,17.472 66.429,17.537 66.207,17.151 65.983,16.767 65.752,16.387L65.313,15.678C64.986,15.15 64.653,14.627 64.31,14.11 64.14,13.854 63.97,13.599 63.797,13.346 63.443,12.826 63.08,12.311 62.711,11.801L62.248,11.161C61.722,10.451,61.185,9.751,60.63,9.063L60.543,8.959C59.98,8.267 59.456,7.649 58.922,7.04 58.922,7.04 58.154,6.184 58.051,6.07 79.202,9.94 94.814,28.407 94.814,50.146 94.814,71.882 79.201,90.349 58.051,94.223z M70.433,23.951C70.373,24.042 70.322,24.138 70.265,24.231 70.05,24.581 69.853,24.94 69.672,25.309 69.613,25.428 69.553,25.546 69.498,25.667 69.29,26.125 69.102,26.592 68.946,27.074 68.944,27.08 68.94,27.086 68.938,27.093 68.846,27.075 68.752,27.072 68.66,27.059 68.476,27.029 68.296,27.011 68.117,27.007 68.104,27.007 68.093,27.004 68.08,27.004 68.073,27.004 68.068,27.006 68.061,27.006 67.248,26.997 66.506,27.244 65.908,27.695 65.88,27.716 65.853,27.738 65.827,27.76 65.642,27.907 65.469,28.068 65.317,28.253 64.6489999999999,29.065 64.3939999999999,30.177 64.6169999999999,31.307 64.671,31.574 64.7499999999999,31.833 64.8489999999999,32.084 64.8819999999999,32.168 64.9289999999999,32.245 64.9669999999999,32.326 65.0429999999999,32.489 65.1159999999999,32.651 65.2089999999999,32.805 65.2649999999999,32.897 65.3329999999999,32.979 65.395,33.067 65.488,33.199 65.578,33.333 65.682,33.456 65.756,33.543 65.84,33.62 65.919,33.702 66.026,33.812 66.133,33.924 66.25,34.024 66.339,34.101 66.436,34.167 66.529,34.237 66.6499999999999,34.327 66.7689999999999,34.416 66.896,34.495 66.998,34.558 67.104,34.61 67.209,34.665 67.34,34.733 67.471,34.801 67.606,34.857 67.717,34.903 67.831,34.939 67.945,34.976 68.084,35.021 68.222,35.063 68.364,35.096 68.482,35.122 68.6,35.14 68.72,35.157 68.805,35.169 68.887,35.194 68.973,35.2 69.013,35.324 69.064,35.443 69.108,35.565 69.151,35.684 69.194,35.802 69.24,35.919 69.39,36.303 69.553,36.682 69.738,37.048 69.745,37.061 69.751,37.075 69.757,37.088 69.95,37.467 70.167,37.832 70.395,38.19 70.458,38.29 70.525,38.387 70.59,38.485 70.78,38.767 70.982,39.041 71.194,39.308 71.253,39.382 71.307,39.458 71.368,39.531 71.637,39.855 71.92,40.165 72.219,40.462 72.287,40.53 72.361,40.594 72.431,40.661 72.675,40.894 72.929,41.118 73.191,41.332 73.286,41.409 73.379,41.485 73.475,41.56 73.54,41.61 73.601,41.665 73.667,41.715 73.574,41.726 73.481,41.724 73.388,41.732 73.381,41.733 73.373,41.733 73.367,41.733 73.119,41.754 72.87,41.778 72.622,41.778 72.029,41.778 71.439,41.717 70.853,41.625 65.531,40.788 60.681,36.31 59.601,30.913 59.029,28.053 59.606,25.389 61.226,23.413 61.947,22.534 62.845,21.847 63.868,21.349 64.612,20.987 65.424,20.732 66.289,20.588 66.292,20.588 66.295,20.588 66.298,20.587 66.832,20.499 67.383,20.443 67.954,20.443 69.455,20.443 71.022,20.778 72.554,21.42 72.493,21.476 72.444,21.542 72.384,21.599 72.089,21.882 71.809,22.178 71.543,22.485 71.468,22.572 71.389,22.655 71.315,22.744 70.998,23.129 70.703,23.532 70.433,23.951z M70.87,31.674C70.871,31.75 70.863,31.822 70.854,31.895 70.851,31.93 70.85,31.965 70.844,31.999 70.807,32.206 70.731,32.394 70.614,32.557 70.387,32.873 70.017,33.081 69.57,33.142 69.568,33.142 69.567,33.143 69.565,33.143 69.563,33.143 69.561,33.143 69.559,33.143 69.455,33.158 69.359,33.184 69.324,33.213 68.086,33.213 66.829,32.159 66.579,30.915 66.471,30.37 66.571,29.875 66.862,29.521 67.136,29.187 67.569,29.002 68.08,29.002 68.156,29.002 68.232,29.018 68.309,29.025 68.571,29.066 68.858,29.152 69.175,29.306 69.175,29.306 69.176,29.306 69.176,29.307 69.807,29.615 70.315,30.113 70.605,30.676 70.614,30.695 70.625,30.712 70.633,30.731 70.722,30.914 70.789,31.102 70.829,31.294 70.829,31.296 70.83,31.299 70.831,31.302 70.856,31.43 70.867,31.554 70.87,31.674z" /> </Viewbox> The result Just look at these shiny tiles: Enjoy!
The taming of the Metro GridView 10 July 2012 Diederik-Krols Metro, WinRT This article describes how to restyle the Metro GridView to host a single page app like the sample Weather and Finance apps. I'll show you how to get rid of the default disco effects of this control - like the swiping behavior and the pressed animation. For this article, I started with my own lightweight version of the weather app, but I got bored very soon. So I upgraded the app to a tribute to the iconic rock band CPeX, with some pictures, some overflowing RichTextBox controls with lyrics, and WebView controls revealing the band's web site and wikipedia page; there's even a YouTube video in it: The GridView is a good candidate to host a single page Metro app: out-of-the-box it's layed out horizontally, and it supports panning through differently sized content pages. It was designed for selecting and moving items, so its default look-and-feel is way too busy. But Visual Studio 11 Release Candidate is the ideal environment to tame it. Visual Studio is now sharing some of its designers with Expression Blend. If you want to restyle a control on a page, all you have to do is right click in the XAML designer, and select "edit template" and "edit a copy". The control template will be pulled out of generic.xaml and applied to the control, after you decided where to save the style: That's exactly what I did to create new copies of the templates for the GridView and GridViewItem control. Here's the list of modifications to the default GridView style: I changed the IsSwipeEnabled property to false: <Setter Property="IsSwipeEnabled" Value="False" /> I added a default value for the SelectionMode property: <Setter Property="SelectionMode" Value="None" /> I changed the ItemsPanelTemplate to a horizontal StackPanel: <Setter Property="ItemsPanel"> <Setter.Value> <ItemsPanelTemplate> <StackPanel Orientation="Horizontal" VerticalAlignment="Stretch" /> </ItemsPanelTemplate> </Setter.Value> </Setter> Finally, I made the outer ScrollViewer snap to the GridViewItems: <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="GridView"> <Border ... > <ScrollViewer x:Name="ScrollViewer" ... HorizontalSnapPointsType="Optional"> <ItemsPresenter .../> </ScrollViewer> </Border> </ControlTemplate> </Setter.Value> </Setter> After a swipe, the GridView pans smoothly to the beginning of the next (or previous) page. Heck, even the Weather and Finance apps don't do this: Here's the list of modifications to the default GridViewItem style: I changed the margin, so that all pages touch the borders and each other: <Setter Property="Margin" Value="-4 -4 -4 0" /> I cleared the animations for the following visual states: PointerOver, Pressed, PointerOverPressed, and Focused: <VisualState x:Name="PointerOver"> <Storyboard /> </VisualState> When I was happy with the result, I moved the two styles to a separate resource dictionary: I added a reference to it in the app.xaml: <Application.Resources> <ResourceDictionary> <ResourceDictionary.MergedDictionaries> <ResourceDictionary Source="Common/StandardStyles.xaml" /> <ResourceDictionary Source="Common/ZenStyles.xaml" /> </ResourceDictionary.MergedDictionaries> </ResourceDictionary> </Application.Resources> Here's how the main GridView uses the styles: <GridView Style="{StaticResource ZenGridViewStyle}" ItemContainerStyle="{StaticResource ZenGridViewItemStyle}"> ... </GridView> Here's the sample project. It was developed with Visual Studio 11 Ultimate RC for the Windows 8 Release Preview: U2UConsult.Metro.GridViewZen.zip (1.26 mb) Enjoy!
Modal Dialogs in Windows 8 Metro 13 June 2012 Diederik-Krols Metro, WinRT This article describes how to create modal dialogs in a Windows 8 MVVM Metro-style app. Here's the use case: a main page opens a dialog and provides it with some information. The dialog is displayed in the center of the screen, and collects some information. As long as the dialog is open, the main page is inaccessible by the user interface. When the dialog is closed, the main page reads the result from the dialog, and continues processing. The Sample Here are some screenshots of the attached solution. You see the main page with a shop button to open the dialog, the dialog that allows you to select multiple items from a catalog and then checkout, and finally the main page displaying the purchased items after the dialog was closed: The Dialog The dialog is implemented as a regular Page. When it's displayed, it will occupy the whole screen to prevent access to the underlying main page: public CatalogDialog() { this.InitializeComponent(); var bounds = Window.Current.Bounds; this.RootPanel.Width = bounds.Width; this.RootPanel.Height = bounds.Height; } The dialog look-and-feel is created by using a semi-transparent background under a white border with a wide margin around the core content: <Page> <Grid x:Name="RootPanel" Opacity=".75" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"> <Border Background="White" Margin="120"> <Grid Background="Blue" Margin="8"> <GridView x:Name="CatalogGridView" ItemsSource="{Binding Cars}" SelectionMode="Multiple" SelectionChanged="CatalogGridView_SelectionChanged"> </GridView> <Button Content="Checkout" Click="CloseButton_Click"> </Button> </Grid> </Border> </Grid> </Page> The dialog notifies its end-of-life by raising a CloseRequested event, in the click event handler of the checkout-button. This is the kind of code that you would factor out into a Dialog base class: public event EventHandler CloseRequested; private void CloseButton_Click(object sender, RoutedEventArgs e) { if (this.CloseRequested != null) { this.CloseRequested(this, EventArgs.Empty); } } The viewmodel always reflects the dialog's state. Binding to the SelectedItems property is not possible, so we have to refrain from hard-core MVVM. We update the viewmodel each time the selection in the catalog gridview is changed: private void CatalogGridView_SelectionChanged(object sender, SelectionChangedEventArgs e) { foreach (var item in e.AddedItems) { PinkFerrari pf = item as PinkFerrari; pf.IsSelected = true; } foreach (var item in e.RemovedItems) { PinkFerrari pf = item as PinkFerrari; pf.IsSelected = false; } } The Main Page The main page has a button that opens the dialog, and a listview to display the result: <Page> <Page.DataContext> <local:MainPageViewModel /> </Page.DataContext> <Grid> <Button Content="Shop" Command="{Binding OpenDialogCommand}"> </Button> <ListView ItemsSource="{Binding ShoppingList}"> </ListView> </Grid> </Page> When the shop-button is clicked, a command is fired in the main page's viewmodel. That viewmodel creates a new instance of the dialog view. MVVM tifosi would prefer to use some kind of ViewLocator here, instead of a hard reference to the view's class. The dialog view is associated with a viewmodel - that's how the two viewmodels can exchange information. An event handler is hooked onto the CloseRequested event. Then the dialog view is wrapped in a Popup element, which is then opened: private void OpenDialog_Executed() { this.catalog = new CatalogViewModel(); CatalogDialog dialog = new CatalogDialog(); dialog.DataContext = this.catalog; dialog.CloseRequested += Dialog_CloseRequested; this.catalogPopup = new Popup(); this.catalogPopup.Child = dialog; this.catalogPopup.IsOpen = true; } When the dialog closes, the main page's viewmodel is updated: private void Dialog_CloseRequested(object sender, EventArgs e) { this.catalogPopup.IsOpen = false; this.shoppingList.Clear(); var query = from f in this.catalog.Cars where f.IsSelected select f; foreach (var item in query) { this.shoppingList.Add(item); } } That's all there is... The Result Last week our CEO used this app to order the new company cars. Here's a picture of our parking area this morning: The Code Here's the full source code of the sample solution. Is was build with Visual Studio 2012 Ultimate RC for the Windows 8 Release Preview: U2UConsult.Metro.DialogSample.zip (409,68 kb) Enjoy !
Binding to a ConverterParameter in Windows 8 Metro 01 June 2012 Diederik-Krols Metro, WinRT This article describes how to simulate databinding to a ConverterParameter in a Windows 8 Metro XAML app. Let's say we have a TextBlock that displays a value from the ViewModel, and a Converter that takes this value together with some Range object as parameter. The converter should return a Brush -green or red- that indicates wether or not the value is within the range. The converter is used for providing the foreground color of the textblock. The following intuitive code would represent this behavior: <TextBlock Text="{Binding SomeValue}" Foreground="{Binding SomeValue, Converter={StaticResource RangeValidationConverter}, ConverterParameter={Binding SomeRange}}" /> Unfortunately, databinding to a ConverterParameter is not supported by any of Microsoft's XAML platforms. But let me share with you a little secret: that binding is actually easy to simulate. The following screenshots from the attached sample application show the bitterness of a beer (a value from the ViewModel) in green if it's within the expected range (the alleged Range parameter for the Converter), and red if it's out of range. With the slider you can throw more hops into the recipe to add bitternes, or remove hops to decrease bitterness. The value in the textblock, the needle in the gauge, but also the color of the textblock will synchonize with the bitterness value: Here's how it works. Instead of using the converter parameter, I decorated the converter with a dependency property to hold the range: public Range<int> Range { get { return (Range<int>)GetValue(RangeProperty); } set { SetValue(RangeProperty, value); } } public static readonly DependencyProperty RangeProperty = DependencyProperty.Register("Range", typeof(Range<int>), typeof(RangeValidationConverter), new PropertyMetadata(new Range<int>(0,100))); The converter can use the Range in its Convert method: public object Convert(object value, Type targetType, object parameter, string language) { int measurement = System.Convert.ToInt32(value); Range<int> range = this.GetValue(RangeProperty) as Range<int>; if (range == null) { return new SolidColorBrush(Colors.Gray); } if (measurement >= range.Min && measurement <= range.Max) { return new SolidColorBrush(Colors.LawnGreen); } return new SolidColorBrush(Colors.Red); } The converter is registered in the traditional way: as a resource. But the Range property can now be assigned through data binding: <Page.Resources> <local:RangeValidationConverter x:Key="RangeValidationConverter" Range="{Binding BitternessRange}" /> </Page.Resources> The textblock just uses the converter without providing a parameter: <TextBlock Text="{Binding CurrentBitterness}" Foreground="{Binding CurrentBitterness, Converter={StaticResource RangeValidationConverter}}" /> There you go ! Here's the source code. It was written in Visual Studio 11 Express Beta for the Windows 8 Consumer Preview: U2UC.Metro.ConverterParameterBinding.zip (115,27 kb) Enjoy!
Look Mom, I’m on the Tablet Show 29 May 2012 Diederik-Krols Metro, WinRT People that know me will definitely confirm that I’m a silent, modest person, always trying to keep a low profile . Last week, however, Carl Franklin and Richard Campbell from the Tablet show pulled me way out of my comfort zone to produce a podcast on the development of enterprise applications for Metro. The talk started from the trenches of development in preview-labelled environments; and evolved into a visionary discussion on Star Trek desk setups and using the Kinect in the enterprise. I definitely sound as if I was sitting in an empty beer kettle [note to myself: buy a microphone], but I'm still proud of the result: Diederik Krols Builds Metro UIs in Windows 8
Using Dynamic XAML in Windows 8 Metro 23 May 2012 Diederik-Krols Metro, WinRT This article describes how the Windows 8 Metro Consumer Preview deals with three standard ways of dynamically applying a look-and-feel to XAML controls. When working in Metro with XAML and data, you will want to maximally leverage the data binding capabilities of the platform from day one. You can do this at multiple levels. In this article, I will discuss the following techniques: value converters, style selectors, and data template selectors. Here's a screenshot of the attached sample project: Using a Value Converter Dependency properties of a XAML control can be directly bound to properties of the viewModel. Sometimes a conversion of value and/or data type needs to take place; that can be done through a ValueConverter. A value converter is a class that implements the IValueConverter interface, with just Convert and ConvertBack functions. Here's an example of a string-to-string converter that translates official beer colors into HTML named colors: using System; using Windows.UI.Xaml.Data; public class BeerColorConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, string language) { switch (value.ToString()) { case "Yellow": return "LemonChiffon"; case "Straw": return "Wheat"; case "Gold": return "GoldenRod"; case "Amber": case "LightCopper": case "Copper": return "Peru"; case "LightBrown": return "Chocolate"; case "Brown": return "Brown"; case "DarkBrown": return "darkRed"; case "VeryDarkBrown": return "SaddleBrown"; case "Black": return "Black"; default: return "Lime"; } } public object ConvertBack(object value, Type targetType, object parameter, string language) { return value; } } Just define an instance of the converter as a resource: <Page.Resources> <local:BeerColorConverter x:Key="BeerColorConverter" /> </Page.Resources> And use it in the binding: <TextBlock Text="{Binding Name}" Foreground="{Binding Color, Converter={StaticResource BeerColorConverter}}" /> Unfortunately Metro doesn't ship with MultiBinding, and bindings don't come with a StringFormat option. That definitely restricts the usage of a value converter, compared to WPF and Silverlight. Using a Style Selector Another way of dynamically styling items in a XAML list control, is with a StyleSelector. This provides a way to apply styles based on custom logic. This seems to be the only way to create a dynamic style in Metro, due to the absence of DataTriggers. Just create a subclass of the StyleSelector class and implement the SelectStyle method. The bound item is the first parameter, the item's style is the return value. Here's a small example on how to modify the style for a list view item: public class BeerStyleSelector: StyleSelector { protected override Style SelectStyleCore(object item, DependencyObject container) { Style style = new Style(typeof(ListViewItem)); style.Setters.Add(new Setter(ListViewItem.ForegroundProperty, new SolidColorBrush(Colors.Red))); return style; } } Again, the class containing the logic should be defined as a resource: <Page.Resources> <local:BeerStyleSelector x:Key="BeerStyleSelector" /> </Page.Resources> You can then assign the ItemContainerStyleSelector to an instance of it: <ListView ItemsSource="{Binding BelgianBeers}" ItemContainerStyleSelector="{StaticResource BeerStyleSelector}" /> I did not elaborate this example to much, since unfortunately style selectors don't work in the Consumer Preview. Hopefully the problem will be solved next week, with the Release Preview. Using a Data Template Selector The most powerful way of dynamically changing XAML, is using a DataTemplateSelector, which allows you to choose or generate a DataTemplate based on the data object and the data-bound element. To create a template selector, create a class that inherits from DataTemplateSelector and override the SelectTemplate method. Once your class is defined you can assign an instance of the class to the template selector property of your element. The business logic could be as simple a selecting an existing template from a resource, like this.DefaultTemplate in the following code snippet. But the entire template could also be generated on the spot through a XamlReader. The following chauvinist logic creates a colored stackpanel including a Belgian flag for superior beers, and applies the default template to the rest: using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Markup; public class BeerTemplateSelector : DataTemplateSelector { public DataTemplate DefaultTemplate { get; set; } protected override DataTemplate SelectTemplateCore(object item, DependencyObject container) { Beer beer = item as Beer; if (!(beer.Category.Contains("Belgian") || beer.Category.Contains("Flemish"))) { return this.DefaultTemplate; } BeerColorConverter converter = new BeerColorConverter(); string backGroundColor = converter.Convert((item as Beer).Color, null, null, null).ToString(); string template = @" <DataTemplate xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'> <StackPanel Background='" + backGroundColor + @"' Width='360'> <TextBlock Text='{Binding Name}' FontWeight='Bold' Margin='2' Foreground='Black' HorizontalAlignment='center' /> <Grid> <TextBlock Text='{Binding Category}' Margin='2' Foreground='Black' /> <Image Source='../Assets/belgianFlag.png' Margin='2' HorizontalAlignment='Right' Stretch='None' /> </Grid> </StackPanel> </DataTemplate>"; return XamlReader.Load(template) as DataTemplate; } } Again, register the class contining the logic as a resource: <Page.Resources> <DataTemplate x:Key="DefaultDataTemplate"> <StackPanel> <TextBlock Text="{Binding Name}" Foreground="{Binding Color, Converter={StaticResource BeerColorConverter}}" FontWeight="Bold" /> <TextBlock Text="{Binding Category}" Foreground="{Binding Color, Converter={StaticResource BeerColorConverter}}" /> </StackPanel> </DataTemplate> <local:BeerTemplateSelector x:Key="BeerTemplateSelector" DefaultTemplate="{StaticResource DefaultDataTemplate}" /> </Page.Resources> Then use an instance as ItemTemplateSelector: <ListView ItemsSource="{Binding BelgianBeers}" ItemTemplateSelector="{StaticResource BeerTemplateSelector}" /> With a DataTemplateSelector you're not restricted to dependency properties. Source Code Here's the source code of the sample project. It was written with Visual Studio 11 Express Beta, for the Windows 8 Consumer Preview: U2UConsult.Metro.DynamicTemplating.zip (70,30 kb) Enjoy!
Using the CarouselPanel in Windows 8 Metro 28 March 2012 Diederik-Krols Metro, WinRT Warning: content applies to Windows 8 Consumer Preview only. In the current release, the CarouselPanel can not be used in stand alone mode anymore. This short article describes how to use the CarouselPanel in a Windows 8 Consumer Preview Metro style app. Don't expect the spectacular rotating 3D effects from the Infragistics WPF control with the same name. In the current release, a Metro CarouselPanel is nothing more than a vertically revolving conveyer belt. That's good enough: the CarouselPanel is one of those controls that keeps your design away from the traditional dull grid-and-combobox layout that we know from excel or standard winforms databinding. Here's a WPF example; it's a prototype of an application that allows you to mix your own cocktails by selecting the products and entering the corresponding quantity: If you would sell this app through the store, you probably won't get rich. Grid-and-combobox design is so passé. On the other hand: if you would generate views on the same data model using the default ASP.NET MVC templates, you'll get exactly the same lame design... Anyway, here's how this application may look like in Metro style. It's operated by swiping and tapping on the products carousel on the left, instead of fighting with comboboxes and empty grid rows: The vertical strip on the left is an instance of a CarouselPanel. The CarouselPanel was already available in the Developer Preview of Windows 8, but only as a part of the ComboBox control template. In the Consumer Preview you can use it as ItemsPanelTemplate of any ItemsControl. <ListView ItemsSource="{Binding YourCollectionHere}"> <ListView.ItemTemplate> <DataTemplate> <!-- Your Data Template Here --> </DataTemplate> </ListView.ItemTemplate> <ItemsControl.ItemsPanel> <ItemsPanelTemplate> <CarouselPanel /> </ItemsPanelTemplate> </ItemsControl.ItemsPanel> </ListView> That's all there is! You can scroll via touch or via the mouse wheel. When you reach the end of the list, you can continue scrolling in the same direction. After the end of the list an empty placeholder is inserted, and then everything starts all over again. The following screenshot shows the placeholder - between the end of the list (pineapple juice) and the new beginning (white rum): For those who want to use the control in a horizontal orientation (e.g. as a filmstrip), here's the good news: there is a CanHorizontallyScroll property in the documentation and in Visual Studio's Designer: Here's the bad news: setting the property gives a compile time error. The horizontal strip in the demo app is just a GridView; you have to move back and forth to have an overview of all ingredients. Anyway, the existence of the CanHorizontallyScroll property (and some of the other non-functioning class members) indicates that Microsoft has plans to do more with this control in the future. Here's the code of the demo app. It was written in Visual Studio 11 Express Beta for Windows 8 Consumer Preview: U2UConsult.WinRT.CarouselSample.zip (770,55 kb) Enjoy !
A StringFormat converter for Windows 8 Metro 20 March 2012 Diederik-Krols Metro, WinRT Some people seem to think that the world has only one language, one currency, and one way to represent a date, a time, or a number. So WinRT comes without a StringFormat option in data binding. I know for sure that I never built a WPF or Silverlight application without extensively using that StringFormat option. Just think about how your calendar or online banking application would look like without the proper content formatting: "Dear customer, at 3/20/2012 6:23:30 PM the balance of your bank account BE43068999999501 was at -850325, so we will withdraw that amount from your credit card 4417123456789113. Please call +442071311117 for further assistance." You simply can't get away with that, so I believe that one of the next Metro versions *will* come with string formatting in bindings. In the mean time, we will have to implement formatting in the viewmodel, or help ourselves using a value converter. Here's an example of the latter. It supports two-way bindings, and it doesn't crash when you forget to specify the format: public class StringFormatConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, string language) { // No format provided. if (parameter == null) { return value; } return String.Format((String)parameter, value); } public object ConvertBack(object value, Type targetType, object parameter, string language) { return value; } } Just define it as a resource in XAML: <Page.Resources> <local:StringFormatConverter x:Key="StringFormatConverter" /> </Page.Resources> And use it: <TextBlock Text="{Binding SomeDateTime, Converter={StaticResource StringFormatConverter}, ConverterParameter='{}{0:dd MMM yyyy}'}" /> It works well against strings, dates, times, and numbers: Here's the full source. It was built with Visual Studio 11 Express Beta, for the Windows 8 Consumer Preview: U2UConsult.WinRT.StringFormatConverterSample.zip (170,03 kb) Enjoy!
A Radial Gauge custom control for Windows 8 Metro 09 March 2012 Diederik-Krols Metro, WinRT A long time ago in a galaxy actually not so far away, Colin Eberhardt wrote a Radial Gauge custom control for Silverlight in less than six hours. It had everything a gauge should have: a needle, and a scale with tick markers and labels. Hell, it even had colored quality range indicators. A couple of months ago, I thought "Hey, I can do that too." So I started a new Metro project in my Developer Preview of Windows 8. I tried to create a Metro version of that same radial gauge in less than six hours. Six lonely nights later, I still had absolutely nothing, and I started to consider anger management training. I gave up and started from scratch, this time on a much simpler control: a minimalistic slider. That involved a lot of work with -let's be honest- a rather lame result. The Developer Preview was clearly not an ideal environment for building custom controls. But that was then and this is now. Yesterday, Tim Heuer revealed how to build a deployable custom control for XAML Metro style apps with the Consumer Preview of Windows 8. I discovered that the current beta of Visual Studio 11 comes with a template for a Templated Control. I happily noticed the return of the themes/generic.xaml file that we know from all the other XAML platforms. So I decided to have another go on the radial gauge custom control. Here's the result: Colin's Silverlight Gauge Diederik's Metro Gauge Here's how the gauge is defined in XAML: <local:RadialGaugeControl Maximum="100" Minimum="-100" Value="65" Unit="°C" Height="200"> <local:RadialGaugeControl.QualitativeRanges> <local:QualitativeRange Color="#FFFFFFFF" Maximum="0" /> <local:QualitativeRange Color="#FFFFFF00" Maximum="40" /> <local:QualitativeRange Color="#FFFFA500" Maximum="80" /> <local:QualitativeRange Color="#FFFF0000" Maximum="100" /> </local:RadialGaugeControl.QualitativeRanges> </local:RadialGaugeControl> The Metro version of the radial gauge is simplified in many ways:• It has the Metro look-and-feel: it's sharp, without gradients or shadows. No chrome, but content!• It does not delegate its look-and-feel to a separate viewmodel. All code is hosted by the control class itself.• I simplified most of the calculations by creating a fixed size control and wrapping it in a Viewbox. After all, the control only contains vector graphics: Ellipse, Path, and Text. There's probably more room for simplification and improvement, but this was a race against time, remember. At the end or the day, I'm happy with the result. Here's how the attached sample solution looks like. By the way, EBC -the unit of color- stands for European Brewing Convention (the European alternative for Lovibond) and EBU -the unit of bitterness- stands for European Bitterness Unit (the European version of IBU): The conversion from Silverlight to Metro went very fast. The Consumer Preview version of Metro uses the same paradigms (and name spaces!) as the existing XAML platforms, so I'm going to spare you the details. A custom control is defined by a control class with dependency properties, and a generic.xaml file with the default style definition: So if you can write a custom control in Silverlight, you can write a custom control in Metro! Sometimes you may have to work your way around some glitches. I noticed that the rotation transformation on the needle path isn't fired if it's defined in XAML. The values are correctly assigned by the TemplateBinding, but the transformation does not happen: <Path.RenderTransform> <RotateTransform Angle="{TemplateBinding ValueAngle}" /> </Path.RenderTransform> So I moved it to the code behind: Path needle = this.GetTemplateChild(NeedlePartName) as Path; needle.RenderTransform = new RotateTransform() { Angle = this.ValueAngle }; I still had a lot of time left in my six hour frame, when I started the implementation of the qualitative ranges - the nicely colored arc segments. I discovered rapidly that there are no TypeConverters for WinRT structs such as Color. There is also no support for custom TypeConverters. Bummer. I had no choice but to redefine the Color property as a String - instead of a 'real' Color type: public class QualitativeRange { public double Maximum { get; set; } public String Color { get; set; } } That type change comes with a huge price: say "bye-bye Visual Studio Designer support" when assigning a Color in XAML. I then had to figure out how to translate the color string into a Brush, because there is no ColorConverter.ConvertFromString in Metro. So back to anger management training... ... or not. I decided to drop support for named colors and restrict the color values to hexadecimal codes only. I then upgraded an old HexToColor converter that I found in one of my legacy Silverlight (2.0?) frameworks. That definitely does the trick - at least if the input falls in the expected range: namespace U2UConsult.Metro.RadialGauge { using System; using Windows.UI; using Windows.UI.Xaml.Data; using Windows.UI.Xaml.Media; public class HexToColorConverter : IValueConverter { /// <summary> /// Converts a hexadecimal string value into a Brush. /// </summary> public object Convert(object value, Type targetType, object parameter, string language) { byte alpha; byte pos = 0; string hex = value.ToString().Replace("#", ""); if (hex.Length == 8) { alpha = System.Convert.ToByte(hex.Substring(pos, 2), 16); pos = 2; } else { alpha = System.Convert.ToByte("ff", 16); } byte red = System.Convert.ToByte(hex.Substring(pos, 2), 16); pos += 2; byte green = System.Convert.ToByte(hex.Substring(pos, 2), 16); pos += 2; byte blue = System.Convert.ToByte(hex.Substring(pos, 2), 16); return new SolidColorBrush(Color.FromArgb(alpha, red, green, blue)); } /// <summary> /// And back again. /// </summary> public object ConvertBack(object value, Type targetType, object parameter, string language) { SolidColorBrush val = value as SolidColorBrush; return "#" + val.Color.A.ToString() + val.Color.R.ToString() + val.Color.G.ToString() + val.Color.B.ToString(); } } } To make the gauge interactive, I hooked a change event handler to the dependency property: public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(double), typeof(RadialGaugeControl), new PropertyMetadata(0.0, OnValueChanged)); private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { RadialGaugeControl c = (RadialGaugeControl)d; // ... } } Unfortunately neither a GetTemplateChild nor a FindName method call were able to find the Needle element: both retrieved a null value. So I created a private field for the Needle, populated the field in the OnApplyTemplate call: this.Needle = this.GetTemplateChild(NeedlePartName) as Path; Then it became accessible in the change event handler: if (c.Needle != null) { c.Needle.RenderTransform = new RotateTransform() { Angle = c.ValueAngle }; } And the radial gauge became truly bindable: Here's the code for the gauge control and the client app. It was written with Visual Studio 11 Express Beta for the Windows 8 Consumer Preview: U2UConsult.Metro.RadialGauge.zip (399,29 kb) Enjoy !