Wednesday, November 24, 2010

My 1st Windows Phone Application

Well, this why you shouldn’t be buying a book relying on title alone.  Winking smile

I figured if you navigate to a URL from Windows Phone browser, I should be able to see the SL app right away.  Silly me!

It brought me to the Silverlight download page!  Isn’t SL supposed to be the ‘Default’ for Windows phone app development?

Should I be “installing” my app as opposed to running directly from the browser?  More on this once I figure this out.

Thursday, July 01, 2010

DynamicMvvM – An Overview

 

Goals
  1. How do you adapt a pre-existing Object Model (that doesn’t support notifications)?
  2. How do I extend my ViewModel with a commanding infrastructure?
  3. How do I make my standard .Net resource available as part of ViewModel Metamodel?
  4. How do I make my model support a State Machine?
    • This is could be really useful when to trigger actions based on object state
    • Essentially, we’re moving all action(s)/behaviors into commands making the models passive
  5. How do I extend the existing Model with new properties?
  6. How do I create a brand new model from scratch (fully dynamic)?
  7. How do I make related information part of Model MetaModel?
    • For example, EmployeeKind can be an enum of ‘Skilled, Unskilled, Others’.
  8. How do I emulate standard static C# property on a Dynamic model?
  9. How do I add support for Radio / Linked Properties
  10. How do I translate an existing object into a dynamic Model?
  11. How do I translate the same back?

DynamicMvvM

Initial Design

ModelProviders

Models-2

 

Assigning AutomationIds Dynamically in Silverlight

I came across an interesting problem recently:  How do you assign AutomationIds automatically for Items in an ItemsControl?

<UserControl 
    x:Class="ThinkFarAhead.AutoAutomationId.MainPage" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:local="clr-namespace:ThinkFarAhead.AutoAutomationId" 
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="400">
 
  <Grid x:Name="LayoutRoot" Background="White">
    <StackPanel>
      <ItemsControl x:Name="ItemsControl">
        <ItemsControl.ItemTemplate>
          <DataTemplate>
            <TextBlock 
                local:AutomationHelper.Parent=
                    "{Binding ElementName=ItemsControl}"
                local:AutomationHelper.CurrentItem=
                    "{Binding Path=DataContext,
                    RelativeSource={RelativeSource TemplatedParent}}"
                local:AutomationHelper.IndexedId="Message{0}"
                    AutomationProperties.AutomationId=
                        "{Binding (local:AutomationHelper.IndexedId),
                        RelativeSource={RelativeSource Self}}"
                Text="{Binding (AutomationProperties.AutomationId), 
                    RelativeSource={RelativeSource Self}}" />
          </DataTemplate>
        </ItemsControl.ItemTemplate>
      </ItemsControl>
    </StackPanel>
  </Grid>
</UserControl>

 

namespace ThinkFarAhead.AutoAutomationId
{
    using System.Windows;
    using System.Windows.Controls;
 
    public static class AutomationHelper
    {
        #region Constants and Fields
 
        public static DependencyProperty CurrentItemProperty =
            DependencyProperty.RegisterAttached(
                "CurrentItem",
                typeof(object),
                typeof(AutomationHelper),
                new PropertyMetadata(null)
            );
 
        public static DependencyProperty IndexedIdProperty =
            DependencyProperty.RegisterAttached(
                "IndexedId",
                typeof(string),
                typeof(AutomationHelper),
                new PropertyMetadata(null)
            );
 
        public static DependencyProperty ParentProperty =
            DependencyProperty.RegisterAttached(
                "Parent",
                typeof(ItemsControl),
                typeof(AutomationHelper),
                new PropertyMetadata(null)
            );
 
        #endregion
 
        #region Public Methods
 
        public static object GetCurrentItem(DependencyObject element)
        {
            return element.GetValue(CurrentItemProperty);
        }
 
        public static string GetIndexedId(DependencyObject element)
        {
            return element.GetValue(IndexedIdProperty) as string;
        }
 
        public static ItemsControl GetParent(DependencyObject element)
        {
            return element.GetValue(ParentProperty) as ItemsControl;
        }
 
        public static void SetCurrentItem
        (
            DependencyObject element, object value
        )
        {
            element.SetValue(CurrentItemProperty, value);
        }
 
