Blog

posts

Windows 8.1 XAML Style for an AccentButton

0 Comments
By Fons Sonnemans, 14-4-2014

When you create a XAML application in Windows 8.1, you get a few built-in styles for TextBlock controls, such as: HeaderTextBlockStyle, TitleTextBlockStyle and SubtitleTextBlockStyle. Unfortunately there is no AccentButtonStyle. An AccentButton is a button with a special accent background color. I thought I’d try to create this Style. You can apply this Style to a Button. Optionally, you can set the Background property of the Button to a custom color.

AccentButtons sample

In the above example I have two StackPanels; the first with the RequestedTheme set to Dark, the second to Light. All Buttons in the StackPanel have the AccentButtonStyle. The bottom buttons have no Background property set. The 'Disabled' buttons have an IsEnabled property set to false.

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

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="1*" />
            <ColumnDefinition Width="1*" />
        </Grid.ColumnDefinitions>
        <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
              RequestedTheme="Dark">
            <TextBlock Style="{StaticResource SubtitleTextBlockStyle}"
            <StackPanel HorizontalAlignment="Center"
                        VerticalAlignment="Center">
                <Button HorizontalAlignment="Stretch"
                        VerticalAlignment="Stretch"
                        Background="Red"
                        Content="Red"
                        Style="{StaticResource AccentButtonStyle}" />
                <Button HorizontalAlignment="Stretch"
                        VerticalAlignment="Stretch"
                        Background="Blue"
                        Content="Blue"
                        Style="{StaticResource AccentButtonStyle}" />
                <Button HorizontalAlignment="Stretch"
                        VerticalAlignment="Stretch"
                        Background="LightGray"
                        Content="Gray"
                        Style="{StaticResource AccentButtonStyle}" />
                <Button HorizontalAlignment="Stretch"
                        VerticalAlignment="Stretch"
                        Background="Blue"
                        Content="Disabled Blue"
                        IsEnabled="False"
                        Style="{StaticResource AccentButtonStyle}" />
                <Button HorizontalAlignment="Stretch"
                        VerticalAlignment="Stretch"
                        Content="Default"
                        Style="{StaticResource AccentButtonStyle}" />
            </StackPanel>
        </Grid>
        <Grid Grid.Column="1"
              Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
              RequestedTheme="Light">
            <StackPanel HorizontalAlignment="Center"
                        VerticalAlignment="Center">
                <Button HorizontalAlignment="Stretch"
                        VerticalAlignment="Stretch"
                        Background="Red"
                        Content="Red"
                        Style="{StaticResource AccentButtonStyle}" />
                <Button HorizontalAlignment="Stretch"
                        VerticalAlignment="Stretch"
                        Background="Blue"
                        Content="Blue"
                        Style="{StaticResource AccentButtonStyle}" />
                <Button HorizontalAlignment="Stretch"
                        VerticalAlignment="Stretch"
                        Background="LightGray"
                        Content="Gray"
                        Style="{StaticResource AccentButtonStyle}" />
                <Button HorizontalAlignment="Stretch"
                        VerticalAlignment="Stretch"
                        Background="Blue"
                        Content="Disabled Blue"
                        IsEnabled="False"
                        Style="{StaticResource AccentButtonStyle}" />
                <Button HorizontalAlignment="Stretch"
                        VerticalAlignment="Stretch"
                        Content="Default"
                        Style="{StaticResource AccentButtonStyle}" />
            </StackPanel>
        </Grid>

    </Grid>
</Page>

AccentButtonStyle

The AccentButtonStyle is distributed in a resource dictionary through NuGet to make it easy to add it to your project and apply to your buttons. You will have to add this AccentButton.xaml ResourceDictionaries to the MergedDictionaries of your app.xaml or your pages or your controls.

<Application
    x:Class="AccentButtonDemo.App"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:AccentButtonDemo">
	<Application.Resources>
        
		<ResourceDictionary>

            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="AccentButton.xaml" />
			</ResourceDictionary.MergedDictionaries>

        </ResourceDictionary>
        
	</Application.Resources>

</Application>

 

Closure and download

I hope you like my solution. You can download the sample project below.

Cheers,

Fons

Windows 8 XAML Tips - Combining Converters

1 Comments
By Fons Sonnemans, 23-12-2013

XAML is a declarative UI language; it's most powerful feature is surely data binding. In XAML you can set a converter on any binding. You use this when you need to display data in a format that differs from how it is stored. But you can only set ONE converter. So if you for instance want to convert a date using a FormatConverter and you want to convert the result to uppercase using a UppercaseConverter you are stuck. To solve this problem I have created a ValueConverterGroup class. It is a converter which contains multiple converters, it is a List of IValueConverter objects and it implements the IValueConverter interface. The Convert() method calls the Convert() method of all containing value converters. I haven't implement the ConvertBack() method because I won't be using it.

ValueConverterGroup

using System;
using System.Collections.Generic;
using System.Linq;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Data;

namespace ReflectionIT.Windows8.Converters {

    public class ValueConverterGroup : List<IValueConverter>, IValueConverter {

        public object Convert(object value, Type targetType, object parameter, 
                                  string language) {
            return this.Aggregate(value, (current, converter) =>
                current == DependencyProperty.UnsetValue ? 
                    current : 
                    converter.Convert(current, targetType, parameter, language)
            );
        }

