pchajer
pchajer

Reputation: 1584

Show "No record found" message on a WPF DataGrid when it's empty

If there is no record available, I want to add a TextBlock on data grid, below the header, showing the message "No Record Found."

Consider the attached image for reference.alt text

Upvotes: 16

Views: 24851

Answers (5)

bergerb
bergerb

Reputation: 116

I was looking for a generic solution that looks the same as the original control, with headers. Just with one line showing that there is no data.

I found this answer: https://stackoverflow.com/a/50233655/

Unfortunately, this does not look the same as the normal control, but from that I got the Idea to try to modify the original template.

This is how my Version looks:

With Data:
DataGrid with Data

Without Data:
DataGrid without Data

  1. If you don't want to know how this works, just copy the code at the bottom
  2. Add the Style below to your App.xaml file:
    <Application.Resources>
    
        <Style  TargetType="{x:Type DataGrid}">
            <Style.Triggers>
                <DataTrigger Binding="{Binding HasItems, RelativeSource={RelativeSource Self}}" Value="false">
                    <Setter Property="Template">
                        <Setter.Value>
                            <ControlTemplate TargetType="{x:Type DataGrid}">
                            <!-- Content of the Original Template and the Label (Part 4). -->
                            </ControlTemplate>
                        </Setter.Value>
                    </Setter>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    
    </Application.Resources>
    
  3. Insert the original Template into the Style
    (To get the original Template: right click your DataGrid in the Designer and select "Edit Style->Edit a Copy")
  4. Add a Label after the last Element inside the Grid (Inside the ControlTemplate for the ScrollViewer)
    <Label Content="No data..." Grid.Row="1" Grid.Column="1" HorizontalAlignment="Center" />
    

Results in the following Style:

<Style  TargetType="{x:Type DataGrid}">
    <Style.Triggers>
        <DataTrigger Binding="{Binding HasItems, RelativeSource={RelativeSource Self}}" Value="false">
            <Setter Property="Template">
                <Setter.Value>
                    <!-- <Original Template> -->
                    <ControlTemplate TargetType="{x:Type DataGrid}">
                        <Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Padding="{TemplateBinding Padding}" SnapsToDevicePixels="True">
                            <ScrollViewer x:Name="DG_ScrollViewer" Focusable="False">
                                <ScrollViewer.Template>
                                    <ControlTemplate TargetType="{x:Type ScrollViewer}">
                                        <Grid>
                                            <Grid.ColumnDefinitions>
                                                <ColumnDefinition Width="Auto"/>
                                                <ColumnDefinition Width="*"/>
                                                <ColumnDefinition Width="Auto"/>
                                            </Grid.ColumnDefinitions>
                                            <Grid.RowDefinitions>
                                                <RowDefinition Height="Auto"/>
                                                <RowDefinition Height="*"/>
                                                <RowDefinition Height="Auto"/>
                                            </Grid.RowDefinitions>
                                            <Button Command="ApplicationCommands.SelectAll" Focusable="False" Style="{DynamicResource {ComponentResourceKey ResourceId=DataGridSelectAllButtonStyle, TypeInTargetAssembly={x:Type DataGrid}}}" Width="{Binding CellsPanelHorizontalOffset, RelativeSource={RelativeSource FindAncestor, AncestorLevel=1, AncestorType={x:Type DataGrid}}}">
                                                <Button.Visibility>
                                                    <Binding Path="HeadersVisibility" RelativeSource="{RelativeSource FindAncestor, AncestorLevel=1, AncestorType={x:Type DataGrid}}">
                                                        <Binding.ConverterParameter>
                                                            <DataGridHeadersVisibility>All</DataGridHeadersVisibility>
                                                        </Binding.ConverterParameter>
                                                    </Binding>
                                                </Button.Visibility>
                                            </Button>
                                            <DataGridColumnHeadersPresenter x:Name="PART_ColumnHeadersPresenter" Grid.Column="1">
                                                <DataGridColumnHeadersPresenter.Visibility>
                                                    <Binding Path="HeadersVisibility" RelativeSource="{RelativeSource FindAncestor, AncestorLevel=1, AncestorType={x:Type DataGrid}}">
                                                        <Binding.ConverterParameter>
                                                            <DataGridHeadersVisibility>Column</DataGridHeadersVisibility>
                                                        </Binding.ConverterParameter>
                                                    </Binding>
                                                </DataGridColumnHeadersPresenter.Visibility>
                                            </DataGridColumnHeadersPresenter>

                                            <ScrollContentPresenter x:Name="PART_ScrollContentPresenter" CanContentScroll="{TemplateBinding CanContentScroll}" CanHorizontallyScroll="False" Grid.ColumnSpan="2" CanVerticallyScroll="False" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" ContentStringFormat="{TemplateBinding ContentStringFormat}" Grid.Row="1"/>
                                            <ScrollBar x:Name="PART_VerticalScrollBar" Grid.Column="2" Maximum="{TemplateBinding ScrollableHeight}" Orientation="Vertical" Grid.Row="1" Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}" Value="{Binding VerticalOffset, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}" ViewportSize="{TemplateBinding ViewportHeight}"/>
                                            <Grid Grid.Column="1" Grid.Row="2">
                                                <Grid.ColumnDefinitions>
                                                    <ColumnDefinition Width="{Binding NonFrozenColumnsViewportHorizontalOffset, RelativeSource={RelativeSource FindAncestor, AncestorLevel=1, AncestorType={x:Type DataGrid}}}"/>
                                                    <ColumnDefinition Width="*"/>
                                                </Grid.ColumnDefinitions>
                                                <ScrollBar x:Name="PART_HorizontalScrollBar" Grid.Column="1" Maximum="{TemplateBinding ScrollableWidth}" Orientation="Horizontal" Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}" Value="{Binding HorizontalOffset, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}" ViewportSize="{TemplateBinding ViewportWidth}"/>
                                            </Grid>
                                                    
                                            <!-- This is the only Modification to the original Template -->
                                            <Label Content="No data..." Grid.Row="1" Grid.Column="1" HorizontalAlignment="Center" />
                                            <!-- -->
                                        </Grid>
                                    </ControlTemplate>
                                </ScrollViewer.Template>
                                <ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
                            </ScrollViewer>
                        </Border>
                    </ControlTemplate>
                    <!-- </Original Template> -->
                </Setter.Value>
            </Setter>
        </DataTrigger>
    </Style.Triggers>

