首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >ItemsControl、ItemContainerStyle和ItemTemplate

ItemsControl、ItemContainerStyle和ItemTemplate
EN

Stack Overflow用户
提问于 2020-08-14 09:20:56
回答 2查看 1.4K关注 0票数 0

由于我仍在努力理解ItemContainerStyle的工作方式,所以我尝试转到定义其行为的根组件,即ItemsControl。

我能想到的最简单的样式应用程序是尝试应用几个设置,比方说项目的背景和前景。

代码语言:javascript
复制
<Window.DataContext>
    <local:VM></local:VM>
</Window.DataContext>

<DockPanel >
    <ItemsControl ItemsSource="{Binding Items}">

        <ItemsControl.ItemContainerStyle>
            <Style>
                <Setter Property="Control.Foreground" Value="red"/>
                <Setter Property="Control.Background" Value="yellow"/>
            </Style>
        </ItemsControl.ItemContainerStyle>
                   
    </ItemsControl>
</Window>

数据的基础类是:

代码语言:javascript
复制
public class VM
{
    public ObservableCollection<string> Items { get; set; } = new ObservableCollection<string>();

    public VM()
    {
        Items.Add("first");
        Items.Add("second");
        Items.Add("third");
    }
}

结果:

好的,背景没有应用,但是这不是我想要检查的,在WPF中,似乎有更多的例外而不是规则。( BTW2,我已经为分配ListBox所选项目的背景进行了斗争,这需要重新定位整个项目,也许这里是类似的?如果你知道答案的话,我们会很感激的,但我暂时不提了,因为它让我偏离了轨道)。

让我们看一看视觉树:

也就是说,对于ItemsControl,条目不会得到一个“包装器元素”;如果我们对一个ListBox做同样的操作,那么对于集合中的每个项,它都将被构造为一个ListBoxItem。

现在,让我们尝试通过添加以下内容(就在</ItemsControl.ItemContainerStyle>之后)来模板该项:

代码语言:javascript
复制
<ItemsControl.ItemTemplate>
    <ItemContainerTemplate>
        <Label MaxWidth="100" Content="{Binding}"/>
    </ItemContainerTemplate>
</ItemsControl.ItemTemplate>

这就是结果(由于MaxWidth=“100”,它们在中间移动;我想看看后面是否有什么东西):

这种样式不再适用了。让我们看一看葡萄树:

这个可视化树并不奇怪,我们只是替换了以前是TextBlock的默认表示形式。在它的位置,我们现在找到了一个标签与它自己的标准子树。

令人惊讶的是,至少前景应该也适用于标签,但不幸的是,它并不适用。

那是怎么回事?

我在这里读到了一个非常类似的问题:Can't use ItemTemplate and ItemContainerStyle together? --它与此不同的是,它试图分配ContentTemplate。因为我仍然在为基本的行为而挣扎(除了有某种复制问题外,我不明白答案),所以我决定提出一个更基本的问题。

然而,这里似乎存在一个样式定位问题,而不是复制问题;这是因为如果我保留ItemTemplate,但是用TextBlock替换标签(这将导致与非模板版本的VisualTree完全相同),我会得到我的前景红色!

代码语言:javascript
复制
<ItemsControl.ItemTemplate>
    <ItemContainerTemplate>
        <TextBlock MaxWidth="100" Text="{Binding}"/>
    </ItemContainerTemplate>
</ItemsControl.ItemTemplate>

变暖了?

因此,框架似乎检查组件是否为TextBlock,如果不是,则检查是否应用样式。但这是应用隐式样式时的默认行为:带有(TargetType,==,被样式化的控件的类型)。

在这种情况下,框架似乎假设TargetType是TextBlock,即使设置了ItemTemplate,也不会重新考虑这个假设。

为了更好地理解样式目标是如何工作的,我尝试显式地设置样式的TargetType,让我们尝试如下:

代码语言:javascript
复制
<ItemsControl.ItemContainerStyle>
    <Style TargetType="Label">
        <Setter Property="Label.Foreground" Value="red"/>
        <Setter Property="Label.Background" Value="yellow"/>
    </Style>
 </ItemsControl.ItemContainerStyle>

看到TargetType=“标签”了吗?太棒了。它给出了错误:

不能应用于ContentPresenter,一种用于标签的样式。

(翻译自意大利语,也许不是英文的确切措辞。)如果你手头有,请用确切的那个代替)。

也就是说,它期望:

代码语言:javascript
复制
<ItemsControl.ItemContainerStyle>
    <Style TargetType="ContentPresenter">
        <Setter Property="Label.Foreground" Value="red"/>
        <Setter Property="Label.Background" Value="yellow"/>
    </Style>
</ItemsControl.ItemContainerStyle>

这有点道理,因为根据前面显示的可视树,每个项的根节点实际上是ContentPresenter。

在这一点上,我很困惑:它应该如何工作?目前的想法是,它没有。像ListBox这样的子类的行为似乎更明智:它为项的容器设置样式;在这里,项的容器不存在。这只是我的猜测,因为我找不到任何这样的文件。

EN

回答 2

Stack Overflow用户

发布于 2020-08-14 10:34:18

您正在查看您的项目,并在设置ItemContainerStyle时考虑它们。

但当然,这是他们的容器,你正在设置一个样式。每个项目的容器。你并不在乎你的容器,因为它做的不多。

也许一个用例的具体例子会比理论更清楚。

如果你看:

https://i.imgur.com/UZ6Nqrc.png

这些红蓝长方形是这个游戏中的单位。

这些都是北约的各种标志,表示步兵、炮兵骑兵等。

项目容器样式用于对它们进行定位。

左边的整个面板都有一个带有画布的itemscontrol,因为它是itemspanel (而不是默认的堆栈面板)。

每个单元都有一个视图模型,这些元素的集合绑定到项目控件的项源。

单元视图模型具有X和Y属性,用于在该画布中定位单元。

一个单位的位置是由它的观点的中心点来定义的。我认为这很有趣,因为单元的视图模型不需要计算从中间到左上角的偏移量。这由视图中的转换器完成,并使用样式应用:

代码语言:javascript
复制
<Style TargetType="ContentPresenter" x:Key="CenteredContentPresenter">
    <Setter Property="Canvas.Top">
        <Setter.Value>
            <MultiBinding Converter="{local:MultiAddConverter}">
                <Binding Path="Y" Mode="TwoWay"/>
                <Binding Path="ActualHeight"
                         Converter="{local:MultiplyConverter Multiplier=-.5}"
                         RelativeSource="{RelativeSource Self}" 
                         Mode="TwoWay" />
            </MultiBinding>
        </Setter.Value>
    </Setter>
    <Setter Property="Canvas.Left">
        <Setter.Value>
            <MultiBinding Converter="{local:MultiAddConverter}">
                <Binding Path="X" Mode="TwoWay"/>
                <Binding Path="ActualWidth"  
                         Converter="{local:MultiplyConverter Multiplier=-.5}"
                         RelativeSource="{RelativeSource Self}" 
                         Mode="TwoWay" />
            </MultiBinding>
        </Setter.Value>
    </Setter>
</Style>

在地图的其他地方,编辑器树也以类似的方式定位。

票数 1
EN

Stack Overflow用户

发布于 2020-08-14 11:13:04

ItemContainerStyle应用于由虚拟GetContainerForItemOverride方法创建的项容器。

ItemsControl基类中,此方法返回一个ContentPresenter

代码语言:javascript
复制
protected virtual DependencyObject GetContainerForItemOverride()
{
    return new ContentPresenter();
}

在派生的ListBox类中,它返回一个ListBoxItem

代码语言:javascript
复制
protected override DependencyObject GetContainerForItemOverride()
{
    return new ListBoxItem();
}

TargetTypeItemContainerStyle必须匹配从这个方法返回的DependencyObject的类型。否则,当在运行时将样式应用于容器时,您将得到一个异常。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/63410059

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档