        public object ConvertBack(object value, Type targetType, object parameter, 
                                  string language) {
            System.Diagnostics.Debugger.Break(); // Not Implemented
            throw new NotImplementedException();
        }
    }
}

Example

The following sample page contains a DatePicker control and two TextBlock controls. The Text property of the TextBlocks are databound to the Date property of the DatePicker. The first (red) TextBlock uses a FormatConverter to convert the date to dd-MMM-yyyy string. The second (blue) TextBlock uses my ValueConverterGroup to combine a FormatConverter and an UppercaseConverter.

The two converters are static resources which are defined in the Page resources.

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


    <Page.Resources>
        <c:FormatConverter x:Key="DateFormat"
                           FormatString="{}{0:dd-MMM-yyyy}" />

        <c:ValueConverterGroup x:Key="UppercaseDateFormat">
            <c:FormatConverter FormatString="{}{0:dd-MMM-yyyy}" />
            <c:UppercaseConverter />
        </c:ValueConverterGroup>
    </Page.Resources>

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <DatePicker x:Name="datePicker"
                    HorizontalAlignment="Left"
                    VerticalAlignment="Top"
                    Margin="136,85,0,0" />
        <TextBlock HorizontalAlignment="Left"
                   TextWrapping="Wrap"
                   Text="{Binding Date, Converter={StaticResource DateFormat},
                                   ElementName=datePicker}"
                   VerticalAlignment="Top"
                   Margin="136,164,0,0"
                   FontSize="64"
                   Foreground="Red" />
        <TextBlock HorizontalAlignment="Left"
                   TextWrapping="Wrap"
                   Text="{Binding Date, Converter={StaticResource UppercaseDateFormat},
                                   ElementName=datePicker}"
                   VerticalAlignment="Top"
                   Margin="136,283,0,0"
                   FontSize="64"
                   Foreground="Blue" />
    </Grid>
</Page>

As you can see in the next screenshot the blue TextBlock is showing the date in a dd-MMM-yyyy format in uppercase.

CombiningConverters

FormatConverter

The code of the FormatConverter class used in the example.

using System;
using System.Globalization;
using Windows.UI.Xaml.Data;

namespace ReflectionIT.Windows8.Converters {

    public class FormatConverter : IValueConverter {

        public string FormatString { get; set; }

        public object Convert(object value, Type typeName, object parameter, 
                                   string language) {

            string formatterString = (parameter as String) ?? FormatString;

            if (!string.IsNullOrEmpty(formatterString)) {
                return string.Format(formatterString, value);
            }

            return System.Convert.ToString(value);
        }

        public object ConvertBack(object value, Type typeName, object parameter, 
                                  string language) {
            System.Diagnostics.Debugger.Break(); // Not Implemented
            throw new NotImplementedException();
        }

    }
}

UppercaseConverter

The code of the UppercaseConverter class used in the example.

using System;
using Windows.UI.Xaml.Data;

namespace ReflectionIT.Windows8.Converters {

    class UppercaseConverter : IValueConverter {
        public object Convert(object value, Type targetType, object parameter, 
                                   string language) {
            return System.Convert.ToString(value).ToUpper();
        }

        public object ConvertBack(object value, Type targetType, object parameter, 
                                   string language) {
            System.Diagnostics.Debugger.Break(); // Not Implemented
            throw new NotImplementedException();
        }
    }
}

 

Closure and download

I hope you like my solution. Combining multiple converters is now very easy. You can download my code below.

Cheers,

Fons

Windows 8 XAML Tips - Popup.ShowAsync()

0 Comments
By Fons Sonnemans, 12-11-2013

In XAML applications you can use a Popup control to displays content on top of existing content. To show a Popup you must set the IsOpen property to true. You can subscribe yourself to the Closed eventhandler which is fired when the IsOpen property is (re)set to false. You can use this event to execute extra logic when the Popup is closed.

To make this easier I have created an ShowAsync() extension method which does this for you. I got my inspiration for this solution from this Awaiting Storyboard completion blog post by Nigel Sampson. He uses this technique to begin a storyboard using a BeginAsync() extension method.

private async void ButtonShowAsync_Click(object sender, RoutedEventArgs e) {
    (sender as Button).Content = "Clicked at " + DateTime.Now.ToString("HH:mm:ss");
    await this.popupTest.ShowAsync();
    (sender as Button).Content = "Closed at " + DateTime.Now.ToString("HH:mm:ss");
}

private void ButtonPopupIsOpen_Click(object sender, RoutedEventArgs e) {
    (sender as Button).Content = "Clicked at " + DateTime.Now.ToString("HH:mm:ss");
    this.popupTest.IsOpen = true;
    // You don't know when the popup was closed!
}

The ShowAsync() extension method uses an TaskCompletionSource. It allows us to create tasks behind the scenes and control whether they are completed, cancelled or failed. With this task you can build up some really handy mechanisms to improve the readability of your code.

static class Extensions {

    public static Task ShowAsync(this Popup popup) {

        TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();

        EventHandler<object> onClosed = null;
        onClosed = (s, e) => {
            popup.Closed -= onClosed;
            tcs.SetResult(true);
        };
        popup.Closed += onClosed;
        popup.IsOpen = true;

        return tcs.Task;
    }

}

The XAML used for this demo.

