0

In my code I have a DataGrid that I want to rotate for certain cases. Basically "rotating" just means switching the view mode of the DataGrid (displaying columns horizontally or vertically).

The idea is to have a boolean variable coming from an interface. Depending on that, I want to activate/deactivate the transformed view. I think I will need like a converter or something..?

I just copy-pasted this code from another stackoverflow-question. I played a bit with it but I cannot answer questions to. I know, that following values HAVE TO be changed when rotating or not:

  • RotateTransform Angle="-90"
  • MatrixTransform Matrix="-1 0 0 1 0 0"
  • RotateTransform Angle="-90"
  • ScaleTransform ScaleX="1" ScaleY="-1"
  • RotateTransform Angle="-90"
  • ScaleTransform ScaleX="1" ScaleY="-1"

This is my XAML:

<DataGrid ItemsSource="{Binding Path=CurrentTable.DefaultView,IsAsync=True}"
          AutoGenerateColumns="True" 
          AutoGeneratingColumn="DataGrid_AutoGeneratingColumn" SelectedItem="{Binding 
          SelectedItem}" IsReadOnly="False" 
          PreviewMouseRightButtonDown="DataGrid_PreviewMouseRightButtonDown" 
          PreviewMouseLeftButtonDown="DataGrid_PreviewMouseLeftButtonDown"  
          CellEditEnding="DataGrid_CellEditEnding" CanUserAddRows="False" 
          CanUserReorderColumns="False" 
          EnableColumnVirtualization="True" EnableRowVirtualization="True" 
          VirtualizingStackPanel.VirtualizationMode="Standard">

  <DataGrid.LayoutTransform>
    <TransformGroup>
      <RotateTransform Angle="90"/>
      <MatrixTransform Matrix="-1 0 0 1 0 0" />
    </TransformGroup>
  </DataGrid.LayoutTransform>
  <DataGrid.ColumnHeaderStyle>
    <Style TargetType="{x:Type DataGridColumnHeader}"
          BasedOn="{StaticResource {x:Type DataGridColumnHeader}}">
      <Setter Property="LayoutTransform">
        <Setter.Value>
          <TransformGroup>
            <RotateTransform Angle="-90"/>
            <ScaleTransform ScaleX="1" ScaleY="-1" />
          </TransformGroup>
        </Setter.Value>
      </Setter>
    </Style>
  </DataGrid.ColumnHeaderStyle>
  <DataGrid.CellStyle>
    <Style TargetType="DataGridCell">
      <Setter Property="LayoutTransform">
        <Setter.Value>
          <TransformGroup>
            <RotateTransform Angle="-90"/>
            <ScaleTransform ScaleX="1" ScaleY="-1" />
          </TransformGroup>
        </Setter.Value>
      </Setter>
    </Style>
  </DataGrid.CellStyle>
</DataGrid>

Could you help me? I really do not know, where to start and what to start with.


Edit 1: With big help of @thatguy I was able to rotate my DataGrid. I wonder how I can change the weird sizes of the cells after being rotated. An example of my code.

Before rotating:

the cells have normal size

After rotating:

the size of the cells is way too big

niconice
  • 15
  • 6
  • I am pretty sure you want a `public bool` in your code behind (of your `View` or `ViewModel`). You can then create `DataTriggers` in your view that will set the `DataGrid` style depending on the value of the `Property` you defined in your View's code behind or in your `ViewModel`. – CodeJunkie Aug 12 '22 at 06:50
  • @darko6977 would you like to provide me an example how you would implement a DataTrigger in this case? – niconice Aug 12 '22 at 07:31

1 Answers1

2

You do not need a converter, it can all be done in the style using triggers. Suppose you created a bool property named IsCertainCase in your view model which must implement the INotifyPropertyChanged interface to notify bindings to update their value, otherwise a changed value in the view model will not be reflected in the user interface.

public class MyViewModel : INotifyPropertyChanged
{
      private bool _isCertainCase;
   public bool IsCertainCase
   {
      get => _isCertainCase;
      set
      {
         if (_isCertainCase == value)
            return;
   
         _isCertainCase = value;
         OnPropertyChanged();
      }
   }

   public event PropertyChangedEventHandler PropertyChanged;
   
   protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
   {
      PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
   }

   // ...other code.
}

Then you can create a Style for the DataGrid. Note that it needs to extend the default style for DataGrid, otherwise its visual appearance or behavior might be different. That is what the BasedOn property is for.

A DataTrigger is used to bind the IsCertainCase property and check whether it is True or not.

Represents a trigger that applies property values or performs actions when the bound data meets a specified condition.

In case it is, the LayoutTransform is applied. The other styles work just the same, with one major difference. Their DataContext is different than the DataContext of the DataGrid. Consequently, a binding to IsCertainCase would fail. In this case we use a RelativeSource which refers to a parent element of a certain type specified with AncestorType, here DataGrid. As path, we specify its DataContext and on that the property IsCertainCase. Now we bind the same data as on the DataGrid style.

