Tag Archives: C#

Fixing WPF GridSplitter

Introduction

While working on a WPF tool for work – I was creating ColumnDefinitions in a Grid dynamically (with code) and also dynamically adding GridSplitters to allow users (myself) to resize the columns. Well, it all worked OK, except for the many times that it did not. I thought I made some mistake when creating these GridSplitters, but it seemed like some other people had similar problems with GridSplitters, namely sometimes – they would not work when used with a mouse. When you drag a splitter – it would spring back to original position and stop being dragged or it would jump to some other random (?) position – maybe resizing one of the columns to 0. I tried to fix it by configuring the GridSplitters in different ways – in code behind, between Begin/EndInit blocks, in XAML – nothing helped. I simplified the repro and it seemed to be happening more often when the Grid had many GridSplitters. I checked the same layout in Silverlight and it worked without any problems (well, except for needing to reference another library since GridSplitters are not available in Silverlight using the basic Visual Studio application template). This seemed to indicate the WPF implementation is just somehow broken and it won’t work. Well then I guess I should implement the control myself – how hard would it be? – handle some mouse and keyboard events and resize an associated ColumnDefinition or RowDefinition…

The Fix – SimpleGridSplitter

Continue reading

Tagged , , , , ,

BindableApplicationBar RC1 + Windows Phone Prism Application Template

Today marks the RC1 release of the BindableApplicationBar. I have spent some time today to do some StyleCop and documentation cleanup and it seems ready. You can grab a zip file with the binary directly from here.

Another part of what I have been working on today is a Windows Phone application template. I have spent too many times working on boilerplate code and cleaning up the default Windows Phone Application template and decided to create something that will get me up to speed faster every time I want to create a phone app. It is called Windows Phone Prism Application and is available as a Visual Studio project template here.

Continue reading

Tagged , , , , , , , , , , , ,

Good Vibrations on Windows Phone

I thought I’d do something foolish today and decided to play with a Windows Phone API I never used before – the VibrateController. How about playing back a rhythm? I coded up a very simple API to play back sequences of vibrations. You just build a VibrationSequence object with a list of Vibrations that specify vibration duration and a delay between the vibration start and the next vibration.

This is the code that plays it back:

var seq = new VibrationSequence(
    new Vibration(250, 500), 
    new Vibration(250, 1250), 
    new Vibration(200, 250), 
    new Vibration(250, 500), 
    new Vibration(250, 1500));
Play(seq);

And here is the VibrationSequence API:

Continue reading

Tagged , , ,

The quest for a Bindable ApplicationBar – Part 3

This is it – for now at least. The BindableApplicationBar meets all the original requirements and while I found some new ones that might be achievable – it might already be in many ways the richest solution to the problem.

DataContext Changes

Continue reading

Tagged , , , , , , , , ,

Dissecting WinRT UserControl

UserControl seems to be one of the most commonly used controls in XAML languages. It most often is at the root of your XAML file structure and usually encapsulates reusable pieces of XAML. Let’s take a look at the class hierarchy and see if it is different than in Silverlight:

Object

DependencyObject

UIElement

FrameworkElement

Control

UserControl

An Object in WinRT (WinRT being my shorthand for Immersive/Metro Windows 8 apps) is just a .NET System.Object.

DependencyObject “represents an object that participates in the dependency property system. DependencyObject is the immediate base class of many important UI-related classes, such as UIElement, Geometry, FrameworkTemplate, Style, and ResourceDictionary.”. It is then the base class for all controls and is required base class for use with bindings, (property value-) animations etc.
Comparing to Silverlight it is missing the CheckAccess property which you can use in Silverlight to see if you are running on a dispatcher thread, but it does still have the Dispatcher property which then gives you CoreDispatcher.HasThreadAccess to possibly gain the same information. Otherwise it has identically named members in both WinRT and Silverlight – methods for clearing, getting and setting dependency property values that depend on default values, set values, bindings, animations and possibly property inheritance.

UIElement is a base class for most of the objects that have visual appearance and can process basic input in a user interface.”. It provides access to events for handling touch, drag&drop, focus, keyboard input, pointer/stylus input (not directly supported in Silverlight), methods for handling layout and a Transitions property that is new to WinRT.

