Click here to Skip to main content
15,878,814 members
Articles / Desktop Programming / XAML

A slightly less simple object browser for Windows phone 7

Rate me:
Please Sign up or sign in to vote.
0.00/5 (No votes)
29 Jan 2012CPOL 9.5K  
A slightly less simple object browser for Windows phone 7

Building slightly on the previous post, we can drill into properties and navigate back up the parent tree.The XAML adds some navigation hyperlinks and styling to differentiate betwwen a property that can drilled into vs one that can't (ValueTypes)

XML
<UserControl x:Class="GoogleAuthDemo.ObjectBrowser"
    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"
    xmlns:local="clr-namespace:GoogleAuthDemo"
    mc:Ignorable="d"
    FontFamily="{StaticResource PhoneFontFamilyNormal}"
    FontSize="{StaticResource PhoneFontSizeNormal}"
    Foreground="{StaticResource PhoneForegroundBrush}"
    d:DesignHeight="480" d:DesignWidth="480"
    x:Name="root">

    <UserControl.Resources>
        <local:ObjectPropertiesConverter x:Key="ObjectPropertiesConvert"/>

        <Style x:Key="PropertyStyle" TargetType="HyperlinkButton">
         <Setter Property="Foreground" Value="{StaticResource PhoneForegroundBrush}"/>
         <Setter Property="Background" Value="Transparent"/>
         <Setter Property="FontSize" Value="{StaticResource PhoneFontSizeMedium}"/>
         <Setter Property="Padding" Value="0"/>
         <Setter Property="Template">
          <Setter.Value>
           <ControlTemplate TargetType="HyperlinkButton">
            <Border Background="Transparent">
             <VisualStateManager.VisualStateGroups>
              <VisualStateGroup x:Name="CommonStates">
               <VisualState x:Name="Normal">
                <Storyboard>
                 <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" 
            Storyboard.TargetName="TextElement">
                  <DiscreteObjectKeyFrame KeyTime="0" 
            Value="{StaticResource PhoneAccentBrush}"/>
                 </ObjectAnimationUsingKeyFrames>
                </Storyboard>      
         </VisualState>
               <VisualState x:Name="MouseOver"/>
               <VisualState x:Name="Pressed">
                <Storyboard>
                 <DoubleAnimation Duration="0" To="0.5" 
            Storyboard.TargetProperty="Opacity" Storyboard.TargetName="TextElement"/>
                </Storyboard>
               </VisualState>
               <VisualState x:Name="Disabled">
                <Storyboard>
                 <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" 
            Storyboard.TargetName="TextElement">
                  <DiscreteObjectKeyFrame KeyTime="0" 
            Value="{StaticResource PhoneForegroundBrush}"/>
                 </ObjectAnimationUsingKeyFrames>
                </Storyboard>
               </VisualState>
              </VisualStateGroup>
             </VisualStateManager.VisualStateGroups>
             <Border Background="{TemplateBinding Background}" 
        Margin="{StaticResource PhoneHorizontalMargin}" 
        Padding="{TemplateBinding Padding}">
              <TextBlock x:Name="TextElement" 
        HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" 
        Text="{TemplateBinding Content}" 
        VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
             </Border>
            </Border>
           </ControlTemplate>
          </Setter.Value>
         </Setter>
        </Style>

        <DataTemplate x:Key="PropertyTemplate">
            <StackPanel Orientation="Horizontal" HorizontalAlignment="Stretch">
                <TextBlock Text="{Binding Name}" Margin="0,0,10,1"/>
                     <HyperlinkButton HorizontalAlignment="Right" 
                    IsEnabled="{Binding HasChildren}"
                    Content="{Binding Value}"                    
                    Click="HyperlinkButton_Click" Style="{StaticResource PropertyStyle}"/>

            </StackPanel>
        </DataTemplate>
    </UserControl.Resources>

    <StackPanel Orientation="Vertical">
        <HyperlinkButton Content="< Back" Foreground="{StaticResource PhoneAccentBrush}" 
        HorizontalAlignment="Left"
                 IsEnabled="{Binding ElementName=root, Path=CanBack}"
                         Click="BackButton_Click" FontWeight="Bold" FontStyle="Normal"/>
        <ScrollViewer>
            <ItemsControl ItemTemplate="{StaticResource PropertyTemplate}"
                      ItemsSource="{Binding Path=., 
            Converter={StaticResource ObjectPropertiesConvert}}">
            </ItemsControl>
        </ScrollViewer>
    </StackPanel>