        public static void SetIndexedId
        (
            DependencyObject element, string value
        )
        {
            value = string.Format(value, 
                GetParent(element)
                .Items
                .IndexOf(GetCurrentItem(element)));
 
            element.SetValue(IndexedIdProperty, value);
        }
 
        public static void SetParent
        (
            DependencyObject element, ItemsControl value
        )
        {
            element.SetValue(ParentProperty, value);
        }
 
        #endregion
    }
}

 

namespace ThinkFarAhead.AutoAutomationId
{
    using System.Collections.Generic;
    using System.Windows.Controls;
 
    public partial class MainPage : UserControl
    {
        #region Constructors and Destructors
 
        public MainPage()
        {
            this.InitializeComponent();
 
            var list = new List<string>();
 
            list.Add("Zero");
            list.Add("One");
            list.Add("Two");
            list.Add("Three");
            list.Add("Four");
            list.Add("Five");
            list.Add("Six");
            list.Add("Seven");
            list.Add("Eight");
            list.Add("Nine");
            list.Add("Ten");
 
            this.ItemsControl.ItemsSource = list;
        }
 
        #endregion
    }
}

 

image

Hope it helps somebody!

 

Tuesday, May 18, 2010

Silverlight 4.0, Dynamic Types & Extension Methods

I’ve been very busy lately earning my living.  Not able to get time to blog!  But hey, I wanted to hack together something - the DynamicMvvM framework which I had hinted about earlier - over this weekend.  This got me working with the beauty that is DynamicObject (and ExpandoObject) in C# 4.0.

The first error I had hit was probably the one every one working with DynamicObject for the first time encounters:

Error    2    Predefined type 'Microsoft.CSharp.RuntimeBinder.Binder' is not defined or imported    TestApp
Error    3    One or more types required to compile a dynamic expression cannot be found. Are you missing references to Microsoft.CSharp.dll and System.Core.dll?   

True enough when I did add Microsoft.CSharp.dll and System.Core.dll, the errors went away.

Using a keyword in C# requires me to add some assemblies?  Oops…  Adding these two assemblies to Visual Studio project templates for Silverlight, by default, would be high on my wish list!  Thankfully, these are added by default to WPF projects (the only one I checked) and hopefully other project types.

Another interesting thing about DynamicObject is that when it’s assigned to a dynamic, it’s not able to find ExtensionMethods.  This is reasonable considering all the calls to the methods and properties are resolved at runtime and the ExtensionMethods are not part of the object’s interface per se.

Just one one more piece of info you need to file away for future use.

Thursday, March 11, 2010

Update: The Need for ViewModels

Well, I left out something that was very close to my heart having been developing UI based applications for such a long time.  That’s testability:  How do you Unit Test your UI with standard / Open Source tools (Project White – now rechristened White -  is one I experimented with couple of years ago).  Though, Microsoft has made it easier than ever before to automate UI for testing with Automation APIs,  if your UI is clearly separated, you could just use MSTest/XUnit frameworks to Unit test.

Wednesday, March 03, 2010

Using ICommand & Triggers – Approach #1

The ChessboardViewModel defines a event handler by exposing a PieceSelectedHandler property. This property returns an object implementing the ICommand interface. The Style trigger on the BoardSquareView binds to this property by accessing the Parent property of the BoardSquareViewModel. The Parent property on the current ViewModel allows accessing the ViewModel's enclosing ViewModel, which in this case is the ChessboardViewModel.

Traversing the ViewModel tree like this adds dependencies and I believe should be used judiciously. This is where RoutedCommands and events could fit right in. More on this later. Blogging from an iPhone is not easy! [Update: Edited from “I’m a PC” ;)]

ViewModelTree

 

Even though, the CanExecute() and Execute() is called – correctly – if I were to wire up the command from the style trigger as below, there’re few issues:

            <Style.Triggers>

                <Trigger Property="IsChecked" Value="true">

                    <Setter Property="Background" Value="DarkBlue" />

                    <Setter Property="Foreground" Value="LightGreen" />

                    <Setter Property="BorderBrush" Value="LightGreen" />

                    <Setter Property="BorderThickness" Value="2" />

                    <Setter Property="CommandParameter" Value="{Binding}"/>

                    <Setter Property="Command" Value="{Binding Parent.PieceSelectedHandler}"/>

                </Trigger>

            </Style.Triggers>

  1. CanExecute() – and Execute() subsequently - get called twice for a single click
  2. Order of CommandParameter assignment seem to matter.  If CommandParameter is assigned after Command property assignment, the first call to CanExecute() is passed ‘null’
  3. None of the auto-magic enablement/disablement of buttons (not RadioButtons?) associated with RoutedCommands are evident when you use ICommand

