Tag Archives: Silverlight

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

Advertisements
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 , , , , , , , , ,

Using a UserControl in a Windows Phone app instead of a PhoneApplicationPage

Saw a weird problem today – an app would break when navigating to a certain page if it was not running under the debugger. Then I found it was actually navigating forward out of the application and to the Windows Phone home screen and if you tapped the back button – it would return back to the application. I first thought it had something to do with the WebBrowser control that was used inside of it and the fact that the page control was defined in a class library and not the application assembly, but it turned out to be a dead end. Well, what it did turn out is what the post title says – a UserControl was used instead of a PhoneApplicationPage. A navigation request to that control worked, but unless the app was running with a debugger attached – after a few seconds after navigating to that control it would navigate out of the app. After switching it back to be a PhoneApplicationPage – everything just started working!

It reminded me of another common problem with a stack overflow exception just killing an app with no feedback to the user, even when running under the debugger. A StackOverflowException exists, but can’t be caught – an app just immediately exits when it occurs, so be warned! 🙂

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 , , , , , , , , ,

Windows RunTime – Timeline of Influences

(Note – while I work for Microsoft, my team was not involved in developing the technologies mentioned below and the thoughts and opinions on this blog are mine only)

This is what Microsoft showed at Build:

image

Microsoft at its spirit – really trying to support everything and everyone – the slide is actually oversimplified, but the Desktop Apps part seems to encompass and indicate support for all the legacy apps, languages and APIs, while Metro style (or Immersive) Apps support the same languages on top of a new set of APIs. Whichever language you used before – you can still use it to create desktop or web applications as well as the new Metro style apps. The APIs are different, but seem to be highly influenced by the .NET technologies.

Thinking about the above made me think about all the influences that brought the above to life. Here’s a timeline I came up with:

image

Complicated? You bet!

Timeline: the blocks should be placed fairly chronologically top to bottom.
Dates: were picked arbitrarily from Wikipedia based on dates of initial public appearance or if available – on release dates of the APIs/frameworks/host environments.
Colors:
L
ight blue (aqua) blocks represent languages, with the shaded ones being the languages available for use with WinRT.
Purple blocks represent Windows API and frameworks that wrap its UI components.
Dark blue blocks show DirectX, DX-based WPF and WPF derived Silverlight (desktop and mobile).
Arrows indicate successors (eg. Winforms –> WPF), dependencies (eg. DX –> WPF) or influences successors (eg. VCL –> WinForms).

Tagged , , , , , , , , ,

Using attached properties to style RowDefinitions of a Grid in WPF

The below sample shows how you can use attached dependency properties to style a Grid’s RowDefinitions. This specific case shows how a single property can be set to specify the number of rows in a Grid.

<Window
    x:Class="GridExtensions.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:GridExtensions="clr-namespace:GridExtensions"
    Title="MainWindow"
    Height="350"
    Width="525">
    <Grid>
        <Grid.Style>
            <Style>
                <Setter Property="GridExtensions:Griddier.RowsCount" Value="4"/>
            </Style>
        </Grid.Style>
        <TextBlock Grid.Row="0" Text="0"/>
        <TextBlock Grid.Row="1" Text="1"/>
        <TextBlock Grid.Row="2" Text="2"/>
        <TextBlock Grid.Row="3" Text="3"/>
    </Grid>
</Window>
namespace GridExtensions
{
    using System;
    using System.Windows;
    using System.Windows.Controls;

    public static class Griddier
    {
        #region RowsCount

        /// <summary>
        /// RowsCount Attached Dependency Property
        /// </summary>
        public static readonly DependencyProperty RowsCountProperty =
            DependencyProperty.RegisterAttached("RowsCount", typeof(int), typeof(Griddier),
                new FrameworkPropertyMetadata((int)0,
                    new PropertyChangedCallback(OnRowsCountChanged)));

        /// <summary>
        /// Gets the RowsCount property. This dependency property 
        /// indicates the number of star-heighted row definitions to include with the grid.
        /// </summary>
        public static int GetRowsCount(DependencyObject d)
        {
            return (int)d.GetValue(RowsCountProperty);
        }

