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; }
}
}
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; }
}
}
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.
If one wants to have Blend and Visual Studio see what the datacontext has in design mode only, that can be defined with the debug options on the page. Say for example my page (in code-behind) binds its data context to MainVM in my nampespace WP8TestBed, by informing that info on the main page's attributes as d:DataContext, it is only used during design time (I can bind to it using the wizards) and also does not create a new instance of the view model during runtime.
Here is the example, all these namespaces are needed(d,mc and the local which is where my ViewModel (MainVM) lives):
<phone:PhoneApplicationPage
x:Class="WP8TestBed.MainPage"
...
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WP8TestBed"
mc:Ignorable="d"
Name="Primary"
d:DataContext="{d:DesignInstance local:MainVM}">
来源:https://stackoverflow.com/questions/824539/how-can-i-use-expression-blend-to-edit-a-datatemplate-created-in-visual-studio