Tuesday, March 02, 2010

Shatranj Updated

ViewModel tree has been introduced and been tied to the Views.

Next Steps:

  1. Have the board respect turns
  2. On selection, a piece must indicate on board the legal moves
  3. The player should be able to move his/her piece to the desired square
  4. Player should be able to customize the starting position of the board
  5. Integrate with Huo Chess
  6. Port to Silverlight 4.0
  7. Make it a multi-player game?

The Need for ViewModels

A Recap:

  1. A GameView –> ChessboardView –> ItemsTemplate –> Items (BoardSquareView in DataTemplate)
  2. GameView uses Game object (Model) as a resource or creates and assigns the Game object to GameView’s DataContext property
  3. Game exposes Chessboard as property
  4. Chessboard exposes ObservableCollection of BoardSquare objects

The code we’ve seen thus far there’s no real difference in functionality if we do not have ViewModels.  In fact, we could use the code-behinds to infuse Views with ViewModel behaviour.  Of course, at design time, there’s still a clean separation:

  1. XAML is in a separate file
  2. Code-behind is a separate file too
    • In classic implementation, you typically would have tons of code assigning values to the controls and objects here.  Let’s just forget for now what pundits would say about such implementation!
  3. Models

Unfortunately, at compile time and hence at runtime, the view requires the code-behind.  Since code-behind is so intricately entwined with the view, it’s important to shun it for holding the UI state. 

Why is UI state such a big deal?  Now, let’s say we want to hold state of the controls.  For example, we have a ‘Turn’ property in the Chessboard model indicating whose turn it is.  We could use it to control which ones to enable and disable.  Assume, we’ll use triggers ingeniously to control and make the view hold this state. The more complex the interactions, the more complex the view would become.  Does that belong there?

Another question is do we care?  Why is this important that we use this another level of indirection? 

It’s elementary, my dear Watson!

It’s all about replacing the View at will.   When XAML allows you to craft your individual controls on screen to such granularity, why turn your back for replacing your application’s entire UI?  Assume, you want to deliver your applications in multiple flavors.  One that takes full advantage of WPF and yet another that runs inside the browser. 

Silverlight 3.0, for example, doesn’t support many of the features that WPF provides.  Triggers are one.  We’ll see what it takes to do this when we release Shatranj for Silverlight 4.0.

Lo and behold!  Enter ViewModels!

 

Sunday, February 28, 2010

Shatranj – WPF Dependency Property value providers & Resolving Conflicts

We’ve couple of wrinkles in the “final” code there.

  • Border has a name specified.  It’s not a problem in itself.  Problem is the name is used by the template style trigger to set the value.
  • Both values are specified using Template triggers and when there’s a conflict, the last one wins.  This is a big one!

TemplateTriggerConflicts

  • Border properties values do not respect the values set at RadioButton level

Fix

1. Use TemplateBinding

<Border BorderBrush="{TemplateBinding BorderBrush}"

    BorderThickness="{TemplateBinding BorderThickness}"

2. Move the ‘IsSelected’ trigger to an a value provider of higher precedence.  From Adam Nathan’s WPF Unleashed, the precedence is as below:

    • Local value
    • Style triggers
    • Template triggers
    • Style setters
    • Theme style triggers
    • Theme style setters
    • Property value inheritance
    • Default value

 

So we have 2 options:

  1. Use code-behind and directly set values (Local value)
  2. Use Style triggers

        protected override void OnClick()

        {

            base.OnClick();

            var context = this.DataContext as BoardSquare;

            this.Background = new SolidColorBrush(Colors.DarkBlue);

            this.Foreground = new SolidColorBrush(Colors.LightGreen);

            this.BorderBrush = new SolidColorBrush(Colors.LightGreen);

            this.BorderThickness = new Thickness(2);

            //context.Select(true, MoveOptions.AllApplicable);

        }

