Retrieve Visible Rows from XamGrid after Filtering and Bind it to ViewModel

XamGrid does not have the facility to get currently visible rows for binding. When users apply Filter on XamGrid through XamGrid column, only filtered rows are displayed. User wants to select only those records which are visible in XamGrid, and not all the records.

This could be done using VisualTreeHelper class in WPF, which helps you to traverse through the Visual Tree. The XamGrid uses RowsPanel for displaying rows in the XamGrid. The RowsPanel has a VisibleRows  property, that returns currently visible rows in ViewPort.

If we have to access this visible records in view model, we will use Behaviors in WPF as show below:

public class XamGridVisibleItemsBehavior : System.Windows.Interactivity.Behavior<XamGrid>

{
private XamGrid Grid
{
get { return AssociatedObject; } }

protected override void OnAttached()
{
     base.OnAttached();
     Grid.Filtered += Grid_Filtered;
}

public INotifyCollectionChanged FilteredItems
{
     get { return (INotifyCollectionChanged)GetValue(FilteredItemsProperty); }
     set { SetValue(FilteredItemsProperty, value); }
}

public static readonly DependencyProperty FilteredItemsProperty = DependencyProperty.Register(“FilteredItems”,typeof(INotifyCollectionChanged), typeof(XamGridSelectedItemsBehavior), null);

void Grid_Filtered(object sender, FilteredEventArgs e)
{
    if (Grid == null) return;
    try
    {
        Grid.Filtered -= Grid_Filtered;

        TransferFilteredRecordsToTarget(GetVisibleRowsInXamGrid(Grid), FilteredItems as IList);
    }
    finally
    {
        Grid.Filtered += Grid_Filtered;
    }
}

public static void TransferFilteredRecordsToTarget(IList source, IList target)
{
    if (source == null || target == null)
        return;

    target.Clear();

    foreach (RowBase r in source)
    {
        // make sure we only take data rows from the root band
        if (r.RowType == RowType.DataRow)
        {
            target.Add(r.Data);
        }
    }
}

private IList GetVisibleRowsInXamGrid(XamGrid xamGrid)
{
    var rowsPanel = FindVisualChild<RowsPanel>(xamGrid);
    if (rowsPanel != null)
    {
        var visibleRows = rowsPanel.VisibleRows;
        if (visibleRows != null)
        {
            return visibleRows;
        }
    }
    return new List<RowBase>();
}

/// <summary>
/// Returns child of specific type from Visual Tree using Visual Tree Helper
/// </summary>
public static T FindVisualChild<T>(DependencyObject parent) where T : DependencyObject
{
    for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++)
    {
        DependencyObject child = VisualTreeHelper.GetChild(parent, i);
        if (child != null && child is T)
            return (T)child;
        else
        {
            T childOfChild = FindVisualChild<T>(child);
            if (childOfChild != null)
                return childOfChild;
        }
    }
    return null;
}
}

In Xaml:

<ig:XamGrid RowHeight=”32″ AutoGenerateColumns=”False” x:Name=”xamGridFundTransactionDetails” Background=”{x:Null}” ItemsSource=”{Binding Entities}”>

<i:Interaction.Behaviors>
<behavior:XamGridSelectedItemsBehavior FilteredItems=”{Binding FilteredEntities,Mode=OneWay}” />
</i:Interaction.Behaviors>

</ig:XamGrid>

As you can see above, we have accessed the RowsPanel of XamGrid in behaviour using VisualTreeHelper class through method FindVisualChild. GetVisibleRowsInXamGrid() returns the VisibleRows from RowsPanel. TransferFilteredRecordsToTarget() method transfers the Model data attached to RowBase.Data property to the FilteredItemsList. 

FilteredItems needs to be bonded in View Model as show in Xaml.

Advertisements