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!