WPF - DataGrid Column Header Alignment - wpf

I'm using the WPFToolkit DataGrid control and I wanted to restyle some of the column headers so that the header text is displayed vertically instead of horizontally (the data in the column is all numeric and therefore, not very wide, but the header text is long).
So I created a DataTemplate to and tried to get the DataGridColumn.HeaderTemplate to it. This is my template:
<DataTemplate x:Key="headerTemplate">
<StackPanel VerticalAlignment="Bottom" HorizontalAlignment="Left" Background="Aqua">
<StackPanel.LayoutTransform>
<RotateTransform Angle="-90"/>
</StackPanel.LayoutTransform>
<TextBlock Text="{Binding}" VerticalAlignment="Bottom" HorizontalAlignment="Left" Background="Pink">
</TextBlock>
</StackPanel>
</DataTemplate>
This works just fine, except the alignment of the header is always left and center. No combination of alignments for the StackPanel or the TextBlock seems to make any difference. I would like to have the text aligned at the bottom and middle. How can I make it do that?
Thanks,
AT

OK, found the answer.
The property I was looking for was VerticalContentAlignment.
I created a style and attached that using the HeaderStyle property, and it worked :)
<Style x:Key="VerticalGridHeaderStyle" TargetType="tk:DataGridColumnHeader">
<Setter Property="VerticalContentAlignment" Value="Bottom"/>
</Style>

If you don't want to mess up the style that you have already applied, use BasedOn:
<DataGrid.ColumnHeaderStyle>
<Style BasedOn="{StaticResource MetroDataGridColumnHeader}" TargetType="{x:Type DataGridColumnHeader}">
<Setter Property="HorizontalContentAlignment" Value="Center" />
</Style>
</DataGrid.ColumnHeaderStyle>

This also works
<Style TargetType="{x:Type DataGridColumnHeader}">
<Setter Property="HorizontalContentAlignment" Value="Center"/>
</Style>
Add the x:Key="" if you don't want to target all DataGridColumnHeader's

Related

TextBlock with vertical scrollbar only

I have a TextBlock which may contain a long text so I want to add a vertical scroll bar to it. My initial attempt was to wrap a ScrollViewer around it. That works but the problem is that when I zoom in, the width is zoomed also. I tried disabling the horizontal scroll bar like this:
<ScrollViewer IsTabStop="True" HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
But it didn't solve the problem. I also tried binding the width:
Width="{Binding ElementName=Scroller, Path=ViewportWidth}"
It didn't help either.
So, my question is, how can I add vertical scrollbar to it but have a fixed width and wrapped text for the TextBlock inside? Here's my full code:
<ScrollViewer Grid.Row="1" IsTabStop="True" VerticalScrollBarVisibility="Auto">
<TextBlock HorizontalAlignment="Center" VerticalAlignment="Top" TextWrapping="Wrap" TextAlignment="Center"/>
</ScrollViewer>
There are two parts to this answer... the first is to simply use a TextBox:
<TextBox ScrollViewer.VerticalScrollBarVisibility="Visible" Text="Something really
really really really really really really really really long"
Style="{StaticResource TextBlockStyle}" />
The second part is to simply Style the TextBox so that it looks like a TextBlock:
<Style x:Key="TextBlockStyle" TargetType="{x:Type TextBox}">
<Setter Property="Background" Value="{x:Null}" />
<Setter Property="BorderBrush" Value="{x:Null}" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="Padding" Value="0" />
<Setter Property="IsReadOnly" Value="True" />
<Setter Property="IsTabStop" Value="False" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="TextWrapping" Value="Wrap" />
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Background" Value="{x:Null}" />
</Trigger>
</Style.Triggers>
</Style>
Feel free to remove any of these properties if they do not suit your situation.
<TextBox HorizontalAlignment="Center"
VerticalAlignment="Top"
TextWrapping="Wrap"
TextAlignment="Center"
VerticalScrollBarVisibility="Auto" Width="300" Style="{StaticResource TextBlockStyle}"/>
You don't need a ScrollViewer wrapped in the TextBox, the TextBox control has its own ScrollViewer. And you need to define the width of the TextBox so that the scrollbar will know its fixed width and will wrap the text.
Then, you have to style the TextBox to look like a TextBlock
A good reason why this ScrollViewer won't work according to to Ifeanyi Echeruo from Microsoft, from MSDN
ScrollViewer first asks its content how large it would like to be in
the absence of constraints, if the content requires more space than
the Viewer has then its time to kick in some ScrollBars
In the absence of constraints TextBlock will always opt to return a
size where all text fits on a single line.
A ScrollViewer with ScrollBars will never get a TextBlock to wrap.
However you may be able to come up with a Measure\Arrange combination
for a panel of your own that is almost like ScrollViewer but I cant
think of any logic that can satify both constraints without explicit
knowlege of the behaviour of said children

ComboBoxItem Selection Area Issue

I have a comboBox with a ComboBox.ItemTemplate
<ComboBox>
<ComboxBox.ItemTemplate>
<DataTemplate DataType="{x:Type ViewModel}">
<TextBlock Text="1" />
</DataTemplate>
</ComboxBox.ItemTemplate>
</ComboBox>
The Item renders properly, but when I try to select the ComboxItem, the selection is only available on "1" rest of the Area in the ComboBoxItem is not clickable.
How do I set up the code so the selection can happen on the whole item and not just the Textblock.
Your ComboBox/ComboBoxItem seems to be templated, by default the highlight brush is a deeper blue. If you have access to the templates make sure the content of the ComboBoxItem stretches horizontally.
If the template is bound "correctly" setting the content-alignments may be enough.
<ComboBox.ItemContainerStyle>
<Style TargetType="{x:Type ComboBoxItem}">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="VerticalContentAlignment" Value="Stretch" />
</Style>
</ComboBox.ItemContainerStyle>

Styling a Textblock autogenerated in a ContentPresenter

As I saw, a lot of people ran into this exact problem but I can't understand why my case is not working and it is starting to drive me crazy.
Context: I have a DataGrid which is to be colored according to the values of each cell. Hence, I have a dynamic style resolving the actual template to be used for each cell. Backgrounds now work accordingly.
New problem: when I have a dark background, I want the font color to be white and the font weight to be bold so the text is correctly readable. And... I can't style it correctly.
I read some Stackoverflow posts about that:
This one fits my problem but doesn't provide me any working solution
This one is also clear and detail but... duh
This is almost the same problem as me but... Solution does not work
Here is what I tried so far:
<!-- Green template-->
<ControlTemplate x:Key="Green" TargetType="{x:Type tk:DataGridCell}">
<Grid Background="Green">
<ContentPresenter
HorizontalAlignment="Center"
VerticalAlignment="Center">
<ContentPresenter.Resources>
<Style BasedOn="{StaticResource BoldCellStyle}" TargetType="{x:Type TextBlock}" />
</ContentPresenter.Resources>
</ContentPresenter>
</Grid>
</ControlTemplate>
Does not work. Background is green, but text stays in black & not bold.
BTW, the BoldCellStyle is as easy as it can be:
<Style x:Key="BoldCellStyle" TargetType="{x:Type TextBlock}">
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="Foreground" Value="White" />
</Style>
Okay. Second try (which is a real stupid one but well...)
<!-- Green template -->
<ControlTemplate x:Key="Green" TargetType="{x:Type tk:DataGridCell}">
<Grid Background="Green">
<ContentPresenter
HorizontalAlignment="Center"
VerticalAlignment="Center">
<ContentPresenter.Resources>
<Style x:Key="BoldCellStyle" TargetType="{x:Type TextBlock}">
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="Foreground" Value="White" />
</Style>
</ContentPresenter.Resources>
</ContentPresenter>
</Grid>
</ControlTemplate>
Doesn't work either.
Then, I tried to play with the ContentPresenter's properties:
<!-- Green template -->
<ControlTemplate x:Key="Green" TargetType="{x:Type tk:DataGridCell}">
<Grid Background="Green">
<ContentPresenter TextElement.FontWeight="Bold" TextElement.Foreground="White" TextBlock.Foreground="White"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
</Grid>
</ControlTemplate>
And... As you can expect, this does not even work.
Intrigued, I used Snoop to browse all the components of my interface.
In the first two cases, Snoop actually shows me that each cell is a Grid with a ContentPresenter containing a TextBlock and the actual Style but... The TextBlock's properties do not apply and FontWeight is still normal.
Last case, even more shocking, I can see that snoop shows me that we actually have a ContentPresenter with the right properties (ie TextElement.FontWeight="Bold"), but the autogenerated TextBlock under is - still - not styled.
I can't get what am I missing here. I tried as you can see almost all I could possibly do here, and the TextBlocks keep being non-formatted.
Any idea here? Thanks again!
The DataGridColumns that derive from DataGridBoundColumn (all except DataGridTemplateColumn) has a property ElementStyle that is applied to the TextBlock when it is created. For e.g. DataGridTextColumn It looks like this
static DataGridTextColumn()
{
ElementStyleProperty.OverrideMetadata(typeof(DataGridTextColumn),
new FrameworkPropertyMetadata(DefaultElementStyle));
// ...
}
It overrides the metadata for ElementStyle and provides a new default value, DefaultElementStyle, which basically just sets the default margin for the TextBlock.
public static Style DefaultElementStyle
{
get
{
if (_defaultElementStyle == null)
{
Style style = new Style(typeof(TextBlock));
// Use the same margin used on the TextBox to provide space for the caret
style.Setters.Add(new Setter(TextBlock.MarginProperty, new Thickness(2.0, 0.0, 2.0, 0.0)));
style.Seal();
_defaultElementStyle = style;
}
return _defaultElementStyle;
}
}
This style is set in code everytime a new DataGridCell is created with element.Style = style; and this is overriding the Style you are trying to set, even if you try to set it implicitly.
As far as I know, you'll have to repeat this for your columns
<DataGridTextColumn Header="Column 1" ElementStyle="{StaticResource BoldCellStyle}" .../>
<DataGridTextColumn Header="Column 2" ElementStyle="{StaticResource BoldCellStyle}" .../>

Expression Blend, an ItemTemplate and an Implicit Style

I'm having an issue with Blend not rendering items in a DataTemplate styled implicity.
I've setup a basic project to replicate the issue. Below is the Xaml + ResourceDictionary for those with Eagle eyes to see what I'm doing wrong (if anything) and if you're really keen theres a link to the Zipped project below.
This is what renders when the application is run:
and this is what Blend presents:
<Color x:Key="TextColor1">#FF3631C4</Color>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="TextWrapping" Value="NoWrap"/>
<Setter Property="TextTrimming" Value="None"/>
<Setter Property="Foreground">
<Setter.Value>
<SolidColorBrush Color="{DynamicResource TextColor1}"/>
</Setter.Value>
</Setter>
</Style>
<Canvas x:Name="LayoutRoot" DataContext="{Binding Source={StaticResource SampleDataSource}}">
<TextBlock Text="Textblock" Canvas.Left="44.954" Canvas.Top="49.305" />
<TextBlock Text="Textblock 2" Canvas.Left="44.954" Canvas.Top="86.284" />
<ListBox ItemsSource="{Binding Collection}" Canvas.Left="134.016" Canvas.Top="29.026" Height="154.275" Width="384.575">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Property1}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Canvas>
Complete Example Project (65kb) - WpfApplication2.zip
The problem can of course be solved by explictly setting the style, but in my main project this will cause a bit of a headache.
I've seen some comments on other posts around that Blend may have issues but nothing concrete.
Any thoughts / suggestions?
Thanks!
Edit:
I discovered that if I give my style an Explicit Key, I can then create an Implicit Style based on the Explicit like so:
<Style x:Key="TextBlockStyle1" TargetType="{x:Type TextBlock}">
<Setter Property="TextWrapping" Value="NoWrap"/>
<Setter Property="TextTrimming" Value="None"/>
<Setter Property="Foreground">
<Setter.Value>
<SolidColorBrush Color="{DynamicResource TextColor1}"/>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type TextBlock}" BasedOn="{StaticResource TextBlockStyle1}" />
This then gives me the ability to add another Implicit Style as a Resource in my DataTemplate:
<DataTemplate>
<DataTemplate.Resources>
<Style TargetType="{x:Type TextBlock}" BasedOn="{StaticResource TextBlockStyle1}"></Style>
</DataTemplate.Resources>
<TextBlock Text="{Binding Property1}" />
</DataTemplate>
This will then give me the blendability I'll need in my main Project but still doesn't quite answer my original question.
Firstly Blend is written in WPF and XAML.
So Blend has its own application style and as your application also defines global styles, in order not to merge them it will be applying them differently and there is probably a bug in the method they used to apply those styles.
This is my guess why this is happening.
It doesn't solve the problem though, but might help you to find out other workarounds.

Problem getting custom content in a TabItem visible

I have a custom ItemsControl (WorKArea) that marks all items it has into a WorkSheet instance.
I have a style for the ItemsControl that uses a TabControl to show the content. Every sheet creates a tab. The style is:
<Style TargetType="{x:Type local:WorkArea}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:WorkArea}">
<TabControl ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:WorkArea}}, Path=Items}">
<TabControl.ItemContainerStyle>
<Style TargetType="TabItem">
<Setter Property="Header" Value="{Binding Title}" />
</Style>
</TabControl.ItemContainerStyle>
</TabControl>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
So far so good. The "sheets" show up, and the title is properly bound to the Header.
How can I get the tabs showing the content now? Whatever I try, nothing shows up at all for every WorkSheet - the content is always empty. Anyone the proper code?
Your work area should provide a ContentTemplate property and the TabControl should have a TemplateBinding which to it.

Resources