4

I've got a WPF DataGrid that I have been using for some time, and it works great. Unlike other posters here, I haven't had issues with the scrollbar or mouse wheel (yet). I have CTRLEND programmed to go to the end of the DataGrid, and then it tracks the most-recently added items. I can scroll up through the DataGrid contents with the up key.

However, I have really weird behavior with the down key! If I start from the top of my DataGrid and hold the down key, it scrolls for a little bit and then eventually bounces back and forth between two adjacent rows. If I pgdn, it will scroll down more, then jump back to the topmost of the previous two rows that it would jump between, then scroll down to the point that I pgdn'd to. If I page down some more, the down key will scroll to the end. If I go to the top of the DataGrid and start over, I get the exact same behavior, over and over again.

I've yet to find a post that addresses this, and I haven't seen anything in the DataGrid documentation that helps.

It's just a three-column DataGrid, where each column displays TextBlocks. Can anyone explain why just this one mode of scrolling is problematic? Here's the XAML:

<DataGrid ItemsSource="{Binding MainLog}" AutoGenerateColumns="False" 
     Name="log_datagrid" SelectedCellsChanged="log_datagrid_SelectedCellsChanged"   
     KeyUp="datagrid_KeyUp" LoadingRow="log_datagrid_LoadingRow">
    <DataGrid.Columns>
        <!-- timestamp -->
        <DataGridTemplateColumn Header="Timestamp">
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding Timestamp}" />
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
        </DataGridTemplateColumn>
        <!-- level -->
        <DataGridTemplateColumn Header="Level">
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding Level}" />
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
        </DataGridTemplateColumn>
        <!-- error message -->
        <DataGridTemplateColumn Header="Message">
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding Message}" />
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
        </DataGridTemplateColumn>
    </DataGrid.Columns>
</DataGrid>

By the way, this behavior occurs even with all of my code-behind for the event handlers commented out.

Here is the definition of the struct that my MainLog collection contains:

public struct MainLogData
{
    public string Timestamp { get; set; }
    public string Level { get; set; }
    public string Message { get; set; }
}
Dave
  • 14,618
  • 13
  • 91
  • 145
  • I get weird behaviors also when using wpf and grids, even to the point where visual studio crashes cause it runs out of memory, probably adding some more RAM will help you out – JohnnBlade Jul 03 '12 at 20:15
  • 3
    Adding more RAM is never the solution to getting out of memory exceptions. ;) Perhaps your datagrid cells are using objects that need to be `Dispose`d. – Dave Jul 04 '12 at 05:47
  • 1
    Does the class in your MainLog collection have a custom implementation of the Equals method? – a little sheep Jul 05 '12 at 00:37
  • @alittlesheep no, it does not. – Dave Jul 05 '12 at 14:52

1 Answers1

6

Ok... I reproduced the behavior with strings (simple list of strings bound to the data grid). The behavior started happening when I introduced duplicates strings in my list. It seems that the data grid gets confused between the "selected index" and the "selected value". The same kind of thing happens when I try to select a value (a string, in my test) that is present on another visible row: the selection gets screwed up: half of the time, the correct row is not selected.

Your problem is that you are using a "struct". The simple solution to your problem is to make your struct a class:

public class MainLogData 
{ 
    public string Timestamp { get; set; } 
    public string Level { get; set; } 
    public string Message { get; set; } 
}

Just changing the struct word to class should fix your problem.

You must understand that structs and classes are not the same, and structs determine their "equality" to another variable (with the same type) based on the values in them (2 variables of a specific structure type containing the same data will be considered equal). In the case of classes, unless specified otherwise, the equality is determined by its memory address; this ensures that by default no 2 instances of an object, even if they contain identical data, will not be considered equal because they do no reside at the same memory address (this behavior can be overwritten by overwriting "GetHashCode" and "Equals" methods in any class definition).

So in conclusion, the DataGrid has problems determining which item your are selecting (or moving too with your arrow key) because many objects in your list are considered "the same", or "equal". That is why it gets confused. Admittedly, I think this is a datagrid bug (or at least stranger behavior if it is by design), but changing your data type from a struct to a class should help you get back on track!

Cheers

JFTxJ
  • 542
  • 6
  • 17
  • Thanks for the tip -- I'll look into this and will let you know! – Dave Jul 05 '12 at 14:14
  • I updated my OP with the definition of the struct that I am trying to display. – Dave Jul 05 '12 at 14:50
  • 1
    +1. I suggest you to read [this question](http://stackoverflow.com/questions/3841602/why-is-valuetype-gethashcode-implemented-like-it-is) if you want to know how `ValueType.GetHashCode` works. In addition, a struct should be immutable (i.e. `private set`). See here for further explanations: [Why are mutable structs evil](http://stackoverflow.com/questions/441309/why-are-mutable-structs-evil) – Paolo Moretti Jul 10 '12 at 12:39
  • Thank you, I will look into all of these suggestions right now. – Dave Jul 11 '12 at 14:05
  • Interesting... it looks like there is a Converter that was added via code-behind, and that Converter's `Convert` method is failing because it can't cast the `value` parameter to `MainLogData`. It's interesting -- if `MainLogData` is defined as a struct, then the `value` object that gets passed to IValueConverter.Convert is of type `MainLogData`. If I declare it as a class, then `value` is of type `NewItemPlaceholder`. Anyhow, I'll read up on that later -- returning null when the cast fails seems to work and the log works great now! Thanks! – Dave Jul 11 '12 at 14:12