我想创建一个“FlipPanel”,它提供同一对象的两个不同视图。这是我正在采取的方法。
这是主页面,它由ItemsControl组成,其ItemTemplate是一个FlipPanel。 FlipPanel公开了两个属性,这两个属性定义了用于Front和Back的DataTemplate。
<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>
主页面的Code Behind非常简单,因为它只是将ItemsControl的DataContext设置为测试数据列表。
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; } }
FlipPanel代码也相对简单,因为它试图根据Side DependencyProperty更改DataTemplate。问题似乎是ContentPresenter的DataContext在某些时候丢失了。在代码中,我有两条注释,表明ContentPresenter的DataContext的有效性。
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); } } } }
有什么想法吗?
我不确定这是否是解决我的要求的正确方法,如果有更好的方法我会欢迎任何进一步的建议。
由于
答案 0 :(得分:2)
克里斯,
我不知道你是否还在寻找一种方法来做到这一点。我知道有很多类似问题的指南,但不完全是你想要的。从我对你想要的东西的理解,你想要一个具有两个面(真正的x面)的控件,用户可以按下按钮并使面板“翻转”并显示不同的数据。但是,您希望显示的数据足够通用,以便此翻盖面板可以在其他位置使用,只是具有不同的实现,而不是完全不同的代码。我能正确理解您的需求吗?如果没有,请澄清我误入歧途的地方,我可以为你找到更好的答案。话虽如此,这就是我所做的(谷歌代码演示项目在底部):
以下是在Silverlight 2.0页面中使用的可能定义:
<Grid x:Name="LayoutRoot" Background="White">
<controls:FlipPanel x:Name="TestingFlipPanel" Side="Front" >
<controls:FlipPanel.BackDataTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Back: "/>
<TextBlock Text="{Binding BackText}" />
</StackPanel>
</DataTemplate>
</controls:FlipPanel.BackDataTemplate>
<controls:FlipPanel.FrontDataTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Front: "/>
<TextBlock Text="{Binding FrontText}" />
</StackPanel>
</DataTemplate>
</controls:FlipPanel.FrontDataTemplate>
</controls:FlipPanel>
</Grid>
替代方法是在用户控件(页面级别,甚至应用级别)中定义数据模板,如下所示:
这样你就知道我的绑定对象在这里是什么样的定义(是的,它是VB ...抱歉!):
Public Class BindingObject
Private _FrontText As String
Private _BackText As String
Public Sub New(ByVal frontText As String, ByVal backText As String)
MyBase.New()
_FrontText = frontText
_BackText = backText
End Sub
Public Property FrontText() As String
Get
Return _FrontText
End Get
Set(ByVal value As String)
_FrontText = value
End Set
End Property
Public Property BackText() As String
Get
Return _BackText
End Get
Set(ByVal value As String)
_BackText = value
End Set
End Property
End Class
在我的代码背后,这里是我的页面的定义和翻转面板的数据上下文设置:
Partial Public Class Page
Inherits UserControl
Dim _BindingObject As New BindingObject("This is the front", "This is the back")
Public Sub New()
InitializeComponent()
End Sub
Private Sub Page_Loaded(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles Me.Loaded
TestingFlipPanel.DataContext = _BindingObject
End Sub
End Class
所以这一切都摆在你面前,我们希望控件会显示一个按钮(在控件样式中)和一个文本块(实际上是两个),上面写着“Front:This is the front”,当按下按钮它“翻转”显示“后退:这是后面”。
尽管如此,这是我用于控件的风格:
<Style TargetType="controls:FlipPanel">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="controls:FlipPanel">
<Grid x:Name="LayoutRoot">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Button x:Name="FlipButton" Content="Flip" Grid.Row="0" />
<ContentPresenter Grid.Row="1" x:Name="FrontContentPresenter" Content="{TemplateBinding DataContext}" ContentTemplate="{TemplateBinding FrontDataTemplate}" />
<ContentPresenter Grid.Row="1" x:Name="BackContentPresenter" Content="{TemplateBinding DataContext}" ContentTemplate="{TemplateBinding BackDataTemplate}" />
</Grid>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
最后,控件的定义(注意 - 很长):
<TemplatePart(Name:=FlipPanel.LayoutRoot_ElementName, Type:=GetType(FrameworkElement))> _
<TemplatePart(Name:=FlipPanel.FrontContentPresenter_ElementName, Type:=GetType(FrameworkElement))> _
<TemplatePart(Name:=FlipPanel.BackContentPresenter_ElementName, Type:=GetType(FrameworkElement))> _
<TemplatePart(Name:=FlipPanel.FlipButton_ElementName, Type:=GetType(FrameworkElement))> _
Public Class FlipPanel
Inherits Control
Public Const LayoutRoot_ElementName As String = "LayoutRoot"
Public Const FlipButton_ElementName As String = "FlipButton"
Public Const FrontContentPresenter_ElementName As String = "FrontContentPresenter"
Public Const BackContentPresenter_ElementName As String = "BackContentPresenter"
Public Enum Sides
Front
Back
End Enum
Private _LayoutRoot As FrameworkElement = Nothing
Private _FlipButton As FrameworkElement = Nothing
Private _FrontContentPresenter As FrameworkElement = Nothing
Private _BackContentPresenter As FrameworkElement = Nothing
Private _ControlUpdating As Boolean = False
Public Sub New()
MyBase.New()
MyBase.DefaultStyleKey = GetType(FlipPanel)
End Sub
Public Overrides Sub OnApplyTemplate()
MyBase.OnApplyTemplate()
UpdateControl()
End Sub
Private Sub UpdateControl()
If _ControlUpdating Then Exit Sub
_ControlUpdating = True
If _LayoutRoot Is Nothing Then _LayoutRoot = TryCast(GetTemplateChild(LayoutRoot_ElementName), FrameworkElement)
If _LayoutRoot IsNot Nothing Then
Dim element As Grid = TryCast(_LayoutRoot, Grid)
If element IsNot Nothing Then
' Update LayoutGrid here.
End If
End If
If _FlipButton Is Nothing Then _FlipButton = TryCast(GetTemplateChild(FlipButton_ElementName), FrameworkElement)
If _FlipButton IsNot Nothing Then
Dim element As Button = TryCast(_FlipButton, Button)
If element IsNot Nothing Then
' Update Button
RemoveHandler element.Click, AddressOf _FlipButton_Click
AddHandler element.Click, AddressOf _FlipButton_Click
End If
End If
If _FrontContentPresenter Is Nothing Then _FrontContentPresenter = TryCast(GetTemplateChild(FrontContentPresenter_ElementName), FrameworkElement)
If _FrontContentPresenter IsNot Nothing Then
Dim element As ContentPresenter = TryCast(_FrontContentPresenter, ContentPresenter)
If element IsNot Nothing Then
' Update FrontContentPresenter here.
If Side = Sides.Front Then
element.Visibility = Windows.Visibility.Visible
Else
element.Visibility = Windows.Visibility.Collapsed
End If
End If
End If
If _BackContentPresenter Is Nothing Then _BackContentPresenter = TryCast(GetTemplateChild(BackContentPresenter_ElementName), FrameworkElement)
If _BackContentPresenter IsNot Nothing Then
Dim element As ContentPresenter = TryCast(_BackContentPresenter, ContentPresenter)
If element IsNot Nothing Then
' Update BackContentPresenter here.
If Side = Sides.Front Then
element.Visibility = Windows.Visibility.Collapsed
Else
element.Visibility = Windows.Visibility.Visible
End If
End If
End If
_ControlUpdating = False
End Sub
Private Sub _FlipButton_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
Select Case Side
Case Sides.Front
Side = Sides.Back
Case Sides.Back
Side = Sides.Front
Case Else
Throw New ArgumentOutOfRangeException("Side")
End Select
UpdateControl()
End Sub
Private Sub FlipPanel_LayoutUpdated(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.LayoutUpdated
UpdateControl()
End Sub
#Region " FrontDataTemplateProperty Dependency Property "
#Region " FrontDataTemplate Property "
Public Property FrontDataTemplate() As DataTemplate
Get
Return DirectCast(GetValue(FrontDataTemplateProperty), DataTemplate)
End Get
Set(ByVal value As DataTemplate)
SetValue(FrontDataTemplateProperty, value)
End Set
End Property
#End Region
#Region " FrontDataTemplate Dependency Property "
Public Shared ReadOnly FrontDataTemplateProperty As DependencyProperty = DependencyProperty.Register("FrontDataTemplate", GetType(DataTemplate), GetType(FlipPanel), New PropertyMetadata(Nothing, AddressOf OnFrontDataTemplatePropertyChanged))
#End Region
#Region " FrontDataTemplate Property Changed CallBack "
Private Shared Sub OnFrontDataTemplatePropertyChanged(ByVal d As DependencyObject, ByVal e As DependencyPropertyChangedEventArgs)
If e.OldValue Is Nothing AndAlso e.NewValue Is Nothing Then Exit Sub
If e.OldValue IsNot Nothing AndAlso e.OldValue.Equals(e.NewValue) Then Exit Sub
Dim source As FlipPanel = TryCast(d, FlipPanel)
If source Is Nothing Then Throw New ArgumentException("source is not an instance of FlipPanel!")
' Provide any other validation here.
' Apply any other changes here.
End Sub
#End Region
#End Region
#Region " BackDataTemplateProperty Dependency Property "
#Region " BackDataTemplate Property "
Public Property BackDataTemplate() As DataTemplate
Get
Return DirectCast(GetValue(BackDataTemplateProperty), DataTemplate)
End Get
Set(ByVal value As DataTemplate)
SetValue(BackDataTemplateProperty, value)
End Set
End Property
#End Region
#Region " BackDataTemplate Dependency Property "
Public Shared ReadOnly BackDataTemplateProperty As DependencyProperty = DependencyProperty.Register("BackDataTemplate", GetType(DataTemplate), GetType(FlipPanel), New PropertyMetadata(Nothing, AddressOf OnBackDataTemplatePropertyChanged))
#End Region
#Region " BackDataTemplate Property Changed CallBack "
Private Shared Sub OnBackDataTemplatePropertyChanged(ByVal d As DependencyObject, ByVal e As DependencyPropertyChangedEventArgs)
If e.OldValue Is Nothing AndAlso e.NewValue Is Nothing Then Exit Sub
If e.OldValue IsNot Nothing AndAlso e.OldValue.Equals(e.NewValue) Then Exit Sub
Dim source As FlipPanel = TryCast(d, FlipPanel)
If source Is Nothing Then Throw New ArgumentException("source is not an instance of FlipPanel!")
' Provide any other validation here.
' Apply any other changes here.
End Sub
#End Region
#End Region
#Region " SideProperty Dependency Property "
#Region " Side Property "
Public Property Side() As Sides
Get
Return DirectCast(GetValue(SideProperty), Sides)
End Get
Set(ByVal value As Sides)
SetValue(SideProperty, value)
End Set
End Property
#End Region
#Region " Side Dependency Property "
Public Shared ReadOnly SideProperty As DependencyProperty = DependencyProperty.Register("Side", GetType(Sides), GetType(FlipPanel), New PropertyMetadata(Sides.Front, AddressOf OnSidePropertyChanged))
#End Region
#Region " Side Property Changed CallBack "
Private Shared Sub OnSidePropertyChanged(ByVal d As DependencyObject, ByVal e As DependencyPropertyChangedEventArgs)
If e.OldValue Is Nothing AndAlso e.NewValue Is Nothing Then Exit Sub
If e.OldValue IsNot Nothing AndAlso e.OldValue.Equals(e.NewValue) Then Exit Sub
Dim source As FlipPanel = TryCast(d, FlipPanel)
If source Is Nothing Then Throw New ArgumentException("source is not an instance of FlipPanel!")
' Provide any other validation here.
' Apply any other changes here.
End Sub
#End Region
#End Region
End Class
现在,你一直在等待,代码!
Google Code - http://code.google.com/p/stackoverflow-answers-by-scott/
Google Code - Source Code (Zip)