如何为基于控件的ItemsControl(如ListView或DataGrid)定义空数据模板

17
ASP.NET的控件,如ListView,允许通过设置ListView.EmptyDataTemplate属性提供自定义模板,在数据源为空时呈现该模板。
在WPF(最好只使用XAML)中,如何为基于ItemsControl的控件(如ListViewDataGrid)执行相同操作?因此,当ItemsSource为空时,我想显示我的自定义DataTemplate。
3个回答

56

有一种100%的XAML解决方案,利用了“HasItems”依赖属性。

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

非常好。Silverlight有类似的解决方案吗? - Shaun Rowan
我已经有一段时间没有使用Silverlight了,但只要触发器正常工作,您应该能够使用DataTrigger和ValueConverter来检查控件上Items.Count的值。如果无法实现,您也可以尝试使用Blend交互库来获得类似的结果。 - Rhys Bevilaqua

9
您可以使用DataTrigger来设置模板属性。
例如,
在资源中:
<ControlTemplate x:Key="EmptyListBoxTemplate">
     <TextBlock Text="Items count == 0" />
</ControlTemplate>

控制本身:

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

可能有一种更简单的绑定方法,但我现在身上没有编译器来找出它是什么 :)


DataContext.SomeCollection为空时,DataContext.SomeCollection.Count会返回0吗? - sll
@sll 不行,但是你可以写第二个触发器来判断“SomeCollection”是否等于“{x:Null}”。 - Rachel
好的,您能把这个也加到答案里吗? - sll
你确定 'DataContext.SomeCollection.Count' 是一个有效的路径吗?另外。 - user572559
@Dmitry 是的,它绑定到 ListBox.DataContext.SomeCollection.Count,这应该是一个有效的路径,因为 ItemsSource="{Binding SomeCollection}" 是一个有效的路径。我记不清如果绑定无效会返回什么值,但是如果 SomeCollection 为空,则 WPF 将忽略由 .Count 引起的绑定错误。它不会抛出异常。 - Rachel

2
你可以使用DataTemplate选择器来做到这一点。 http://msdn.microsoft.com/zh-cn/library/system.windows.controls.datatemplateselector.aspx 更新 1
代码:
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>


即使我正在寻找仅限XAML的解决方案,您能否提供一个示例,说明如何处理空数据集与模板选择器,考虑到选择器在SelectTemplate()方法中处理单个项目且根本没有任何项目? - sll

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接