我找不到Panel.IstItemsHost附加属性的任何好文档。我看到很多人在ItemsControl的ItemsContainer模板上设置它的例子,但在MSDN上的un-documentation并没有解释为什么或者设置属性赋予什么优势。我已经构建了大量不设置此属性的容器,但尚未注意到任何不良影响。
答案 0 :(得分:36)
说我有一个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>
你必须以某种方式做到这一点吗?不是。比另一个更有优势吗?好吧,第二种方式可以让你更多地控制UI,第一种方式更容易。接受你的选择,真的。我从来没有亲自以第二种方式完成它,但我认为可能有几个地方可能有用。
答案 1 :(得分:12)
请参阅http://msdn.microsoft.com/en-us/library/system.windows.controls.panel.isitemshost(v=vs.90).aspx
基本上,这篇文章所说的是,如果要替换ListBox的ControlTemplate并想要一个新布局,请在某些面板上设置IsItemsHost = true,例如一个StackPanel。然后,ListBox中的任何项目将自动添加为StackPanel的子项。如果ListBox的方向是水平,则ListBox将是水平的。
另一种方法是将ListBox的ItemsPanel属性设置为ItemsTemplate,并在该模板中设置StackPanel。在这种情况下,ListBox项目将被添加到StackPanel子项中,就像在第一种情况下一样。但是,您不需要设置IsItemsHost = true,它将完全没有效果。这是通过您设置ItemsPanel属性来完成的。
答案 2 :(得分:2)
尽管以上所有答案在技术上都是正确的,但实际上它们缺少说明IsItemsPanel
与ControlTemplate
的相关性以及{{1 }}元素和ItemsPresenter
属性。
在ItemsPanel
这样的ControlTemplate
的典型ItemsControl
中,模板定义了一个称为ListBox
的元素。这是ItemsPresenter
模板的简化摘录:
ListBox
如您所见,模板的中间指定了一个<Border x:Name="Bd"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Background="{TemplateBinding Background}"
Padding="1" SnapsToDevicePixels="true">
<ScrollViewer Focusable="false" Padding="{TemplateBinding Padding}">
<ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</ScrollViewer>
</Border>
。但是,您看不到的是实际布置项目的面板。
因此,如果模板中没有定义面板,它来自哪里?这就是ItemsPresenter
属性的来源。顾名思义,该属性定义了将用于托管和布置项目的面板。但是,它没有说该面板在模板中的位置。这使我们回到了ItemsPanel
。实质上,它是ItemsPresenter
中的占位符,上面写着:“当您设置ControlTemplate
时,我将其放在此处并将其ItemsPanel
设置为true!”
在IsItemsHost
模板中使用ItemsPresenter
的优势在于,您可以轻松地使控件的使用者替换ItemsControl
,而不必完全重新模板您的整个控制。
但是,如果您不希望某人因为需要一些自定义实现而能够更改您的面板,而其他任何事情都会破坏该功能,该怎么办?在这种情况下,您 不要 在模板中使用ItemsPanel
。您可以直接定义面板。这就是ItemsPresenter
发挥作用的地方。
与上面的示例相同,但是它不是IsItemsHost
,而是硬编码ItemsPresenter
来布置项目。我们通过在面板上设置SpecializedPanel
属性来指示该面板用于托管项目。
IsItemsHost
在这种情况下,由于没有<Border x:Name="Bd"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Background="{TemplateBinding Background}"
Padding="1" SnapsToDevicePixels="true">
<ScrollViewer Focusable="false" Padding="{TemplateBinding Padding}">
<SpecializedPanel IsItemsHost="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</ScrollViewer>
</Border>
,并且模板直接定义了要用于项目的面板,因此用户无法完全替换整个{{ 1}}。
回顾一下,如果您是控制作者,并且想让用户能够交换用于布置项目的面板,请使用ItemsPresenter
为ControlTemplate
定义模板(并且不要忘记通过ItemsControl
属性定义要使用的默认面板。)
如果您想“锁定”控件使用的面板,请不要在模板中使用ItemsPresenter
。而是直接指定要用于托管项目的面板,并将其ItemsTemplate
属性设置为'True'
注意:从技术上讲,存在第三种情况,这可能是更常见的情况:您不是控件的作者,而是只是将
ItemsPresenter
之类的模板重新模板化以供您在特定页面/屏幕上使用。在这种情况下,由于您不必担心下游的其他人,因此最好直接使用IsItemsHost
指定面板,而不用担心ListBox
/IsItemsHost
组合或其增加的复杂性。这是因为您的本地更改不会影响控件的任何其他用法,因为您没有更改默认的
ItemsPanel
。您只需针对自己的特定实例进行更改。也就是说,您可以仍然使用
ItemsPresenter
/ControlTemplate
组合,而纯粹主义者则认为这是正确的方法,但是除非您实际上需要灵活下游有其他人根据他们的需要换面板(即,您正在应用的根目录重新模板ItemsPanel
以使布局看起来不错,但是应用中其他地方的消费者可能仍需要换出面板)面板以显示特定屏幕),只是为您自己添加了更多工作。
希望这可以清楚地说明正在发生的事情。