Style Triggers

 

 

    1 <RadioButton x:Class="Shatranj.BoardSquareView"

    2             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    3             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    4             xmlns:Shatranj="clr-namespace:Shatranj">

    5 

    6   <RadioButton.Resources>

    7     <Shatranj:ForceToGroupNameConverter x:Key="ForceToGroupNameConverter" />

    8     <Style TargetType="{x:Type Shatranj:BoardSquareView}">

    9       <Setter Property="IsChecked" Value="false" />

   10       <Setter Property="Background"

   11               Value="{Binding Converter={StaticResource LocationToColorConverter},

   12                             Mode=OneWay}" />

   13       <Setter Property="BorderThickness" Value="0" />

   14       <Setter Property="GroupName"

   15               Value="{Binding Converter={StaticResource ForceToGroupNameConverter}}" />

   16       <Setter Property="Template">

   17         <Setter.Value>

   18           <ControlTemplate TargetType="{x:Type Shatranj:BoardSquareView}">

   19             <ControlTemplate.Resources>

   20               <Style TargetType="RadioButton">

   21                 <Setter Property="Background"

   22                         Value="{Binding Converter={StaticResource LocationToColorConverter},

   23                                       Mode=OneWay}" />

   24               </Style>

   25               <Shatranj:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter" />

   26             </ControlTemplate.Resources>

   27 

   28             <Grid Background="{TemplateBinding Background}">

   29               <Grid.RowDefinitions>

   30                 <RowDefinition Height="0.200*" />

   31                 <RowDefinition Height="0.800*" />

   32               </Grid.RowDefinitions>

   33               <Grid.ColumnDefinitions>

   34                 <ColumnDefinition />

   35               </Grid.ColumnDefinitions>

   36               <Border BorderBrush="{TemplateBinding BorderBrush}"

   37                       BorderThickness="{TemplateBinding BorderThickness}"

   38                       Grid.Row="0" Grid.Column="0" Grid.RowSpan="2" />

   39               <Viewbox Grid.Row="0" Grid.Column="0"

   40                       HorizontalAlignment="Right" VerticalAlignment="Stretch">

   41                 <TextBlock FontSize="12"

   42                           FontFamily="Consolas"

   43                           Text="{Binding Path=AlgebraicIdentity, Mode=OneWay}" />

   44               </Viewbox>

   45               <Viewbox Grid.RowSpan="2" Grid.Row="0"

   46                       HorizontalAlignment="Center" VerticalAlignment="Center">

   47                 <TextBlock FontFamily="Chess Cases"

   48                           Margin="3,3,3,3"

   49                           Text="{Binding Path=CurrentPiece.AltChar,

   50                                         Mode=OneWay}" />

   51               </Viewbox>

   52 

   53               <Ellipse

   54                 MaxHeight="30" MaxWidth="30"

   55                 MinHeight="10" MinWidth="10"

   56                 Grid.Row="0" Grid.RowSpan="2"

   57                 Visibility="{Binding IsHit,

   58                                     Converter={StaticResource BoolToVisibilityConverter}}">

   59                 <Ellipse.Fill>

   60                   <SolidColorBrush Color="Black" Opacity="0.5" />

   61                 </Ellipse.Fill>

   62               </Ellipse>

   63 

   64             </Grid>

   65 

   66             <ControlTemplate.Triggers>

   67               <Trigger Property="IsMouseOver" Value="true">

   68                 <Setter Property="Foreground" Value="Red" />

   69                 <Setter Property="Background" Value="Black" />

   70                 <Setter Property="BorderBrush" Value="Red" />

   71                 <Setter Property="BorderThickness" Value="2" />

   72               </Trigger>

   73             </ControlTemplate.Triggers>

   74           </ControlTemplate>

   75         </Setter.Value>

   76       </Setter>

   77       <Style.Triggers>

   78         <Trigger Property="IsChecked" Value="true">

   79           <Setter Property="Background" Value="DarkBlue" />

   80           <Setter Property="Foreground" Value="LightGreen" />

   81           <Setter Property="BorderBrush" Value="LightGreen" />

   82           <Setter Property="BorderThickness" Value="2" />

   83         </Trigger>

   84       </Style.Triggers>

   85     </Style>

   86   </RadioButton.Resources>

   87 </RadioButton>

ChessBoardWith3GroupsOfRadioButtons

Yes, now when you hover d2, d3 or d7, the selected highlight remains (remember, we have three distinct RadioButton groups there)!  Of course, we need to make sure only one group is selectable at any one point of time.  Topic for further posts!