<Page x:Class="App4.MainPage"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:local="using:App4"
      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}">
        <StackPanel Margin="20">

            <Button Content="Popup.IsOpen"
                    Click="ButtonPopupIsOpen_Click" />

            <Button Content="Popup.ShowAsync"
                    Click="ButtonShowAsync_Click" />

        </StackPanel>

        <Popup x:Name="popupTest"
               Margin="150"
               IsLightDismissEnabled="True">
            <Grid Background="Red"
                  Width="200"
                  Height="200">
                <Button Content="Close"
                        HorizontalAlignment="Center"
                        Click="ButtonClose_Click" />
            </Grid>
        </Popup>
    </Grid>
</Page>

ExtensionMethod.net website

I have published this extension method on www.extensionmethod.net. It is a website where c#, vb.net, f# and javascript extension methods are shared. You can also find my Chooser.ShowAsync() extension method which you can use to show a Windows Phone Chooser (AddressChooserTask, EmailAddressChooserTask, PhoneNumberChooserTask, PhotoChooserTask) using this technique.

private async void Button_Click(object sender, RoutedEventArgs e) {
    try {
        var c = new PhotoChooserTask();
        var result = await c.ShowAsync();
        var bi = new BitmapImage();
        bi.SetSource(result.ChosenPhoto);
        imagePhoto.Source = bi;
    } catch (Exception ex) {
        MessageBox.Show(ex.Message);
    }
}

Closure and download

I hope you like my solution. My demo is writen as a Windows Store app but you can also use it in Windows Phone, Silverlight and WPF. You can download my code below.

Cheers,

Fons

Windows 8 XAML Tips - Conditional Behaviors

2 Comments
By Fons Sonnemans, 21-10-2013

A few weeks ago I have written a blog post about how you can write your own Behaviors and Actions for Windows 8.1. I noticed that the Windows 8.1 Actions are not compatible with the Silverlight and Windows Phone Actions. Those actions have a IsEnabled property.

The Windows 8.1 Actions don't have an IsEnabled property.

Luckily I got a tip from the Microsoft XAML Tools team. A new feature that is added to the Behaviors SDK is that Actions can return results as well as have its own ActionLists. This helps in relaying execution results or building conditional behaviors. 

IsEnabledCondition

A have written a simple class called IsEnabledCondition. This class implements the IAction interface which makes it an Action. It has two dependency properties: IsEnabled and Actions. The Actions property is set as the ContentProperty using the ContentPropertyAttribute on the class. The Execute() method only executes the contained actions when the IsEnabled property is set. I learned this ActionList from reading Mark Smith Behaviors in Windows 8.1 Store Apps blog post.

using System;
using Microsoft.Xaml.Interactivity;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Markup;

namespace ConditionalBehaviorsDemo.Behaviors {

    [ContentProperty(Name = "Actions")]
    public class IsEnabledCondition : DependencyObject, IAction {


        #region IsEnabled Dependency Property

        /// <summary> 
        /// Get or Sets the IsEnabled dependency property.  
        /// </summary> 
        public bool IsEnabled {
            get { return (bool)GetValue(IsEnabledProperty); }
            set { SetValue(IsEnabledProperty, value); }
        }

        /// <summary> 
        /// Identifies the IsEnabled dependency property. 
        /// This enables animation, styling, binding, etc...
        /// </summary> 
        public static readonly DependencyProperty IsEnabledProperty =
            DependencyProperty.Register("IsEnabled",
                                        typeof(bool),
                                        typeof(IsEnabledCondition),
                                        new PropertyMetadata(true));

        #endregion IsEnabled Dependency Property

        #region Actions Dependency Property

        /// <summary>
        /// Actions collection
        /// </summary>
        public ActionCollection Actions {
            get {
                var actions = (ActionCollection)base.GetValue(ActionsProperty);
                if (actions == null) {
                    actions = new ActionCollection();
                    base.SetValue(ActionsProperty, actions);
                }
                return actions;
            }
        }

        /// <summary>
        /// Backing storage for Actions collection
        /// </summary>
        public static readonly DependencyProperty ActionsProperty =
            DependencyProperty.Register("Actions", 
                                        typeof(ActionCollection), 
                                        typeof(IsEnabledCondition), 
                                        new PropertyMetadata(null));

        #endregion Actions Dependency Property

        public object Execute(object sender, object parameter) {
            if (this.IsEnabled) {
                return Interaction.ExecuteActions(sender, this.Actions, parameter);
            }
            return null;
        }
    }
}

I use this action as the parent of my previously used ControlStoryBoardAction. The IsEnabled property is databound to the IsOn property of the ToggleSwitch. Databinding is possible because the IsEnabled property is an Dependency Property. If you start the application you can click the button it moves to the right and then comes back. If you turn the 'Animate' switch Off an then click the button nothing happens.

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <Button x:Name="button"
            Content="Button"
            HorizontalAlignment="Left"
            Height="97"
            Margin="90,72,0,0"
            VerticalAlignment="Top"
            Width="194"
            RenderTransformOrigin="0.5,0.5">
        <Button.RenderTransform>
            <CompositeTransform />
        </Button.RenderTransform>
        <Interactivity:Interaction.Behaviors>
            <Core:EventTriggerBehavior EventName="Click">
                <Behaviors:IsEnabledCondition 
                    IsEnabled="{Binding IsOn, ElementName=toggleSwitchAnimate}">
                    <Media:ControlStoryboardAction 
                        Storyboard="{StaticResource Storyboard1}" />
                </Behaviors:IsEnabledCondition>
            </Core:EventTriggerBehavior>
        </Interactivity:Interaction.Behaviors>
    </Button>
    <ToggleSwitch x:Name="toggleSwitchAnimate"
                    Header="Animate"
                    HorizontalAlignment="Left"
                    Margin="87,220,0,0"
                    VerticalAlignment="Top"
                    IsOn="True" />

