DataGrid and “No Records Found” message

Here is one way to display a message when we dont have any data returned

1. Grab the Template of the DataGrid from Blend

2. Modify the Template. All we do is find the RowsPresenter line
<sdk:DataGridRowsPresenter x:Name=”RowsPresenter” Grid.ColumnSpan=”2″ Grid.Row=”1″/>
and move it inside a Grid element, we add in the template in the same place.
The Grid has a TextBlock to show the message and Visibility is controlled by a Converter

Here is the relevant part of the Template

<sdk:DataGridColumnHeader x:Name=”TopLeftCornerHeader” Template=”{StaticResource TopLeftHeaderTemplate}” Width=”22″/>
<sdk:DataGridColumnHeadersPresenter x:Name=”ColumnHeadersPresenter” Grid.Column=”1″/>
<sdk:DataGridColumnHeader x:Name=”TopRightCornerHeader” Grid.Column=”2″ Template=”{StaticResource TopRightHeaderTemplate}”/>
<Rectangle x:Name=”ColumnHeadersAndRowsSeparator” Grid.ColumnSpan=”3″ Fill=”#FFC9CACA” Height=”1″ StrokeThickness=”1″ VerticalAlignment=”Bottom” Width=”Auto”/>
<!–<sdk:DataGridRowsPresenter x:Name=”RowsPresenter” Grid.ColumnSpan=”2″ Grid.Row=”1″/>–>
<Grid Grid.ColumnSpan=”2″ Grid.Row=”1″ >
<sdk:DataGridRowsPresenter x:Name=”RowsPresenter”/>
<TextBlock Text=”No Records found”
Visibility=”{Binding ElementName=RowsPresenter, Path=Children.Count, Converter={StaticResource noRecordsConverter}}”
HorizontalAlignment=”Center” VerticalAlignment=”Center”
FontSize=”12″/>
</Grid>

<Rectangle x:Name=”BottomRightCorner” Grid.Column=”2″ Fill=”#FFE9EEF4″ Grid.Row=”2″/>
<Rectangle x:Name=”BottomLeftCorner” Grid.ColumnSpan=”2″ Fill=”#FFE9EEF4″ Grid.Row=”2″/>
<ScrollBar x:Name=”VerticalScrollbar” Grid.Column=”2″ Margin=”0,-1,-1,-1″ Orientation=”Vertical” Grid.Row=”1″ Width=”18″/>

And the Converter

public class NoRecordsConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value.ToString() == “0” ? Visibility.Visible : Visibility.Collapsed;
}

public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return null;
}
}

Advertisements

