简体   繁体   中英

Binding Itemscontrol to ObservableCollection: extra generated item

Binding ItemsControl to an ObservableCollection<T> places an extra {NewItemPlaceholder} in the control at runtime. How can I remove it?

NB I have seen posts related to this problem but those are limited to DataGrid where you can set CanUserAddRows or IsReadOnly properties to get rid of this item. ItemsControl doesn't have any such property.

XAML

Here's my ItemsControl ( MyPoints is ObservableCollection<T> in the underlying ViewModel):

<ItemsControl ItemsSource="{Binding MyPoints}">
  <ItemsControl.ItemsPanel>
    <ItemsPanelTemplate>
      <Canvas />
    </ItemsPanelTemplate>
  </ItemsControl.ItemsPanel>

  <ItemsControl.ItemContainerStyle>
    <Style TargetType="FrameworkElement">
      <Setter Property="Canvas.Left" Value="{Binding Path=X}" />
      <Setter Property="Canvas.Top" Value="{Binding Path=Y}" />
    </Style>
  </ItemsControl.ItemContainerStyle>

  <ItemsControl.ItemTemplate>
    <DataTemplate DataType="{x:Type vm:PointVM}">
      <Ellipse Width="10" Height="10" Fill="#88FF2222" />
    </DataTemplate>       
  </ItemsControl.ItemTemplate>
</ItemsControl>

This displays an extra point at 0,0. Live Property Explorer shows that this point is bound to the {NewItemPlaceholder} object.

MSDN writes the following:

When a CollectionView that implements IEditableCollectionView has NewItemPlaceholderPosition set to AtBeginning or AtEnd , the NewItemPlaceholder is added to the collection. The NewItemPlaceholder always appears in the collection; it does not participate in grouping, sorting, or filtering.

Link: MSDN

Hopefully if you set the NewItemPlaceholderPosition to something else, the placeholder will disappear from the collection.

Edit:

If your ObservableCollection<T> is being binded to somewhere else as well (eg: to a DataGrid , you have to set the CanUserAddRows to false), you have to deal with that other item. It would add a new NewItemPlaceholder to your collection.

Finally finally!

After spending a day, the solution turned out to be simple (although not ideal). ItemTemplateSelector allows me to select a template based on the item type, so I can create a simple selector to get rid of the extra item:

public class PointItemTemplateSelector : DataTemplateSelector
{
  public override DataTemplate SelectTemplate(object item, DependencyObject container)
  {
    if (item == CollectionView.NewItemPlaceholder)
      return (container as FrameworkElement).TryFindResource("EmptyDataTemplate") as DataTemplate;
    else
      return (container as FrameworkElement).TryFindResource("MyDataTemplate") as DataTemplate;
  }
}

MyDataTemplate and EmptyDataTemplate should be defined in the Resources section of your ItemsControl . EmptyDataTemplate doesn't need to have any content.

Edit

@KAI's guess (see accepted answer) was correct. My ObservableCollection<T> was bound to a DataGrid which was causing this problem. I'll still keep my answer here as it provides a solution for other related situations.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM