0
votes

I've got a WPF datagrid that has a dependency property. I'm using MVVM and the WPF datagrid is bound to an observable collection. The grid holds the results of a search. When the search button is clicked, I retrieve data from the database, populate the observable collection which then populates the grid. All good! On the last line of the 'search button click' method, I update the dependency property to true, which triggers the dependency property to fire. The code in the dependency property class will format the datagrid (things like setting borders on certain cells and also ensuring that repeating data is shown only once).

My problem started when one of the business requirements was to be able to sort by clicking the column headers. Using the default sorting of the wpf datagrid causes the grid to lose the formatting, so I created my own sorting handler and at the end of it I set e.handled = true to disable default sorting by the wpf datagrid control. Still all good!

The problem appears now. If I do a fresh search by clicking the search button, the results in the grid are displayed but are not getting formatted! I've put a breakpoint on the dependency code and found that the breakpoint doesn't get hit in this instance.

So my question is... does the code in the sorting (which I've included below) somehow break the binding of the dependency property with the datagrid? I'd like to draw your attention to the code where I'm clearing the collection and repopulating it again. Should I perhaps be implementing the sorting differently that might not give rise to this behaviour?

Code in my sorting handler.

void fg_Sorting(object sender, DataGridSortingEventArgs e) {
        System.Windows.Controls.DataGrid dg = sender as System.Windows.Controls.DataGrid;
        if (dg == null) {
            return;
        }
        DataGridMergeCellBehavior.SetIsMerged(dg, false);

        ObservableCollection<AssetPPM2GridEntity> source = dg.ItemsSource as ObservableCollection<AssetPPM2GridEntity>;

        List<AssetPPM2GridEntity> myList = source.OrderBy(x => x.Property).ToList();
        source.Clear();
        foreach (var item in myList) {
            source.Add(item);
        }
// This property below is the dependency property which formats the grid.
        DataGridMergeCellBehavior.SetIsMerged(dg, true);

        e.Handled = true;
    }

Please let me know if you need any more information. I'm just looking for possible reasons from your experience that might explain this behaviour and I can investigate further.

Here is the code of the dependency property.

public class DataGridMergeCellBehavior : DependencyObject
{
    private static bool inUse = false;

    public static readonly System.Windows.DependencyProperty IsMergedProperty = System.Windows.DependencyProperty.RegisterAttached("IsMerged", typeof(bool), typeof(DataGridMergeCellBehavior), new PropertyMetadata(false, IsMergedChanged));


    public static bool GetIsMerged(DataGrid grid) {
        return (bool)grid.GetValue(IsMergedProperty);
    }

    public static void SetIsMerged(DataGrid grid, bool value) {
        grid.SetValue(IsMergedProperty, value);

    }

    private static void dg_ScrollChanged(object sender, ScrollChangedEventArgs e) {
        if (e.VerticalChange != 0 && !inUse) {
            inUse = true;
            DataGrid dataGrid = sender as DataGrid;

            int visibleRows = 0;
            int firstVisibleRowIndex = 0;

            foreach (var Item in dataGrid.Items) {
                var Row = (DataGridRow)dataGrid.ItemContainerGenerator.ContainerFromItem(Item);
                if (Row != null && Row.IsVisible) {
                    if (visibleRows == 0) {
                        firstVisibleRowIndex = Convert.ToInt32(Math.Floor(e.VerticalOffset));
                    }
                    visibleRows++;
                }
            }

            MergeDataGrid(dataGrid, visibleRows, firstVisibleRowIndex);
            inUse = false;
        }
    }

    private static void IsMergedChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e) {
        DataGrid dg = obj as DataGrid;
        if (dg != null) {
            int rowCount = dg.Items.Count;
            if (rowCount > 100) {
                rowCount = 100; // Why process the entire grid when not more than 100 will be shown to the user. 
            }
            if (dg.Columns.Count > 0 && (bool)e.NewValue) {
                dg.UpdateLayout();
                MergeDataGrid(dg, rowCount, 0);
            }
            else {
                if (dg.Columns.Count == 0) {
                    dg.Loaded += AssociatedObjectLoaded;
                    //dg.LoadingRow += OnLoadingRow;
                    //dg.UnloadingRow += OnUnloadingRow;
                    //dg.MouseLeftButtonDown += AssociatedObjectMouseLeftButtonUp;
                }
                MergeDataGrid(dg, rowCount, 0);
            }                
        }
    }

    static void AssociatedObjectLoaded(object sender, RoutedEventArgs e) {
        DataGrid grid = sender as DataGrid;
        grid.RemoveHandler(ScrollViewer.ScrollChangedEvent, new ScrollChangedEventHandler(dg_ScrollChanged));
        grid.AddHandler(ScrollViewer.ScrollChangedEvent, new ScrollChangedEventHandler(dg_ScrollChanged));
    }

    private static void MergeDataGrid(DataGrid grid, int visibleRows, int firstVisibleRowIndex) {
        // Code which will format cells with borders and delete cell content if its the same.
    }

Here is my XAML for the datagrid.

<DataGrid TabIndex="8" x:Name="fg" Grid.Row="7" Grid.Column="0" Grid.ColumnSpan="4" Margin="5" RowHeaderWidth="0"  ContextMenu="{StaticResource menuContext}"
              ItemsSource="{Binding AssetPPM2GridEntityCollection}" ColumnHeaderStyle="{StaticResource ColumnContextMenus}" HeadersVisibility="{Binding HeaderVisibility}"
              SelectedIndex="{Binding GrdFgSelectedIndex}" AutoGenerateColumns="False" SelectionUnit="FullRow"  SelectionMode="Extended"
              EnableRowVirtualization="True" EnableColumnVirtualization="False" RowBackground="PapayaWhip"
              VirtualizingPanel.VirtualizationMode="Standard" CanUserReorderColumns="False" 
              CanUserAddRows="False" GridLinesVisibility="Vertical" CanUserSortColumns="True" CanUserResizeColumns="False"                 
              dp:DataGridMergeCellBehavior.IsMerged="{Binding Source={StaticResource assetPPM2ViewModel}, Path=IsMergeCellsChecked, UpdateSourceTrigger=PropertyChanged}"
              PreviewMouseWheel="fg_PreviewMouseWheel" >

The sort handler resides in the code behind of the view.

1
Less words and more code . what is this formatting , how do you apply it etc.eran otzap
There's too much code to post here. Maybe I'll try and replicate this problem in a poc and post here.Andrew N
what you need is to provide one example . not your entire code . it's impossible to answer your question. since it is impossible to understand it . It's like posting a question call "I Do TROLOLO on a WPF Datarid why is my TROLOLO getting lost when performing a search"eran otzap
@eran. I've posted code for the dependency property and xaml. Let me know if you need more info. Any ideas on possible reasons will be much appreciated.Andrew N
My guess is that this is caused by virtualization in the datagrid's panel the formatting is done only on cells in rows that exist and thous who are not yet realized don't get formatted . further more the container's of the visible ones might get destroyed and created again (like when sorting). and the formatting again does not apply . try setting msdn.microsoft.com/en-us/library/… to falseeran otzap

1 Answers

0
votes

Thanks everyone for your comments. I'm posting the answer if someone else should chance upon this or a similar problem.

The problem was being caused by this line of code.

// This property below is the dependency property which formats the grid.
    DataGridMergeCellBehavior.SetIsMerged(dg, true);

Setting the dependency property directly was causing the inconsistent behaviour. I still don't know the exact technical reason behind why though.

The solution was to get an instance of the ViewModel and then set the property of the ViewModel which was bound to the dependency property (to TRUE).