Looking at the stylus events – I started playing with the CapturePointer method (it captures stylus input to only be handled by the specific UIElement) which reminded me of a non-default way of testing WinRT apps – by using a Simulator. You go to project properties/Debug and switch Target Device to Simulator, then simply hit F5.

image

It looks a bit like a Surface or Windows Phone simulator, runs as a remote desktop connection to another session on your own machine and helps when you want to monitor VS Output window while running a full-screen immersive app…

image

Tagged , ,

Beginning WinRT development

I finally found some time to take a look at Windows 8 development + a shiny Build edition Samsung Series 7 Slate device, so it is time to start taking apart this WinRT development thing.
I am starting by setting up my development and blogging environment. I installed Live Essentials to use my usual Live Writer, which took a while since it first had to enable .NET 3.5… Well, it is good to know 3.5 is alive and kicking! 🙂

Now I still haven’t figured out the best way to paste code in Live Writer – VS Paste plugins output weird frames, while copying through Word looks nice, but it breaks quotes and some other characters, besides – I don’t have Office on this unit, which also means – I can’t use OneNote to do screen clippings. Well – I’ll figure something out I guess.

Creating a simple Windows 8 app

When you start Visual Studio 11 Express and start a new project you get to choose a couple of things:

image

First – the language – JS/VB/C#/C++. I chose C#. This is what I know best and while I believe it is worth learning something new – I still believe C# and .NET is in many ways better than JS+HTML5+CSS and what these are best is in attracting developers who already know these best.

Then – picking a project template – I just selected Application, since I want to focus on the basics first before I look at the cooler controls, whatever the temptation says.

I end up with the following minus the yellow rectangle that I added to see some content in the window and the one attribute per line formatting that I prefer:

<UserControl
    x:Class="Application1.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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DesignHeight="768"
    d:DesignWidth="1366">
    <Grid 
        x:Name="LayoutRoot" 
        Background="#FF0C0C0C">
        <Rectangle
            Fill="Yellow"
            Width="200"
            Height="200" />
    </Grid>
</UserControl>

When I hit F5 – I get the below image displayed on full-screen:

image

Great! A simple application is indeed very similar to Silverlight or WPF. I wonder why the default background is almost black… It looks pretty cool when I change my rectangle background to black – almost a screen test: Smile

image

I mentioned one attribute per line – this is my preference and recommendation in XAML development as it reduces the need for horizontal scrolling and makes reviewing and merging changes easier. It turns out it works just as well in VS11Express as it does in VS10 for Silverlight and WPF – just go to Tools/Options/Text Editor/XAML/Formatting/Spacing (yeah, just) – and click to “Position each attribute on a separate line” and uncheck “Position attribute on same line as start tag”:

image

Now while at it – also save yourself some time and go to XAML/Miscellaneous and check “Always open documents in full XAML view” – this gives you more space to code your XAML and prevents delays when VS loads the XAML designer.

image

It seems like the root control in XAML of a default screen type in WinRT is a UserControl – just like in Silverlight – another point for compatibility with Silverlight and against WPF where you usually deal with Window objects.

What is a UserControl? According to MSDN – it has same inheritance hierarchy as the one in Silverlight, though it throws in a bunch of interfaces – a side effect of WinRT’s close ties to C++ and cross-language support I guess:

image

Tagged , , , , ,

Debugging visual tree/layout issues in Silverlight and on Windows Phone

Many times, especially when you are doing dynamic layouts, dealing with bindings and DataTemplates – you start scratching your head trying to figure out why your control is not showing up where you want it to show, how to fix it or simply what are the coordinates of some point. You can look through your code, but sometimes the problem is not in your code or it just is not clear.

In such situations it often helps to debug the visual tree of the application and you can do it by browsing the object tree in Visual Studio, but it is sometimes quite laborious since you would usually have to call VisualTreeHelper.GetParent many times before you get to where the problem lies – if you do not get confused on the way and give up. In WPF – I used to use Snoop or Mole. In Silverlight – I did not hear of such tool (I just found about Silverlight Spy today – but it is not free if you want to do anything with Windows Phone). To help solve it I used to write or copy&paste a small attached dependency property that makes the VisualTreeHelper.GetParent calls for me and outputs the interesting properties when a control gets loaded. Today I finally decided to clean it up a bit, make it better and share it with you, so here it is.