        /// <summary>
        /// Sets the RowsCount property. This dependency property 
        /// indicates the number of star-heighted row definitions to include with the grid.
        /// </summary>
        public static void SetRowsCount(DependencyObject d, int value)
        {
            d.SetValue(RowsCountProperty, value);
        }

        /// <summary>
        /// Handles changes to the RowsCount property.
        /// </summary>
        private static void OnRowsCountChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (d == null)
                throw new ArgumentNullException("d");
            var grid = d as Grid;
            if (grid == null)
                throw new ArgumentException("d should be of type Grid.", "d");
            int newRowsCount = (int)d.GetValue(RowsCountProperty);

            if (grid.RowDefinitions.Count > newRowsCount)
                while (grid.RowDefinitions.Count > newRowsCount)
                    grid.RowDefinitions.RemoveAt(newRowsCount);

            var gl = (GridLength)(new GridLengthConverter().ConvertFromString("*"));

            for (int i = 0; i < newRowsCount; i++)
            {
                if (grid.RowDefinitions.Count > i)
                    grid.RowDefinitions[i].Height = gl;
                else
                    grid.RowDefinitions.Add(new RowDefinition {Height = gl});
            }
        }

        #endregion
    }
}
Tagged , , , , , ,

Real-time Client-Server Communication on Windows Phone with Long Polling

I was looking for some information on Push technologies and the first thing I found was the Wikipedia article which among other things mentioned a technique called “Long Polling”. This seemed like a really feasible solution to the problem I have seen many people struggling with – getting real-time communication from the phone. Now I can see some limitations – an app needing to keep running to keep it from losing connection (this might change with one of the upcoming updates though if it enables multitasking), possible data usage or server security exposure problems, but I have little experience with server development, so why would I worry about that? Smile

I decided to try writing a Windows Phone 7 app with a back-end WCF service and a non-WP7-Silverlight app to talk to. This is simple – just create a solution in VS and a project for each of these three programs.

image

Let’s start with the WCF service.

Configure the WCF service to run on a specific port – go to project/properties/web and set “Specific port” to 2333. This will give you a reliable location to reference the service.

Add clientaccesspolicy.xml to the WCF service to let the SL4 app connect.

Rename (Refactor/Rename) Service1 to ChatService and define the service contract:

[ServiceContract]
public interface IChatService
{
    [OperationContract]
    void Send(string message, string recipient);
 
    [OperationContract]
    string GetMessage(string recipient);
}

As you can see – the service class will be simplified to only send messages to a given recipient and let a recipient poll for a message. This is the service class definition:

public class ChatService : IChatService
{
    // A list of messages for each recipient
    private static Dictionary<string, List<string>> messages = 
        new Dictionary<string, List<string>>();
 
    private static object syncObject = 
        new object();
 
    private static readonly TimeSpan Timeout = 
        TimeSpan.FromSeconds(50);
    
    public void Send(string message, string recipient)
    {
        lock (syncObject)
        {
            if (!messages.ContainsKey(recipient))
                messages.Add(recipient, new List<string>());
 
            messages[recipient].Add(message);
        }
    }
 
    public string GetMessage(string recipient)
    {
        var start = DateTime.Now;
 
        do
        {
            lock (syncObject)
            {
                if (!messages.ContainsKey(recipient))
                    messages.Add(recipient, new List<string>());
                else
                {
                    if (messages[recipient].Count > 0)
                    {
                        var message = messages[recipient][0];
                        messages[recipient].RemoveAt(0);
                        return message;
                    }
                }
 
                // Yes, just faking it out a bit to avoid the distractions from events,
                // resetevents etc.
                Thread.Sleep(50);
            }
        } while (DateTime.Now - start < Timeout);
 
        return null;
    }
}

We have a dictionary that has a list of messages for each registered recipient, the Send method just adds a message to the list and the GetMessage keeps polling to see if there is a new message in the list, then either returns one if one shows up or times out and returns null. The client will handle nulls and timeouts and interpret them as the good time to start another long poll.