<DataGrid ItemsSource="{Binding Path=CurrentTable.DefaultView, IsAsync=True}" AutoGenerateColumns="True" AutoGeneratingColumn="DataGrid_AutoGeneratingColumn"   
          SelectedItem="{Binding SelectedItem}" IsReadOnly="False">
   <DataGrid.Style>
      <Style TargetType="{x:Type DataGrid}"
             BasedOn="{StaticResource {x:Type DataGrid}}">
         <Style.Triggers>
            <DataTrigger Binding="{Binding IsCertainCase}" Value="True">
               <Setter Property="LayoutTransform">
                  <Setter.Value>
                     <TransformGroup>
                        <RotateTransform Angle="90"/>
                        <MatrixTransform Matrix="-1 0 0 1 0 0" />
                     </TransformGroup>
                  </Setter.Value>
               </Setter>
            </DataTrigger>
         </Style.Triggers>
      </Style>
   </DataGrid.Style>
   <DataGrid.ColumnHeaderStyle>
      <Style TargetType="{x:Type DataGridColumnHeader}"
             BasedOn="{StaticResource {x:Type DataGridColumnHeader}}">
         <Style.Triggers>
            <DataTrigger Binding="{Binding DataContext.IsCertainCase, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}" Value="True">
               <Setter Property="LayoutTransform">
                  <Setter.Value>
                     <TransformGroup>
                        <RotateTransform Angle="-90"/>
                        <ScaleTransform ScaleX="1" ScaleY="-1" />
                     </TransformGroup>
                  </Setter.Value>
               </Setter>
            </DataTrigger>
         </Style.Triggers>
      </Style>
   </DataGrid.ColumnHeaderStyle>
   <DataGrid.CellStyle>
      <Style TargetType="DataGridCell"
             BasedOn="{StaticResource {x:Type DataGridCell}}">
         <Style.Triggers>
            <DataTrigger Binding="{Binding DataContext.IsCertainCase, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}" Value="True">
               <Setter Property="LayoutTransform">
                  <Setter.Value>
                     <TransformGroup>
                        <RotateTransform Angle="-90"/>
                        <ScaleTransform ScaleX="1" ScaleY="-1" />
                     </TransformGroup>
                  </Setter.Value>
               </Setter>
            </DataTrigger>
         </Style.Triggers>
      </Style>
   </DataGrid.CellStyle>
</DataGrid>

Update For Your Edit

The DataGrid is not entirely compatible with these transformations applied. Indeed you can do it and the columns and cells will be oriented correctly, but resizing still assumes that everything is oriented as before, meaning you the width and height are swapped and the column resize indicator will still show horizontally and behave this way, although visually everything is scaled vertically. Please be aware that there may be other side-effects of uninteded usage of the DataGrid.

The reason for the odd cell scaling is simply that the headers were sized to their content and on layout transformation the sizes are not recalculated. Now that width and height are swapped, the row height equals the former column width. The following attached property provides a workaround by forcing recalculation of sizes through resetting the Width on all columns.

public static class DataGridLayoutTransformProperties
{
   public static bool GetIsHeaderSizeUpdateRequired(DependencyObject obj)
   {
      return (bool)obj.GetValue(IsHeaderSizeUpdateRequiredProperty);
   }

   public static void SetIsHeaderSizeUpdateRequired(DependencyObject obj, bool value)
   {
      obj.SetValue(IsHeaderSizeUpdateRequiredProperty, value);
   }

   public static readonly DependencyProperty IsHeaderSizeUpdateRequiredProperty = DependencyProperty.RegisterAttached(
      "IsHeaderSizeUpdateRequired", typeof(bool), typeof(DataGridLayoutTransformProperties),
      new PropertyMetadata(false, IsHeaderSizeUpdateRequired));

   private static void IsHeaderSizeUpdateRequired(DependencyObject d, DependencyPropertyChangedEventArgs e)
   {
      var dataGrid = (DataGrid)d;
      var isHeaderSizeUpdateRequired = (bool)e.NewValue;
      if (isHeaderSizeUpdateRequired)
      {
         foreach (var dataGridColumn in dataGrid.Columns)
         {
            dataGridColumn.Width = 0;
            dataGridColumn.Width = DataGridLength.Auto;
         }
      }
   }
}

Bind the attached property IsHeaderSizeUpdateRequired on the DataGrid to IsCertainCase.

<DataGrid ... local:DataGridLayoutTransformProperties.IsHeaderSizeUpdateRequired="{Binding IsCertainCase}">
thatguy
  • 21,059
  • 6
  • 30
  • 40
  • wow! thank you. I have one question left: after rotating, the size of the cells are different and i cannot figure out why. do you also have an ideo there? i will edit the main question – niconice Aug 12 '22 at 09:06
  • @niconice You do a transformation, which means in your case, that the original width of the column becomes the height, that is why the height of the cells seems to be odd. Try to resize the rotated header horizontally and you will see that the header strangely scales vertically. What I want to say is that this way of putting the headers on the side has severe side-effects and may not work properly. – thatguy Aug 12 '22 at 09:51
  • i accept this fact. before adding a boolean to rotate, the output was compleately fine. so why the cells are now in a weird size is soomething i will not understand. this code works as expected (only difference, there is no boolean): https://stackoverflow.com/a/6322082/18091434 – niconice Aug 12 '22 at 10:02
  • @niconice I have created a workaround for this issue if you really want to do it this way. – thatguy Aug 12 '22 at 10:54