Panel.IsItemsHost是用来做什么的?

41

Panel.IstItemsHost 附加属性用于什么目的?

我看到很多人在 ItemsControlItemsContainer 模板上设置它,但是MSDN上的非官方文档并没有解释为什么或者设置这个属性会带来哪些优势。

3个回答

39

假设我有一个ItemsControl。我想使用一个自定义面板,在滚动时将项目向内和向外扫过来;它被称为SwoopPanel。现在,我该如何告诉ItemsControl使用我的SwoopPanel来包含它创建的模板?

快速的方法是在ItemsControl上设置ItemsPanel:

<ItemsControl.ItemsPanel>
    <ItemsPanelTemplate>
      <lol:SwoopPanel />
    </ItemsPanelTemplate>
</ItemsControl.ItemsPanel>

然而,有时候这并不适用于你。也许你希望自定义SwoopPanel在UI中的呈现方式,而唯一的解决方法就是更改ItemsControl的控件模板。现在你可以直接将SwoopPanel添加到控件模板中,并使用属性将其标记为ItemsControl将放置所有模板化项的ItemsHost。

<Style TargetType="ItemsControl">
  <Setter Property="Template">
    <Setter.Value>
      <ControlTemplate TargetType="ItemsControl">
        <Border CornerRadius="5">
          <ScrollViewer VerticalScrollBarVisibility="Hidden">
            <lol:SwoopPanel IsItemsHost="True"/>
          </ScrollViewer>
        </Border>
      </ControlTemplate>
    </Setter.Value>
  </Setter>
</Style>

你必须以一种方式或另一种方式来做吗?不必。有一个比另一个更有优势吗?嗯,第二个方法允许您更多地控制用户界面,而第一个方法更容易。实际上,您可以自己选择。我从未亲自使用过第二种方法,但我认为可能有一些场合它会很有用。

1
IsItemsHost不是一个附加属性。 - treehouse
1
@kai 谢谢更新。看起来是个不错的候选者;只是感觉 Panel 知道 ItemsControl 的实现有点奇怪。 - user1228
我相信您可以在任何对象上设置任何dp; 它们只是作为DP映射此指针到值的静态实例中的映射实现。 - Armentage
哦,太棒了。这真是救命稻草啊。不再需要ItemsPanelTemplate了!至少现在不需要了。 - Jesse Seger
3
使用IsItemsHost="True"而不是ItemsPresenter的另一个原因是你可以将其作为模板部件进行访问。 - Mitch
显示剩余6条评论

19

请再解释一下!

虽然上面的所有答案在技术上都是正确的,但我觉得它们没有说明IsItemsPanel如何与ControlTemplate以及ItemsPresenter的存在(或不存在)以及它使用的相应ItemsPanel属性相关。本答案将尝试阐明这些问题,并希望澄清何时应该或不应该使用每个选项。

ItemsControls、Panels 和 IsItemsHost?

ItemsControl只是一个显示项目集合的控件。它首先生成单独的容器*来表示项目的可视化,然后将这些容器交给特定的面板进行屏幕展示。随着项目的添加或删除,ItemsControl会根据需要向面板添加或删除相应的容器。

* 注意:如果一个项目已经是容器类型的实例(由ItemsControlIsItemItsOwnContainer覆盖确定),即您将ListBoxItem实例添加到ListBoxItems集合中,则该项目将直接传递为其自己的容器。

用于托管和布局容器的特定面板是在{{ItemControl}}控件模板中找到的第一个将其{{IsItemsHost}}属性设置为“True”的面板。
有两种方法可以指定该面板:
1.通过将{{ItemsPresenter}}插入{{ControlTemplate}}中,以充当由{{ItemsPanel}}属性指定的面板的占位符。(这是最常见的方法。) 2.通过直接将{{Panel}}插入{{ControlTemplate}}中,并显式设置其{{IsItemsHost}}属性为{{True}}。
但是你应该使用哪种方法以及为什么?继续阅读以了解更多!
{{ItemsPresenter}}-“随心所欲!”
在诸如{{ListBox}}之类的{{ItemsControl}}的典型{{ControlTemplate}}中,模板在其中的某个位置指定了一个{{ItemsPresenter}}。以下是显示其用法的简化摘录:
<Border x:Name="Bd"
    Background="{TemplateBinding Background}"
    BorderBrush="{TemplateBinding BorderBrush}"
    BorderThickness="{TemplateBinding BorderThickness}">

    <ScrollViewer Focusable="false" Padding="{TemplateBinding Padding}">
        <ItemsPresenter />
    </ScrollViewer>

</Border>

