Making the Windows Phone toolkit ListPicker ItemsPanel display a WrapPanel

by Shawn 7. May 2013 23:46

There are many things that should be part of the Windows Phone SDK and the ListPicker is one of them. There are so many aspects of the phone that use a control that the ListPicker was based on. One of these is the accent color picker. In Phone 7 the phone would display a vertical list of colors with the name of the colors next to it. In Phone 8 they changed this experience and went with a WrapPanel.

AccentColors

If you want to to get this same experience, you’ll want to use the ListPicker from the Windows Phone Toolkit. The ListPicker will first display what appears to be a ComboBox, yet when you tap this ‘ComboBox’ it opens up and takes over the entire phone. This is all pretty standard and the ListPicker covers this perfectly. Where it fails us is when we want to change how the ListPicker stacks it’s items. This is generally done by changing the ItemsPanel of an ItemsControl. Unfortunately the ListPicker does not allow you to change the ItemsPanel. If you download or browse the source for the ListPicker, you’ll see that it uses a PhoneApplicationPage that has a ListBox in it. The code behind of this page set the ItemTemplate based on a property from the ListPicker, but this is the only one it sets based on a property of the ListPicker.

So here we are with a great control from the toolkit. It allows us to do pretty much everything we need to, but it does not allow us to display a color picker like the native accent color picker. There are two ways that we can accomplish this. The first is to download the source and change it to allow for the ability to override the ItemsPanel. Downloading and changing source is usually risky. It’s risky because you then loose out on any updates that the maintainers may push out. It’s ok provided that you’re ok merging updates with your source.

The second it much easier, does not require you to download source or change any of the toolkit. This simple change requires you to create an implicit style for the ListBox control. Implicit styles allow you to specify a style that all controls of that type will use. To accomplish this, place the following style within your app.xaml

<Style TargetType="ListBox">
    <Setter Property="ItemsPanel">
        <Setter.Value>
            <ItemsPanelTemplate>
                <toolkit:WrapPanel />
            </ItemsPanelTemplate>
        </Setter.Value>
    </Setter>
</Style>

With this in your app.xaml, you can display a WrapPanel within the ListPicker. Unfortunately, with this style, all ListBoxes will display a WrapPanel. If you have other ListBoxes within your app, they will display WrapPanels. You can get the other ListBoxes to display as normal by adding the following style to your app.xaml.

<Style x:Key="StandardListBoxStyle" TargetType="ListBox">
    <Setter Property="Background" Value="Transparent"/>
    <Setter Property="Foreground" Value="{StaticResource PhoneForegroundBrush}"/>
    <Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Disabled"/>
    <Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/>
    <Setter Property="BorderThickness" Value="0"/>
    <Setter Property="BorderBrush" Value="Transparent"/>
    <Setter Property="Padding" Value="0"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="ListBox">
                <ScrollViewer x:Name="ScrollViewer" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Foreground="{TemplateBinding Foreground}" Padding="{TemplateBinding Padding}">
                    <ItemsPresenter/>
                </ScrollViewer>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>


Now use this style within your app.

<ListBox Style="{StaticResource StandardListBoxStyle}" ItemsSource="{Binding MyItems}"/>

If you use this approach and you have another ListPicker that you want to use in FullMode, it will display a WrapPanel. If you don’t want to always display a WrapPanel within your ListPicker, or if you don’t want to set the style manually then you’ll need to modify the source code of the ListPicker.

To begin you’ll need to download the source for the Windows Phone Toolkit. The toolkit comes with four projects, two under a Windows Phone 7 folder and two under a Windows Phone 8 folder. The projects use the same code for most of the files. These files have a central location and are added as a link.  You’ll need to modify one file that is shared and another file within each phone 7 and phone 8 project. Open the ListPickerPage.xamlcs file from both the phone 7 and phone 8 project. The ListPicker already has a FullModeItemTemplate, we can continue this style by adding a FullModeItemsPanel.

/// <summary>
/// Gets or sets the ItemsPanel.
/// </summary>
public ItemsPanelTemplate FullModeItemsPanel { get; set; }

Within the OnNavigatedTo method, add the following

if (FullModeItemsPanel != null)
{
    Picker.ItemsPanel = FullModeItemsPanel;
}

Now open the LickPicker.cs file from one of the toolkit projects (not a samples project). Add the following dependency property.

/// <summary>
/// Gets or sets the ItemsPanel used to display the items when ListPickerMode is set to Full.
/// </summary>
public ItemsPanelTemplate FullModeItemsPanel
{
    get { return (ItemsPanelTemplate)GetValue(FullModeItemsPanelProperty); }
    set { SetValue(FullModeItemsPanelProperty, value); }
}
 
/// <summary>
/// Identifies the FullModeItemsPanel DependencyProperty.
/// </summary>
public static readonly DependencyProperty FullModeItemsPanelProperty =
    DependencyProperty.Register(
    "FullModeItemsPanel", 
    typeof(ItemsPanelTemplate), 
    typeof(ListPicker), 
    new PropertyMetadata(null));

Within the OnFrameNavigatedTo method you’ll want to add the following line.

_listPickerPage.FullModeItemsPanel = FullModeItemsPanel;

It’s best to add this line under where the FullModeItemTemplate is set for the page.

I said earlier that changing the source can be risky because you loose out on updates that come from the maintainers of the source. Unfortunately there is little effort going into any of the toolkits from Microsoft and we have yet to see a toolkit for Windows Store apps. Not only does no one work on these toolkit, but you cannot provide contributions. Pedro Lamas has taken the source from codeplex and put it up on github. Pedro is providing fixes and taking pull requests for this repro. I’ve already provided this change and you can download it and start going.

Tags: ,

Windows Phone

Please, please, please stop using IEnumerable.Count()

by Shawn 12. April 2013 16:20

As the title says, please, please, please stop using the IEnumerabler Count() extension method UNLESS you must have the count. Over and over again I see this small little snippet

 1: if(myEnumerable.Count() > 0)
 2: {
 3:     // Do stuff
 4: }

This has a huge performance impact on your application. Let’s take a look at what this code is actually doing (decompiled using DotPeek 1.1)

 1: public static int Count<TSource>(this IEnumerable<TSource> source)
 2: {
 3:     if (source == null)
 4:         throw Error.ArgumentNull("source");
 5:     ICollection<TSource> collection1 = source as ICollection<TSource>;
 6:     if (collection1 != null)
 7:         return collection1.Count;
 8:     ICollection collection2 = source as ICollection;
 9:     if (collection2 != null)
 10:         return collection2.Count;
 11:     int num = 0;
 12:     using (IEnumerator<TSource> enumerator = source.GetEnumerator())
 13:     {
 14:         while (enumerator.MoveNext())
 15:             checked { ++num; }
 16:     }
 17:     return num;
 18: }