Download the sample project here

http://dl.dropbox.com/u/1192076/VisualTreeDebugger.zip

This is just a Windows Phone (Mango) panorama project straight from the template with my VisualTreeDebugger class added and two breakpoint properties defined on some TextBlocks. One will break each time the DataTemplate that includes the TextBlock is applied and the TextBlock loaded, the other one – when you tap on one of the TextBlocks.

How do I use it?

Simply add the VisualTreeDebugger class to your own project, spacify the xmlns reference in XAML and either set VisualTreeDebugger.BreakOnLoaded="True" on your control to have the debugger break when the control first loads or VisualTreeDebugger.BreakOnTap="True" to break when you tap that control. Then just run it from your Visual Studio with debugger attached (F5).

<!--Panorama control-->
<controls:Panorama
    Title="my application">
    <controls:Panorama.Background>
        <ImageBrush
            ImageSource="PanoramaBackground.png" />
    </controls:Panorama.Background>

    <!--Panorama item one-->
    <controls:PanoramaItem
        Header="first item">
        <!--Double line list with text wrapping-->
        <ListBox
            Margin="0,0,-12,0"
            ItemsSource="{Binding Items}">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <StackPanel
                        xmlns:local="clr-namespace:VisualTreeDebugger"
                        Margin="0,0,0,17"
                        Width="432"
                        Height="78">
                        <TextBlock
                            local:VisualTreeDebugger.BreakOnTap="True"
                            Text="{Binding LineOne}"
                            TextWrapping="Wrap"
                            Style="{StaticResource PhoneTextExtraLargeStyle}" />
                        <TextBlock
                            local:VisualTreeDebugger.BreakOnLoaded="True"
                            Text="{Binding LineTwo}"
                            TextWrapping="Wrap"
                            Margin="12,-6,12,0"
                            Style="{StaticResource PhoneTextSubtleStyle}" />
                    </StackPanel>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
    </controls:PanoramaItem>
</controls:Panorama>

 

What do I get

You get a “path” field which is a list of all dependency objects between your debugged element and the visual tree root. It makes it easy to quickly get access to any of those objects to inspect their properties, when you debug it.

image

image

In the Output/Debug (Ctrl+W, O) window for each of these elements – you get a trace of selected layout properties that may affect the layout, including type, name, actual dimensions and position, logical properties like Width, Height, alignments, Margin and Padding, DataContext and brush information to sweeten the deal.

path[22] is Control: Microsoft.Phone.Controls.Panorama():
    ActualWidth=480
    ActualHeight=800
    Position – X=0, Y=0, Right=480, Bottom=800
    DataContext: VisualTreeDebugger.MainViewModel HashCode: 74304488
    Width=NaN
    Height=NaN
    HorizontalAlignment=Stretch
    VerticalAlignment=Stretch
    Margins=0,0,0,0
    Background=ImageBrush: System.Windows.Media.Imaging.BitmapImage, UriSource: PanoramaBackground.png
    Foreground=SolidColorBrush: #FFFFFFFF

How does it help?

You can find that control somewhere in the dozens of elements of your visual tree that has the alignment or margins set incorrectly or screen position of some invisible panel, find the actual dimensions of the element you need to fill with an image, see which element sets the background for all its contents or simply learn about the structure of the UI. It is easy to use and modify.

Why should I care about this project?

On Windows Phone – rather than coding it yourself – the only other option for debugging visual trees I found was to pay for Silverlight Spy.

With the source code – you can easily add more details that you think might be missing or simply browse the Visual Studio “Watch” window to find them.

Full Source Code of VisualTreeDebugger for quick copy & paste

#if DEBUG
using System.Collections.Generic;
using System.Diagnostics;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;