</Grid>

Closure and download

I hope you like my solution. Maybe you can use it in your apps too. I plan to write many more conditional actions so check out my blogs for more posts. You can download my code below.

Cheers,

Fons

Windows Phone Week - Slides

0 Comments
By Fons Sonnemans, 8-10-2013

Op 5 oktober heb ik een 'Designing WP apps UI using Blend' presentatie verzorgt op de Windows Phone Developer Day. Deze dag was perfect georganiseerd door Joost van Schaik, Tom Verhoeff en Dennis Vroegop. Er waren ruim 60 deelnemers en de locatie bij Macaw was natuurlijk top. Meer info op http://www.dotned.nl/register/66/zaterdag-5-oktober--windows-phone-developer-day-.aspx

De slides zijn via onderstaande download link te bekijken. Voor vragen kan je natuurlijk altijd contact met mij opnemen.

Fons

Windows 8 XAML Tips - Creating Blend Behaviors

0 Comments
By Fons Sonnemans, 16-9-2013

The new version of Blend for Visual Studio 2013 RC now supports Behaviors. I have used Behaviors for developing Silverlight and Windows Phone applications a lot. Now you can use them for you Windows Store XAML apps too. Two type of Behaviors are supported: Behaviors and Actions. Triggers are "dropped" and can/should now be implemented using Behaviors. 

There are 7 build-in Actions: CallMethodAction, ChangePropertyAction, ControlStoryboardAction, GoToStateAction, InvokeCommandAction, NavigateToPageAction and PlaySoundAction. Many were already available in Silverlight. The NavigateToPageAction is new.

There are 3 build-in Behaviors: DataTriggerBehavior, EventTriggerBehavior and IncrementalUpdateBehavior.

You can create your own Actions and Behaviors.  Before you can do that you must add a reference to the 'Behaviors SDK'.

Creating Actions

You create an Action by adding a class to your project which derives from the DependencyObject class and implements the IAction interface. In Silverlight there was a TriggerAction base class, but that no longer exists. The IAction interface has only one Execute() method you should implement.

As an example I have created a simple ShowMessageAction. It contains a Text dependency property which you can set or databind. The Execute() method will show this Text in a MessageDialog.

using Microsoft.Xaml.Interactivity;
using System;
using Windows.UI.Popups;
using Windows.UI.Xaml;

namespace BehaviorsDemo.Behaviors {
    
    public class ShowMessageAction : DependencyObject, IAction {

        #region Text Dependency Property

        /// <summary> 
        /// Get or Sets the Text dependency property.  
        /// </summary> 
        public string Text {
            get { return (string)GetValue(TextProperty); }
            set { SetValue(TextProperty, value); }
        }

        /// <summary> 
        /// Identifies the Text dependency property. This enables animation, styling, 
        /// binding, etc...
        /// </summary> 
        public static readonly DependencyProperty TextProperty =
            DependencyProperty.Register("Text",
                                        typeof(string),
                                        typeof(ShowMessageAction),
                                        new PropertyMetadata(string.Empty));

        

        #endregion Text Dependency Property

        public object Execute(object sender, object parameter) {
#pragma warning disable 4014
            new MessageDialog(this.Text).ShowAsync();
#pragma warning restore
            return null;
        }
    }
}

Use ShowMessageAction

After you have compiled the application you should switch to Blend to add this behavior to a control. I my case I have created a simple page containing a TextBox and a Button. In the 'Assets' window you should select the 'Behaviors'. You should see the 'ShowMessageAction' at the bottom of the list. Drag this action and drop it on the Buttton. The Text property of the action is databound to the Text property of the TextBox.

The ShowMessageAction is placed in an EventTriggerBehavior which has its EventName set to 'Click'. You can of course select a different event if you want. You can also select a different SourceObject. This is the object whose event is handled to trigger the action. The EventTriggerBehavior replaces the EventTrigger of Silverlight.

The XAML of this page.

<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:local="using:BehaviorsDemo"
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
      xmlns:Interactivity="using:Microsoft.Xaml.Interactivity"
      xmlns:Core="using:Microsoft.Xaml.Interactions.Core"
      xmlns:Behaviors="using:BehaviorsDemo.Behaviors"
      x:Class="BehaviorsDemo.MainPage"
      mc:Ignorable="d">

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
          DataContext="{Binding Source={StaticResource SampleDataSource}}">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="85*" />
        </Grid.RowDefinitions>
        <StackPanel Orientation="Horizontal">
            <TextBox x:Name="textBoxDemo"
                     HorizontalAlignment="Left"
                     TextWrapping="Wrap"
                     Text="Hello World"
                     Width="400" />
            <Button Content="Show"
                    HorizontalAlignment="Left"
                    VerticalAlignment="Top">
                <Interactivity:Interaction.Behaviors>
                    <Core:EventTriggerBehavior EventName="Click">
                        <Behaviors:ShowMessageAction Text="{Binding Text, ElementName=textBoxDemo}" />
                    </Core:EventTriggerBehavior>
                </Interactivity:Interaction.Behaviors>
            </Button>
        </StackPanel>
    </Grid>
</Page>

When you run the app and click the Button the text of the TextBox is shown in a MessageDialog. I think this is very cool. Designers can now add interactivity to the app without writing code.

