数据模板中的样式只被应用到ItemsControl中的最后一个项目?

13
在下面的XAML中,我有一个ItemsControl,其中有三个DataObjects。我使用DataTemplate将DataObjects显示为带有“X”的按钮。该按钮使用样式来设置其内容。
如果Setter.Value是“X”,一切都很好!然而,如果我将Setter.Value更改为TextBlock,其TextProperty为“X”,则“X”仅出现在最后一个按钮(第三个DataObject)上,前两个按钮为空。
这是一个错误吗?还是有人能够解释为什么会发生这种情况?
注1:这是一个虚构的示例,以分离遇到的问题。
注2:我在代码中放置了Setter.Value选项,以便您可以通过注释其中一个来重新创建成功和不成功的情况。
注3:似乎此问题特定于“Content”属性的Setter。如果我使用Background属性的Setter,则它会正确地应用于所有DataObjects。
<Grid>
    <Grid.Resources>
        <Style x:Key="myButtonStyle" TargetType="{x:Type Button}">
            <Setter Property="Content">
                <!--<Setter.Value>X</Setter.Value>-->
                <Setter.Value><TextBlock Text="X" /></Setter.Value>
            </Setter>
            <Setter Property="Background">
                <Setter.Value>
                    <SolidColorBrush Color="Red" />
                </Setter.Value>
            </Setter>
        </Style>
    </Grid.Resources>
    <ItemsControl>
        <ItemsControl.ItemTemplate>
            <DataTemplate DataType="{x:Type DataObject}">
                <Button Height="24" Width="24" Style="{StaticResource myButtonStyle}" />
            </DataTemplate>
        </ItemsControl.ItemTemplate>
        <ItemsControl.Items>
            <DataObject />
            <DataObject />
            <DataObject />
        </ItemsControl.Items>
    </ItemsControl>
</Grid>

解决方案:

遗憾的是,我仍无法解释为什么在将内容设置为控件(例如TextBlock)而非纯文本时,“Content”设置器只能在最后一个DataObject上工作而失败。

然而,使用“ContentTemplate”而非“Content”进行设置正是Dmitry所建议的非常可接受的解决方法,它仍然允许重用样式。

<Grid>
    <Grid.Resources>
        <DataTemplate x:Key="textBlockWithX">
            <TextBlock Text="X" />
        </DataTemplate>
        <Style x:Key="myButtonStyle" TargetType="{x:Type Button}">
            <Setter Property="ContentTemplate" Value="{StaticResource textBlockWithX}" />
        </Style>
    </Grid.Resources>
    <ItemsControl>
        <ItemsControl.ItemTemplate>
            <DataTemplate DataType="{x:Type DataObject}">
                <Button Height="24" Width="24" Style="{StaticResource myButtonStyle}" />
            </DataTemplate>
        </ItemsControl.ItemTemplate>
        <ItemsControl.Items>
            <DataObject />
            <DataObject />
            <DataObject />
        </ItemsControl.Items>
    </ItemsControl>
</Grid>

嗨,你显然知道罪魁祸首是DataTemplate中的Style,一旦你使用Content作为普通属性,一切就开始正常工作了。 - user572559
在这个人为简化的例子中,肯定有一种解决方法。但我不确定为什么这是“按设计要求”的。将Content Setter的值设置为“X”可以工作...但将其设置为TextBlock仅适用于最后一个项目...对我来说似乎很奇怪。 - Scott
2个回答

12
这个问题的答案其实很简单,每个可视化对象只能是一个对象的子级,不像文本"X"那样只是数据。
如果您创建了这样的样式:
<Style>
    <Setter Property="Content">
        <Setter.Value>
             <TextBlock Text="X"/>
        </Setter.Value>
    </Setter>
<Style>

应用样式后,所有实例只创建一个TextBlock,因此TextBlock会在每次应用时“跳到”最后一项。

但是,如果您将ContentTemplate设置为名称所示,则会创建一个模板,用于独立地为每个对象生成内容,因此每个控件都会有一个适用样式的实例。


7
以下是一个可用的示例:
<Window x:Class="Styles.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:Styles"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Grid.Resources>
            <Style x:Key="A" TargetType="{x:Type Button}">
                <Style.Setters>
                    <Setter Property="Content" Value="X"></Setter>
                </Style.Setters>
            </Style>
        </Grid.Resources>
        <ItemsControl>
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <Button Height="24" Width="24" Style="{StaticResource A}">

                    </Button>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
            <ItemsControl.Items>
                <DataObject></DataObject>
                <DataObject></DataObject>
                <DataObject></DataObject>
            </ItemsControl.Items>
        </ItemsControl>
    </Grid>

</Window>

编辑1:哦,搞定了,诀窍是使用ContentTemplate。

<Window x:Class="Styles.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:Styles"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Grid.Resources>
            <DataTemplate x:Key="A">
                <TextBlock>X</TextBlock>
            </DataTemplate>
        </Grid.Resources>
        <ItemsControl>
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <Button Height="24" Width="24" ContentTemplate="{StaticResource A}">

                    </Button>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
            <ItemsControl.Items>
                <DataObject></DataObject>
                <DataObject></DataObject>
                <DataObject></DataObject>
            </ItemsControl.Items>
        </ItemsControl>
    </Grid>

</Window>

编辑2:更复杂的ContentTemplate示例:

<Window x:Class="Styles.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:Styles"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Grid.Resources>
            <DataTemplate x:Key="A">
                <StackPanel Width="30" Orientation="Horizontal">
                    <Grid Background="White" Width="10" Height="10"></Grid>
                    <Grid Background="Blue" Width="10" Height="10"></Grid>
                    <Grid Background="Red" Width="10" Height="10"></Grid>
                </StackPanel>
            </DataTemplate>
        </Grid.Resources>
        <ItemsControl>
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <Button Height="24" Width="34" ContentTemplate="{StaticResource A}">

                    </Button>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
            <ItemsControl.Items>
                <DataObject></DataObject>
                <DataObject></DataObject>
                <DataObject></DataObject>
            </ItemsControl.Items>
        </ItemsControl>
    </Grid>

</Window>

嗨,你好,<Style TargetType = "{x: Type Button}"> 被认为是隐式的,因为你不需要为集合中的每个项目设置它,而是基于 CLR 类型进行分配。 - user572559
我明确地设置了<Button.Style>,这不在资源部分中。我可以删除TargetType = "{x:Type Button}",并将我的Setter更改为Property = "Button.Content",我仍然会得到相同的结果。(我只使用TargetType来获得智能感知)。 - Scott
1
我在我的问题中添加了第二个示例,以提供一个没有隐式样式的示例。如果我理解正确,在我的样式中有一个x:Key不能被视为隐式样式。即使没有隐式样式,我仍然看到这个问题。 - Scott
此外,我相当确定我的<Button.Style>不是隐式的。<Button><Button.Style ... /></Button>只是一种不同的写法,与<Button Style="..." />不同。我的样式没有应用于多个按钮,因为它是隐式样式。它是显式地应用于DataTemplate中的单个按钮。然后为每个DataObject创建多个DataTemplate实例。感谢您抽出时间帮助解决这个问题。但我认为提供的链接并不是我遇到的同样问题。 - Scott
1
抱歉,我以为问题已经解决了 - 你不必设置整个模板,因为似乎有一个专门的rproperty - ContentTemplate。我已经更新了我的答案,包括实际的解决方案。您可以创建任何复杂度级别的模板。 - user572559
显示剩余11条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接