Now do both WP7 and SL4 apps in parallel

Add service reference to both Silverlight projects, referencing http://localhost:2333/ChatService.svc as ChatService.

Now edit MainPage.xaml files for both apps – set x:Name=”root” on the root elements, then add this simplistic view as its Content shared by both apps:

 <Grid
    x:Name="LayoutRoot"
    Loaded="LayoutRoot_Loaded">

    <Grid.RowDefinitions>
        <RowDefinition Height="*"/>
        <RowDefinition Height="Auto"/>
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*"/>
        <ColumnDefinition Width="Auto"/>
    </Grid.ColumnDefinitions>
 
    <ListBox
        Grid.Row="0"
        Grid.ColumnSpan="2"
        ItemsSource="{Binding Messages, ElementName=root}" />

    <TextBox 
        x:Name="InputBox"
        Grid.Row="1"
        Grid.Column="0"
        Text="{Binding Message, Mode=TwoWay, ElementName=root}" />

    <Button
        x:Name="SendButton"
        Grid.Row="1"
        Grid.Column="1"
        Click="SendButton_Click"
        Content="Send"
        Padding="10,0" />

</Grid>

The code behind for both will be almost identical – just swap recipients. The one below is for the WP7 version – it sends messages to “SLPC” and gets messages for “WP7”.

public partial class MainPage : PhoneApplicationPage
{
    public static readonly DependencyProperty MessagesProperty =
        DependencyProperty.Register("Messages", typeof(ObservableCollection<string>), typeof(MainPage),
            new PropertyMetadata(null));
 
    public ObservableCollection<string> Messages
    {
        get { return (ObservableCollection<string>)GetValue(MessagesProperty); }
        set { SetValue(MessagesProperty, value); }
    }
 
    public static readonly DependencyProperty MessageProperty =
        DependencyProperty.Register("Message", typeof(string), typeof(MainPage),
            new PropertyMetadata(""));
 
    public string Message
    {
        get { return (string)GetValue(MessageProperty); }
        set { SetValue(MessageProperty, value); }
    }
 
    // Constructor
    public MainPage()
    {
        this.DataContext = this;
        Messages = new ObservableCollection<string>();
        InitializeComponent();
    }
 
    private void SendButton_Click(object sender, RoutedEventArgs e)
    {
        var svc = new ChatService.ChatServiceClient();
        svc.SendAsync(Message, "SLPC");
        Message = "";
    }
 
    private void LayoutRoot_Loaded(object sender, RoutedEventArgs e)
    {
        var svc = new ChatService.ChatServiceClient();
        svc.GetMessageCompleted += OnGetMessageCompleted;
        svc.GetMessageAsync("WP7");
    }
 
    private void OnGetMessageCompleted(object sender, ChatService.GetMessageCompletedEventArgs e)
    {
        var svc = (ChatService.ChatServiceClient) sender;
 
        if (e.Error is TimeoutException ||
            (e.Error == null && e.Result == null))
        {
            svc.GetMessageAsync("WP7");
            return;
        }
 
        if (e.Error != null)
            MessageBox.Show(e.Error.Message);
        else
        {
            Messages.Add(e.Result);
        }
 
        svc.GetMessageAsync("WP7");
    }
}

As you can see we have two dependency properties for the view to bind to – the list of Messages and the Message to send. Clicking on Send will simply send the message through the service proxy, while as soon as the page is loaded – polling on GetMessage starts and OnGetMessageCompleted will check for timeouts and nulls, show any messages incoming and reissue requests to GetMessage.

Conclusion

What we get is this – a Windows Phone and a Silverlight 4 apps talking to one another with virtually no delay.

image

Now there are a lot of simplifications here – no authentication of clients, hardcoded recipients, the server using polling to check for new messages from other threads etc. but it works!

I am sure this can be improved and perhaps there are better or simpler ways to achieve this and I am open to comments.

Tagged , , , , ,
Advertisements