views:

1533

answers:

2

For those of you using Expression Blend as well as Visual Studio in your real projects, please help me understand how you use Blend and Visual Studio in your everyday development/design tasks, here is a real scenario:

I created the following simple WPF application in Visual Studio. It shows a list of customer objects with a DataTemplate that shows the customers in simple orange boxes.

I now want put some pizazz into this DataTemplate by using Expression Blend.

I open the project in Expression Blend thinking that I'm going to see the orange boxes which I can change the color of, create an animation as I mouse over them, resize it, etc. However, all I see in Expression Blend is a completely blank box.

So I understand:

  • Expression Blend can't seem to understand that my data is coming from the ViewModel and hence doesn't display it. Is this a limitation of Blend or do I need to change my code in some way so that Blend can interpret what data will be coming out at run-time?
  • I'm using Expression Blend 3 which has "sample data" capability. What is the best way to use this sample data feature so that even if it can't interpret the C# and understand what data will be coming out of the ViewModel property to fill the Listbox, how can I get it to at least produce some dummy data so that I can manipulate the DataTemplate?

XAML:

<Window x:Class="TestStringFormat234.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="300" Width="300">
    <Window.Resources>
        <DataTemplate x:Key="DataTemplateCustomers">
            <Border CornerRadius="5" Background="Orange" Padding="5" Margin="3">
                <StackPanel Orientation="Horizontal">
                    <TextBlock>
                    <TextBlock.Text>
                        <MultiBinding StringFormat="{}{0} {1} (hired on {2:MMM dd, yyyy})">
                            <Binding Path="FirstName"/>
                            <Binding Path="LastName"/>
                            <Binding Path="HireDate"/>
                        </MultiBinding>
                    </TextBlock.Text>
                    </TextBlock>
                </StackPanel>
            </Border>
        </DataTemplate>
    </Window.Resources>
    <Grid>
        <ListBox ItemsSource="{Binding GetAllCustomers}"
                 ItemTemplate="{StaticResource DataTemplateCustomers}">
        </ListBox>
    </Grid>
</Window>

Code Behind:

using System.Windows;
using System.Collections.ObjectModel;
using System;

namespace TestStringFormat234
{
    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();
            DataContext = new CustomerViewModel();
        }
    }

    //view model
    public class CustomerViewModel
    {
        public ObservableCollection<Customer> GetAllCustomers {
            get {
                ObservableCollection<Customer> customers = new ObservableCollection<Customer>();
                customers.Add(new Customer { FirstName = "Jim", LastName = "Smith", HireDate = DateTime.Parse("2007-12-31") });
                customers.Add(new Customer { FirstName = "Jack", LastName = "Jones", HireDate = DateTime.Parse("2005-12-31") });
                return customers;
            }
        }
    }

    //model
    public class Customer
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public DateTime HireDate { get; set; }
    }
}
+2  A: 

I just figured this out so allow me to answer my own question.

I read Laurent's Bugnion enlighting article on this and it turns out I just had to tweak the above code so that I could see the Data from my ViewModel displayed in the Expression Blend GUI and was able to edit the DataTemplate in Blend, save it, and then continued editing in Visual Studio.

Basically the changes are: (1) take out the DataContext statement from code behind, (2) add the "local" namespace in XAML, (3) define a local data provider in XAML ("TheDataProvider"), (4) bind to it directly from the ListBox.

Here is the code that works in Expression Blend and Visual Studio in full:

XAML:

<Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:TestStringFormat234"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" x:Name="window" x:Class="TestStringFormat234.Window1"
    Title="Window1" Height="300" Width="300" mc:Ignorable="d">
    <Window.Resources>
     <local:CustomerViewModel x:Key="TheDataProvider"/>

        <DataTemplate x:Key="DataTemplateCustomers">
            <Border CornerRadius="5" Padding="5" Margin="3">
                <Border.Background>
                 <LinearGradientBrush EndPoint="1.007,0.463" StartPoint="-0.011,0.519">
                  <GradientStop Color="#FFF4EEEE" Offset="0"/>
                  <GradientStop Color="#FFA1B0E2" Offset="1"/>
                 </LinearGradientBrush>
                </Border.Background>
                <StackPanel Orientation="Horizontal">
                 <TextBlock>
                    <TextBlock.Text>
                        <MultiBinding StringFormat="{}{0} {1} (hired on {2:MMM dd, yyyy})">
                            <Binding Path="FirstName"/>
                            <Binding Path="LastName"/>
                            <Binding Path="HireDate"/>
                        </MultiBinding>
                    </TextBlock.Text>
                    </TextBlock>
                </StackPanel>
            </Border>
        </DataTemplate>
    </Window.Resources>
    <Grid >
        <ListBox 
         ItemsSource="{Binding Path=GetAllCustomers, Source={StaticResource TheDataProvider}}"
         ItemTemplate="{StaticResource DataTemplateCustomers}" />
    </Grid>
</Window>

Code Behind:

using System.Windows;
using System.Collections.ObjectModel;
using System;

namespace TestStringFormat234
{
    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();
        }
    }

    //view model
    public class CustomerViewModel
    {
        public ObservableCollection<Customer> GetAllCustomers {
            get {
                ObservableCollection<Customer> customers = new ObservableCollection<Customer>();
                customers.Add(new Customer { FirstName = "Jim", LastName = "Smith", HireDate = DateTime.Parse("2007-12-31") });
                customers.Add(new Customer { FirstName = "Jack", LastName = "Jones", HireDate = DateTime.Parse("2005-12-31") });
                return customers;
            }
        }
    }

    //model
    public class Customer
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public DateTime HireDate { get; set; }
    }
}
Edward Tanguay
Thanks Edward, I was struggling with a similar problem - trying to expose my main window view model inside a data template and this solved it perfectly. Kudos to yourself, and Laurent if he ever reads this :)
Si
A: 

I've got a blog post on this issue: http://www.robfe.com/2009/08/design-time-data-in-expression-blend-3/

My post is all about showing data in blend without having to have that data displayed or even created at runtime.

Rob Fonseca-Ensor