If your IEnumerable<T> collection supports ICollection<T> then all is well, you'll be accessing a property that already has a value. Many times this is not the case. A lot of APIs will only expose collections as IEnumerable<T>. This follows the Frame Design Guidelines to only expose the minimum (base) interface that you need to. In these cases it must iterate over every single item in your collection to get the count. And all that just to decide if there is at least one item in the collection?!?

Let’s take a look at the Any() extension method.

 1: public static bool Any<TSource>(this IEnumerable<TSource> source)
 2: {
 3:     if (source == null)
 4:         throw Error.ArgumentNull("source");
 5:     using (IEnumerator<TSource> enumerator = source.GetEnumerator())
 6:     {
 7:         if (enumerator.MoveNext())
 8:             return true;
 9:     }
 10:     return false;
 11: }

This method just determines if there is one item in the collection. It does this by attempting to move the iterator one spot. If it can then the collection has an item!. Now, I would probably change lines 7 and 8 to be

 7: return enumerator.MoveNext();

This would remove the need for the if check. Again, this is decompiled code so it’s possible that the actual code is doing this.

So, if you need to know if the collection contains at least one item, and you’re working with an IEnumerable<T>, use the Any() method.

If you want to squeeze every little ounce of performance out of your app continue reading! What I find odd is that the Any() extension method does not try to take advantage of the two most common collections, arrays and ICollection<T>. Both of these collections have a ready made property to aid in this effort. Array has the Length property and ICollection<T> has the Count property. By making a new extension method that takes advantage of these properties you can shrink your calls to Any() in half.

NOTE: In half is very minimal. We’re talking about ticks, not even milliseconds unless it is called MANY MANY times.

   1: public static bool FastAny<TSource>(this IEnumerable<TSource> source)
   2: {
   3:     if (source == null) throw new ArgumentNullException("source");
   4:  
   5:     var array = source as TSource[];
   6:     if (array != null) return array.Length > 0;
   7:  
   8:     var coll = source as ICollection<TSource>;
   9:     if (coll != null) return coll.Count > 0;
  10:  
  11:     using (var enumerator = source.GetEnumerator())
  12:     {
  13:         return enumerator.MoveNext();
  14:     }
  15: }
If you’d like to test this out for yourself, you can run this console application.

Tags:

Using the WebAPI to send errors for your apps

by Shawn 26. January 2013 22:55

If your app does anything at all complex, it’s going to crash. You’re going to miss checking something for null. You’re going to use a resource that doesn’t exist. Your app is going to crash. Luckily there are some ways to make sure you know what’s happening. Little Watson by Andy Pennell shows us a great way to send application errors but requires the user to email them to you. Bjorn Kuiper extended Little Watson by removing the need to email the report and instead send it to a PHP endpoint. This is a great solution provided your server is setup to run PHP . If you don’twant to use PHP, don’t want to install PHP or if you are using a hosting service that does not run PHP then you’re out of luck.

Instead of convincing your web provider to install PHP you could create a WCF service, but that seems like a lot of overhead. You could also create an ASP.NET web page and put the error details into a query parameter, but that seems hokie. Luckily the Web API has been introduced. The Web API makes this easy to implement, easy to deploy, and easy to use. Web API allows you to send an object of information in a well known format. Overall, the Web API is just awesome.

The first step is to start with the minimal Web API project. Open the Global.asax file and add the following line inside the Application_Start method

RouteTable.Routes.MapHttpRoute("ErrorRoute", "{controller}/{id}", new { id = RouteParameter.Optional });

Right click on the on controllers folder and create a new ApiController.

image

Following Bjorn’s example, we need an ExceptionContainer class that will hold our exception information. I’ve added an additional property for the application name. This is handy if you have more than one app.

public class ExceptionContainer
{
    ///  <summary>  
    /// Gets or sets the message.  
    /// </summary>  
    public string Message { get; set; }
 
    ///  <summary>  
    /// Gets or sets the stacktrace.  
    /// </summary>  
    public string StackTrace { get; set; }
 
    /// <summary>
    /// The application the error was found.
    /// </summary>
    public string ApplicationName { get; set; }
}

Inside the controller we need one method that we can send exception info to. The Web API allows you to take advantage of the HTTP verbs and structure your API in terms of them. For this error reporting, the most logical verb is POST. We’re not trying to GET, update (PUT) or DELETE anything. Create a new Post method that returns an HttpResponseMessage and takes an ExceptionContainer as a parameter. In the method we’ll send an email.

public HttpResponseMessage Post(ExceptionContainer exception)
{
    if (exception == null) return new HttpResponseMessage(HttpStatusCode.PaymentRequired);
 
    var message = new System.Net.Mail.MailMessage
        {
            From = new System.Net.Mail.MailAddress("phoneApps@yourdomain.com"),
            Subject = "Error report: " + exception.ApplicationName,
            Body = exception.Message + Environment.NewLine + exception.StackTrace
        };
    message.To.Add("you@domain.com");
 
    System.Net.Mail.SmtpClient client = new System.Net.Mail.SmtpClient("localhost", 25);
    // send credentials if your SMTP server requires them
    //client.Credentials = new NetworkCredential("user", "password");
    client.Send(message);
 
    return new HttpResponseMessage(HttpStatusCode.Created);
}

Deploy this up to your site and you’re ready to start sending application errors from your app.  Inside your app you simply need to package up the error and send to your server.

public void SendException(string uri, ExceptionContainer exception)
{
    try
    {
        JsonSerializerSettings settings = new JsonSerializerSettings();
        settings.NullValueHandling = NullValueHandling.Ignore;
        string postStr = JsonConvert.SerializeObject(exception, settings).Replace("\"", "'");
 
        WebClient client = new WebClient();
        client.Headers[HttpRequestHeader.ContentType] = "application/json";
        client.UploadStringAsync(new Uri(uri), postStr);
    }
    catch { }
}

Remember that you should ask the user if you can send error reports, or give them the option to turn it on or off.

Tags: , , ,

Creating a custom async dialog for your Win8 apps Part 2: CustomDialog

by Shawn 12. November 2012 04:36

