2

我想创建一个“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>

主页的后面的代码非常简单,因为它只是将 ItemsControl 的 DataContext 设置为测试数据列表。

使用 System.Collections.Generic;
使用 System.Windows.Controls;

命名空间 SLTest
{
    公共部分类 NewPage : UserControl
    {
        公共新页面()
        {
            初始化组件();

            _items.ItemsSource = 物品;
        }

        公共 IList 项
        {
            得到
            {
                返回新列表
                           {
                               新的NewClass {名称=“名称1”,描述=“描述1”},
                               新的NewClass {名称=“名称2”,描述=“描述2”},
                               新的NewClass {名称=“名称3”,描述=“描述3”},
                               新的NewClass {名称=“名称4”,描述=“描述4”}
                           };
            }
        }
    }

    公共类 NewClass
    {
        公共字符串名称;
        公共字符串描述;
    }
}

FlipPanel 代码也相对简单,因为它尝试根据 Side DependencyProperty 更改 DataTemplate。问题似乎是 ContentPresenter 的 DataContext 在某些时候丢失了。在代码中,我有两条注释指示 ContentPresenter 的 DataContext 的有效性。

使用系统;
使用 System.Windows;
使用 System.Windows.Controls;

命名空间 SLTest
{
    [模板部分(名称 = FlipPanel.ButtonPart,类型 = typeof(按钮))]
    [模板部分(名称= FlipPanel.ContentPart,类型= typeof(ContentPresenter))]
    公共部分类 FlipPanel:ContentControl
    {
        私有常量字符串 ButtonPart = "PART_FlipButton";
        私有常量字符串 ContentPart = "PART_FlipContent";

        公共枚举 FlipSide
        {
            正面,
            后退
        }

        私人翻转 _flipSide;

        公共静态只读 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);
        公共静态只读 DependencyProperty BackDataTemplateProperty = DependencyProperty.Register("BackDataTemplate", typeof(DataTemplate), typeof(FlipPanel), null);

        私人按钮_flipButton;
        私有 ContentPresenter _content;

        公共翻转面板()
        {
            初始化组件();
        }

        公共数据模板 FrontDataTemplate
        {
            得到
            {
                返回 (DataTemplate) GetValue(FrontDataTemplateProperty);
            }
            放
            {
                SetValue(FrontDataTemplateProperty, 值);
            }
        }

        公共数据模板 BackDataTemplate
        {
            得到
            {
                返回 (DataTemplate)GetValue(BackDataTemplateProperty);
            }
            放
            {
                SetValue(BackDataTemplateProperty, value);
            }
        }

        私有静态无效 FlipSidePropertyChanged(DependencyObject d,DependencyPropertyChangedEventArgs e)
        {
            var flipSide = (FlipSide)e.NewValue;
            var 翻转面板 = d 作为翻转面板;

            flipPanel._content.ContentTemplate = flipSide == FlipSide.Front ?flipPanel.FrontDataTemplate : FlipPanel.BackDataTemplate;
        }

        公共覆盖无效 OnApplyTemplate()
        {
            _flipButton = GetTemplateChild(ButtonPart) 作为按钮;
            _flipButton.Click += OnFlipClicked;

            _content = GetTemplateChild(ContentPart) 作为 ContentPresenter;

            // _content.DataContext 在这里有效...

            _content.ContentTemplate = Side == FlipSide.Front ?前数据模板:后数据模板;

            base.OnApplyTemplate();
        }

        私人无效 OnFlipClicked(对象发送者,RoutedEventArgs e)
        {
            // _content.DataContext 现在为 NULL!!!!

            边 =(边 == FlipSide.Front)?FlipSide.Back : FlipSide.Front;
        }

        公共 FlipSide 侧
        {
            得到
            {
                返回(FlipSide)GetValue(SideProperty);
            }
            放
            {
                SetValue(SideProperty, 值);
            }
        }
    }
}

有任何想法吗?

我不确定这是否是解决我的要求的正确方法,如果有更好的方法,我会欢迎任何进一步的建议。

谢谢

4

1 回答 1

2

克里斯,

我不知道你是否还在寻找一种方法来做到这一点。我知道有很多类似问题的指南,但不完全是您正在寻找的。根据我对您想要什么的理解,您需要一个具有两个面(实际上是 x 个面)的控件,用户可以在其中按下按钮并导致面板“翻转”并显示不同的数据。但是,您希望此数据显示为足够通用,以便此翻转面板可以在其他位置使用,只需不同的实现而不是完全不同的代码。我是否正确理解您的需求?如果没有,请澄清我误入歧途的地方,我可能会为你得到更好的答案。话虽如此,这就是我所做的(谷歌代码演示项目在底部):

  1. 我创建了一个包含我的 FlipPanel 的控件库(因为这就是我做事的方式;这样我就可以在以后的其他项目中使用这些控件。)
  2. 我对控件库中的控件进行了样式设置,以包含您在场景中需要的上述属性。
  3. 我创建了一个 Silverlight 2.0 应用程序来创建控件的一个实例。
  4. 我创建了一个用于绑定的基本对象,它具有一些属性,以便我可以展示控件的潜力。

下面是在 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

因此,所有这些都摆在您面前,我们希望控件将显示一个按钮(以控件样式)和一个文本块(实际上是两个),上面写着“前面:这是前面”,并且当按钮是按下它被“翻转”以显示“Back:This is the back”。

说了这么多,这里是我用于控件的样式:

    <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

现在,您一直在等待什么,代码!

享受!

谷歌代码 - http://code.google.com/p/stackoverflow-answers-by-scott/

Google 代码 - 源代码 (Zip)

谢谢!

于 2009-11-09T20:10:21.463 回答