Change DataGrid column visibility (WPF, MVVM)

1.2k views Asked by At

I just recently started learning MVVM. I hope that a solution to this problem will come.

In my application, the user is authorized in the system, after which a window with a table opens. Users are divided into roles: Administrator and Employee. I want the Employee to be unable to see a certain column (ID).

I have an AuthorizationMeth class, where the IDRoleAuthorization variable stores role ID of the authorized user. How can I now use this value to hide the column ID? In my case if IDRoleAuthorization = 2 to hide column ID

Found solutions using the Freezable class and creating a FrameworkElement in XAML but I can't figure out how this is solved for my problem.

Methods/AuthorizationMeth.cs

public class AuthorizationMeth
    {
        public static int IDRoleAuthorization;
        public bool Enter(string login, string password)
        {
            Intis6Context db = new Intis6Context();
            if (login == "" || password == "")
            {
                MessageBox.Show("You have not completed all fields", "Authorization", MessageBoxButton.OK, MessageBoxImage.Error);
                return false;
            }
            var auth_check = db.Users.AsNoTracking().FirstOrDefault(ch => ch.Login == login && ch.Password == password);
            if (auth_check == null)
            {
                MessageBox.Show("Login or password entered incorrectly", "Authorization", MessageBoxButton.OK, MessageBoxImage.Error);
                return false;
            }
            IDRoleAuthorization = auth_check.IdRole;
            return true;
        }
    }

View/ContractView.xaml

        <DataGrid Background="White" AutoGenerateColumns="False" EnableColumnVirtualization="True" EnableRowVirtualization="True" 
                        ItemsSource="{Binding AllContrsupl_saleDTO, IsAsync=True}"
                        Grid.Row="0">
            <DataGrid.Columns>
                <DataGridTextColumn Header="ID" Binding="{Binding Path=Cnssid}"/>
                <DataGridTextColumn Header="Author" Binding="{Binding Path=FULLNAMEstaff}"/>
                <DataGridTextColumn Header="Type" Binding="{Binding Path=typeTable}"/>
2

There are 2 answers

2
Florian_Schaf On

Have you tried something like this?

If you want to Hide the "ID" column try this:

You need to assign a x:Name to your DataGrid control first. For example "myDataGrid" Then you can do this in Code behind.

if(IDRoleAuthorization == 2)
{
    myDataGrid.Columns[0].Visibility = Visibility.Collapsed;
} 

Hope this helps.

0
BionicCode On

Don't expose a public static int IDRoleAuthorization field. Such a field must be a property (never define public fields) and at least read-only, and a read-only instance property (non-static) at best. Additionally, don't expose the numeric value, but a bool property e.g., IsAuthorized. The logic to determine whether the numeric code evaluates to an authorized user must be encapsulated and not spread across the application. External classes must depend on the result of this evaluation only.

The DataGridColumn definitions are not part of the visual tree. They are not rendered. they are just placeholders that contain information about the column, which will be generated by the DataGrid later. The actual column consists of a DataGridColumnHeader and DataGridCell elements.

Because of this, you can't configure a Binding on the DataGridColumn.Visbility property.

You can now simply toggle the cells and their associated header (which will not remove the column, but the values of the header and cells):

<!-- ToggleButton to simulate the IsAuthorized property -->
<ToggleButton x:Name="ToggleButton" Content="Hide/show column content" />
<DataGrid>
  <DataGrid.Columns>
      <DataGridTextColumn Header="Dynamic Column">
        <DataGridTextColumn.CellStyle>
          <Style TargetType="DataGridCell">
            <Style.Triggers>
              <DataTrigger Binding="{Binding ElementName=ToggleButton, Path=IsChecked}" Value="True">
                <Setter Property="Visibility" Value="Collapsed" />
              </DataTrigger>
            </Style.Triggers>
          </Style>
        </DataGridTextColumn.CellStyle>

        <DataGridTextColumn.HeaderStyle>
          <Style TargetType="DataGridColumnHeader">
            <Style.Triggers>
              <DataTrigger Binding="{Binding ElementName=ToggleButton, Path=IsChecked}" Value="True">
                <Setter Property="Visibility" Value="Collapsed" />
              </DataTrigger>
            </Style.Triggers>
          </Style>
        </DataGridTextColumn.HeaderStyle>
      </DataGridTextColumn>
    <DataGridTextColumn Header="Static Column" />
  </DataGrid.Columns>
</DataGrid>

To remove the column completely in your scenario (no auto-generated columns), you must remove it manually from the DataGrid.Columns collection or toggle the column definitions Visibilty explicitly (by accessing the DataGrid.Columns).

Add and AuthorizationChanged event to your view model class and make the view listen to it. In the event handler set the columns visibility or remove/add the column. Alternatively, write a simple attached behavior:

DataGridHelper.cs

class DataGridHelper : DependencyObject
{
  public static string GetHidableColumnIndices(DependencyObject attachingElement) => (string)attachingElement.GetValue(HidableColumnIndicesProperty);
  public static void SetHidableColumnIndices(DependencyObject attachingElement, string value) => attachingElement.SetValue(HidableColumnIndicesProperty, value);

  public static readonly DependencyProperty HidableColumnIndicesProperty = DependencyProperty.RegisterAttached(
      "HidableColumnIndices",
      typeof(string),
      typeof(DataGridHelper),
      new PropertyMetadata(default(string), OnHidableColumnIndicesChanged));

  public static Visibility GetColumnVisibility(DependencyObject attachingElement) => (Visibility)attachingElement.GetValue(ColumnVisibilityProperty);
  public static void SetColumnVisibility(DependencyObject attachingElement, Visibility value) => attachingElement.SetValue(ColumnVisibilityProperty, value);

  public static readonly DependencyProperty ColumnVisibilityProperty = DependencyProperty.RegisterAttached(
    "ColumnVisibility", 
    typeof(Visibility), 
    typeof(DataGridHelper), 
    new PropertyMetadata(default(Visibility), OnColumnVisibilityChanged));

  private static void OnColumnVisibilityChanged(DependencyObject attachingElement, DependencyPropertyChangedEventArgs e)
  {
    if (attachingElement is not DataGrid dataGrid)
    {
      throw new ArgumentException("Attaching element must be of type DataGrid.");
    }
    ToggleColumnVisibility(dataGrid);
  }

  private static void OnHidableColumnIndicesChanged(DependencyObject attachingElement, DependencyPropertyChangedEventArgs e)
  {
    if (attachingElement is not DataGrid dataGrid)
    {
      throw new ArgumentException("Attaching element must be of type DataGrid.");
    }
    ToggleColumnVisibility(dataGrid);
  }

  private static void ToggleColumnVisibility(DataGrid dataGrid)
  {
    IEnumerable<int> columnIndices = GetHidableColumnIndices(dataGrid)
      .Split(new[] { ' ', ',' }, StringSplitOptions.RemoveEmptyEntries)
      .Select(numericChar => int.Parse(numericChar));
    foreach (int columnIndex in columnIndices)
    {
      dataGrid.Columns[columnIndex].Visibility = GetColumnVisibility(dataGrid);
    }
  }
}

Usage example

<DataGrid DatGridHelper.HidableColumnIndices="0,3,8"
          DataGridHelper.ColumnVisiblility="{Binding IsAuthenticated, Converter={StaticResource BooleanToVisibilityConverter}}" />