正如您所看到的,在模板中间指定了一个ItemsPresenter,放在了ScrollViewer内。然而,您没有看到实际用于布局项目的面板。
因此,如果模板中没有定义面板,那么它从哪里来呢?这就是ItemsPanel属性发挥作用的地方。顾名思义,此属性定义将用于托管和布置项目的面板。但它并不说该面板出现在ControlTemplate的哪个位置。
这就把我们带回到ItemsPresenter。简而言之,它是一个占位符,基本上表示“当设置ItemsPanel属性时,我会自动将该面板插入到这里,并将其IsItemsHost设置为True。”
使用ItemsControl模板中的ItemsPresenter的优点是,您使消费者轻松替换面板,而无需完全重新设计整个控件。
然而,如果您不希望有人更改面板,因为您的控件依赖于某些自定义面板实现,而其他任何面板都会破坏功能怎么办?在这种情况下,您不应在模板中使用ItemsPresenter。相反,您需要指定要使用的确切面板。
这就是{{IsItemsHost}}属性发挥作用的地方。当在{{ControlTemplate}}中的面板上设置时,它告诉{{ItemsControl}}使用该特定面板来托管生成的容器,而不管设置为什么{{ItemsPanel}}。{{ItemsPanel}}属性基本上被忽略了。
直接在模板中指定面板的另一个好处是,您可以将其命名并像任何其他模板部分一样访问它。
以下与上面相同的示例,但不是使用{{ItemsPresenter}},而是硬编码一个{{SpecializedPanel}}来布局项目。我们通过将其{{IsItemsHost}}属性设置为{{True}}来指示要使用该面板来托管项目,最后,我们为其命名以便可以直接从代码中访问它。
<Border x:Name="Bd"
    Background="{TemplateBinding Background}"
    BorderBrush="{TemplateBinding BorderBrush}"
    BorderThickness="{TemplateBinding BorderThickness}">

    <ScrollViewer Focusable="false" Padding="{TemplateBinding Padding}">
        <SpecializedPanel name="PART_MainPanel" IsItemsHost="True" />
    </ScrollViewer>

</Border>

在这种情况下,由于模板没有使用ItemsPresenter,而是直接包含一个面板,并将其IsItemsHost设置为True,因此用户无法更改该面板,除非完全替换整个ControlTemplate。(如前所述,ItemsPanel属性被忽略。)
将所有内容带回家...
简而言之,如果您是控件作者,并希望为控件的消费者提供灵活性以交换用于布局项目的面板,则使用ItemsPresenter定义您的ItemsControl的模板。还要确保在模板中设置ItemsPanel属性以指定默认面板。
但是,如果要“锁定”控件使用的面板,则不要在ControlTemplate中使用ItemsPresenter。相反,在模板中直接指定要使用的特定面板,然后将其IsItemsHost属性设置为True
注意:实际上有第三种情况,也许更常见:您不是控件作者,创建的东西供其他用户使用,而是在自己的应用程序中为某些专业用途重新模板化ItemsControl(例如ListBox)。 在这种情况下,由于您是控件的最终消费者,所以您很可能不必担心下游的其他消费者需要更改面板,因此直接在模板中指定面板是完全可以的(再次将其IsItemsHost设置为true),而不用担心使用ItemsPresenter及其关联的ItemsPanel属性。后者虽然有效,但只会增加不必要的复杂性而没有任何实际的好处。 希望这澄清了正在发生的事情。

13

请参见http://msdn.microsoft.com/en-us/library/system.windows.controls.panel.isitemshost(v=vs.90).aspx

本质上,这篇文章说明了如果你正在替换ListBox的ControlTemplate并希望使用新布局,则需要在某个面板(例如StackPanel)上设置IsItemsHost=true。那么ListBox中的任何项都将自动添加为StackPanel的子项。如果ListBox的方向是水平的,则ListBox将是水平的。

另一种方法是将ListBox的ItemsPanel属性设置为一个ItemsTemplate,在该模板中您有一个StackPanel。在这种情况下,ListBox项将像第一种情况一样添加到StackPanel的子项中。但是,您不需要设置IsItemsHost = true,它完全没有影响。这是由于您设置了ItemsPanel属性。


1
这是解释 +1 - mkb
几乎了! :) 没有任何东西说您必须在自定义ControlTemplate中使用IsItemsHost。如果您这样做,它只是更容易。但是,如果您重新设计面板周围的Chrome但不想影响其他人更改该面板的能力,则仍然可以使用ItemsPresenter / ItemsPanel组合。您可以查看我的答案以获取有关它们如何相关的更多澄清。 - Mark A. Donohoe

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