Creating Behaviors

3 years ago I wrote this blog post Keyboard selection on Silverlight ListBox and ComboBox. It describes a Behavior which I used in my Silverlight apps. You could drop the Behavior on a ListBox or ComboBox. This enabled keyboard selection. So, if you pressed the key 'X', the ListBox/ComboBox would select the first row which started with the letter X. I always found this Behavior very userful. So I converted it to the new Windows 8.1 Behaviors. This allows me to use it for the Windows 8 DataControls (GridView, ListView and FlipView) without any extra programming effort.

You create a Behavior by adding a class to your project which derives from the DependencyObject class and implements the IBehavior interface. In Silverlight there was a Behavior and Behavior<T> base class, but that no longer exists. To make my conversion easier I re-created the Behavior<T> base class. It is an abstract class with an AssociatedObject (oftype T) property and two virtual methods OnAttached() and OnDetatching().

using Microsoft.Xaml.Interactivity;
using System;
using Windows.UI.Xaml;

namespace BehaviorsDemo.Behaviors {

    public abstract class Behavior<T> : DependencyObject, IBehavior where T : DependencyObject {

        [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
        public T AssociatedObject { get; set; }

        protected virtual void OnAttached() {
        }

        protected virtual void OnDetaching() {
        }

        public void Attach(Windows.UI.Xaml.DependencyObject associatedObject) {
            this.AssociatedObject = (T)associatedObject;
            OnAttached();
        }

        public void Detach() {
            OnDetaching();
        }

        DependencyObject IBehavior.AssociatedObject {
            get { return this.AssociatedObject; }
        }
    }
}

The KeyboardSelectionBehavior class is almost a one-on-one port of my Silverlight code. I just changed some namespaces and cleaned up some code. The ListBoxItem is now replaced by a SelectorItem.

using System;
using System.Collections.Generic;
using System.Linq;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;

namespace BehaviorsDemo.Behaviors {

    /// <summary>
    /// This behavior can be attached to a ListBox or ComboBox to 
    /// add keyboard selection
    /// </summary>
    public class KeyboardSelectionBehavior : Behavior<Selector> {

        private bool _boundToComboBoxItemsPanelRoot;

        /// <summary>
        /// Gets or sets the Path used to select the text
        /// </summary>
        public string SelectionMemberPath { get; set; }

        /// <summary>
        /// Attaches to the specified object: subscribe on KeyDown event
        /// </summary>
        protected override void OnAttached() {
            base.OnAttached();
            this.AssociatedObject.KeyDown += DoKeyDown;

            // subscribe on KeyDown event of the ItemsPanelRoot
            // of a ComboBox when it is opened
            var cb = this.AssociatedObject as ComboBox;
            if (cb != null) {
                cb.DropDownOpened += ComboBox_DropDownOpened;
            }
        }

        /// <summary>
        /// subscribe on KeyDown event of ItemsPanelRoot of a ComboBox
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ComboBox_DropDownOpened(object sender, object e) {
            var itemsPanelRoot = this.AssociatedObject.ItemsPanelRoot;
            if (_boundToComboBoxItemsPanelRoot == false && itemsPanelRoot != null) {
                _boundToComboBoxItemsPanelRoot = true;
                itemsPanelRoot.KeyDown += DoKeyDown;
            }
        }

        /// <summary>
        /// Detaches to the specified object: Unsubscribe on KeyDown event(s)
        /// </summary>
        protected override void OnDetaching() {
            this.AssociatedObject.KeyDown -= DoKeyDown;
            
            var cb = this.AssociatedObject as ComboBox;
            if (cb != null) {
                cb.DropDownOpened -= ComboBox_DropDownOpened;
                if (_boundToComboBoxItemsPanelRoot) {
                    var itemsPanelRoot = this.AssociatedObject.ItemsPanelRoot;
                    if (itemsPanelRoot != null) {
                        _boundToComboBoxItemsPanelRoot = false;
                        itemsPanelRoot.KeyDown -= DoKeyDown;
                    }
                }
            }
            base.OnDetaching();
        }

        /// <summary>
        /// Select the correct item
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void DoKeyDown(object sender, KeyRoutedEventArgs e) {
            // Create a list of strings and indexes
            int index = 0;
            IEnumerable<Item> list = null;

            var path = SelectionMemberPath ??
                this.AssociatedObject.DisplayMemberPath;

            var evaluator = new BindingEvaluator();
            if (path != null) {
                list = this.AssociatedObject.Items.OfType<object>()
                    .Select(item => {
                        // retrieve the value using the supplied Path
                        var binding = new Binding();
                        binding.Path = new PropertyPath(path);
                        binding.Source = item;

                        BindingOperations.SetBinding(evaluator,
                            BindingEvaluator.TargetProperty, binding);
                        var value = evaluator.Target;

                        return new Item {
                            Index = index++,
                            Text = Convert.ToString(value)
                        };
                    });
            } else {
                list = this.AssociatedObject.Items.OfType<SelectorItem>()
                    .Select(item => new Item {
                        Index = index++,
                        Text = Convert.ToString(item.Content)
                    });
            }
            // Sort the list starting at next selectedIndex to the end and 
            // then from the beginning
            list = list.OrderBy(item => item.Index <=
                this.AssociatedObject.SelectedIndex ?
                item.Index + this.AssociatedObject.Items.Count : item.Index);

            // Find first starting with 
            var text = e.Key.ToString();
            var first = list.FirstOrDefault(item => item.Text.StartsWith(text,
                StringComparison.CurrentCultureIgnoreCase));

            if (first != null) {
                // found
                this.AssociatedObject.SelectedIndex = first.Index;
            }
        }