</Style>

Upvotes: 1

Atul Mogal
Atul Mogal

Reputation: 71

  1. Add the grid inside the stackpanel
  2. Place below border code next to datagrid
<Border HorizontalAlignment="Stretch" VerticalAlignment="Center" 
        BorderThickness="1,0,1,1" BorderBrush="Black" Height="35">
    <Border.Style> 
        <Style TargetType="Border">
            <Setter Property="Visibility" Value="Collapsed" />
            <Style.Triggers>
                <DataTrigger Binding="{Binding YourListName.Count}" Value="0">
                    <Setter Property="Visibility" Value="Visible" />
                </DataTrigger>
            </Style.Triggers>
        </Style> 
    </Border.Style> 
    <TextBlock Text="No record fount" HorizontalAlignment="Center"
               VerticalAlignment="Center" /> 
</Border>

It will show/hide based on your collection/list count.

Upvotes: 6

VisDev
VisDev

Reputation: 216

Its been a long time since the question has been posted. But I thought this might be useful to someone else.

<Window.Resources>
   <BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
</Window.Resources>

<DataGrid Name="dgProjects" ItemsSource="{Binding Projects}" AutoGenerateColumns="True" />

<TextBlock Text="Employee has no projects" Visibility="{Binding Items.IsEmpty, Converter={StaticResource BooleanToVisibilityConverter}, ElementName=dgProjects}" />

For simplicity purpose I have set AutoGenerateColumns="True". Please define the columns. This way when a empty datasource is bound, the column names will be shown along with 'Empty row' message.

Upvotes: 20

Mike Rowley
Mike Rowley

Reputation: 958

I find that it is easy to center a text block over the grid and set its visibility based on the number of rows. I am usually using MVVM and will bind the visibility to a View Model property:

<Grid>
    <toolkit:DataGrid>
        <toolkit:DataGrid.Columns>
           .
           .
           .
        </toolkit:DataGrid.Columns>
    </toolkit:DataGrid>
    <TextBlock Text="No Records Found" HorizontalAlignment="Center"  VerticalAlignment="Center" Visibility="{Binding EmptyMessageVisibility, Mode=OneWay, FallbackValue=Visible}" />
</Grid>

Upvotes: 5

pchajer
pchajer

Reputation: 1584

Finally I am able to findout the way.

  1. When the grid in empty, add a default row on grid
  2. Create a RowDetailTemplate which contain a text block with a message "No Record Found"

    <DataGrid.RowDetailsTemplate>
        <DataTemplate>
            <StackPanel>
                <TextBlock Text="No Record Found" Width="400"></TextBlock>
            </StackPanel>
        </DataTemplate>
    </DataGrid.RowDetailsTemplate>
    
  3. Set the style on datagrid

    <DataGrid.Style>
        <Style TargetType="DataGrid">
            <Setter Property="RowDetailsVisibilityMode" Value="Collapsed"></Setter>
            <Style.Triggers>
                <DataTrigger Binding="{Binding DataContext.IsRecordExists, 
                                        RelativeSource={RelativeSource Mode=FindAncestor,
                                        AncestorType={x:Type local:MainWindow}}}" Value="false">
                    <Setter Property="RowHeight" Value="0"></Setter>
                    <Setter Property="RowDetailsVisibilityMode" Value="Visible"></Setter>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </DataGrid.Style>
    

By default (record available on datagrid) row detail template will be collapsed.

DataTrigger that checks CLR poperty, if it is false then show the row detail template.

The reason for setting the rowheight as 0 to hide the default row which we haved added on 1st step.

Upvotes: 9

Related Questions