I want to create a “FlipPanel”, which provides two different views of the same object. Here is the approach I am taking.
This is the main page, which consists of an ItemsControl whose ItemTemplate is a FlipPanel. The FlipPanel exposes two properties which define the DataTemplate to use for the Front and the Back.
<UserControl.Resources>
<ControlTemplate x:Key="MyFlipTemplate">
<StackPanel>
<Button Content="Flip" x:Name="PART_FlipButton"/>
<ContentPresenter Content="{TemplateBinding Content}" x:Name="PART_FlipContent"/>
</StackPanel>
</ControlTemplate>
<DataTemplate x:Key="Front">
<StackPanel Orientation="Horizontal">
<TextBlock Text="Front"/>
<TextBlock Text="{Binding Name}"/>
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="Back">
<StackPanel>
<TextBlock Text="Back"/>
<TextBlock Text="{Binding Description}"/>
</StackPanel>
</DataTemplate>
</UserControl.Resources>
<StackPanel>
<ItemsControl x:Name="_items">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel></StackPanel>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<SLTest:FlipPanel Template="{StaticResource MyFlipTemplate}" FrontDataTemplate="{StaticResource Front}" BackDataTemplate="{StaticResource Back}" Side="Front"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
The Code Behind for the main page, is very simple as it just sets the DataContext of the ItemsControl to a list of Test Data.
using System.Collections.Generic;
using System.Windows.Controls;
namespace SLTest
{
public partial class NewPage : UserControl
{
public NewPage()
{
InitializeComponent();
_items.ItemsSource = Items;
}
public IList Items
{
get
{
return new List
{
new NewClass { Name = "Name 1", Description = "Description 1"},
new NewClass { Name = "Name 2", Description = "Description 2"},
new NewClass { Name = "Name 3", Description = "Description 3"},
new NewClass { Name = "Name 4", Description = "Description 4"}
};
}
}
}
public class NewClass
{
public string Name;
public string Description;
}
}
The FlipPanel code is relatively simple as well, as it attempts to change the DataTemplate based on the Side DependencyProperty. The issue appears to be that the ContentPresenter’s DataContext is lost at some point. In the code I have two comments that indicate the validity of the DataContext for the ContentPresenter.
using System;
using System.Windows;
using System.Windows.Controls;
namespace SLTest
{
[TemplatePart(Name = FlipPanel.ButtonPart, Type = typeof(Button))]
[TemplatePart(Name = FlipPanel.ContentPart, Type = typeof(ContentPresenter))]
public partial class FlipPanel : ContentControl
{
private const string ButtonPart = "PART_FlipButton";
private const string ContentPart = "PART_FlipContent";
public enum FlipSide
{
Front,
Back
}
private FlipSide _flipSide;
public static readonly DependencyProperty SideProperty = DependencyProperty.RegisterAttached("FlipSide", typeof(FlipSide), typeof(FlipPanel), new PropertyMetadata(FlipSide.Front, FlipSidePropertyChanged));
public static readonly DependencyProperty FrontDataTemplateProperty = DependencyProperty.Register("FrontDataTemplate", typeof (DataTemplate), typeof (FlipPanel), null);
public static readonly DependencyProperty BackDataTemplateProperty = DependencyProperty.Register("BackDataTemplate", typeof(DataTemplate), typeof(FlipPanel), null);
private Button _flipButton;
private ContentPresenter _content;
public FlipPanel()
{
InitializeComponent();
}
public DataTemplate FrontDataTemplate
{
get
{
return (DataTemplate) GetValue(FrontDataTemplateProperty);
}
set
{
SetValue(FrontDataTemplateProperty, value);
}
}
public DataTemplate BackDataTemplate
{
get
{
return (DataTemplate)GetValue(BackDataTemplateProperty);
}
set
{
SetValue(BackDataTemplateProperty, value);
}
}
private static void FlipSidePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var flipSide = (FlipSide)e.NewValue;
var flipPanel = d as FlipPanel;
flipPanel._content.ContentTemplate = flipSide == FlipSide.Front ? flipPanel.FrontDataTemplate : flipPanel.BackDataTemplate;
}
public override void OnApplyTemplate()
{
_flipButton = GetTemplateChild(ButtonPart) as Button;
_flipButton.Click += OnFlipClicked;
_content = GetTemplateChild(ContentPart) as ContentPresenter;
// _content.DataContext is valid right here...
_content.ContentTemplate = Side == FlipSide.Front ? FrontDataTemplate : BackDataTemplate;
base.OnApplyTemplate();
}
private void OnFlipClicked(object sender, RoutedEventArgs e)
{
// _content.DataContext is now NULL!!!!
Side = (Side == FlipSide.Front) ? FlipSide.Back : FlipSide.Front;
}
public FlipSide Side
{
get
{
return (FlipSide) GetValue(SideProperty);
}
set
{
SetValue(SideProperty, value);
}
}
}
}
Any ideas?
I am not sure if this is the right approach to solving my requirement, if there is a better way I would welcome any further suggestions.
Thanks
Chris,
I don’t know if you’re still looking for a way to do this. I know that there are a lot of guides to similar problems, but not exactly what you’re looking for. From my understanding of what you want, you want a control which has two faces (really x faces) where a user can push a button and cause the panel to “flip” and show different data. However, you want this data that is shown to be generic enough so that this flip panel can be used in other locations with just a different implementation rather than totally different code. Do I understand your needs correctly? If not, please clarify where I’ve gone astray and I can maybe get a better answer for you. With that being said, here is what I’ve done (Google code demo project at bottom):
Here is a possible definition to use in a Silverlight 2.0 page:
The alternative to this is to define the data templates in the user control (page level, or even the app level) like this:
So that you have an idea of what my binding object looks like here is that definition (yes, it’s VB… Sorry!):
In my code behind, here is the definition of my page and setting of the data context for the flip panel:
So with this all laid out in front of you, we would expect that the control would display a button (in the control style) and a text block (actually two) that says “Front: This is the front” and when the button is pressed it is “flipped” to display “Back: This is the back”.
With all that being said, here is the style that I used for the control:
And finally, the control’s definition (Caution – It’s long):
Now, what you’ve been waiting for, the code!
Enjoy!
Google Code – http://code.google.com/p/stackoverflow-answers-by-scott/
Google Code – Source Code (Zip)
Thank you!