In Part 1 of this series I discussed how to create a custom async LoginDialog to use within your Windows Store apps. In this blog, I’ll explain how to turn that dialog into an async dialog that allows for custom content and works more like the MessageDialog. Most of the hard work was already completed in the LoginDialog. It has a constructor that takes content, a ShowAsync method that has the ability to show a dialog asynchronously, responds to a CancellationToken, it is shown as a Popup and responds to the size of the window changing and many more. To create the CustomDialog, I’ll start with the LoginDialog.

For the CustomDialog, I wanted to make it almost identical to the MessageDialog class except replacing the content parameter of the constructor from string, to object. For the new constructors, we’ll be using a ContentPresenter to place the custom content. The ContentPresenter will be created in code when the dialog is shown.

public CustomDialog(object content)
{
    Title = string.Empty;
    Commands = new List<IUICommand>();
    CancelCommandIndex = Int32.MaxValue;
    DefaultCommandIndex = Int32.MaxValue;
    HeaderBrush = new SolidColorBrush(Colors.White);
    Content = content;
}
 
public CustomDialog(object content, string title) : this(content)
{
    if (string.IsNullOrEmpty(title) == false)
    {
        Title = title;
    }
}

Matching the MessageDialog also meant added in custom commands to the dialog that allow for a custom action. Also want to make sure that the default and cancel buttons can be specified.

/// <Summary>
/// Gets or sets the index of the command you want to use as the default. This
/// is the command that fires by default when users press the ENTER key.
/// </Summary>
/// <Returns>The index of the default command.</Returns>
public int DefaultCommandIndex { get; set; }
 
/// <Summary>
/// Gets or sets the index of the command you want to use as the cancel command.
/// This is the command that fires when users press the ESC key.
/// </Summary>
/// <Returns>The index of the cancel command.</Returns>
public int CancelCommandIndex { get; set; }
 
/// <Summary>
/// Gets the set of commands that appear in the command bar of the message dialog.
/// </Summary>
/// <Returns>The commands.</Returns>
public IList<IUICommand> Commands { get; private set; }

When we create the buttons, we need to apply the properties from the custom commands and ensure that the user gets to do their custom action.

private UIElement CreateDialog()
{
    // Create the dialog
    ...
 
    if (Commands.Count == 0)
    {
        Button button = new Button();
        button.Style = buttonStyle;
        button.Content = "Close";
        button.MinWidth = 92;
        button.Margin = new Thickness(20, 20, 0, 20);
        button.Click += (okSender, okArgs) => CloseDialog(null);
        buttonPanel.Children.Add(button);
    }
    else
    {
        foreach (var command in Commands)
        {
            IUICommand currentCommand = command;
            Button button = new Button();
            button.Style = buttonStyle;
            button.Content = command.Label;
            button.Margin = new Thickness(20, 20, 0, 20);
            button.MinWidth = 92;
            button.Click += (okSender, okArgs) => CloseDialog(currentCommand);
            buttonPanel.Children.Add(button);
        }
    }
    ...
}
 
private void CloseDialog(IUICommand command)
{
    UnsubscribeEvents();
 
    if (command != null)
    {
        command.Invoked(command);
    }
    _popup.IsOpen = false;
    _taskCompletionSource.SetResult(command);
}

Now that the dialog has the ability to specify if there is a default and cancel button, we nee to make changes to the KeyDown event of the window.

private void OnKeyDown(object sender, KeyRoutedEventArgs e)
{
    // Only respond to Esc if there is a cancel index
    if ((e.Key == VirtualKey.Escape) && (CancelCommandIndex < Commands.Count))
    {
        OnCanceled();
    }
 
    // Only respond to Enter if there is a cancel index
    if ((e.Key == VirtualKey.Enter) && (DefaultCommandIndex < Commands.Count))
    {
        CloseDialog(Commands[(int)DefaultCommandIndex]);
    }
}

The rest of the CustomDialog is pretty much identical to the LoginDialog. You can download the source and sample application which has both the LoginDialog and CustomDialog in it. If you just want just the CustomDialog class you can download it from GitHub.

Tags: ,

Creating a Custom Async Dialog for your Win8 Apps Part 1 - LoginDialog

by Shawn 28. October 2012 02:10

For a recent Windows Store App, I needed to create a way for people to log into it. I could have easily created a simple control or page that allowed the user to enter their login information, but I wanted to work with the new async/await architecture. I thought that creating a login dialog that fit this pattern would be a fun adventure. I wanted to follow the same pattern found in the MessageDialog class so it would be easy to use and understand how it works. This is part one of a two part blog. Part one will cover creating a LoginDialog class that only handles the ability to login. Part two will cover changing that class to be a more generic CustomDialog class that allows for custom content. By the end of the blog we’ll create a LoginDialog that will display a login to the user that looks a lot like the login you get from the People app.

image

This first stab at the dialog will focus on the ability to login, so we need a class that will hold the information the user enters.

public class LoginResult
{
    public string Name { get; set; }
    public string Password { get; set; }
}

Next, let’s stub out what the API will look like. As I said, I wanted this to follow the same pattern as the MessageDialog, so I’ll create a class that closely mirrors that functionality

public class LoginDialog
{
    public LoginDialog(string title) { }
 
    public LoginDialog(string title, string content) : this(title) { }
 
    public IAsyncOperation<LoginResult> ShowAsync() { }
}

Notice that this is just a plain class. It is not a UIControl. This means that you cannot place this into your XAML, you can only create and show it from code. The LoginDialog is not a control, but it does need to display a control. There are two ways you could go about doing this. One is to create a class that is the control to display, the other is to create the control in code within the LoginDialog itself. The first is probably the easiest, but I wanted to accomplish this with only one class. I could have made an internal control that is only used by the LoginDialog (and this is the easier way to go and I’ll show using it later) but I wanted to try doing this with only one class. For now I’ll just stub out the creation of the actual control within the ShowAsync method. This method needs to return an IAsyncOperation of type LoginResult. To create an instance of IAsyncOperation you use the static Run method of the AsyncInfo.

private Popup _popup;
private TaskCompletionSource<LoginResult> _taskCompletionSource;
 
public IAsyncOperation<LoginResult> ShowAsync()
{
    _popup = new Popup { Child = CreateLogin() };
    if (_popup.Child != null)
    {
        _popup.IsOpen = true;
    }
    return AsyncInfo.Run(token => WaitForInput(token));
}
 
private Task<LoginResult> WaitForInput(CancellationToken token)
{
    _taskCompletionSource = new TaskCompletionSource<LoginResult>();
 
    token.Register(OnCanceled);
    
    return _taskCompletionSource.Task;
}