        /// <summary>
        /// Helper class
        /// </summary>
        private class Item {
            public int Index { get; set; }
            public string Text { get; set; }
        }

        /// <summary>
        /// Helper class used for property path value retrieval
        /// </summary>
        private class BindingEvaluator : FrameworkElement {

            public static readonly DependencyProperty TargetProperty =
                DependencyProperty.Register(
                    "Target",
                    typeof(object),
                    typeof(BindingEvaluator), null);

            public object Target {
                get { return GetValue(TargetProperty); }
                set { SetValue(TargetProperty, value); }
            }

        }
    }
}

Use KeyboardSelectionBehavior

To demonstrate the KeyboardSelectionBehavior I added some sample data containing Employees to the Project. An Employee has a Name, Salary and Image property. I created a GridView in which the ItemSource is data bound to the Employees sample data.

Next, I dragged the KeyboardSelectionBehavior from the Assets Window and dropped it on the GridView.

The behavior has a SelectionMemberPath property to specify which property to use for selection. In this example it is set to the Name of the databound Employee.

The XAML of this page.

<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:local="using:BehaviorsDemo"
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
      xmlns:Interactivity="using:Microsoft.Xaml.Interactivity"
      xmlns:Core="using:Microsoft.Xaml.Interactions.Core"
      xmlns:Behaviors="using:BehaviorsDemo.Behaviors"
      x:Class="BehaviorsDemo.MainPage"
      mc:Ignorable="d">
    <Page.Resources>
        <DataTemplate x:Key="EmployeesItemTemplate">
            <Grid Height="110"
                  Width="480"
                  Margin="10">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="Auto" />
                    <ColumnDefinition Width="*" />
                </Grid.ColumnDefinitions>
                <Border Background="{StaticResource ListViewItemPlaceholderBackgroundThemeBrush}"
                        Width="110"
                        Height="110">
                    <Image Source="{Binding Image}"
                           Height="110"
                           Width="110" />
                </Border>
                <StackPanel Grid.Column="1"
                            Margin="10,0,0,0">
                    <TextBlock Text="{Binding Name}"
                               Style="{StaticResource TitleTextBlockStyle}" />
                    <TextBlock Text="{Binding Salary}"
                               Style="{StaticResource CaptionTextBlockStyle}"
                               TextWrapping="NoWrap" />
                </StackPanel>
            </Grid>
        </DataTemplate>
    </Page.Resources>

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
          DataContext="{Binding Source={StaticResource SampleDataSource}}">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="85*" />
        </Grid.RowDefinitions>
        <StackPanel Orientation="Horizontal">
            <TextBox x:Name="textBoxDemo"
                     HorizontalAlignment="Left"
                     TextWrapping="Wrap"
                     Text="Hello World"
                     Width="400" />
            <Button Content="Show"
                    HorizontalAlignment="Left"
                    VerticalAlignment="Top">
                <Interactivity:Interaction.Behaviors>
                    <Core:EventTriggerBehavior EventName="Click">
                        <Behaviors:ShowMessageAction Text="{Binding Text, ElementName=textBoxDemo}" />
                    </Core:EventTriggerBehavior>
                </Interactivity:Interaction.Behaviors>
            </Button>

        </StackPanel>
        <GridView ItemTemplate="{StaticResource EmployeesItemTemplate}"
                  ItemsSource="{Binding Employees}"
                  IsSwipeEnabled="False"
                  Grid.Row="1"
                  SelectionMode="Single">
            <Interactivity:Interaction.Behaviors>
                <Behaviors:KeyboardSelectionBehavior SelectionMemberPath="Name" />
            </Interactivity:Interaction.Behaviors>
        </GridView>

    </Grid>
</Page>

Run the app and focus the GridView. You can then press the key 'C' when and it will select 'Carpenter, Terry' employee.

Closure and download

I hope you like my solution. Maybe you can use it in your apps too. You can download my code below. My colleague MVP Timmy Kokke has created the http://www.blendbehaviors.net/ website which is used to share Behaviors and Actions. You will soon be able to download my ShowMessageAction and KeyBoardSelectionBehavior there as well.

Cheers,

Fons

Windows 8 XAML Tips - Inline data-driving Unit Testing

0 Comments
By Fons Sonnemans, 13-8-2013

While watching the Automated Testing of XAML-Based Windows Store Apps Build session on video I learned something new. There is a new way in Visual Studio 2013 to do data-driven unit testing using the DataRowAttribute. I couldn't find much documentation or samples so I will try to explain it in this blog post.

Calculator App

For this demo I created a 'silly' Calculator Windows Store application which uses C# and XAML. It has a public Calculator class which is used by the MainPage to add or subtract two values. This is the class which I want to unit test.

using System;

namespace UnitTestDemo {

    public class Calculator {

        public int Add(int a, int b) {
            return a + b;
        }

        public int Subtract(int a, int b) {
            return a - b;
        }
    }
}

The MainPage.xaml has two TextBox controls, two Buttons and a TextBlock to show the result in.

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