namespace VisualTreeDebugger
{
    /// <summary>
    /// The class contains an BreakOnLoaded property that
    /// allows to debug a visual tree from the control
    /// it was applied to up to the visual tree root.
    /// </summary>
    /// <remarks>
    /// Just put a breakpoint in ControlLoaded()
    /// and set VisualTreeDebugger.BreakOnLoaded="True"
    /// on any FrameworkElement/Control.
    /// <para/>
    /// Set BreakOnLoaded if you want to put the breakpoint yourself.
    /// <para/>
    /// DebugVisualTree method has a local path variable
    /// that contains a list of all elements from the visual tree root
    /// up to the debugged control.
    /// <para/>
    /// Debug Output window contains common visual tree layout properties
    /// helpful in debugging layout issues.
    /// </remarks>
    public class VisualTreeDebugger
    {
        #region BreakOnLoaded
        /// <summary>
        /// BreakOnLoaded BreakOnLoaded Dependency Property
        /// </summary>
        public static readonly DependencyProperty BreakOnLoadedProperty =
            DependencyProperty.RegisterAttached(
            "BreakOnLoaded",
            typeof(bool),
            typeof(VisualTreeDebugger),
            new PropertyMetadata(false, OnBreakOnLoadedChanged));

        /// <summary>
        /// Gets the BreakOnLoaded property. This dependency property 
        /// indicates whether the debugger should BreakOnLoaded when control is loaded.
        /// </summary>
        public static bool GetBreakOnLoaded(DependencyObject d)
        {
            return (bool)d.GetValue(BreakOnLoadedProperty);
        }

        /// <summary>
        /// Sets the BreakOnLoaded property. This dependency property 
        /// indicates whether the debugger should BreakOnLoaded when control is loaded.
        /// </summary>
        public static void SetBreakOnLoaded(DependencyObject d, bool value)
        {
            d.SetValue(BreakOnLoadedProperty, value);
        }

        /// <summary>
        /// Handles changes to the BreakOnLoaded property.
        /// </summary>
        private static void OnBreakOnLoadedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if ((bool)e.NewValue)
            {
                ((FrameworkElement)d).Loaded += ControlLoaded;
            }
            else
            {
                ((FrameworkElement)d).Loaded -= ControlLoaded;
            }
        }
        #endregion

        #region BreakOnTap
        /// <summary>
        /// BreakOnTap Attached Dependency Property
        /// </summary>
        public static readonly DependencyProperty BreakOnTapProperty =
            DependencyProperty.RegisterAttached(
                "BreakOnTap",
                typeof(bool),
                typeof(VisualTreeDebugger),
                new PropertyMetadata(false, OnBreakOnTapChanged));

        /// <summary>
        /// Gets the BreakOnTap property. This dependency property 
        /// indicates whether the attached debugger should break when
        /// the FrameworkElement on which this property is set is tapped.
        /// </summary>
        public static bool GetBreakOnTap(DependencyObject d)
        {
            return (bool)d.GetValue(BreakOnTapProperty);
        }

        /// <summary>
        /// Sets the BreakOnTap property. This dependency property 
        /// indicates whether the attached debugger should break when
        /// the FrameworkElement on which this property is set is tapped.
        /// </summary>
        public static void SetBreakOnTap(DependencyObject d, bool value)
        {
            d.SetValue(BreakOnTapProperty, value);
        }

        /// <summary>
        /// Handles changes to the BreakOnTap property.
        /// </summary>
        private static void OnBreakOnTapChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var frameworkElement = d as FrameworkElement;

            Debug.Assert(frameworkElement != null, "BreakOnTapProperty should only be set on FrameworkElements.");