The WaitForInput method allows me to create the TaskCompletionSource that will be used to return the result. TaskCompletionSource is an awesome class that allows you to set the result at a later time. The WaitForInput method returns the Task property of the TaskCompletionSource. This allows for some async goodness. The code will wait until the SetResult method is called on the TaskCompletionSource.

To set the result of TaskCompletionSource and ultimately of the ShowAsync method we need two methods, one for when the dialog is canceled and one for when the user has entered their information. The cancel method will handle if the user clicked the cancel button or, if the application cancels the IAsyncOperation within the code. Note: I tried using the cancelation token for the cancel method but that did not do anything.

private void OnCompleted()
{
    var result = new LoginResult();
    result.Name = _userTextBox.Text;
    result.Password = _passwordTextBox.Password;
 
    _popup.IsOpen = false;
    _taskCompletionSource.SetResult(result);
}
 
private void OnCanceled()
{
    // null to indicate dialog was canceled
    LoginResult result = null;
 
    _popup.IsOpen = false;
    _taskCompletionSource.SetResult(result);
}

This is the basics of what we need, however windows store apps have so many visual states and sizes to be aware of. This means that the dialog needs to be aware of a few different events. If the app is on a small tablet the dialog needs to adjust for the virtual keyboard. If your app supports portrait the dialog needs to resize itself correctly.

// adjust for different view states
private void OnWindowSizeChanged(object sender, WindowSizeChangedEventArgs e)
{
    if (_popup.IsOpen == false) return;
 
    var child = _popup.Child as FrameworkElement;
    if (child == null) return;
 
    child.Width = e.Size.Width;
    child.Height = e.Size.Height;
} 
 
// Adjust the name/password textboxes for the virtual keyuboard
private void OnInputShowing(InputPane sender, InputPaneVisibilityEventArgs args)
{
    var child = _popup.Child as FrameworkElement;
    if (child == null) return;
 
    var transform = _passwordTextBox.TransformToVisual(child);
    var topLeft = transform.TransformPoint(new Point(0, 0));
 
    // Need to be able to view the entire textblock (plus a little more)
    var buffer = _passwordTextBox.ActualHeight + 10; 
    if ((topLeft.Y + buffer) > sender.OccludedRect.Top)
    {
        var margin = topLeft.Y - sender.OccludedRect.Top;
        margin += buffer;
        child.Margin = new Thickness(0, -margin, 0, 0);
    }
}
 
private void OnInputHiding(InputPane sender, InputPaneVisibilityEventArgs args)
{
    var child = _popup.Child as FrameworkElement;
    if (child == null) return;
 
    child.Margin = new Thickness(0);
}

We only want to subscribe to these events when the user shows the dialog (within the ShowAsync method), and we want to unsubscribe from the events when the dialog closes (OnCanceled and OnCompleted methods). Another event that is good to listen to is the KeyDown event of the window. If the user presses the escape key you should cancel the dialog.

private void SubscribeEvents()
{
    Window.Current.SizeChanged += OnWindowSizeChanged;
    Window.Current.Content.KeyDown += OnKeyDown;
 
    var input = InputPane.GetForCurrentView();
    input.Showing += OnInputShowing;
    input.Hiding += OnInputHiding;
}
 
private void UnsubscribeEvents()
{
    Window.Current.SizeChanged -= OnWindowSizeChanged;
    Window.Current.Content.KeyDown -= OnKeyDown;
 
    var input = InputPane.GetForCurrentView();
    input.Showing -= OnInputShowing;
    input.Hiding -= OnInputHiding;
}

The only thing I’ve left out is the creation of the actual dialog. For this I used a cool github project called XAML Conversion by Petr Onderka. I created the XAML in a user control and used this tool to convert it to code. For the most part it worked really well. It did require that you put children of panels into the Child tag.

<Border Background=""Red"" Height=""80"">
    <Border.Child>
        <TextBlock Text="Hello"/>
    </Border.Child>
</Border>

Download the source and sample application today. This application takes advantage of the light styles I blogged about before.

Tags: ,

Easily create light themed styles for your Win8 Settings Flyout

by Shawn 25. October 2012 00:18

One of the things I love about Windows Store apps is their ability to integrate with the system. One of these integration points is the Settings Charm. I’m not going to show you how to create a settings flyout. There are already lots of examples out there that do this. There are even some helpers like the SettingsFlyout available in Callisto and the helper created by Jerry Nixon. Recently Tim Heuer made a change to the SettingsFlyout to set the background to white. This change allows your app to follow the guidelines. He also added a property that allows you to change what you want the background to be if you are not a fan of white.

This change to the background works great if your app has a light requested theme. If you are using the default dark theme then the new background on the flyout becomes a nightmare. It’s a nightmare because now you have to style all of the controls you use for your settings to work properly with a light theme. You could easily start changing the brushes of various properties, but this doesn’t look very polished. You could restyle the controls and start to guess and what colors to use for this or that, but you’ll forget something (at least I did, and it was a lot).

There has to be an easy way to get light themed styles at runtime, right? I couldn’t find one, but I did find a way to easily create them. The resources and styles used by controls within the app are generally not found within the app itself. The app requests a theme from the system and that theme has all of resources and styles. This allows you to create custom controls and use those brushes and have the control be styled properly for both light and dark themed apps. These resources are not found within your app, but they are on your machine.

The styles and resources for WinRT are found at C:\Program Files (x86)\Windows Kits\8.0\Include\WinRT\Xaml\Design. This folder has two files in it. The themeresource.xaml file has all of the brushes, and other resources like font size and opacity. The generic.xaml file has all of these resources as well as all of the styles for each control. We can use these files to build our light themed styles.

To start with, open the project you need the light styles in, and add a new resource dictionary to store all of our light styles. In the Solution Explorer window, right click the Common folder and select Add –> New Item…

image

In the Add New Item dialog, select Resource Dictionary and name it LightStandardStyles.xaml

image

We then need to add this xaml to our App.xaml. Open the App.xaml and copy the line that adds the StandardStyles.xaml, paste it directly under that line and change the name to LightStandardStyles.

<ResourceDictionary Source="Common/StandardStyles.xaml"/>
<ResourceDictionary Source="Common/LightStandardStyles.xaml"/>

