无法转换到VisualState - “没有适用的名称范围”

时间:2013-01-09 08:59:47

标签: c# wpf silverlight porting

我已将Silverlight TemplatedControl移植到WPF,虽然它的行为基本相同,但动画不再有效。

当我致电VisualStateManager.GoToState()时,它会返回false。为了手动强制执行此操作,我按照别人的建议,按名称查找了可视状态组,并强制运行故事板:

        foreach (VisualStateGroup vsg in VisualStateManager.GetVisualStateGroups(part_LayoutRoot))
        {
            VisualState vs = vsg.States.Cast<VisualState>().FirstOrDefault(o => o.Name == visualState);

            if (vs == null)
                throw new ApplicationException("No visual state found with name: " + visualState);

            vs.Storyboard.Begin();
            break;
        }

但是这会引发异常No applicable name scope exists to resolve the name 'PART_MyPart'  当我打电话给Storyboard.Begin()时。

经过进一步调查,VisualStateManager,可视状态和故事板在调用NameScope.GetNameScope()时都返回null,因此我尝试在代码中手动设置它们:

        var nameScope = NameScope.GetNameScope(vs.Storyboard);
        if (nameScope == null)
        {
            nameScope = NameScope.GetNameScope(part_LayoutRoot);
            NameScope.SetNameScope(vsg, nameScope);
            NameScope.SetNameScope(vs, nameScope);
            NameScope.SetNameScope(vs.Storyboard, nameScope);
        }

然而,异常继续被提升,我不能为我的生活思考为什么。这与Silverlight中的预期完全一样。

任何人都可以了解为什么Silverlight和WPF之间在NameScope方面会有不同的行为?

由于

2 个答案:

答案 0 :(得分:1)

在控件模板中检索命名元素并调用storyboard.Begin(_retrievedElement)而不是storyboard.Begin()来解决“无适用名称范围”问题。在这种情况下,传递的框架元素的名称范围将应用于故事板。有关此方法的不同重载的详细信息,请参阅http://msdn.microsoft.com/en-us/library/system.windows.media.animation.storyboard.begin.aspx

不幸的是,我无法重建Visual States Manager无法为您的控件工作的原因。

更新:我可能已经弄清楚为什么你的控件模板在Silverlight中工作,但在WPF中却没有。详情请见下一个答案。

答案 1 :(得分:0)

我也可能知道为什么你的移植控件模板不起作用:在WPF中,不可能在视觉状态下的故事板中使用绑定。请考虑以下模板:

<ControlTemplate x:Key="ButtonTemplate" TargetType="Button">
        <Border x:Name="ButtonBorder" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="5" Background="White" BorderBrush="#FFB0B0B0">
        <VisualStateManager.VisualStateGroups>
            <VisualStateGroup x:Name="CommonStates">
                <VisualStateGroup.Transitions>
                    <VisualTransition GeneratedDuration="0:0:0.3"/>
                </VisualStateGroup.Transitions>
                <VisualState x:Name="Normal"/>
                <VisualState x:Name="MouseOver">
                    <Storyboard>
                        <ColorAnimation Storyboard.TargetName="rectangle" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)"
                            To="{Binding (Border.BorderBrush).(SolidColorBrush.Color), ElementName=ButtonBorder}"
                            Duration="0"/>
                    </Storyboard>
                </VisualState>
                <VisualState x:Name="Pressed"/>
                <VisualState x:Name="Disabled"/>
            </VisualStateGroup>
        </VisualStateManager.VisualStateGroups>
        <Grid>
            <Rectangle x:Name="rectangle" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Fill="#00000000"/>
            <ContentPresenter x:Name="ContentPresenter" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
        </Grid>
    </Border>

</ControlTemplate>

重要的部分是Visual State“MouseOver”,其中使用了元素绑定:此代码将在Silverlight中完美执行,而在WPF中数据绑定引擎将告诉您它找不到名为“ButtonBorder”的元素(查看“输出”窗口以跟踪数据绑定错误)。这与我在前面的回答中提到的命名空间问题有关。 解决此问题的方法如下:

<ControlTemplate x:Key="ButtonTemplate" TargetType="Button">
        <Border x:Name="ButtonBorder" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="5" Background="White" BorderBrush="#FFB0B0B0">
            <Border.Resources>
            <Storyboard x:Key="MouseOverStoryboard">
                <ColorAnimation Storyboard.TargetName="rectangle" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)"
                            To="{Binding (Border.BorderBrush).(SolidColorBrush.Color), ElementName=ButtonBorder}"
                            Duration="0"/>
            </Storyboard>
        </Border.Resources>
        <VisualStateManager.VisualStateGroups>
            <VisualStateGroup x:Name="CommonStates">
                <VisualStateGroup.Transitions>
                    <VisualTransition GeneratedDuration="0:0:0.3"/>
                </VisualStateGroup.Transitions>
                <VisualState x:Name="Normal"/>
                <VisualState x:Name="MouseOver" Storyboard="{StaticResource MouseOverStoryboard}">

                </VisualState>
                <VisualState x:Name="Pressed"/>
                <VisualState x:Name="Disabled"/>
            </VisualStateGroup>
        </VisualStateManager.VisualStateGroups>
        <Grid>
            <Rectangle x:Name="rectangle" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Fill="#00000000"/>
            <ContentPresenter x:Name="ContentPresenter" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
        </Grid>
    </Border>

</ControlTemplate>

现在,故事板被添加到根模板元素的Resource属性中,因此名称范围似乎适合。在可视状态下,只需使用静态资源标记扩展来引用故事板。请注意,此XAML不会在Silverlight中运行,因此您无法在两个项目中共享该文件。此外,Blend无法处理此标记,因此必须手动编写。

希望这有帮助。