0
votes

I am trying to achieve a simple hide/show rows functionality.

The user is shown a DataGrid with rows, on the left side there is a button "Hide", which hides the given row. There is also a button "Show hidden rows" which shall show all rows, with the hidden ones aswell. The hidden rows, should have a button on the left "Show row", to bring it back to the main DataGrid. After that, the user can again press "hide hidden rows".

All rows are being auto generated.

My approach:

The DataGrid's source is:

 private DataView _dataView;

        public DataView DataView
        {
            get { return _dataView; }
            set
            {
                _dataView = value;
                RaisePropertyChanged("DataView");
            }
        }

The XAML adding a "Hide" button:

<DataGrid.Columns>
                <DataGridTemplateColumn Header="Hide Row">
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <Button Content="{Binding Path=DataContext.HideRowButtonText, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=DataGrid}}"  CommandParameter="{Binding Path=SelectedIndex, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}" Command="{Binding Path=DataContext.HideRowCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=DataGrid}}" />
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>
</DataGrid.Columns>

My attempts:

  1. Manipulating the DataView
private void HideRow(object obj)
        {
            DataView.Table.Rows.RemoveAt(Convert.ToInt32(obj));
        }

The issue with this approach, which modifies the actual DataView, is that I want to be able to show all rows, once the user presses "Show hidden rows". I could create several DataViews, which will hold the hidden rows, current rows, and the rows that are being brought back. This seems not intuitive.

  1. Visibility Property on DataGridRow based on this.
<DataGrid.RowStyle>
                <Style TargetType="DataGridRow">
                    <Setter Property="Visibility" Value="{Binding Path=DataContext.RowVisibility, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=DataGrid}}" />
                </Style>
</DataGrid.RowStyle>

If I set the RowVisibilty to "Collapsed", it collapses all the rows, I was not able to hide only a certain row.

  1. I also know about DataTriggers, but it does not depend on the row data iself, but on the user. I also looked into Row Filters, but it seems those depend on the DataView being manipulated. I also looked into having a CheckBox, which will hide a certain row, using a Visibility Converter, but then again, not allowing me to manage hidden rows.

I am trying to find a way to hide x amount of rows, and be able to manage them. I want to be able to make them appear again, marked as hidden, so the user can make them visible again!

1

1 Answers

0
votes

The correct approach would be to register event handlers with the Button.Click event. This behavior is view logic and doesn't belong into the view model. The view model doesn't execute view related logic. It always executes model related logic.

An alternative solution would be to implement an Attached Behavior, which is an attached property with behavior.

MainWindow.xaml

<StackPanel>
  <Button Content="Show All Hidden Rows"  
          Click="ShowAllRows_OnButtonClicked" />

  <DataGrid x:Name="MyDataGrid">
    <DataGrid.Columns>
      <DataGridTemplateColumn Header="Hide Row">
        <DataGridTemplateColumn.CellTemplate>
          <DataTemplate>
            <Button Content="Hide Row"
                    Click="HideRow_OnButtonClicked" />
          </DataTemplate>
        </DataGridTemplateColumn.CellTemplate>
      </DataGridTemplateColumn>
    </DataGrid.Columns>    
  </DataGrid>
</StackPanel>

MainWindow.xaml.cs

public partial class MainWindow : Window
{
  private void HideRow_OnButtonClicked(object sender, RoutedEventArgs e)
  {
    // Use the extension method to traverse the visual tree to search for the parent row
    if ((sender as DependencyObject).TryFindVisualParentElement(out DataGridRow dataGridRow))
    {
      dataGridRow.Visibility = Visibility.Collapsed;
    }
  }

  private void ShowAllRows_OnButtonClicked(object sender, RoutedEventArgs e)
  {
    foreach (object rowData in this.MyDataGrid.Items)
    {
      (this.MyDataGrid.ItemContainerGenerator.ContainerFromItem(rowData) as FrameworkElement).Visibility =
        Visibility.Visible;
    }
  }
}

HelperExtensions.cs

public static class HelperExtensions
{
  public static bool TryFindVisualParentElement<TParent>(this DependencyObject child, out TParent resultElement)
    where TParent : DependencyObject
  {
    resultElement = null;

    DependencyObject parentElement = VisualTreeHelper.GetParent(child);

    if (parentElement is TParent parent)
    {
      resultElement = parent;
      return true;
    }

    return parentElement?.TryFindVisualParentElement(out resultElement) ?? false;
  }
}