Let’s say you need to have a light themed button. Open LightStandardStyles.xaml and also open C:\Program Files (x86)\Windows Kits\8.0\Include\WinRT\Xaml\Design\generic.xaml.  Search for TargetType="Button" in the file. Copy the style and paste the style into LightStandardStyles. Give the style the key LightButtonStyle. Next open themeresource.xaml and collapse the “Default” and “HighContrast” ResourceDictionary. Now it’s as simple as searching for the brushes used within the button style and copy/paste them from the themeresource file and paste them into LightStandardStyles.

After doing this, the file should look like this.

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
 
    <SolidColorBrush x:Key="ButtonBackgroundThemeBrush" Color="#B3B6B6B6" />
    <SolidColorBrush x:Key="ButtonBorderThemeBrush" Color="#33000000" />
    <SolidColorBrush x:Key="ButtonDisabledBackgroundThemeBrush" Color="#66CACACA" />
    <SolidColorBrush x:Key="ButtonDisabledBorderThemeBrush" Color="#1A000000" />
    <SolidColorBrush x:Key="ButtonDisabledForegroundThemeBrush" Color="#66000000" />
    <SolidColorBrush x:Key="ButtonForegroundThemeBrush" Color="#FF000000" />
    <SolidColorBrush x:Key="ButtonPointerOverBackgroundThemeBrush" Color="#D1CDCDCD" />
    <SolidColorBrush x:Key="ButtonPointerOverForegroundThemeBrush" Color="#FF000000" />
    <SolidColorBrush x:Key="ButtonPressedBackgroundThemeBrush" Color="#FF000000" />
    <SolidColorBrush x:Key="ButtonPressedForegroundThemeBrush" Color="#FFFFFFFF" />
    <SolidColorBrush x:Key="FocusVisualBlackStrokeThemeBrush" Color="Black" />
    <SolidColorBrush x:Key="FocusVisualWhiteStrokeThemeBrush" Color="White" />
 
    <Style x:Key="LightButtonStyle" TargetType="Button">
        <Setter Property="Background" Value="{StaticResource ButtonBackgroundThemeBrush}" />
        <Setter Property="Foreground" Value="{StaticResource ButtonForegroundThemeBrush}"/>
        <Setter Property="BorderBrush" Value="{StaticResource ButtonBorderThemeBrush}" />
        <Setter Property="BorderThickness" Value="{StaticResource ButtonBorderThemeThickness}" />
        <Setter Property="Padding" Value="12,4,12,4" />
        <Setter Property="HorizontalAlignment" Value="Left" />
        <Setter Property="VerticalAlignment" Value="Center" />
        <Setter Property="FontFamily" Value="{StaticResource ContentControlThemeFontFamily}" />
        <Setter Property="FontWeight" Value="SemiBold" />
        <Setter Property="FontSize" Value="{StaticResource ControlContentThemeFontSize}" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="Button">
                    <Grid>
                        <VisualStateManager.VisualStateGroups>
                            <VisualStateGroup x:Name="CommonStates">
                                <VisualState x:Name="Normal" />
                                <VisualState x:Name="PointerOver">
                                    <Storyboard>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Border"
                                                                       Storyboard.TargetProperty="Background">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource ButtonPointerOverBackgroundThemeBrush}" />
                                        </ObjectAnimationUsingKeyFrames>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter"
                                                                       Storyboard.TargetProperty="Foreground">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource ButtonPointerOverForegroundThemeBrush}" />
                                        </ObjectAnimationUsingKeyFrames>
                                    </Storyboard>
                                </VisualState>
                                <VisualState x:Name="Pressed">
                                    <Storyboard>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Border"
                                                                       Storyboard.TargetProperty="Background">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource ButtonPressedBackgroundThemeBrush}" />
                                        </ObjectAnimationUsingKeyFrames>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter"
                                                                       Storyboard.TargetProperty="Foreground">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource ButtonPressedForegroundThemeBrush}" />
                                        </ObjectAnimationUsingKeyFrames>
                                    </Storyboard>
                                </VisualState>
                                <VisualState x:Name="Disabled">
                                    <Storyboard>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Border"
                                                                       Storyboard.TargetProperty="Background">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource ButtonDisabledBackgroundThemeBrush}" />
                                        </ObjectAnimationUsingKeyFrames>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Border"
                                                                       Storyboard.TargetProperty="BorderBrush">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource ButtonDisabledBorderThemeBrush}" />
                                        </ObjectAnimationUsingKeyFrames>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter"
                                                                       Storyboard.TargetProperty="Foreground">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource ButtonDisabledForegroundThemeBrush}" />
                                        </ObjectAnimationUsingKeyFrames>
                                    </Storyboard>
                                </VisualState>
                            </VisualStateGroup>
                            <VisualStateGroup x:Name="FocusStates">
                                <VisualState x:Name="Focused">
                                    <Storyboard>
                                        <DoubleAnimation Storyboard.TargetName="FocusVisualWhite"
                                                         Storyboard.TargetProperty="Opacity"
                                                         To="1"
                                                         Duration="0" />
                                        <DoubleAnimation Storyboard.TargetName="FocusVisualBlack"
                                                         Storyboard.TargetProperty="Opacity"
                                                         To="1"
                                                         Duration="0" />
                                    </Storyboard>
                                </VisualState>
                                <VisualState x:Name="Unfocused" />
                                <VisualState x:Name="PointerFocused" />
                            </VisualStateGroup>
                        </VisualStateManager.VisualStateGroups>
                        <Border x:Name="Border"
                                Background="{TemplateBinding Background}"
                                BorderBrush="{TemplateBinding BorderBrush}"
                                BorderThickness="{TemplateBinding BorderThickness}"
                                Margin="3">
                            <ContentPresenter x:Name="ContentPresenter"
                                              Content="{TemplateBinding Content}"
                                              ContentTransitions="{TemplateBinding ContentTransitions}"
                                              ContentTemplate="{TemplateBinding ContentTemplate}"
                                              Margin="{TemplateBinding Padding}"
                                              HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                                              VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
                        </Border>
                        <Rectangle x:Name="FocusVisualWhite"
                                   IsHitTestVisible="False"
                                   Stroke="{StaticResource FocusVisualWhiteStrokeThemeBrush}"
                                   StrokeEndLineCap="Square"
                                   StrokeDashArray="1,1"
                                   Opacity="0"
                                   StrokeDashOffset="1.5" />
                        <Rectangle x:Name="FocusVisualBlack"
                                   IsHitTestVisible="False"
                                   Stroke="{StaticResource FocusVisualBlackStrokeThemeBrush}"
                                   StrokeEndLineCap="Square"
                                   StrokeDashArray="1,1"
                                   Opacity="0"
                                   StrokeDashOffset="0.5" />
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

