Recently a colleague of mine has been struggling to write generic code for assigning converters to control the background of a DataGridTextColumn. I pointed him to my April 24th 2014 blog entry entitled "NamedObject Error in Converters" which starts off by showing how to utilize a foreground and background converter in XAML for a DataGridTextColumn (and then goes on to break them).
He got that working but could not determine the code for implementing those same converters in code. While I don't normally feel the need to do something in code that can be done in XAML, I recognize that there are times when you don't know everything at design time and things have to be delayed to run time.
It really isn't too difficult to do this. You start by assigning an ElementStyle to the DataGridColumn, then creating a binding for the background and a multibinding for the foreground and binding them to the ElementStyle. The correlation between the code and the XAML it replaces is easy to see.
Here's the XAML with the style, bindings, and converters removed...
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication6"
Title="MainWindow" Height="350" Width="525"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Window.Resources>
<local:ForegroundConverter x:Key="ForegroundConverter"></local:ForegroundConverter>
<local:BackgroundConverter x:Key="BackgroundConverter"></local:BackgroundConverter>
</Window.Resources>
<Grid>
<DataGrid Name="DG" ItemsSource="{Binding Path=DT.DefaultView}" AutoGenerateColumns="False" CanUserAddRows="False">
<DataGrid.Columns>
<DataGridTextColumn x:Name="Description" Header="Description" Binding="{Binding Path=Description}" IsReadOnly="true"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>
The converters stay the same as you move from XAML to code...
Public Class ForegroundConverter
Implements IMultiValueConverter
Public Function Convert(values() As Object, targetType As System.Type, parameter As Object, culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IMultiValueConverter.Convert
Dim IsError As Boolean = values(0)
Dim IsWarning As Boolean = values(1)
If IsError Then
Return New SolidColorBrush(System.Windows.Media.Colors.Red)
End If
If IsWarning Then
Return New SolidColorBrush(System.Windows.Media.Colors.Blue)
End If
Return New SolidColorBrush(System.Windows.Media.Colors.Black)
End Function
Public Function ConvertBack(value As Object, targetTypes() As System.Type, parameter As Object, culture As System.Globalization.CultureInfo) As Object() Implements System.Windows.Data.IMultiValueConverter.ConvertBack
Throw New NotImplementedException()
End Function
End Class
Public Class BackgroundConverter
Implements IValueConverter
Public Function Convert(value As Object, targetType As System.Type, parameter As Object, culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IValueConverter.Convert
If CBool(value) Then
Return New SolidColorBrush(System.Windows.Media.Colors.Yellow)
Else
Return New SolidColorBrush(System.Windows.Media.Colors.White)
End If
End Function
Public Function ConvertBack(value As Object, targetType As System.Type, parameter As Object, culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IValueConverter.ConvertBack
Throw New NotImplementedException()
End Function
End Class
And lastly here is the code that replaces the XAML. Note that about eight lines of simple XAML has been replaced by 13 lines of difficult to read code. And for anyone who might point out that code is reusable, I would point out that styles make XAML just as reusable.
Notice I do not attempt to set the Source property of the bindings. The binding will use the ItemsSource property of the column's DataGrid which itself makes use of the DataContext of the window. If we were trying to control properties of UIElements such as a Label we would need to set the binding's Source property ie Binding.Source = DT.Rows(0).
Imports System.Data
Class MainWindow
Public Property DT As DataTable
Public Sub New()
' Create and populate the DataContext
DT = New DataTable
DT.Columns.Add(New DataColumn("Description", Type.GetType("System.String")))
DT.Columns.Add(New DataColumn("IsError", Type.GetType("System.Boolean")))
DT.Columns.Add(New DataColumn("IsWarning", Type.GetType("System.Boolean")))
DT.Columns.Add(New DataColumn("IsImportant", Type.GetType("System.Boolean")))
DT.Rows.Add("Error", True, False, True)
DT.Rows.Add("Warning", False, True, False)
DT.Rows.Add("Info", False, False, True)
InitializeComponent()
' Create ElementStyle
Dim s As New Style(GetType(TextBlock))
Description.ElementStyle = s
' Create background setter
Dim bBinding As New Binding()
Dim BackgroundConverter As Object = FindResource("BackgroundConverter")
bBinding.Path = New System.Windows.PropertyPath("IsImportant")
bBinding.Converter = BackgroundConverter
s.Setters.Add(New Setter(TextBlock.BackgroundProperty, bBinding))
' Create Foreground setter
Dim fBinding As New MultiBinding()
Dim ForegroundConverter As Object = FindResource("ForegroundConverter")
fBinding.Bindings.Add(New Binding("IsError"))
fBinding.Bindings.Add(New Binding("IsWarning"))
fBinding.Converter = ForegroundConverter
s.Setters.Add(New Setter(TextBlock.ForegroundProperty, fBinding))
End Sub
End Class
No comments:
Post a Comment