Windows Phone:XAML Style'重置'在C#中应用时本身

时间:2015-02-09 03:11:16

标签: c# wpf xaml windows-phone

编辑:我找到了重现问题的最小方法。在Visual Studio中启动新的Windows Phone应用程序模板并将其添加到MainPage.xaml

<Page.Resources>
    <Style x:Name="TileStyle" TargetType="Button">
        <Setter Property="BorderThickness" Value="0,0,0,0" />
    </Style>
</Page.Resources>

<Button x:Name="btn" Style="{StaticResource TileStyle}" />

这是你的代码隐藏文件(在OnNavigatedTo事件处理程序中):

btn.Style = TileStyle;
导航到页面时

重现问题。

原文:我正在尝试创建一个Windows Phone应用程序,它是Whack-A-Mole的最小版本。在我的游戏页面上,我有两个用XAML编写的按钮样式,代表一个被占用或未被占用的洞。

<Page.Resources>
    <Style x:Name="TileStyle" TargetType="Button">
        <Setter Property="BorderThickness" Value="0,0,0,0" />
        <Setter Property="Width" Value="125"/>
        <Setter Property="Height" Value="125" />
    </Style>

    <Style x:Name="CircleStyle" TargetType="Button"
           BasedOn="{StaticResource TileStyle}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="Button">
                    <Grid>
                        <Rectangle Fill="{TemplateBinding Background}" />
                        <Image Source="/Assets/white_circle.png" />
                        <ContentPresenter HorizontalAlignment="Center"
                                          VerticalAlignment="Center"/>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</Page.Resources>

<!--later on...-->

<Grid>
    <Button Style="{StaticResource TileStyle}" Click="Button_Click"/>
    <Button Style="{StaticResource TileStyle}" Click="Button_Click"/>
    <Button Style="{StaticResource TileStyle}" Click="Button_Click"/>
    ...
</Grid>

第一种风格基本上只是一个空白,黑色,125x125平方;你只能通过点击它来告诉它。第二种风格是基于那个,它显示一个white_circle.png来代替空白方块。

在我的代码隐藏C#文件中,我有两种利用这些样式的方法:一种是在页面加载时调用,另一种是在单击一个Button时调用。

protected async void OnNavigatedTo(NavigationEventArgs e)
            while (!hasUserWonGame)
                //sleeps for 1-3 secs
                //decides where circle should go at by generating random int
            b.Style = CircleStyle; //this works fine!
            //more logic, waits
            if (!hasUserWonRound)
                b.Style = TileStyle; //reverts to TileStyle (this does not work!)

    private void Button_Click(object sender, RoutedEventArgs e)
        //decides if user is clicking on a circle
        if (areTilesDisplayingCircle[index - 1])
            //user has won the round!
            hasUserWonRound = true;
            b.Style = TileStyle; //this also does not work!
            //more logic, updates score, decides if user has won the entire game

当用户首次导航到页面时,按钮都是黑色和不可见的,因为它们应该是。白色圆圈按预期出现在空间中。然而,当它从斑点消失时,圆圈留下一个没有应用样式的按钮。 (见图。)

buttons

1 个答案:

答案 0 :(得分:1)

感谢您提供最小的repro案例。这样可以很容易地解释发生了什么(好吧,至少在这种情况下......人们希望你的真实场景实际上是相似的,这看起来很可能)。

问题源于您使用x:Name代替x:Key作为样式资源。在这种情况下,它不会按照您的想法或希望它做到。特别是,虽然编译器确实根据资源的指定TileStyle值创建名为x:Name的字段,而XAML编译器允许使用x:Name代替正确的密钥由x:Key指定,两者不连接。

用于检索TileStyle字段的值的编译器生成的代码使用与资源字典不兼容的机制,即它调用FindName()方法,传递您提供的名称。但是该方法用于在FrameworkElement的对象图中查找命名对象;它不会(也不打算)在资源字典中查找对象。

基本上,编译器会看到x:Name并愉快地发出其样板来实现支持元素的字段。它只是盲目地遵循实施x:Name的规则,结果证明不适用于资源。

那么当你调用FindName(),传递一个不存在的名字时会发生什么?它返回null。因此,TileStyle字段获取(或者更确切地说,因为这是默认设置)null的值。

当您将Button对象的Style属性设置为null时会发生什么?它只是将所有内容重置为默认值! Button最初看起来很好(在您的实际场景中),因为{StaticResource TileStyle}语法是处理资源的正确语法,并且x:Name被允许替代资源x:Key属性。

在最小的repro示例中,最直接的解决方法是正确初始化资源(x:Name语法的使用是针对特定的基于Storyboard的场景,通常不是正确的方法要做到这一点),然后明确实现您的TileStyle字段:

<Page.Resources>
    <Style x:Key="TileStyle" TargetType="Button">
        <Setter Property="BorderThickness" Value="0,0,0,0" />
    </Style>
</Page.Resources>

然后在您的代码隐藏中:

partial class MainPage : Page
{
    private Style TileStyle;

    public MainPage()
    {
        InitializeComponent();

        TileStyle = (Style)this.Resources["TileStyle"];

        // etc.
    }
}

使用它,您现在应该为样式字段设置非空值,并将它们分配给Style属性应该正确更新样式而不是重置它。

那就是说,我不清楚,虽然上面显然是你的最小repro案例的解决方案,但它将适用于你的真实场景。特别是,在您的问题中,您声称将CircleStyle的值分配给Button的{​​{1}}属性 可以正常工作。但假设Style字段初始化与CircleStyle字段相同,则不清楚为什么在分配TileStyle时不起作用。

即。我希望TileStyle字段也是CircleStyle,但显然不能分配它确实将样式更改为您想要的样式。如果不是null,那么为什么nullTileStyle?但是,如果没有一个能够精确解决 场景的优秀,最小,完整的代码示例,我无法回答任何问题。


最后,我要指出,有可能更好的方法来做到这一点,而不是硬编码代码隐藏中的样式。不幸的是,我对Phone API不太熟悉,不知道它可能是什么。

在WPF中,我将使用nullStyleSelector根据底层模型对象中的某些绑定属性更改对象外观。但是经过相当长的一段时间的实验,我发现我还不太了解Phone API,知道它们是如何工作的。 Trigger类似乎甚至不被支持,或者至少不被允许在Trigger集合中使用,并且我最终落入了一个兔子洞,试图找出如何做{{1}在Phone中基于Style.Triggers(在WPF中并不难,但是在绑定到GridItemsControl附加属性时有一点小技巧,这些属性在Phone中不起作用,因为显然手机不支持在样式Grid.Row的{​​{1}}属性中使用Grid.Column

基本上,与WPF相比,基于XAML的API的Silverlight / Phone / WinRT实现中缺少 ton 。我以前使用WPF的经验对上述问题没有多大帮助,因为我在WPF中依赖于实现这些行为的大部分功能在其他API中都不存在(微软的耻辱!)。

与此同时,我希望上述基于代码隐藏的方法能够解决您的具体问题。如果没有,那么您需要发布一个更好但仍然是最小和完整的代码示例。