Next it’s as simple as prefixing each of these resources with ‘Light’. Doing this results in the following file

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
 
    <SolidColorBrush x:Key="LightButtonBackgroundThemeBrush" Color="#B3B6B6B6" />
    <SolidColorBrush x:Key="LightButtonBorderThemeBrush" Color="#33000000" />
    <SolidColorBrush x:Key="LightButtonDisabledBackgroundThemeBrush" Color="#66CACACA" />
    <SolidColorBrush x:Key="LightButtonDisabledBorderThemeBrush" Color="#1A000000" />
    <SolidColorBrush x:Key="LightButtonDisabledForegroundThemeBrush" Color="#66000000" />
    <SolidColorBrush x:Key="LightButtonForegroundThemeBrush" Color="#FF000000" />
    <SolidColorBrush x:Key="LightButtonPointerOverBackgroundThemeBrush" Color="#D1CDCDCD" />
    <SolidColorBrush x:Key="LightButtonPointerOverForegroundThemeBrush" Color="#FF000000" />
    <SolidColorBrush x:Key="LightButtonPressedBackgroundThemeBrush" Color="#FF000000" />
    <SolidColorBrush x:Key="LightButtonPressedForegroundThemeBrush" Color="#FFFFFFFF" />
    <SolidColorBrush x:Key="LightFocusVisualBlackStrokeThemeBrush" Color="Black" />
    <SolidColorBrush x:Key="LightFocusVisualWhiteStrokeThemeBrush" Color="White" />
 
    <Style x:Key="LightButtonStyle" TargetType="Button">
        <Setter Property="Background" Value="{StaticResource LightButtonBackgroundThemeBrush}" />
        <Setter Property="Foreground" Value="{StaticResource LightButtonForegroundThemeBrush}"/>
        <Setter Property="BorderBrush" Value="{StaticResource LightButtonBorderThemeBrush}" />
        <Setter Property="BorderThickness" Value="{StaticResource ButtonBorderThemeThickness}" />
        <Setter Property="Padding" Value="12,4,12,4" />
        <Setter Property="HorizontalAlignment" Value="Left" />
        <Setter Property="VerticalAlignment" Value="Center" />
        <Setter Property="FontFamily" Value="{StaticResource ContentControlThemeFontFamily}" />
        <Setter Property="FontWeight" Value="SemiBold" />
        <Setter Property="FontSize" Value="{StaticResource ControlContentThemeFontSize}" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="Button">
                    <Grid>
                        <VisualStateManager.VisualStateGroups>
                            <VisualStateGroup x:Name="CommonStates">
                                <VisualState x:Name="Normal" />
                                <VisualState x:Name="PointerOver">
                                    <Storyboard>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Border"
                                                                       Storyboard.TargetProperty="Background">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource LightButtonPointerOverBackgroundThemeBrush}" />
                                        </ObjectAnimationUsingKeyFrames>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter"
                                                                       Storyboard.TargetProperty="Foreground">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource LightButtonPointerOverForegroundThemeBrush}" />
                                        </ObjectAnimationUsingKeyFrames>
                                    </Storyboard>
                                </VisualState>
                                <VisualState x:Name="Pressed">
                                    <Storyboard>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Border"
                                                                       Storyboard.TargetProperty="Background">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource LightButtonPressedBackgroundThemeBrush}" />
                                        </ObjectAnimationUsingKeyFrames>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter"
                                                                       Storyboard.TargetProperty="Foreground">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource LightButtonPressedForegroundThemeBrush}" />
                                        </ObjectAnimationUsingKeyFrames>
                                    </Storyboard>
                                </VisualState>
                                <VisualState x:Name="Disabled">
                                    <Storyboard>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Border"
                                                                       Storyboard.TargetProperty="Background">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource LightButtonDisabledBackgroundThemeBrush}" />
                                        </ObjectAnimationUsingKeyFrames>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Border"
                                                                       Storyboard.TargetProperty="BorderBrush">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource LightButtonDisabledBorderThemeBrush}" />
                                        </ObjectAnimationUsingKeyFrames>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter"
                                                                       Storyboard.TargetProperty="Foreground">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource LightButtonDisabledForegroundThemeBrush}" />
                                        </ObjectAnimationUsingKeyFrames>
                                    </Storyboard>
                                </VisualState>
                            </VisualStateGroup>
                            <VisualStateGroup x:Name="FocusStates">
                                <VisualState x:Name="Focused">
                                    <Storyboard>
                                        <DoubleAnimation Storyboard.TargetName="FocusVisualWhite"
                                                         Storyboard.TargetProperty="Opacity"
                                                         To="1"
                                                         Duration="0" />
                                        <DoubleAnimation Storyboard.TargetName="FocusVisualBlack"
                                                         Storyboard.TargetProperty="Opacity"
                                                         To="1"
                                                         Duration="0" />
                                    </Storyboard>
                                </VisualState>
                                <VisualState x:Name="Unfocused" />
                                <VisualState x:Name="PointerFocused" />
                            </VisualStateGroup>
                        </VisualStateManager.VisualStateGroups>
                        <Border x:Name="Border"
                                Background="{TemplateBinding Background}"
                                BorderBrush="{TemplateBinding BorderBrush}"
                                BorderThickness="{TemplateBinding BorderThickness}"
                                Margin="3">
                            <ContentPresenter x:Name="ContentPresenter"
                                              Content="{TemplateBinding Content}"
                                              ContentTransitions="{TemplateBinding ContentTransitions}"
                                              ContentTemplate="{TemplateBinding ContentTemplate}"
                                              Margin="{TemplateBinding Padding}"
                                              HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                                              VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
                        </Border>
                        <Rectangle x:Name="FocusVisualWhite"
                                   IsHitTestVisible="False"
                                   Stroke="{StaticResource LightFocusVisualWhiteStrokeThemeBrush}"
                                   StrokeEndLineCap="Square"
                                   StrokeDashArray="1,1"
                                   Opacity="0"
                                   StrokeDashOffset="1.5" />
                        <Rectangle x:Name="FocusVisualBlack"
                                   IsHitTestVisible="False"
                                   Stroke="{StaticResource LightFocusVisualBlackStrokeThemeBrush}"
                                   StrokeEndLineCap="Square"
                                   StrokeDashArray="1,1"
                                   Opacity="0"
                                   StrokeDashOffset="0.5" />
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

