C# ItemsControl、ItemContainerStyle和ItemTemplate
由于我仍在努力理解ItemContainerStyle是如何工作的,所以我尝试转到定义其行为的根组件,即ItemsControl。C# ItemsControl、ItemContainerStyle和ItemTemplate,c#,wpf,C#,Wpf,由于我仍在努力理解ItemContainerStyle是如何工作的,所以我尝试转到定义其行为的根组件,即ItemsControl。 我能想到的最简单的样式应用就是尝试应用一些设置,比如说背景和前景 <Window.DataContext> <local:VM></local:VM> </Window.DataContext> <DockPanel > <ItemsControl ItemsSource="
我能想到的最简单的样式应用就是尝试应用一些设置,比如说背景和前景
<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>
数据的基础类是:
public class VM
{
public ObservableCollection<string> Items { get; set; } = new ObservableCollection<string>();
public VM()
{
Items.Add("first");
Items.Add("second");
Items.Add("third");
}
}
公共类虚拟机
{
公共ObservableCollection项{get;set;}=new ObservableCollection();
公共虚拟机()
{
项目。添加(“第一”);
项目。添加(“第二项”);
项目。添加(“第三项”);
}
}
结果是:
好的,没有应用背景,但这不是我想要检查的,顺便说一句,在WPF中,似乎有比规则更多的异常。(顺便说一句,我已经尝试过为一个列表框选择的项目分配背景,这需要重新设置整个项目的模板,也许在这里是类似的?如果你知道答案,我会很感激,但我暂时不做了,因为这会让我偏离正轨)
我们还来看看可视化树:
也就是说,对于ItemsControl,项不获取“包装器元素”;如果我们对ListBox执行相同的操作,那么对于集合的每个项,它都将被构造为ListBoxItem
现在,让我们通过添加以下内容(就在
之后)来尝试为该项设置模板:
这是结果(由于MaxWidth=“100”;我想看看后面是否有东西,所以它们在中心移动):
不再应用该样式。让我们看看Viusal树:
这个可视化树并不奇怪,我们只是替换了以前是文本块的默认表示。在它的位置上,现在我们可以找到一个带有自己标准子树的标签。令人惊讶的是,至少前景应该也适用于标签,但不幸的是,它没有 那是怎么回事? 我在这里读到了一个非常类似的问题: 与此不同的是,它尝试分配ContentTemplate。由于我仍在与这里的基本行为作斗争(我不理解那里的答案,只是有一些复制问题),我决定提出这个更基本的问题。
然而,这里似乎有一个风格定位问题,而不是复制问题;这是因为如果我保留ItemTemplate,但用TextBlock替换标签(这将导致与非模板版本完全相同的VisualTree),我将恢复前景色红色
<ItemsControl.ItemTemplate>
<ItemContainerTemplate>
<TextBlock MaxWidth="100" Text="{Binding}"/>
</ItemContainerTemplate>
</ItemsControl.ItemTemplate>
变暖了?
因此,框架似乎会检查组件是否为TextBlock,如果不是,则不会应用样式。
但这是应用隐式样式时的默认行为:带有(TargetType==正在设置样式的控件类型)的stile。在这种情况下,框架似乎假设TargetType是TextBlock,即使设置了ItemTemplate,也不会重新考虑这个假设 为了更好地理解这里的样式目标是如何工作的,我尝试显式地设置样式的TargetType,who knwos,因此让我们尝试以下方法:
<ItemsControl.ItemContainerStyle>
<Style TargetType="Label">
<Setter Property="Label.Foreground" Value="red"/>
<Setter Property="Label.Background" Value="yellow"/>
</Style>
</ItemsControl.ItemContainerStyle>
请参见TargetType=“标签”?伟大的它给出了错误:
无法将用于标签的样式应用于ContentPresenter
(翻译自意大利语,可能不是英语中的确切措辞。如果您手头有,请用准确的措辞替换)
也就是说,它期望:
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<Setter Property="Label.Foreground" Value="red"/>
<Setter Property="Label.Background" Value="yellow"/>
</Style>
</ItemsControl.ItemContainerStyle>
这有点道理,因为根据前面显示的可视化树,每个项目的根节点实际上是ContentPresenter
在这一点上,我很困惑:它应该如何工作?目前的想法是,它不是。
类似ListBox的子类的行为似乎更合理:它为项目的容器设置样式;此处不存在该项目的容器。这只是我的猜测,因为我找不到任何说明这一点的文档。在设置ItemContainerStyle时,您正在查看项目并考虑它们 当然,这是他们的容器,你要在上面设置样式。每个物品的容器。你并不真正关心你的容器,因为它没什么用处 也许一个用例的具体例子比理论更清晰 如果你看: 这些红色和蓝色的矩形是游戏中的单位 这些是北约的各种符号,表示步兵、炮兵骑兵等 itemcontainerstyle用于定位它们 左侧的整个面板有一个itemscontrol,其画布作为itemspanel(而不是默认的stackpanel) 每个单元都有一个viewmodel,其中一个集合绑定到该itemscontrol的itemssource unit viewmodel有一个X和Y属性,用于在画布中定位单元 单元的位置由一个点定义,该点是其视图的中心。我想这很有趣,因为单元的viewmodel不需要计算从中心到左上角的偏移量。这由视图中的转换器完成,并使用样式应用:
<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>
在地图编辑器的其他地方,树的位置与此类似。在设置ItemContainerStyle时,您正在查看项目并考虑它们 当然,这是他们的容器,你要在上面设置样式。每个物品的容器。你并不真正关心你的容器,因为它没什么用处 也许一个用例的具体例子比理论更清晰 如果你看到
<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>
protected virtual DependencyObject GetContainerForItemOverride()
{
return new ContentPresenter();
}
protected override DependencyObject GetContainerForItemOverride()
{
return new ListBoxItem();
}