使用ContentPresenter显示Xaml中的嵌套内容

时间:2013-07-19 08:50:56

标签: wpf xaml winrt-xaml

考虑以下usercontrol:

Filter Pane Control

这是我编写的自定义用户控件,它有两个嵌套元素。

FilterContent 会显示一种特殊类型的标记,用于过滤屏幕右侧的内容

MainContent 托管过滤后的内容。

控件的唯一真正目的是在整个应用程序中提供一致的UI和动画,因为这种过滤器/内容模式经常被使用。

usercontrol的(简化)Xaml如下所示:

<UserControl>
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="3*"/>
            <ColumnDefinition Width="7*"/>
        </Grid.ColumnDefinitions>
        <ContentPresenter Grid.Column="0"  Content="{Binding ElementName=filterControl, Path=FilterControl}" DataContext="{Binding}" />
        <ContentPresenter Grid.Column="1"  Content="{Binding ElementName=filterControl, Path=MainControl}" DataContext="{Binding}" />
    </Grid>

代码隐藏是:

public sealed partial class FilterPaneControl : UserControl
{
    public static DependencyProperty FilterControlProperty = DependencyProperty.Register("FilterControl", typeof(object), typeof(FilterPaneControl), new PropertyMetadata(default(object), PropertyChangedCallback));

    public static DependencyProperty MainControlProperty = DependencyProperty.Register("MainControl", typeof (object), typeof (FilterPaneControl), new PropertyMetadata(default(object)));

    public FilterPaneControl()
    {
        this.InitializeComponent();
    }

    public object FilterControl
    {
        get { return (object)GetValue(FilterControlProperty); }
        set { SetValue(FilterControlProperty, value); }
    }

    public object MainControl
    {
        get { return (object) GetValue(MainControlProperty); }
        set { SetValue(MainControlProperty, value); }
    }
}

实施页面中控件的用法是:

    <Generic:FilterPaneControl>
        <Generic:FilterPaneControl.FilterControl>
            <Grid>
                <TextBlock Text="Filter Content here"/>
            </Grid>
        </Generic:FilterPaneControl.FilterControl>
        <Generic:FilterPaneControl.MainControl>
            <Grid>
                <TextBlock Text="Main Content here"/>
            </Grid>
        </Generic:FilterPaneControl.MainControl>
    </Generic:FilterPaneControl>

工作正常!

问题

问题是当我想要从实现页面引用控件中的一些内容时。一个很好的例子是处理快照/纵向(WinRT实现)的可视状态

enter image description here

    <Generic:FilterPaneControl>
        <Generic:FilterPaneControl.FilterControl>
            <Grid>
                <TextBlock x:Name="filterContent1"  Text="Filter Content here"/>
            </Grid>
        </Generic:FilterPaneControl.FilterControl>
        <Generic:FilterPaneControl.MainControl>
            <Grid>
                <TextBlock Text="Main Content here"/>
            </Grid>
        </Generic:FilterPaneControl.MainControl>
    </Generic:FilterPaneControl>

    <VisualStateManager.VisualStateGroups>
        <VisualState x:Name="FullScreenPortrait">
            <Storyboard>
                <ObjectAnimationUsingKeyFrames Storyboard.TargetName="filterContent1" Storyboard.TargetProperty="Width">
                    <DiscreteObjectKeyFrame KeyTime="0" Value="200"/>
                </ObjectAnimationUsingKeyFrames>
            </Storyboard>
        </VisualState>
    </VisualStateManager.VisualStateGroups>

这会导致运行时异常,因为visualstatemanager无法找到引用的元素'filterContent1',即使它存在于Visual Tree中。 另外,如果我尝试直接在Page.Loaded事件处理程序中引用该元素,则filterContent1为null。

就好像嵌套的Xaml直到稍后才会呈现 - 这也会引发visualstatemanager。

有什么建议吗?

1 个答案:

答案 0 :(得分:1)

首先,VisualStateManager应放在一个面板中,其中包含完成它的元素,否则它将是异常。对于你的情况,结果如下:

<Generic:FilterPaneControl>
    <Generic:FilterPaneControl.FilterControl>
        <Grid>
            <VisualStateManager.VisualStateGroups>
                <VisualState x:Name="FullScreenPortrait">
                    <Storyboard>
                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="filterContent1" Storyboard.TargetProperty="Width">
                            <DiscreteObjectKeyFrame KeyTime="0" Value="200"/>
                        </ObjectAnimationUsingKeyFrames>
                    </Storyboard>
                </VisualState>
            </VisualStateManager.VisualStateGroups>

            <TextBlock x:Name="filterContent1" Text="Filter Content here"/>
        </Grid>
    </Generic:FilterPaneControl.FilterControl>

    ...

其次,通常VisualStateManager放置在Template / StyleUserControl中。通过代码或通过XAML(使用特殊技术)执行到状态的转换。代码后面的设置状态示例:

VisualStateManager.GoToState(NameOfControl, "State1", true);

第三,以某种方式:

<Storyboard>
     <ObjectAnimationUsingKeyFrames Storyboard.TargetName="filterContent1" Storyboard.TargetProperty="Width">
           <DiscreteObjectKeyFrame KeyTime="0" Value="200"/>
     </ObjectAnimationUsingKeyFrames>
</Storyboard>

Width没有设置,在我的情况下是一个例外。我们需要使用这样的动画:

<Storyboard Storyboard.TargetName="filterContent1" Storyboard.TargetProperty="Width">
    <DoubleAnimation To="200" Duration="0:0:1.0"/>
</Storyboard>

作为他的话的证明,我举了一个例子:

MainWindow

<Window x:Class="VSMinUserControlHelp.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"        
    xmlns:local="clr-namespace:VSMinUserControlHelp"
    Title="MainWindow" Height="350" Width="525"
    WindowStartupLocation="CenterScreen">

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition Height="40"/>
        </Grid.RowDefinitions>

        <local:UserControl1 x:Name="Control1" Height="118" VerticalAlignment="Top" Margin="50,12,101,0" />

        <StackPanel Orientation="Horizontal" Grid.Row="1">
            <Button Name="State1Button" Width="75" Click="State1Button_Click">State1</Button>
        </StackPanel>
    </Grid>
</Window>

Code behind

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();        
    }

    private void State1Button_Click(object sender, RoutedEventArgs e)
    {
        VisualStateManager.GoToState(Control1, "State1", true);
    }
}

UserControl

<UserControl x:Class="VSMinUserControlHelp.UserControl1"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         xmlns:sys="clr-namespace:System;assembly=mscorlib"
         mc:Ignorable="d"              
         d:DesignHeight="300" d:DesignWidth="300">

    <Grid>
        <VisualStateManager.VisualStateGroups>
            <VisualStateGroup x:Name="Common1">
                <VisualState x:Name="State1">
                    <Storyboard Storyboard.TargetName="filterContent1" Storyboard.TargetProperty="Width">
                        <DoubleAnimation To="200" Duration="0:0:1.0"/>
                    </Storyboard>
                </VisualState>
            </VisualStateGroup>
        </VisualStateManager.VisualStateGroups>

        <TextBlock x:Name="filterContent1" Background="Aqua" Width="100" HorizontalAlignment="Left" Text="Filter Content here"/>
    </Grid>
</UserControl>

Note:示例在VS 2010,Windows XP上运行,而不是在WinRT下测试