With this you can create a light style pretty quickly. But today, and today only, you can download the light styles that I created. This resource file has light themed styles for the ToggleSwitch, TextBox, ComboBox, Button (including a LightAppBarButtonStyle) and all of the standard TextBlock styles.

Tags: ,

Creating an inline AppBar Button Style for your Win8 apps

by Shawn 17. September 2012 04:02

As you have probably read, the Metro Windows Store projects in Visual Studio now comes preloaded with a ton of styles for your AppBar. I love this style of button. You get the nice icon and text to indicate what the icon is supposed to mean (sometimes the icon can be confusing). Having both the icon and the text removes the need for users to hover over an icon to see what it’s suppose to be.

As awesome as the style is for buttons on the AppBar, they do have some limitations. They are big, and the text is on the bottom. This design makes it hard to put this style of icon in other parts of your app. Take a look at the Games app. Go all the way to the left and you’ll see your avatar along with ways to do more with your Xbox account. You accomplish these actions with buttons that look a lot like the AppBar buttons.

image

Creating a style for these button is really easy. In the past you’ve needed Expression Blend to be really effective at editing or creating styles of controls. Now this can be done within Visual Studio. If you need to edit any of the VisualStates of the control it’s best to use Blend. Once again you’re in luck because Blend comes with Visual Studio 2012! 

Open Visual Studio and create a new Windows Store project. On a xaml page, add a new Button and apply the AppBarButtonStyle style.

<Button Style="{StaticResource AppBarButtonStyle}"/>

If you like using the designer, drag a Button onto the page, right click and select Edit Template –> Apply Resource –> AppBarButtonStyle.

image

When looking at the button in the designer all you will see is a circle. Now right click the Button and select Edit Template –> Edit a Copy…

image

Name the style InlineAppBarButonStyle and place the style into the StandardStyles.xaml file.

image

I’m a XAML guy. I’ll rarely have the designer open. So from here on it’s pure XAML. All of this is still possible if you like using the designer. The AppBarButtonStyle limits the Width of the Button to 100. That Width will not allow for much text, so we’ll need to remove it. Find the Grid with the name “RootGrid” and remove the Width property. Removing the property will set the Width to be auto sized.

<Grid x:Name="RootGrid" Background="Transparent">

The first child of the Grid (collapse all of the VisualStates) is a StackPanel. By default the StackPanel stacks it’s children vertically. We want to change it stack Horizontally. Set the Orientation property to be Horizontal.

<StackPanel VerticalAlignment="Top" Margin="0,12,0,11" Orientation="Horizontal">

Let’s take a look at how our button currently is styled*

image
*I applied another style  to get this image. I’ll go into that later.

Not quite what we want. The text is wrapping whereas the text in the Games app does not. The TextBlock has a  width of 88 that we’ll want to delete that. While we’re making changes to the TextBlock set the VerticalAlignment to Center, remove the TextAlignment and change the FontSize.

<TextBlock x:Name="TextLabel"
           Text="{TemplateBinding AutomationProperties.Name}"
           Foreground="{StaticResource AppBarItemForegroundThemeBrush}"
           FontSize="16"
           VerticalAlignment="Center"
           MaxHeight="32"
           TextTrimming="WordEllipsis"
           Style="{StaticResource BasicTextStyle}"/>

This gets us very close, but the icons are still very large. The first thing we need to do is change the FontSize of the button itself. The FontSize is used for the icon that is inside the circle. The text has it’s own FontSize. Change the FontSize from 20 to 14.

<Setter Property="FontSize" Value="14"/>

I won’t bore you with setting each of the properties to get the circle and background smaller, so here’s the rest of the StackPanel.

<StackPanel VerticalAlignment="Top" Margin="0,3" Orientation="Horizontal">
    <Grid Width="40" Height="40" HorizontalAlignment="Center">
        <TextBlock x:Name="BackgroundGlyph" Text="&#xE0A8;" FontFamily="Segoe UI Symbol" FontSize="41" Margin="2,-10,0,0" Foreground="{StaticResource AppBarItemBackgroundThemeBrush}"/>
        <TextBlock x:Name="BackgroundCheckedGlyph" Visibility="Collapsed" Text="&#xE0A8;" FontFamily="Segoe UI Symbol" FontSize="41" Margin="2,-10,0,0" Foreground="{StaticResource AppBarItemForegroundThemeBrush}"/>
        <TextBlock x:Name="OutlineGlyph" Text="&#xE0A7;" FontFamily="Segoe UI Symbol" FontSize="41" Margin="2,-10,0,0"/>
        <ContentPresenter x:Name="Content" HorizontalAlignment="Center" Margin="-1,0,0,0" VerticalAlignment="Center"/>
    </Grid>
    <TextBlock x:Name="TextLabel"
               Text="{TemplateBinding AutomationProperties.Name}"
               Foreground="{StaticResource AppBarItemForegroundThemeBrush}"
               Margin="3,0,2,0"
               FontSize="16"
               VerticalAlignment="Center"
               MaxHeight="32"
               TextTrimming="WordEllipsis"
               Style="{StaticResource BasicTextStyle}" 
               FontFamily="Segoe UI Symbol"/>
</StackPanel>

And here is how it looks when compared to the original. I couldn’t find the exact share image used within the Games app,.

image

Now you’re ready to create some inline “appbar” buttons. If you want to use the same button in many places then create a style for that type of button. Then set that style for your button.

<Style x:Key="EditInlineAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource InlineAppBarButtonStyle}">
    <Setter Property="AutomationProperties.AutomationId" Value="InlineEditAppBarButton"/>
    <Setter Property="AutomationProperties.Name" Value="Inline AppBar Button Style"/>
    <Setter Property="Content" Value="&#xE104;"/>
</Style>

If you’re only going to use it once, then you can set the properties within the button itself.

<Button Style="{StaticResource InlineAppBarButtonStyle}"
        AutomationProperties.Name="Inline AppBar Button Style"
        Content="&#xE104;"/>

From here it’s up to you to change the color of the button  along with the VisualStates to match the theme you need. As is, the style will work great with dark backgrounds.

Tags:

Fixing the VisualState of your Win8 AppBar ToggleButton

by Shawn 4. September 2012 02:37

This post would also be called Fixing the AppBarButtonStyle for ToggleButton support in you Win8 app: Part 2

In my last post I explained how to fix the AppBarButtonStyle to support ToggleButtons. This minor fix does give you the correct style, however there are more problems. I’m not sure if it is a problem with the style, or with the ToggleButton, but after checking and unchecking the button, the state becomes completely messed up.