17 thoughts on “DataGrid and “No Records Found” message

  1. Thank you for this! I’ve searched the Net and could not find anything quite like this approach and I like it. Unfortunately, the binding does not update when Children.Count changes (because it’s an int variable, not a DependencyProperty or a property that is backed by INotifyPropertyChanged.)

    Perhaps there’s another way to achieve similar sophistication while maintaining bindings?

  2. I did some testing on this because it’s the only solution to the problem without wild changes to a project.

    I made a DataGrid whose ItemSource is bound to an ObservableCollection. I found some very bizarre behavior that indicates there might be a bug in the SL4 framework. Starting from scratch, the Children.Count binding is invoked (and the Value Convertor is thus used) on each ObservableCollection.Add(T) UNTIL you do .Clear(T) or .Remove(T) (or any variation thereof.) Once a Clear or Remove is performed, the binding for .Add is ONLY invoked when you add one more item than the highest number of items ever present in the collection.

    For example: (Starting from scratch) Add four items to the collection. On each add, the binding is invoked and all is well (confirmed with breakpoints and output window.) Take note of the fact you had four items in the collection. Now do a .Clear() and add four more items to the collection. You should be back at Children.Count = 4 items. The binding will NOT evaluate on each of these Add operations. Now perform one more Add (so you have 5 items in the Collection). The binding will evaluate on this new 5th item. It’s as if the ObservableCollection has a memory leak or is somehow choosing to remember the items previously in it even though it is the only element in .NET that has a reference to the objects that have been cleared.

    This same issue occurs with .Remove() instead of .Clear. As long as you take note of how many items you had before either a .Remove or .Clear, your binding will not evaluate unless you add one MORE than your last highest number of items in the collection.

    To tie this problem back to your post: the problem here is that the binding in your template is not evaluated on .Clear or .Remove. Upon either operation, the DataGrid IS updated to show the accurate list of items in the collection. This means that if you add items to the DataGrid, and then clear the DataGrid, the “No Records Displayed” message does not show up and you’re presented with a no-rows DataGrid with no message.

    Now that I’ve written this post, I’m going to check this problem with another IList type to see if this is a problem with ObservableCollection alone.

  3. Hi,
    If the “Count” Propertychanged notification is not raised, this wont work. there might be situations where this notification is not being raised and so it is not working.

  4. That’s what I said in my first post. And for that reason your example does not work for situations where the contents of a DataGrid changes in runtime. The only way to implement the objective in this example (to display a “No Records” message) is to either

    A) Bind a textblock OUTSIDE the DataGrid control TO DataGrid.DataContext.Count
    or
    B) Create a UserControl or CustomControl that allows you to do something similar to A.

    Option A works reliably, however it means you have to do it for each DataGrid in your application. I am currently exploring Option B (I know it should work theoretically.)

  5. I would think, If they are at the same level in the template, TextBlock will have the same datacontext as the Datagrid. unless there are setting in code

  6. Can you be more specific? When you say Template, are you referring to a Style in App.xaml targeting the DataGrid, creating a UserControl (as I mentioned), or some other approach?

    I don’t think it can be done in App.xaml as a Style because the TextBlock does not have visibility of the DataGrid’s DataContext. (Unless you’re talking about using RelativeSource or Visual Tree traversing… then maybe?)

  7. I was talking about the section … in the style of the DataGrid. To keep things simple usercontrol solution is not a bad idea also

  8. Then I ask again: How would you bind the TextBlock to DataContext.Count (which should resolve to ObservableCollection.Count)? I tried the following inside the Style in App.xaml:

    {Binding Path=Count…}
    {Binding ElementName=RowsPresenter, Path=DataContext.Count….}
    {Binding Path=DataContext.Count…}

    None of them work. 😦 How is the DataGridRowsPresenter picking up items in the DataContext? If I know the answer to that question, I may be able to mimic that strategy for the TextBlock…

  9. Also, the UserControl method works EXCEPT no other properties of the DataGrid are accessible from outside the control without manual coding. This means no fine tuning or tweaking the Grid without serious effort, which is a little overkill for this objective.

    It’s looking more and more like the only solution to this problem is to make a TextBlock that binds to List.Count on the DataGrid’s DataContext separately. There MUST be an easier way!

  10. you could look at the source code, which comes with the download. If they are adding rows in the code, you are out of luck

  11. I’ve determined that it is impossible to truly achieve a portable “No Rows” message solution in the DataGrid. I’ve spent a couple days trying different techniques to no avail. Various methods work “sort of”, including your article’s, but ultimately none of them bring the expected functionality. Your solution works when the control is first instantiated, but the binding is lost after Children.Count property changes for the first time in runtime thereby making it unreliable for most situations where your DataGrid is cleared and re-used many times over.

    The UserControl idea does not work because you lose the ability to customize the DataGrid unless you’re willing to completely clone the DataGrid in source code (and that’s just unrealistic unless you have many other features you want to add…)

    The CustomControl idea does not work because bindings to a parent control’s CLR property while inside of a ControlTemplate are not allowed (correct me if I’m wrong but you can’t reference the parent control’s class while inside a ControlTemplate and/or TemplateBinding.) Also, unlike WPF, Silverlight’s UIElementCollection definition is sealed so you can’t subclass it. Same goes for DataGridRowsPresenter. Neither object has Change Notification, so it would be desirable to sub-class them to add that functionality.

  12. Hi,
    You now have lot more experience on this issue 😉 . Looks like my solution will address the plain vanilla case not any fancy ones. Thanks for sharing your experience.

  13. I spoke too soon. I figured out a way to do it using a different twist on making a CustomControl. I would love to explain it but I should probably start my own blog so the tutorial can be explained better. Because it’s a CustomControl, it can easily be ported to any projects multiple times over without losing customization… and you can specify Content as opposed to just Strings too and whether or not you even WANT content to display at all!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s