    <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
        <StackPanel VerticalAlignment="Center"
                    HorizontalAlignment="Center">
            <TextBox x:Name="textBoxA"
                     Text="3"
                     Header="A"
                     Margin="3"
                     Width="100" />
            <TextBox x:Name="textBoxB"
                     Text="2"
                     Header="B"
                     Margin="3"
                     Width="100" />
            <Button Content="Add"
                    Width="106"
                    Click="ButtonAdd_Click" />
            <Button Content="Subtract"
                    Width="106"
                    Click="ButtonSubtract_Click" />
            <TextBlock x:Name="textBlockResult"
                       Style="{StaticResource SubtitleTextBlockStyle}"
                       Width="100" />
        </StackPanel>
    </Grid>
</Page>

The MainPage.xaml.cs contains the click eventhandlers which use the Calculator class to execute the Add() and Subtract() methods. These methods are not Monkey proof, they don't contain any logic for checking valid integer input values.

using System;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;

namespace UnitTestDemo {

    public sealed partial class MainPage : Page {

        private Calculator _calc = new Calculator();

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

        private void ButtonAdd_Click(object sender, RoutedEventArgs e) {
            int result = _calc.Add(int.Parse(textBoxA.Text), int.Parse(textBoxB.Text));
            textBlockResult.Text = result.ToString();
        }

        private void ButtonSubtract_Click(object sender, RoutedEventArgs e) {
            int result = _calc.Subtract(int.Parse(textBoxA.Text), int.Parse(textBoxB.Text));
            textBlockResult.Text = result.ToString();
        }
    }
}

If you run the app it will look like this.

UnitTest Project

Next I added an extra 'Unit Test Library' project to the Solution.

I added a Reference from this UnitTest project to my Windows Store app. This will allow me to create an instance of the Calculator class which I want to test.

I renamed the default generated UnitTest1.cs file containing the UnitTest1 class to CalculatorUnitTest.cs. This will prompt to rename the class name too.

DataRow

In the CalculatorUnitTest class I added two test methods: TestAdd() and TestSubtract(). Both methods are decorated with the TestMethodAttribute. These methods have 3 parameters: a, b and expected. These parameters are used to calculate the result (actual) and to assert whether actual is equal to expected. The methods have also two DataRowAttributes which will be used to execute the test method twice using the given values (test data) for the parameter values. This makes it possible to do inline data-driven unit testing. You don't have to create a database or csv file with the test data.

using System;
using Microsoft.VisualStudio.TestPlatform.UnitTestFramework;
using UnitTestDemo;

namespace UnitTestLibrary1
{
    [TestClass]
    public class CalculatorUnitTest
    {
        [TestMethod]
        [DataRow(1, 2, 3)]
        [DataRow(4, 5, 9)]
        public void TestAdd(int a, int b, int expected)
        {
            Calculator c = new Calculator();
            int actual = c.Add(a, b);

            Assert.AreEqual(expected, actual);
        }

        [TestMethod]
        [DataRow(4, 2, 2)]
        [DataRow(4, 1, 1, DisplayName = "Should fail for demo purposes")] 
        public void TestSubtract(int a, int b, int expected) {
            Calculator c = new Calculator();
            int actual = c.Subtract(a, b);

            Assert.AreEqual(expected, actual);
        }
    }
}

When you run all tests you will get a Failed Test report for the TestSubtract method. The expected value should have been set to 3.

Closure

I will use the new DataRow attribute in my Unit Tests a lot. It will help me to write them more easily. I imagine you will do that too.

Cheers,

Fons

Microsoft MVP

1 Comments
By Fons Sonnemans, 2-7-2013

Gisteren ben ik voor de 1ste keer uitgeroepen tot Microsoft Most Valuable Professional (MVP) op het gebied van Client Development. De titel Microsoft MVP wordt uitsluitend toegekend aan geselecteerde specialisten die zich actief inzetten voor een community rondom een Microsoft product. Microsoft beloont deze personen voor hun inzet en hulp aan de gemeenschap door middel van de titel MVP.

Microsoft MVP Logo

Tags: MVP

Windows 8 XAML Tips - Peeking AppBar

1 Comments
By Fons Sonnemans, 21-5-2013

I have many app is the Windows 8 store now. My Sudoku Free app is the most popular one. It has almost 500.000 downloads and it is played around 13.000 a day. Until recently I received many mails on how to use the app. My support email address is in the Settings panel. Most of these mails where request to add features to the game like 'can I validate a puzzle?', 'can I solve a puzzle?', 'can I get statistics?' or 'can I get a hint?'. Those features are all available in the AppBar of the game. My conclusion is that the average user is still not aware that the AppBar can exist.

To make my users aware of the AppBar I added a peek behavior to it. When you start a sudoku puzzle the AppBar peeks out for just a second, see video. After the implementation I got almost no more of those emails any more.

Solution

The implementation of a peeking AppBar is very easy. For my demo I used Blend for Visual Studio to generate a new project using the 'Grid App (XAML)' template. Next I add an AppBar to the ItemDetailPage page.

I named the AppBar 'MyBottomAppBar' and set the Background color to Green. I also added a Button to the first StackPanel of the AppBar. I have reset the Content property of the Button and selected the 'AddAppBarButtonStyle' Style. Before I could do that I had to uncomment this Style from the 'Common\StandardStyles.xaml' resource dictionary.

<common:LayoutAwarePage x:Name="pageRoot"
                        x:Class="PeekingAppBar.ItemDetailPage"
                        DataContext="{Binding DefaultViewModel, RelativeSource={RelativeSource Self}}"
                        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                        xmlns:local="using:PeekingAppBar"
                        xmlns:data="using:PeekingAppBar.Data"
                        xmlns:common="using:PeekingAppBar.Common"
                        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
                        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
                        mc:Ignorable="d">