image

When the ToggleButton does not have an AppBarButtonStyle set, it works great. We only get this problem when we apply one of those styles. With that said, it would seem there is a problem with the style, but the style looks just fine. I’ve seen some solutions that change the Unchecked and/or Normal VisualState back to the original style. I have not seen this working and this should happen by default. It appeared to me that the VisualState was actually getting messed up. I tried subscribing to the Click event and changing the VisualState manually.

void AppBarToggleButton_Click(object sender, RoutedEventArgs e)
{
    ToggleButton button = (ToggleButton)sender;
    VisualStateManager.GoToState(button, button.IsChecked.Value ? "Checked" : "Unchecked", false);
}

I was happy surprised to find that this worked great. Of course you do not want to subscribe to this event for every ToggleButton that you have. So I created a ToggleButton that would fix this (until MS fixes it).

public class AppBarToggleButton : ToggleButton
{
    public AppBarToggleButton()
    {
        this.Click += AppBarToggleButton_Click;
    }
 
    void AppBarToggleButton_Click(object sender, RoutedEventArgs e)
    {
        VisualStateManager.GoToState(this, IsChecked.Value ? "Checked" : "Unchecked", false);
    }
}

<sidenote>ToggleButton has a virtual OnToggle method that you would think would work great, however this method is called before the click event and before IsChecked changes.</sidenote>

Tags:

Fixing the AppBarButtonStyle for ToggleButton support in your Win8 apps

by Shawn 27. August 2012 02:34

With the RTM release of Visual Studio 2012 the Visual Studio team made some improvements to the various AppBar button styles. One of these awesome improvements was to add support for ToggleButtons. Now you can use any of these out-of-the-box styles with Buttons AND ToggleButtons. This is pretty awesome… Except for the fact that the style is missing a required TextBlock. This TextBlock toggles the color of the button. Without it you do not get a visual indication that the ToggleButton is checked. Even worse, is that the application crashes when the ToggleButton is checked.

To fix this, open the StandardStyles.xaml file and go to the AppBarButtonStyle. Insert the following line directly under the “BackgroundGlyph” TextBlock

<TextBlock x:Name="BackgroundCheckedGlyph" Visibility="Collapsed" Text="&#xE0A8;" FontFamily="Segoe UI Symbol" FontSize="53.333" Margin="-4,-19,0,0" Foreground="{StaticResource AppBarItemForegroundThemeBrush}"/>

After inserting that line, the Grid within the StackPanel should look like:

<Grid Width="40" Height="40" Margin="0,0,0,5" HorizontalAlignment="Center">
    <TextBlock x:Name="BackgroundGlyph" Text="&#xE0A8;" FontFamily="Segoe UI Symbol" FontSize="53.333" Margin="-4,-19,0,0" Foreground="{StaticResource AppBarItemBackgroundThemeBrush}"/>
    <TextBlock x:Name="BackgroundCheckedGlyph" Visibility="Collapsed" Text="&#xE0A8;" FontFamily="Segoe UI Symbol" FontSize="53.333" Margin="-4,-19,0,0" Foreground="{StaticResource AppBarItemForegroundThemeBrush}"/>
    <TextBlock x:Name="OutlineGlyph" Text="&#xE0A7;" FontFamily="Segoe UI Symbol" FontSize="53.333" Margin="-4,-19,0,0"/>
    <ContentPresenter x:Name="Content" HorizontalAlignment="Center" Margin="-1,-1,0,0" VerticalAlignment="Center"/>
</Grid>

Now your app will not crash with that ToggleButton!

Next, read Part 2 to fix the VisualState of the ToggleButton.

Tags: ,

Adding multiple lines of text to your Win8 tile update

by Shawn 15. August 2012 00:04

Live tiles is one of the best things about Windows Phone and WinRT. I love how easy it is to add updates to your tiles in Windows Phone but hate how limited the updates are allowed to be. With WinRT comes a new API and more robust tile updates. There are many different types of tile updates and your app should support AT LEAST one square and one wide tile. If you’re like me (and I know I am), I started with the C# WinRT sample for App tiles and badges. I began looking at the code and could easily see that you use the TileUpdateManager to update your tile. The TileUpdateManager needs a TileNotification in order to update your tile. Here is where I quickly became lost in this rather complex sample. The TileNotification needs to be created with XML (an XmlDocumentto be exact) but I had not idea what XML elements are needed (Seriously, is all of the abstraction really needed here? This is suppose to be a simple sample to let devs see how to quickly and easily create a tile update). All I wanted to know was how to format the XML!

I was about to give up on the sample and look online (which so far hasn’t netted much for WinRT searches) when I decided to run the app. I was very pleased to find that the app will output what the XML would look like for the type of tile update you select.

image

This was perfect! Exactly what I was looking for. I copied the XML provided and put it into my app and I now had a tile update. I was now very happy with this sample after becoming very frustrated with it. As easy as this was I figured adding one more line of text must be just as easy. So I added a new <text> element to each wide and square tile.

<tile>
  <visual version="1">
    <binding template="TileWideText03">
      <text id="1">Hello World!</text>
      <text id="2">My very own tile notification</text>
    </binding>
    <binding template="TileSquareText04">
      <text id="1">Hello World!</text>
      <text id="2">My very own tile notification</text>
    </binding>
  </visual>
</tile>

After adding the two new lines, I ran the app, switched to the start screen and found that there was only one line of text. This was true for both the wide and square tile.

I’m pretty new to updating tiles in WinRT so I’m sure it’s something that I did. Time spans about a half hour later trying many changes to the XML and it turns out that the problem was with the version attribute of the visual element. I removed the version attribute and everything worked great!

<tile>
  <visual>
    <binding template="TileWideText03">
      <text id="1">Hello World!</text>
      <text id="2">My very own tile notification</text>
    </binding>
    <binding template="TileSquareText04">
      <text id="1">Hello World!</text>
      <text id="2">My very own tile notification</text>
    </binding>
  </visual>
</tile>

So, if you are starting with the sample as I did, do not use the version attribute of the visual element. I’m hoping that this is fixed with the RTM version of Windows 8. Only a few hours left until I can test for myself…

Tags:

About the author

Shawn KendrotShawn Kendrot is a Technical Lead at Telvent, specializing in GIS (ArcGIS) Desktop and Silverlight.

Month List

Page List

DISCLAIMER

The opinions expressed herein are my own personal opinions and do not represent my employer’s view in any way.