1

考虑以下用户控件:

过滤器窗格控制

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

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

MainContent托管过滤后的内容。

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

用户控件的(简化的)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 实现)

在此处输入图像描述

    <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”,即使它存在于可视树中。此外,如果我尝试直接在 Page.Loaded 事件处理程序中引用该元素,则 filterContent1 为空。

好像嵌套的 Xaml 直到稍后才会呈现 - 这也抛出了 visualstatemanager。

有什么建议么?

4

1 回答 1

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放在 aTemplate / 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 下测试。

于 2013-07-19T11:07:47.070 回答