            if ((bool)e.NewValue)
            {
                ((FrameworkElement)d).Tap += ControlTapped;
            }
            else
            {
                ((FrameworkElement)d).Tap -= ControlTapped;
            }
        }
        #endregion

        /// <summary>
        /// Occurs when the control this behavior is BreakOnLoaded to gets loaded.
        /// Put a breakpoint in here and set ControlDebugOnLoaded.BreakOnLoaded="True"
        /// on any control to debug its visual tree.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private static void ControlLoaded(object sender, RoutedEventArgs e)
        {
            var startElement = (DependencyObject)sender;
            DebugVisualTree(startElement);
        }

        static void ControlTapped(object sender, GestureEventArgs e)
        {
            var startElement = (DependencyObject)sender;
            DebugVisualTree(startElement);
        }

        #region DebugVisualTree()
        public static void DebugVisualTree(DependencyObject startElement)
        {
            var path = new List<DependencyObject>();
            var dob = startElement;

            while (dob != null)
            {
                path.Add(dob);
                dob = VisualTreeHelper.GetParent(dob);
            }

            for (int i = path.Count - 1; i >= 0; i--)
            {
                TraceDependencyObject(path[i], i);
            }

            // Put breakpoint here
            Debug.WriteLine(
                string.Format("Watch path[0] to path[{0}]",
                path.Count - 1));

            if (Debugger.IsAttached)
            {
                Debugger.Break();
            }
        }
        #endregion

        #region TraceDependencyObject()
        private static void TraceDependencyObject(DependencyObject dob, int i)
        {
            var frameworkElement = dob as FrameworkElement;

            if (frameworkElement == null)
            {
                Debug.WriteLine(
                    "path[{0}] is Dependency Object: {1}",
                    i,
                    dob.GetType());
            }
            else
            {
                var c = frameworkElement as Control;
                var cc = frameworkElement as ContentControl;
                var panel = frameworkElement as Panel;
                var parentGrid = frameworkElement.Parent as Grid;

                Debug.WriteLine(
                    "path[{0}] is Control: {1}({2}):",
                    i,
                    frameworkElement.GetType(),
                    frameworkElement.Name ?? "<unnamed>");

                // Actual layout information
                Debug.WriteLine("\tActualWidth={0}\r\n\tActualHeight={1}", frameworkElement.ActualWidth, frameworkElement.ActualHeight);
                var pos = frameworkElement.TransformToVisual(Application.Current.RootVisual).Transform(new Point());
                var pos2 = frameworkElement.TransformToVisual(Application.Current.RootVisual).Transform(new Point(frameworkElement.ActualWidth, frameworkElement.ActualHeight));
                Debug.WriteLine("\tPosition - X={0}, Y={1}, Right={2}, Bottom={3}",
                    pos.X,
                    pos.Y,
                    pos2.X,
                    pos2.Y);

                // DataContext often turns out to be a surprise
                Debug.WriteLine("\tDataContext: {0} {1}", frameworkElement.DataContext, frameworkElement.DataContext != null ? "HashCode: " + frameworkElement.DataContext.GetHashCode() : "");

                // List common layout properties
                Debug.WriteLine("\tWidth={0}\r\n\tHeight={1}", frameworkElement.Width, frameworkElement.Height);
                Debug.WriteLine("\tHorizontalAlignment={0}\r\n\tVerticalAlignment={1}", frameworkElement.HorizontalAlignment, frameworkElement.VerticalAlignment);

                if (cc != null)
                {
                    Debug.WriteLine(
                        "\tHorizontalContentAlignment={0}\r\n\tVerticalContentAlignment={1}",
                        cc.HorizontalContentAlignment,
                        cc.VerticalContentAlignment);
                }

                Debug.WriteLine("\tMargins={0}", frameworkElement.Margin);

                if (cc != null)
                {
                    Debug.WriteLine("\tPadding={0}", cc.Padding);
                }

                if (panel != null)
                {
                    Debug.WriteLine("\tBackground={0}", panel.Background);
                }
                else if (c != null)
                {
                    Debug.WriteLine("\tBackground={0}", BrushToString(c.Background));
                    Debug.WriteLine("\tForeground={0}", BrushToString(c.Foreground));
                }

                if (parentGrid != null)
                {

                    var col = Grid.GetColumn(frameworkElement);
                    var row = Grid.GetRow(frameworkElement);

                    if (parentGrid.ColumnDefinitions.Count != 0 || col != 0)
                    {
                        Debug.Assert(
                            col < parentGrid.ColumnDefinitions.Count,
                            string.Format("Column {0} not defined on the parent Grid!", col));
                        Debug.WriteLine(
                            "\tColumn: {0} ({1})",
                            col,
                            parentGrid.ColumnDefinitions[col].Width);
                    }

                    if (parentGrid.RowDefinitions.Count != 0 || row != 0)
                    {
                        Debug.Assert(
                            row < parentGrid.RowDefinitions.Count,
                            string.Format("Row {0} not defined on the parent Grid!", row));
                        Debug.WriteLine(
                            "\tRow: {0} ({1})",
                            row,
                            parentGrid.RowDefinitions[col].Height);
                    }
                }

                if (frameworkElement.Parent is Canvas)
                {
                    var x = Canvas.GetLeft(frameworkElement);
                    var y = Canvas.GetTop(frameworkElement);
                    var zIndex = Canvas.GetZIndex(frameworkElement);

                    Debug.WriteLine("\tCanvas - X={0}, Y={1}, ZIndex={2}", x, y, zIndex);
                }
            }
        }
        #endregion

        #region BrushToString()
        private static string BrushToString(Brush brush)
        {
            if (brush == null)
                return "";

            var solidColorBrush = brush as SolidColorBrush;

            if (solidColorBrush != null)
            {
                return string.Format("SolidColorBrush: {0}", solidColorBrush.Color);
            }

            var imageBrush = brush as ImageBrush;

            if (imageBrush != null)
            {
                var bi = imageBrush.ImageSource as BitmapImage;

                if (bi != null)
                {
                    return string.Format(
                        "ImageBrush: {0}, UriSource: {1}",
                        bi,
                        bi.UriSource);
                }

                return string.Format("ImageBrush: {0}", imageBrush.ImageSource);
            }

            return brush.ToString();
        }
        #endregion
    }
}
#endif
Tagged , , , , ,

