Visual Studio 2012 has introduced a concept called a Portable Class Library. In theory this should allow us to write assemblies to target multiple platforms such as Windows Store apps, the Xbox, and Windows phone; reusing as much code as possible. This should leave us to only need to write platform specific code as it relates to the particulars of the UI of each of those systems.
What is odd, and what in my mind renders the whole concept almost irrelevant is that there is not an http client library shared across the major platform targets. I don't know about your code but the aside from maybe some serialization POCO types, maybe a sprinkle of business logic and perhaps some MVVM stuff, the important and complex things that need to be shared across platforms is accessing http services.
I find that supporting diverse REST services, their peculiarities and differences of authentication and authorization to be the most tedious part of writing device apps and therefore the most valuable candidate for abstraction in a class library.
But as far as I can tell if you make a PCL that targets store apps and windows phone apps you have to implement platform specific extension points to govern http access.
Silverlight and Windows phone get's the WebClient; store apps the HttpClient. I have no idea what the Xbox needs. Without a cross platform http abstraction I can't really see making much use of a PCL and will likely resort to linking source files across project types or conditional compilation directives.
Random babblings about creating, using and generally considering software.
Saturday, December 1, 2012
Sunday, November 25, 2012
Converter to hide items when something is null or empty string
Often times in Xaml you've got some labels, or buttons that you don't want to show if something in the ViewModel is null or empty. Take the example below. The ViewModel has a nullable property rating. If it is non-null a handful of controls are rendered to show it graphically and numerically. If it is null however we don't want to see any of that stuff. This converter helps hide elements of something in the ViewModel is null or empty string.
<Grid Margin="30,0,0,0" Visibility="{Binding Location.rating, Converter={StaticResource NullToVisibilityConverter}}"> <Grid.RowDefinitions> <RowDefinition Height="Auto"></RowDefinition> <RowDefinition Height="Auto"></RowDefinition> <RowDefinition Height="*"></RowDefinition> </Grid.RowDefinitions> <TextBlock Grid.Row="0" Text="Rating" VerticalAlignment="Center" HorizontalAlignment="Center" Style="{StaticResource BasicTextStyle}"/> <Grid Margin="0,15,0,0" Grid.Row="1" > <TextBlock Text="{Binding Location.rating}" Style="{StaticResource BasicTextStyle}" HorizontalAlignment="Center" VerticalAlignment="Center" TextAlignment="Center" FontSize="24"/> <controls:RingSlice InnerRadius="35" Radius="45" StartAngle="0" EndAngle="{Binding RatingAngle}" Fill="{Binding RatingColor}"> </controls:RingSlice> </Grid> <TextBlock Grid.Row="2" Text="{Binding Reviews}" Margin="0,15,0,0" Style="{StaticResource BodyTextStyle}" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" TextAlignment="Center"/> </Grid>
public sealed class NullToVisibilityConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, string language) { if (value is string) return string.IsNullOrEmpty((string)value) ? Visibility.Collapsed : Visibility.Visible; return object.ReferenceEquals(value, null) ? Visibility.Collapsed : Visibility.Visible; } public object ConvertBack(object value, Type targetType, object parameter, string language) { throw new NotImplementedException(); } }
Saturday, January 28, 2012
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 styling and a couple of hyperlinks:
** warning - I'm posting as I code this so test coverage is well, um... limited **
CodeProject
<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 little bit to the control code behind to handle forward and backward navigation:
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; } } ///And we still need this converter class to take an object and break out its properties into something we can enumerate over to get theproperty name and instance's value:/// The public const string CanBackPropertyName = "CanBack"; ///dependency property's name. /// /// Gets or sets the value of the public bool CanBack { get { return (bool)GetValue(CanBackProperty); } set { SetValue(CanBackProperty, value); } } ////// property. This is a dependency property. /// /// Identifies the public static readonly DependencyProperty CanBackProperty = DependencyProperty.Register( CanBackPropertyName, typeof(bool), typeof(ObjectBrowser), new PropertyMetadata(false)); }dependency property. ///
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:
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 rought UI structure and then deal with UI styling later.
** warning - I'm posting as I code this so test coverage is well, um... limited **
CodeProject
A simple SilverLight object viewer
Sometimes when you're making a windows phone 7 rest client you just want to look at some of the returned data to poke see what's there. You can wire up some ui or use tracing. Maybe some breakpoints. But it's also nice to be able to drop something into the UI that you can access easily and as needed.
The WinForms object browser is a handy for instance for debugging things at runtime, within the app without a debugger or a designed UI.
This little widget isn't as fully functional as taht but can be helpful in a similar way. Drop it somewhere in the UI and set its DataContext property and all public readable properies will be listed with name and value.
First some XAML
CodeProject
<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"> <UserControl.Resources> <local:ObjectPropertiesConverter x:Key="ObjectPropertiesConvert"/> <DataTemplate x:Key="PropertyTemplate"> <StackPanel Orientation="Horizontal" HorizontalAlignment="Stretch"> <TextBlock Text="{Binding Key}" Margin="0,0,10,1"/> <TextBlock Text="{Binding Value}" TextWrapping="Wrap" TextAlignment="Right" HorizontalAlignment="Right" /> </StackPanel> </DataTemplate> </UserControl.Resources> <ScrollViewer> <ItemsControl ItemTemplate="{StaticResource PropertyTemplate}" ItemsSource="{Binding Path=., Converter={StaticResource ObjectPropertiesConvert}}"> </ItemsControl> </ScrollViewer> </UserControl>With a smidge of C# to create a name value pair collection, given an object's properties
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 select new KeyValuePair<string, string> ( p.Name, p.GetValue(value, null) != null ? p.GetValue(value, null).ToString() : null ); } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } }
CodeProject
Subscribe to:
Posts (Atom)