How-to define Empty DataTemplate for the ItemsControl based controls like ListView or DataGrid

ASP.NET controls like ListView allows providing a custom template by setting the ListView.EmptyDataTemplate property, this template will be rendered in case of empty data source.

How to do the same in WPF (XAML only preferrable) for ItemsControl based controls like ListView and DataGrid? So I want to show my custom DataTemplate in case when ItemsSource is empty.

Answers


You can use set the Template property based on a DataTrigger

For example,

In Resources:

<ControlTemplate x:Key="EmptyListBoxTemplate">
     <TextBlock Text="Items count == 0" />
</ControlTemplate>

Control itself:

<ListBox ItemsSource="{Binding SomeCollection}">
    <ListBox.Style>
        <Style TargetType="{x:Type ListBox}">
            <Style.Triggers>
                <DataTrigger Value="{x:Null}" Binding="{Binding DataContext.SomeCollection, RelativeSource={RelativeSource Self}}">
                    <Setter Property="Template" Value="{StaticResource EmptyListBoxTemplate}" />
                </DataTrigger>
                <DataTrigger Value="0" Binding="{Binding DataContext.SomeCollection.Count, RelativeSource={RelativeSource Self}}">
                    <Setter Property="Template" Value="{StaticResource EmptyListBoxTemplate}" />
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </ListBox.Style>
</ListBox>

There might be an simplier way of doing the binding, but I don't have a compiler on me right now to figure out what it would be :)


There is a 100% xaml solution that makes use of the "HasItems" dependancy property.

<ItemsControl>
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding Description}"/>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
    <ItemsControl.Style>
        <Style TargetType="ItemsControl">
            <Style.Triggers>
                <Trigger Property="HasItems" Value="false">
                        <Setter Property="Template">
                            <Setter.Value>
                                <ControlTemplate>
                                    <TextBlock Text="This Control is empty"/>
                                </ControlTemplate>
                            </Setter.Value>
                        </Setter>
                </Trigger>
            </Style.Triggers>
        </Style>
    </ItemsControl.Style>
</ItemsControl>

you can use DataTemplate selector to do that.

http://msdn.microsoft.com/en-us/library/system.windows.controls.datatemplateselector.aspx

UPDATE 1

Code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace EmptyRowsTemplate
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            this.Loaded += (o, e) => 
            {
                this.l.ItemsSource = new List<string>(3)
                {
                    "A",
                    null,
                    "B"
                };
            };
        }
    }

    public class TemplateManager : DependencyObject
    {
        public static readonly DependencyProperty BlankDataTemplateProperty =
            DependencyProperty.RegisterAttached("BlankDataTemplate",
            typeof(DataTemplate),
            typeof(TemplateManager),
            new PropertyMetadata(new PropertyChangedCallback((o, e) => 
            {
                ((ItemsControl)o).ItemTemplateSelector = new BlankDataTemplateSelector();
            })));

        public static void SetBlankDataTemplate(DependencyObject o, DataTemplate e)
        {
            o.SetValue(TemplateManager.BlankDataTemplateProperty, e);
        }

        public static DataTemplate GetBlankDataTemplate(DependencyObject o)
        {
            return (DataTemplate)o.GetValue(TemplateManager.BlankDataTemplateProperty);
        }

        private class BlankDataTemplateSelector : DataTemplateSelector
        {
            public BlankDataTemplateSelector()
                : base()
            {
            }

            public override DataTemplate SelectTemplate(object item, DependencyObject container)
            {
                ItemsControl itemControl =
                    (ItemsControl)ItemsControl.ItemsControlFromItemContainer(ItemsControl.ContainerFromElement(null, container));

                if (item == null)
                {
                    return TemplateManager.GetBlankDataTemplate(itemControl);
                }
                else
                {
                    return base.SelectTemplate(item, container);
                }

            }
        }
    }
}

Markup

<Window x:Class="EmptyRowsTemplate.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:EmptyRowsTemplate"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <ListBox x:Name="l">
            <local:TemplateManager.BlankDataTemplate>
                <DataTemplate>
                    <Button Background="Red">No Data!</Button>
                </DataTemplate>
            </local:TemplateManager.BlankDataTemplate>
        </ListBox>
    </Grid>
</Window>


Need Your Help

Pair inside priority queue

c++ stl priority-queue std-pair

I am trying to store pairs in priority queue and I am using a compare function that compares second value of each pair.