The quest for a Bindable ApplicationBar – Part 2

Seems like the application bar finally works for predefined buttons. Next step – binding to ItemsSource-like view model collections for buttons and menu items and getting “ElementName bindings” to work…

For now – this will have to be it. I am sharing my library and test projects here.

Continue reading

Tagged , , , , , , , , ,

Workaround for binding not supported on Style/Setter/Value in Silverlight

This morning I was trying to bind the Orientation property of a PanoramaItem to a panorama item view model bound to the Panorama through its ItemsSource property and kept getting this weird exception when Silverlight was parsing the xaml stating that it “Cannot set read-only property”. I tried working around that and have some sort of attached property that would be bound to Orientation property of the view model and update the PanoramaItem’s Orientation property, but that produced the same result…

Well, it just so turns out – it is one of those things that are supported in WPF, but not in Silverlight

As a workaround – I created some attached dependency properties that are not bound to the view model themselves, but specify just the binding path and when set – create a binding to the PanoramaItem properties – as below…

public static class BindingBuilder
{
    #region PanoramaItemOrientation
    /// <summary>
    /// PanoramaItemOrientation Attached Dependency Property
    /// </summary>
    public static readonly DependencyProperty PanoramaItemOrientationProperty =
        DependencyProperty.RegisterAttached(
            "PanoramaItemOrientation",
            typeof(string),
            typeof(BindingBuilder),
            new PropertyMetadata(null, OnPanoramaItemOrientationChanged));

    /// <summary>
    /// Gets the PanoramaItemOrientation property. This dependency property
    /// indicates the binding path for the PanoramaItem.Orientation binding.
    /// </summary>
    public static string GetPanoramaItemOrientation(DependencyObject d)
    {
        return (string)d.GetValue(PanoramaItemOrientationProperty);
    }

    /// <summary>
    /// Sets the PanoramaItemOrientation property. This dependency property
    /// indicates the binding path for the PanoramaItem.Orientation binding.
    /// </summary>
    public static void SetPanoramaItemOrientation(DependencyObject d, string value)
    {
        d.SetValue(PanoramaItemOrientationProperty, value);
    }

    /// <summary>
    /// Handles changes to the PanoramaItemOrientation property.
    /// </summary>
    private static void OnPanoramaItemOrientationChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        string oldPanoramaItemOrientation = (string)e.OldValue;
        string newPanoramaItemOrientation = (string)d.GetValue(PanoramaItemOrientationProperty);
        var panoramaItem = (PanoramaItem)d;

        panoramaItem.SetBinding(PanoramaItem.OrientationProperty, new Binding(newPanoramaItemOrientation));
    }
    #endregion

    #region PanoramaItemHeader
    /// <summary>
    /// PanoramaItemHeader Attached Dependency Property
    /// </summary>
    public static readonly DependencyProperty PanoramaItemHeaderProperty =
        DependencyProperty.RegisterAttached(
            "PanoramaItemHeader",
            typeof(string),
            typeof(BindingBuilder),
            new PropertyMetadata(null, OnPanoramaItemHeaderChanged));

    /// <summary>
    /// Gets the PanoramaItemHeader property. This dependency property
    /// indicates the binding path for the PanoramaItem.Header binding.
    /// </summary>
    public static string GetPanoramaItemHeader(DependencyObject d)
    {
        return (string)d.GetValue(PanoramaItemHeaderProperty);
    }

    /// <summary>
    /// Sets the PanoramaItemHeader property. This dependency property
    /// indicates the binding path for the PanoramaItem.Header binding.
    /// </summary>
    public static void SetPanoramaItemHeader(DependencyObject d, string value)
    {
        d.SetValue(PanoramaItemHeaderProperty, value);
    }

    /// <summary>
    /// Handles changes to the PanoramaItemHeader property.
    /// </summary>
    private static void OnPanoramaItemHeaderChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        string oldPanoramaItemHeader = (string)e.OldValue;
        string newPanoramaItemHeader = (string)d.GetValue(PanoramaItemHeaderProperty);
        var panoramaItem = (PanoramaItem)d;

        panoramaItem.SetBinding(PanoramaItem.HeaderProperty, new Binding(newPanoramaItemHeader));
    }
    #endregion
}

Now I can have my Panorama built dynamically from its ItemsSource binding while still supporting setting of the PanoramaItems’ orientation and header through binding:

<Controls:Panorama
    DataContext="{Binding Panorama}"
    ItemsSource="{Binding Items}"
    Title="{Binding Title}"
    Background="{Binding Background}">
    <Controls:Panorama.ItemContainerStyle>
        <Style
            TargetType="Controls:PanoramaItem">
            <Setter
                Property="CacheMode"
                Value="BitmapCache" />
            <Setter
                Property="HorizontalContentAlignment"
                Value="Stretch" />
            <Setter
                Property="VerticalContentAlignment"
                Value="Stretch" />
            <Setter
                Property="local:BindingBuilder.PanoramaItemOrientation"
                Value="Orientation" />
            <Setter
                Property="local:BindingBuilder.PanoramaItemHeader"
                Value="HeaderText" />
            <Setter
                Property="Template">
                <Setter.Value>
                    <ControlTemplate
                        TargetType="Controls:PanoramaItem">
                        <Grid
                            Background="{TemplateBinding Background}"
                            Margin="12,0,0,0″>
                            <Grid.RowDefinitions>
                                <RowDefinition
                                    Height="auto" />
                                <RowDefinition
                                    Height="*" />
                            </Grid.RowDefinitions>
                            <ContentControl
                                x:Name="header"
                                ContentTemplate="{TemplateBinding HeaderTemplate}"
                                Content="{TemplateBinding Header}"
                                FontSize="{StaticResource PhoneFontSizeExtraExtraLarge}"
                                FontFamily="{StaticResource PhoneFontFamilySemiLight}"
                                HorizontalAlignment="Left"
                                Margin="10,-2,0,26″>
                                <ContentControl.RenderTransform>
                                    <TranslateTransform
                                        x:Name="headerTransform" />
                                </ContentControl.RenderTransform>
                            </ContentControl>
                            <ContentPresenter
                                Content="{TemplateBinding Content}"
                                HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                                Margin="{TemplateBinding Padding}"
                                Grid.Row="1″
                                VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
                        </Grid>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </Controls:Panorama.ItemContainerStyle>
</Controls:Panorama>

Then again – you could use the generic solution…

Tagged , , , , , , , ,

The quest for a Bindable ApplicationBar – Part 1

The funny thing about MVVM is that you might get so hooked on it that when you find something that just does not work with MVVM – you start trying to figure out how to bend it to the right path. One of such things is the ApplicationBar on Windows Phone.

Continue reading

Tagged , , , , , , , , ,