    <Page.Resources>

        <!-- Collection of items displayed by this page -->
        <CollectionViewSource x:Name="itemsViewSource"
                              Source="{Binding Items}"
                              d:Source="{Binding AllGroups[0].Items, Source={d:DesignInstance Type=data:SampleDataSource, IsDesignTimeCreatable=True}}" />
    </Page.Resources>
    <common:LayoutAwarePage.BottomAppBar>
        <AppBar x:Name="MyBottomAppBar"
                Background="#FF00CD00">
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition />
                    <ColumnDefinition />
                </Grid.ColumnDefinitions>
                <StackPanel Orientation="Horizontal">
                    <Button HorizontalAlignment="Stretch"
                            VerticalAlignment="Stretch"
                            Style="{StaticResource AddAppBarButtonStyle}" />
                </StackPanel>
                <StackPanel Grid.Column="1"
                            HorizontalAlignment="Right"
                            Orientation="Horizontal" />
            </Grid>
        </AppBar>
    </common:LayoutAwarePage.BottomAppBar>

    <!--
        This grid acts as a root panel for the page that defines two rows:
        * Row 0 contains the back button and page title
        * Row 1 contains the rest of the page layout
    -->
    <Grid Style="{StaticResource LayoutRootStyle}"
          DataContext="{Binding Group}"
          d:DataContext="{Binding AllGroups[0], Source={d:DesignInstance Type=data:SampleDataSource, IsDesignTimeCreatable=True}}">

        <Grid.RowDefinitions>
            <RowDefinition Height="140" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>

        <!--
            The remainder of the page is one large FlipView that displays details for
            one item at a time, allowing the user to flip through all items in the chosen
            group
        -->
        <FlipView x:Name="flipView"
                  AutomationProperties.AutomationId="ItemsFlipView"
                  AutomationProperties.Name="Item Details"
                  TabIndex="1"
                  Grid.RowSpan="2"
                  ItemsSource="{Binding Source={StaticResource itemsViewSource}}">

To implement the peeking behavior I wrote just a few lines of C# code in the Code Behind of the page. I added a subscription to the Loaded event of the page in the constructor. In the ItemDetailPage_Loaded method I used the Task.Delay() method to pause for a half second. Then I opened the AppBar, paused again but now for 1 second and then closed it again. The ItemDetailPage_Loaded was marked as async to allow the 'await' on the Task.Delay() call. This async keyword is not allowed in a constructor. This is the reason why I wrote my code in the Loaded eventhandler and not in the constructor of the page.

public sealed partial class ItemDetailPage : PeekingAppBar.Common.LayoutAwarePage
{
    public ItemDetailPage()
    {
        this.InitializeComponent();

        this.Loaded += ItemDetailPage_Loaded;
    }

    private async void ItemDetailPage_Loaded(object sender, RoutedEventArgs e) {
        await Task.Delay(500);
        MyBottomAppBar.IsOpen = true;
        await Task.Delay(1000);
        MyBottomAppBar.IsOpen = false;
    }

Closure and download

I hope you like my solution. Maybe you can use it in your apps too. You can download my code below.

Cheers,

Fons

Windows 8 Store Search Problems

0 Comments
By Fons Sonnemans, 11-4-2013

Before I start I want to make clear that I love Windows 8 and I love to develop for it. But I really have a problem with the implementation of the Search Charm of the Store app. The order in which apps are shown as a result of a search makes no sense. Let me demonstrate this with an example.

Search for sudoku

As you maybe know I have developed a very popular Sudoku game called Sudoku Free. It has almost 440.000 downloads and 2150 ratings with an average of 4 stars. The reviews are very positive. The game is usually in the ranked around place 25 in the Free games section of the US Store. It is the only Sudoku game in the list.  60% of the downloads are from the United States and it has 763 reviews. It is played a lot and has an average app usage of about 25 minutes per day. Quality reports show a very low crash rate between 0 and 0.01%. All statistics indicate this app is the most popular and downloaded Sudoku game in the store.

On my device a search for 'Sudoku' results in 127 apps. But you don't see my Sudoku Free app. My app has position 55 in the list. You have to scroll to the 10th column to see my app. I'm using a tablet with a 1366x768 resolution. Most of the games in front of mine have no reviews and just a few ratings (about 10 or even none). There are even 2 apps in the top 20 which have the default tile, see screenshot below.

I find this very strange. I think search should show popular apps first, and I’m sure tons of other developers and users will agree.

Search Recommendations

If I search on 'Sudoku Free' I get a recommendation for the 'Sudoku (Free)' app. This app has only 3 ratings and no reviews. It is not in the top 100 free apps so it will probably not have many downloads.

I find this also very strange. I think search should show the most popular apps in the recommendations.

Conclusion

I know Microsoft has had similar problems with the search in the Windows Phone store, as you can read here and here. I think they fixed this problem by now. If I search for Sudoku I get the most popular apps first.

I hope someone at Microsoft is listening, because they are only shooting themself in the foot by pissing off developers and users. Maybe you can leave a comment if you agree, hopefully that will persuade Microsoft to fix it (soon).

Cheers,

Fons

 

Tags: Windows 8, Store

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.