WPF Datagrid aligns numeric columns

I am trying to create a style for DataGrid cells in my application. I see that there may be a way to properly align the number cells in a DataGrid using a DataTrigger. Is it possible?

My style for my DataGrid:

<Style TargetType="{x:Type DataGrid}"> <Setter Property="AlternatingRowBackground"> <Setter.Value> <SolidColorBrush Color="#CCCCCC" /> </Setter.Value> </Setter> <Setter Property="CanUserAddRows" Value="False" /> <Setter Property="CanUserDeleteRows" Value="False" /> <Setter Property="Background"> <Setter.Value> <LinearGradientBrush StartPoint="0,0" EndPoint="1,1"> <GradientStop Offset="0" Color="#CCCCCC" /> <GradientStop Offset="1" Color="#FFFFFF" /> </LinearGradientBrush> </Setter.Value> </Setter> <Setter Property="RowHeaderWidth" Value="0" /> <Setter Property="CellStyle"> <Setter.Value> <Style TargetType="{x:Type DataGridCell}"> <Style.Triggers> <Trigger Property="IsSelected" Value="True"> <Setter Property="Background"> <Setter.Value> <SolidColorBrush Color="#7777FF" /> </Setter.Value> </Setter> </Trigger> </Style.Triggers> </Style> </Setter.Value> </Setter> </Style> 

I think that I can add some kind of trigger on CellStyle to determine if the content is numeric (int, double, decimal, ...) and fits the cell accordingly. Is it possible?

Update

Thinking about it, I tried a few things, but it did not work. I tried using a DataTrigger like:

 <DataTrigger Binding="{Binding Converter={StaticResource IsNumericConverter}}" Value="True"> <Setter Property="HorizontalContentAlignment" Value="Right" /> </DataTrigger> 

Where IsNumericConverter :

 public class IsNumericConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { return value is int || (value is decimal || value is double); } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { return null; } } 

But when I set a breakpoint on the converter, I get that the value is of the type of the whole row, and not every single cell ...

+4
source share
3 answers

One way to do this is to set up triggers in your code. The following method applies a trigger to a DataGridColumn. This trigger uses your IsNumericConverter to determine if it needs to be right aligned:

  public static void AddTriggerToColumnStyle(DataGridColumn column) { var boundColumn = column as DataGridBoundColumn; if (boundColumn != null && boundColumn.Binding is Binding) { string bindingPropertyPath = (boundColumn.Binding as Binding).Path.Path; column.CellStyle = new Style() { BasedOn = column.CellStyle, TargetType = typeof(DataGridCell), Triggers = { new DataTrigger { Binding = new Binding(bindingPropertyPath) { Converter = new IsNumericConverter() }, Value = true, Setters = { new Setter { Property = TextBlock.TextAlignmentProperty, Value = TextAlignment.Right } } } } }; } } 

This method should be safe to call after InitializeComponent() in the code constructor.

This approach will work if the column has a mixture of numeric and non-numeric data. If the columns contain only data of one type, then this approach will still work, but the created triggers will never shoot or stay for long.

Of course, the above method does not have to be repeated in code for each control that uses DataGrids. You can transfer it to the static class of the utility, and all your classes with code code will call this method. You can even add a static utility method that goes through the columns in the DataGrid that you pass in and calls the above method for each column.

UPDATE I modified the above method to allow it to determine the path of the binding property. This facilitates use. I also made the method public and static so that it clears it, and it can be ported to a static class.

I thought of a different approach that would go through all the columns in the DataGrid, check for bindings, infer the type of the associated property, and apply right alignment to the column. This will avoid creating triggers that never fire or never run all the time, and are probably suitable for the case when each column contains the same type of data only slightly better. However, this approach will still be related to the code, it will need to pass the type to which each line is bound, and will become more complicated if the binding property paths include more than one property name.

I am afraid that I do not see a solution to this problem that uses only Style and avoids code.

+1
source

I know that I was three years old, but I was able to do this without adding any code to the code word, and I decided that I would share that. using this article as a model: link

It uses multi-connectivity as a means around your original problem of passing a string to your converter.

add this to your datagrid:

 CellStyle="{StaticResource CellRightAlignStyle }" 

then add this style to your xaml, you should add it as a resource:

 <Window.Resources> <Style x:Key="CellRightAlignStyle" TargetType="{x:Type DataGridCell}"> <Setter Property="HorizontalAlignment"> <Setter.Value> <MultiBinding Converter="{converters:IsNumericConverter}" > <MultiBinding.Bindings> <Binding RelativeSource="{RelativeSource Self}"/> <Binding Path="Row" Mode="OneWay"/> </MultiBinding.Bindings> </MultiBinding> </Setter.Value> </Setter> </Style> </Window.Resources> 

Then converter:

  public object Convert( object[] values, Type targetType, object parameter, CultureInfo culture) { if (values[1] is DataRow) { //change the text alignment left or right. var cell = (DataGridCell)values[0]; var row = (DataRow)values[1]; var columnName = cell.Column.SortMemberPath; if (row[columnName] is int || (row[columnName] is decimal || row[columnName] is double)) return System.Windows.HorizontalAlignment.Right; } return System.Windows.HorizontalAlignment.Left; } public override object ConvertBack( object value, Type targetType, object parameter, CultureInfo culture) { return null; } 

that is, all of your cells containing numerical data should be right-aligned!

+1
source

I use a converter to create a fixed-length string and a monospaced font for viewing.

Converter:

 public class AlignRightConverter : IValueConverter // samples: _10 F2_14 C2_16 { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { try { if (parameter == null) throw new Exception("Format is missing"); var parameterStr = "" + parameter; if (!parameterStr.Contains('_')) throw new Exception("Uncnown format"); var parameterParts = parameterStr.Split('_'); var format = parameterParts[0]; var lengthStr = parameterParts[1]; var length = int.TryParse(lengthStr, out int l) ? l : 12; if (value == null) return new String(' ', length); var type = value.GetType(); String targetStr; if (type == typeof(Double) && format.Length > 0) targetStr = ((Double)value).ToString(format, CultureInfo.GetCultureInfo("ru-ru")); else if (type == typeof(Single) && format.Length > 0) targetStr = ((Single)value).ToString(format, CultureInfo.GetCultureInfo("ru-ru")); else if (type == typeof(Int32) && format.Length > 0) targetStr = ((Int32)value).ToString(format, CultureInfo.GetCultureInfo("ru-ru")); else targetStr = value.ToString(); if (targetStr.Length >= length) return targetStr; else return new String(' ', length - targetStr.Length) + targetStr; } catch (Exception exception) { exception.Log($"Failed convert to string value {value} with parameters {parameter}"); return "" + value; } } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } } 

Look:

 <DataGridTextColumn Header=" " Binding="{Binding Data.CostBuy.Value, Converter={StaticResource AlignRightConverter}, ConverterParameter=C2_16}" FontFamily="Courier New" /> 

Result:

Result

0
source

All Articles