17

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.

3 Answers 3

58

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>
Sign up to request clarification or add additional context in comments.

2 Comments

Very nice. Is there a similar solution for silverlight?
It's been a while since i've worked with silveright, but as long as the trigger stuff works then you should be able to use a DataTrigger with a valueconverter to check the value of Items.Count on the control. Failing that you should be able to achieve similar results using the blend interaction libraries
9

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

5 Comments

Does DataContext.SomeCollection.Count return 0 in case when DataContext.SomeCollection is null?
@sll Nope, but you can always write a 2nd trigger to see if SomeCollection is equal to {x:Null}
Right, could you please add this to the answer as well?
are you sure 'DataContext.SomeCollection.Count' is a valid path? Also
@Dmitry Yes, it binds to ListBox.DataContext.SomeCollection.Count, which should be a valid path since ItemsSource="{Binding SomeCollection}" is a valid path. I can't remember the value that gets returned if the binding is not valid, but if SomeCollection is null then WPF will ignore the binding error caused by .Count. It won't throw an exception.
2

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>

1 Comment

Even I'm looking for XAML only solution could you provide an example how you would handle Empty data set with template selector considering that selector handling single item in SelectTemplate() method and there are no items at all?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.