Thursday, February 25, 2016

Right-align textblock in datagrid

Let's suppose you have a datagrid with a column that you want right-aligned. Sounds easy right? Your code behind might look something like this...

Imports System.Collections.ObjectModel
Class MainWindow
    Public Property Amounts As New ObservableCollection(Of Decimal) From {0}
End Class

and your XAML might look like this...

<Window x:Class="MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="350" Width="525"
    DataContext="{Binding RelativeSource={RelativeSource Self}}">
    <DataGrid ItemsSource="{Binding Amounts}">
        <DataGrid.Columns>
            <DataGridTextColumn Header="Amounts" Binding="{Binding}">
                <DataGridTextColumn.ElementStyle>
                    <Style TargetType="TextBlock">
                        <Setter Property="HorizontalAlignment" Value="Right"></Setter>
                    </Style>
                </DataGridTextColumn.ElementStyle>
            </DataGridTextColumn>
        </DataGrid.Columns>
    </DataGrid>
</Window>

The results look like this, which is what we expected.


Now let's put a background on the text block by adding another setter to the element style.

<Setter Property="Background" Value="Yellow"></Setter>


Well that isn't right :-(
The problem is that the text block is only as wide as it needs to be. What if we explicitly set the width to 50px by adding yet another setter?

<Setter Property="Width" Value="50"></Setter>


Wow - so now our cell is yellow but the right-align is broken. If you look carefully you can see the text block is right aligned within the cell but the contents of the text block are left aligned. The HorizontalAlignment property controls how the text block is aligned, not how it's contents are aligned. This is consistent, but a little non-intuitive. But there isn't a HorizontalContentAlignment property on a text block :-(

It turns out there are two simple ways to fix this.
Option 1 is to set the background of the cell. If no background is set for the textblock it is transparent and the cell background bleeds through.

<Window x:Class="MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="350" Width="525"
    DataContext="{Binding RelativeSource={RelativeSource Self}}">
    <DataGrid ItemsSource="{Binding Amounts}">
        <DataGrid.Columns>
            <DataGridTextColumn Header="Amounts" Binding="{Binding}">
                <DataGridTextColumn.ElementStyle>
                    <Style TargetType="TextBlock">
                        <Setter Property="HorizontalAlignment" Value="Right"></Setter>
                    </Style>
                </DataGridTextColumn.ElementStyle>
                <DataGridTextColumn.CellStyle>
                    <Style TargetType="DataGridCell">
                        <Setter Property="Background" Value="Yellow"></Setter>
                    </Style>
                </DataGridTextColumn.CellStyle>
            </DataGridTextColumn>
        </DataGrid.Columns>
    </DataGrid>
</Window>

Option 2 is to set the horizontal alignment to Stretch and set the TextAlignment property to Right. So for text blocks the TextAlignment property replaces the HorizontalContentAlignment property.

<Window x:Class="MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="350" Width="525"
    DataContext="{Binding RelativeSource={RelativeSource Self}}">
    <DataGrid ItemsSource="{Binding Amounts}">
        <DataGrid.Columns>
            <DataGridTextColumn Header="Amounts" Binding="{Binding}">
                <DataGridTextColumn.ElementStyle>
                    <Style TargetType="TextBlock">
                        <Setter Property="HorizontalAlignment" Value="Stretch"></Setter>
                        <Setter Property="TextAlignment" Value="Right"></Setter>
                        <Setter Property="Background" Value="Yellow"></Setter>
                    </Style>
                </DataGridTextColumn.ElementStyle>
            </DataGridTextColumn>
        </DataGrid.Columns>
    </DataGrid>
</Window>



Wednesday, February 3, 2016

Adding x:Name to a user control causes a compilation error

I was developing a new user control and realized I wanted to bind to a dependency property. The XAML looks like this.

<UserControl x:Class="FileInputBox.FileInputBox"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <DockPanel>
        <Button x:Name="theButton" DockPanel.Dock="Right" Click="theButton_Click">Browse...</Button>
        <TextBox x:Name="theTextBox" MinWidth="{Binding ActualWidth, ElementName=theButton}" Margin="0,0,2,0"/>
    </DockPanel>
</UserControl>

I want to bind the text property of the TextBox to a dependency property of the UserControl called FileName. So I gave the UserControl a name and bound to it so the XAML looks like this.

<UserControl x:Class="FileInputBox.FileInputBox"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             x:Name="root">
    <DockPanel>
        <Button x:Name="theButton" DockPanel.Dock="Right" Click="theButton_Click">Browse...</Button>
        <TextBox x:Name="theTextBox" MinWidth="{Binding ActualWidth, ElementName=theButton}" Margin="0,0,2,0" Text="{Binding Path=FileName, ElementName=root}"/>
    </DockPanel>
</UserControl>

Now I get a compilation error...
The type name 'FileInputBox' does not exist in the type 'FileInputBox.FileInputBox'

It turns out the problem is that the Namespace and the Class names are the same. All I had to do is rename the Class. The problem occurs as the compiler tries to figure out exactly what is being called "root". Seems odd to me.