views:

18

answers:

1

I'm building a wp7 app.

I have a UserControl that displays a news article headline, teaser, and image. The entire class is pretty short:

public partial class StoryControl : UserControl
{
    public Story Story { get; private set; }

    public StoryControl()
    {
        InitializeComponent();
    }

    internal StoryControl(Story story) : this()
    {
        this.Story = story;
        Teaser.Text = story.Teaser;
        Headline.Text = story.Title;

        if (story.ImageSrc == null)
        {
            Thumbnail.Visibility = Visibility.Collapsed;
        } else
        {
            Thumbnail.Source = new BitmapImage(story.ImageSrc);
        }
    }
}

And the corresponding XAML:

<Grid x:Name="LayoutRoot" Background="Transparent" Margin="0,0,0,20">
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto" />
        <RowDefinition Height="*" />
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="Auto" />
        <ColumnDefinition Width="*" />
    </Grid.ColumnDefinitions>

    <Image x:Name="Thumbnail" Grid.Column="0" Width="89" HorizontalAlignment="Left" VerticalAlignment="Top" />

    <!-- sometimes there's a hanging word in the headline that looks a bit awkward -->
    <TextBlock x:Name="Headline" Grid.Column="1" Grid.Row="0" Style="{StaticResource PhoneTextAccentStyle}" TextWrapping="Wrap" HorizontalAlignment="Left" FontSize="23.333" VerticalAlignment="Top" />
    <TextBlock x:Name="Teaser" Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="1" HorizontalAlignment="Left" Style="{StaticResource PhoneTextSubtleStyle}"  TextWrapping="Wrap" VerticalAlignment="Top" Width="384"/>
</Grid>

Is there a way to do with with less code-behind and more XAML? Some way to use binding to bind the text for Headline and Teaser to properties of the Story, while not crashing if Story is null?

About about the image? I've got a bit of logic there; is there some way to automatically do that in XAML, or am I stuck with that in C#?

+1  A: 

Looks like a ViewModel is in order:

public class StoryViewModel
{
    readonly Story story;

    public StoryViewModel(Story story)
    {
        this.story = story;
    }

    public string Teaser { get { return story == null ? "" : story.Teaser; } }
    public string Title { get { return story == null ? "" : story.Title; } }
    public bool IsThumbnailVisible { get { return story != null && story.ImageSrc != null; } }
    public BitmapImage Thumbnail { get { return IsThumbnailVisible ? new BitmapImage(story.ImageSrc) : null; } }
}

Making your codebehind nice and simple:

public partial class StoryControl : UserControl
{
    public Story Story { get; private set; }

    public StoryControl()
    {
        InitializeComponent();

    }

    internal StoryControl(Story story)
        : this()
    {
        this.DataContext = new StoryViewModel(story);
    }
}

And your XAML becomes a set of bindings:

<Grid x:Name="LayoutRoot" Background="Transparent" Margin="0,0,0,20">
    <Grid.Resources>
        <BooleanToVisibilityConverter x:Key="booleanToVisiblityConverter"/>
    </Grid.Resources>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto" />
        <RowDefinition Height="*" />
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="Auto" />
        <ColumnDefinition Width="*" />
    </Grid.ColumnDefinitions>

    <Image Visibility="{Binding IsThumbnailVisible, Converter={StaticResource booleanToVisiblityConverter}}" Source="{Binding Thumbnail}" Grid.Column="0" Width="89" HorizontalAlignment="Left" VerticalAlignment="Top" />

    <!-- sometimes there's a hanging word in the headline that looks a bit awkward -->
    <TextBlock Text="{Binding Title}" Grid.Column="1" Grid.Row="0" Style="{StaticResource PhoneTextAccentStyle}" TextWrapping="Wrap" HorizontalAlignment="Left" FontSize="23.333" VerticalAlignment="Top" />
    <TextBlock Text="{Binding Teaser}" Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="1" HorizontalAlignment="Left" Style="{StaticResource PhoneTextSubtleStyle}"  TextWrapping="Wrap" VerticalAlignment="Top" Width="384"/>

</Grid>

Ok, it would probably be possible to do this with just model (story) and view (xaml) via fallbackvalues and more complex converters, but i hope you'll find that viewmodels give you the most power in terms of testable, brick-wall-less, view-specific logic...

Rob Fonseca-Ensor