38

What does the Panel.IstItemsHost attached property used for?

I see plenty of examples of people setting it on the ItemsContainer template for an ItemsControl, but the un-documentation over at MSDN does not explain why or what advantages setting property confers.

ΩmegaMan
  • 22,885
  • 8
  • 76
  • 94
Armentage
  • 8,994
  • 6
  • 30
  • 33

3 Answers3

38

Say I have an ItemsControl. I want to use a custom panel that swoops items in and out as you scroll; its called a SwoopPanel. Now, how do I tell the ItemsControl to use my SwoopPanel to contain the templates it creates?

The quick way is to set the ItemsPanel on the ItemsControl:

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

However, sometimes that doesn't work for you. Maybe you wish to customize how the SwoopPanel is presented in the UI, and the only way to get around this is to change the control template of the ItemsControl. Now you can add your SwoopPanel directly to the control template and, using the property, mark it as the ItemsHost that the ItemsControl will put all the templated items it creates.

<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>

Do you have to do it one way or the other? No. Is one more advantageous than the other? Well, the second way allows you more control of the UI, the first way is easier. Take your pick, really. I've never personally done it the second way, but I think there might be a couple of places where it might be useful.

  • So basically, the ItemsControl code will make note of the control being set as IsItemsHost in it's hierarchy, and if no ItemsPanelTemplate is set, use that child as the container for the generated children? (I guess it's possible it might throw if you have a template AND an IsItemsHost child). – Armentage May 18 '10 at 21:25
  • 1
    IsItemsHost is not an attached property – treehouse May 19 '10 at 12:40
  • 1
    @kai thanks updated. Seems like a good candidate; feels a little odd that Panel knows how ItemsControl is implemented. –  May 19 '10 at 12:56
  • I agree. Off the top of my head I thought that was an AP as well. Then I realize its syntax is not AP. – treehouse May 19 '10 at 13:02
  • I believe you can set any dp on any object; they are just implemented as a map in the static instance of the DP mapping this-ptrs to values. – Armentage May 20 '10 at 14:05
  • Mentioned in this 2005 Avalon blog: "How do I change the layout of an ItemsControl?" http://bea.stollnitz.com/blog/?p=10 – Mikhail Feb 11 '12 at 16:52
  • Oh man. This is a life saver. No more ItemsPanelTemplate! Well not for now anyways. – Jesse Seger Feb 08 '13 at 21:19
  • Sorry, but I still do not understand. Because there is a `ItemsPresenter` and MSDN says: _Used within the template of an item control to specify the place in the control’s visual tree where the ItemsPanel defined by the ItemsControl is to be added._ So why would we want to use items panel directly in ControlTemplate? [System.Windows.Controls.ItemsPresenter](https://msdn.microsoft.com/en-us/library/system.windows.controls.itemspresenter%28v=vs.110%29.aspx?f=255&MSPPError=-2147217396) – Pavels Ahmadulins Aug 15 '15 at 12:58
  • @Robot1que Because you're changing the template of the control, and within this template you define the items presenter. What's confusing about this? –  Aug 15 '15 at 16:01
  • 3
    Another reason for using `IsItemsHost="True"` rather than `ItemsPresenter` is that you can access it as a [template part](https://msdn.microsoft.com/en-us/library/system.windows.templatepartattribute(v=vs.110).aspx) – Mitch Dec 04 '16 at 17:53
14

More Explanation, Please!

While all of the above answers are technically correct, I feel they don't illustrate how IsItemsPanel correlates to the ControlTemplate and the presence (or absence) of an ItemsPresenter and the corresponding ItemsPanel property which it uses. This answer will attempt to shed light on those things and hopefully clarify when you should, or shouldn't use each.

ItemsControls, Panels and IsItemsHost, Oh my!

An ItemsControl is simply a control that displays a collection of items. It does this by first generating individual containers* to represent the items visually, then it hands those containers over to a specific panel to be laid out for display on screen. As items are added or removed, the ItemsControl adds or removes the corresponding containers from the panel as needed.

Note: If an item is already an instance of the container type--i.e. you add a ListBoxItem to the Items collection of a ListBox--that item itself gets added directly to the panel, acting as its own container. The ItemsControl simply passes it through.

The specific panel used for hosting and laying out the containers is the first one found in the ItemControl's control template that has its IsItemsHost property set to 'True'.

There are two ways to specify which panel that is:

  1. By setting the ItemsPanel property, then inserting an ItemsPresenter into the ControlTemplate as a placeholder, or...
  2. By inserting a Panel directly into the ControlTemplate and setting its IsItemsHost to 'True' explicitly.

But which do you use and why? Read on to find out!

ItemsPresenter - "Have It Your Way!"

In a typical ControlTemplate for an ItemsControl such as a ListBox, the template specifies an ItemsPresenter somewhere inside of it. Here's a simplified excerpt showing how it's used:

<Border x:Name="Bd"
    Background="{TemplateBinding Background}"
    BorderBrush="{TemplateBinding BorderBrush}"
    BorderThickness="{TemplateBinding BorderThickness}">

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

</Border>

As you can see, there is an ItemsPresenter specified inside of a ScrollViewer in the middle of the template. What you don't see however is an actual panel to lay out the items.

So if there's no panel defined in the template, where does it come from? That's where the ItemsPanel property comes in. As its name suggests, this property defines which panel will be used to host and lay out the items. It doesn't however say where that panel appears in the ControlTemplate.

That brings us back to the ItemsPresenter. In short, it's a placeholder that essentially says "When the ItemsPanel property is set, I'll insert that panel here and set its IsItemsHost to 'True' automatically."

The advantage of using an ItemsPresenter in the template for your ItemsControl is that you're making it very easy for consumers of your control to replace the panel without having to completely re-template your entire control.

IsItemsHost - "My Way or the Highway!"

However, what if you don't want someone to be able to change out your panel because your control depends on some custom panel implementation and anything else will break the functionality? In that case, you don't use an ItemsPresenter in your template. You instead need to specify the exact panel you want to use.

This is where IsItemsHost property comes into play. When set on a panel in the ControlTemplate, it tells that ItemsControl to use that specific panel to host the generated containers, regardless of what ItemsPanel is set to. The ItemsPanel property is essentially ignored.

Another benefit of specifying the panel directly in the template is you can then name it and access it just like any other template part.

Here's the same example as above, but rather than an ItemsPresenter, it hard-codes a SpecializedPanel to lay out the items. We indicate that's the panel we want to use to host the items by setting its IsItemsHost property to 'True' and finally, we give it a name so we can access it directly from code.

<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>

In this case, because the template doesn't use an ItemsPresenter and instead directly includes a panel with its IsItemsHost set to 'True', there is no way for the user to change out that panel short of completely replacing the entire ControlTemplate. (As mentioned before, the ItemsPanel property is ignored.)

Bringing it all home...

To recap, if you're a control author and want to give consumers of your control the ability to swap out the panel used to lay out your items, define your template for your ItemsControl using an ItemsPresenter. Make sure to also set the ItemsPanel property in the template to specify a default panel.

If however, want to 'lock' which panel your control uses, then do not use an ItemsPresenter in the ControlTemplate. Instead, specify the specific panel you want to use directly in the template, then set its IsItemsHost property to 'True'.

Note: There's technically a third scenario, which is arguably more common: You're not a control author creating something to be consumed by other users, but rather are simply re-templating an ItemsControl (like say a ListBox) for some specialized use in your own application.

In that case, since you are the ultimate consumer of the control, you most likely won't have to worry about other consumers downstream needing to change out the panel, so it's completely fine to simply specify the panel directly in your template (again, setting its IsItemsHost true) and not worry about using an ItemsPresenter and its associated ItemsPanel property as the latter, while valid, would just add unnecessary complexity without any actual benefit.

Hope this clarifies exactly what's going on.

Community
  • 1
  • 1
Mark A. Donohoe
  • 23,825
  • 17
  • 116
  • 235
12

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

Essentially, what this post says is that if you are replacing the ControlTemplate of a ListBox and want a new layout, set IsItemsHost=true on some panel, e.g. a StackPanel. Then any items in the ListBox will be automatically added as children of the StackPanel. If the orientation of the ListBox is Horizontal, then the ListBox will be horizontal.

The other way is to set the ItemsPanel property of the ListBox to an ItemsTemplate and in that template you have a StackPanel. In this case the ListBox items will be added to the StackPanel children just as in the first case. However, you do not need to set IsItemsHost = true, it will have absolutely no effect. This is done for you by the fact that you are setting the ItemsPanel property.

Richard
  • 151
  • 1
  • 3
  • 1
    this is the explanation +1 – mkb Jun 22 '15 at 23:08
  • Almost! :) There's nothing saying you have to use IsItemsHost in a custom ControlTemplate. It's just easier if you do. But you can still use the ItemsPresenter/ItemsPanel combination if say, you're re-styling the chrome around the panel itself, but don't want to affect the ability of others to change that panel. You can check out my answer for more clarification about how they all correlate. – Mark A. Donohoe Jan 23 '19 at 18:58