</UserControl>

Then we need to add a little bit to the control's code behind to handle forward and backward navigation:

C#
public partial class ObjectBrowser : UserControl
{
    public ObjectBrowser()
    {
        InitializeComponent();
    }
    
    private Stack<object> _backStack = new Stack<object>();
    
    private void HyperlinkButton_Click(object sender, RoutedEventArgs e)
    {
        if (this.DataContext != null)
        {
            _backStack.Push(this.DataContext);
            CanBack = true;
        }
        ObjectProperty p = ((HyperlinkButton)sender).DataContext as ObjectProperty;
        if (p != null)
            this.DataContext = p.TheObject;
    }
    
    private void BackButton_Click(object sender, RoutedEventArgs e)
    {
        if (_backStack.Count > 0)
        {
            DataContext = _backStack.Pop();
            CanBack = _backStack.Count > 0;
        }
    }
    
    /// <summary>
    /// The <see cref="CanBack"> dependency property's name.
    /// </summary>
    public const string CanBackPropertyName = "CanBack";
    
    /// <summary>
    /// Gets or sets the value of the <see cref="CanBack">
    /// property. This is a dependency property.
    /// </summary>
    public bool CanBack
    {
        get
        {
            return (bool)GetValue(CanBackProperty);
        }
        set
        {
            SetValue(CanBackProperty, value);
        }
    }
    
    /// <summary>
    /// Identifies the <see cref="CanBack"> dependency property.
    /// </summary>
    public static readonly DependencyProperty CanBackProperty = 
                    DependencyProperty.Register(
        CanBackPropertyName,
        typeof(bool),
        typeof(ObjectBrowser),
        new PropertyMetadata(false));
}

And we still need this converter class to take an object and break out its properties into something we can enumerate over to get the property name and instance's value:

C#
public class ObjectPropertiesConverter : IValueConverter
{
    public object Convert(object value, Type targetType, 
            object parameter, CultureInfo culture)
    {
        if (value == null)
            return null;
            
        return from p in value.GetType().GetProperties()
               where p.CanRead && 
        p.GetIndexParameters().Count() == 0 // skip indexer properties
               select new ObjectProperty
               {
                   Name = p.Name,
                   TheObject = p.GetValue(value, null)
               };
    }
    
    public object ConvertBack(object value, Type targetType, 
            object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

But we're going to replace the KeyValuePair with a small helper class. This is what each row in the browser will bind to and it keeps the actual object around to support navigation through the tree:

C#
public class ObjectProperty
{
    public object TheObject { get; set; }
    public string Name { get; set; }
    public string Value
    {
        get
        {
            return TheObject != null ? TheObject.ToString() : "(null)";
        }
    }
    
    public bool HasChildren
    {
        get
        {
            if (TheObject != null)
                return !TheObject.GetType().IsValueType;// && !(TheObject is string);
                
            return false;
        }
    }
}

So with that, you should be able to navigate object hierarchies from within the phone app at runtime. I wouldn't use it in an app, but I'm hoping it will be good debugging and rapid prototyping tool so I can build the data layer and rough UI structure and then deal with UI styling later.

** Warning - I'm posting as I code this so test coverage is well, um... limited **

This article was originally posted at http://spookycoding.blogspot.com/feeds/posts/default

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Team Leader Starkey Laboratories
United States United States
The first computer program I ever wrote was in BASIC on a TRS-80 Model I and it looked something like:
10 PRINT "Don is cool"
20 GOTO 10

It only went downhill from there.

Hey look, I've got a blog

Comments and Discussions

 
-- There are no messages in this forum --