V tomto příspěvku ukáži jak definovat a nastavit sloupce v Silverlight DataGrid controlu. Od jednodušší definice sloupce se postupně dostaneme k definici sloupce s tooltipem.
Začneme definicí stránky s DataGrid controlem, který bude mít sloupce s String, int, bool? a DateTime hodnotami.
<UserControl x:Class="SilverlightSample.MainPage"
xmlns:my="clr-namespace:IMP.Windows.Controls"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:interactivity="clr-namespace:IMP.Windows.Interactivity"
xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"
mc:Ignorable="d" d:DesignHeight="600" d:DesignWidth="800">
<Grid x:Name="LayoutRoot" Background="WhiteSmoke" Margin="40">
<sdk:DataGrid x:Name="DataGrid" AutoGenerateColumns="False" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<sdk:DataGrid.Columns>
<sdk:DataGridTextColumn Binding="{Binding StrValue, Mode=TwoWay}" Header="String" Width="110" />
<sdk:DataGridTextColumn Binding="{Binding NumberValue, Mode=TwoWay}" Header="Číslo" Width="60" />
<sdk:DataGridCheckBoxColumn Binding="{Binding CheckValue, Mode=TwoWay}" Header="Check" Width="65" />
<sdk:DataGridTextColumn Binding="{Binding DateValue, Mode=TwoWay, StringFormat=\{0:d\}}" Header="Date" Width="80" />
</sdk:DataGrid.Columns>
</sdk:DataGrid>
</Grid>
</UserControl>
U sloupce Date jsme pomoci Binding parametru StringFormat určili formátování, aby byl zobrazen pouze datum bez času (tato funkce Binding výrazu je dostupná od Silverligt 4.0, ve WPF je od .NET 3.5 SP1). Aby byl datum ale správně česky formátován, musíme stránce nastavit xml Language vlastnost podle aktuální culture, provedeme to následovně v Application_Startup metodě třídy App.
private void Application_Startup(object sender, StartupEventArgs e)
{
var page = new MainPage();
page.Language = XmlLanguage.GetLanguage(System.Threading.Thread.CurrentThread.CurrentCulture.Name);
this.RootVisual = page;
}
Nyní se už vrhneme na vlastní úpravu sloupců datagridu. První věc co chceme upravit je, že text pole Číslo zarovnáme doprava, kód druhého sloupce tedy nahradíme touto definicí:
<sdk:DataGridTextColumn Binding="{Binding NumberValue, Mode=TwoWay}" Header="Číslo" Width="60">
<sdk:DataGridTextColumn.ElementStyle>
<Style TargetType="TextBlock">
<Setter Property="TextAlignment" Value="Right" />
</Style>
</sdk:DataGridTextColumn.ElementStyle>
<sdk:DataGridTextColumn.EditingElementStyle>
<Style TargetType="TextBox">
<Setter Property="TextAlignment" Value="Right" />
</Style>
</sdk:DataGridTextColumn.EditingElementStyle>
</sdk:DataGridTextColumn>
Zarovnání měníme vlastností TextAlignment prvku TextBlock a TextBoxu buňky, na jejich styly se dostaneme vlastnostmi ElementStyle a EditingElementStyle. (V případě needitačního datagridu můžeme nastavení EditingElementStyle vynechat.)
Pokud bychom potřebovali zarovnat jiný než textový sloupec (tj. kde není TextBlock a TextBox), můžeme to provést změnou vlastnosti HorizontalContentAlignment ve stylu buňky CellStyle, tím změníme umístění celého controlu v buňce. Předvedeme si to na třetím sloupci, který zobrazuje CheckBox.
<sdk:DataGridCheckBoxColumn Binding="{Binding CheckValue, Mode=TwoWay}" Header="Check" Width="65" >
<sdk:DataGridCheckBoxColumn.CellStyle>
<Style TargetType="sdk:DataGridCell">
<Setter Property="HorizontalContentAlignment" Value="Right"/>
</Style>
</sdk:DataGridCheckBoxColumn.CellStyle>
</sdk:DataGridCheckBoxColumn>
Dále doplníme sloupcům ToolTip. Pokud by nám stačil mít v tooltipu pouze statický text můžeme to provést takto:
<sdk:DataGridTextColumn Binding="{Binding StrValue, Mode=TwoWay}" Header="String" Width="110">
<sdk:DataGridTextColumn.ElementStyle>
<Style TargetType="TextBlock">
<Setter Property="ToolTipService.ToolTip" Value="ToolTip text" />
</Style>
</sdk:DataGridTextColumn.ElementStyle>
</sdk:DataGridTextColumn>
Pokud ale potřebujeme složitější ToolTip (např. s více elementy) nebo text tooltipu odvozený z dat zadaný pomoci binding výrazu např. {Binding StrValue}, nemůžeme v Silverlight tento postup nastavení pomoci Setter elementu použít (dostali by jsme runtime chybu při parsování XAML).
Napadají mě dva způsoby jak přidat tooltip ke sloupci datagridu. První možností, kterou v praxi ale asi nevyužijeme, protože je kód příliš dlouhý, je umístit tooltip k Grid elementu, který je součástí šablony CellStyle buňky DataGridu. Kód vypadá následovně, do sloupce DataGridTextColumn je vložen výchozí styl buňky datagridu s jedinou změnou, a to přidání definice tooltipu: ToolTipService.ToolTip="{Binding StrValue}" (na řádku 11). (Získání výchozího stylu bylo popsáno v prvním příspěvku této serie.)
<sdk:DataGridTextColumn Binding="{Binding StrValue, Mode=TwoWay}" Header="String" Width="110">
<sdk:DataGridTextColumn.CellStyle>
<Style TargetType="sdk:DataGridCell">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="VerticalContentAlignment" Value="Stretch"/>
<Setter Property="IsTabStop" Value="False"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="sdk:DataGridCell">
<Grid x:Name="Root" Background="{TemplateBinding Background}" ToolTipService.ToolTip="{Binding StrValue}">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CurrentStates">
<VisualState x:Name="Regular"/>
<VisualState x:Name="Current">
<Storyboard>
<DoubleAnimation Duration="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="FocusVisual"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="ValidationStates">
<VisualState x:Name="Valid"/>
<VisualState x:Name="Invalid">
<Storyboard>
<DoubleAnimation Duration="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="InvalidVisualElement"/>
<ColorAnimation Duration="0" To="#FFFFFFFF" Storyboard.TargetProperty="(Fill).Color" Storyboard.TargetName="FocusVisual"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Rectangle x:Name="FocusVisual" Fill="#66FFFFFF" HorizontalAlignment="Stretch" IsHitTestVisible="false" Opacity="0" Stroke="#FF6DBDD1" StrokeThickness="1" VerticalAlignment="Stretch"/>
<ContentPresenter ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
<Rectangle x:Name="InvalidVisualElement" HorizontalAlignment="Stretch" IsHitTestVisible="False" Opacity="0" Stroke="#FFDC000C" StrokeThickness="1" VerticalAlignment="Stretch"/>
<Rectangle x:Name="RightGridLine" Grid.Column="1" VerticalAlignment="Stretch" Width="1"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</sdk:DataGridTextColumn.CellStyle>
</sdk:DataGridTextColumn>
Protože toto je opravdu velmi nepraktické, doporučuji možnost druhou. To ale vyžaduje zaměnit DataGridTextColumn za DataGridTemplateColumn, ve kterém definujeme šablony pro prvek v needitačním (CellTemplate) módu a v editačním módu (CellEditingTemplate). V šabloně bude TextBlock resp. TextBox, ke kterému tooltip už lehce dodáme.
<sdk:DataGridTemplateColumn Header="String" Width="110">
<sdk:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Margin="4" VerticalAlignment="Center" Text="{Binding StrValue}"
ToolTipService.ToolTip="{Binding StrValue}"/>
</DataTemplate>
</sdk:DataGridTemplateColumn.CellTemplate>
<sdk:DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<TextBox Text="{Binding StrValue, Mode=TwoWay}" Background="Transparent" VerticalAlignment="Stretch"
ToolTipService.ToolTip="{Binding StrValue}"/>
</DataTemplate>
</sdk:DataGridTemplateColumn.CellEditingTemplate>
</sdk:DataGridTemplateColumn>
Nevýhoda je v tom, že záměnou na DataGridTemplateColumn se změní i vzhled samotné buňky. Protože my ale chceme, aby buňka sloupce vypadala stejně jako v původním případě použití typu DataGridTextColumn, nastavíme v šabloně u TextBlocku Margin="4" a VerticalAlignment="Center" (tak je totiž nastaven textblock, který je vytvářen v buňce typu DataGridTextColumn).
Ještě si nadefinujeme složitější tooltip (s definicí obsahu v panelu) např. u sloupce Check. Zde DataGridCheckBoxColumn nahradíme DataGridTemplateColumn sloupcem s CheckBox controlem v šabloně. Aby byl tooltip zobrazen na celé buňce a ne jenom na vlastním checkboxu uprostřed, umístíme CheckBox a ToolTip do panelu Grid.
<sdk:DataGridTemplateColumn Header="Check" Width="65">
<sdk:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Grid Background="Transparent">
<CheckBox IsChecked="{Binding CheckValue}" IsThreeState="True" IsEnabled="False" IsHitTestVisible="False" VerticalAlignment="Center" HorizontalAlignment="Center"/>
<ToolTipService.ToolTip>
<ToolTip>
<StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Check:" FontWeight="Bold" Margin="0,0,4,0" />
<TextBlock Text="{Binding CheckValue}" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Number:" FontWeight="Bold" Margin="0,0,4,0" />
<TextBlock Text="{Binding NumberValue, StringFormat=\{0:N2\}}" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Date:" FontWeight="Bold" Margin="0,0,4,0" />
<TextBlock Text="{Binding DateValue, StringFormat=\{0:d\}}" />
</StackPanel>
</StackPanel>
</ToolTip>
</ToolTipService.ToolTip>
</Grid>
</DataTemplate>
</sdk:DataGridTemplateColumn.CellTemplate>
<sdk:DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<Grid Background="Transparent">
<CheckBox IsChecked="{Binding CheckValue, Mode=TwoWay}" IsThreeState="True" IsEnabled="True" VerticalAlignment="Center" HorizontalAlignment="Center"/>
<ToolTipService.ToolTip>
<ToolTip>
<StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Check:" FontWeight="Bold" Margin="0,0,4,0" />
<TextBlock Text="{Binding CheckValue}" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Number:" FontWeight="Bold" Margin="0,0,4,0" />
<TextBlock Text="{Binding NumberValue, StringFormat=\{0:N2\}}" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Date:" FontWeight="Bold" Margin="0,0,4,0" />
<TextBlock Text="{Binding DateValue, StringFormat=\{0:d\}}" />
</StackPanel>
</StackPanel>
</ToolTip>
</ToolTipService.ToolTip>
</Grid>
</DataTemplate>
</sdk:DataGridTemplateColumn.CellEditingTemplate>
</sdk:DataGridTemplateColumn>
Tooltip obsahuje také zobrazení naší DateTime hodnoty, pokud ale koukneme na tooltip, není zde datum správně formátován. To je dáno tím, že u tooltipu není nastavena vlastnost Language, a ta co je nastavena v RootVisual aplikace se nepřebírá.
Abychom toto opravili, napíšeme Behavior třídu, kterou k tooltipu připojíme:
/// <summary>
/// Set UI Language property to element
/// </summary>
public class SetLanguageBehavior : Behavior<FrameworkElement>
{
#region action methods
/// <summary>
/// Overrides OnAttached
/// </summary>
protected override void OnAttached()
{
base.OnAttached();
this.AssociatedObject.Language = XmlLanguage.GetLanguage(System.Threading.Thread.CurrentThread.CurrentCulture.Name);
}
#endregion
}
<ToolTip>
<StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Check:" FontWeight="Bold" Margin="0,0,4,0" />
<TextBlock Text="{Binding CheckValue}" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Number:" FontWeight="Bold" Margin="0,0,4,0" />
<TextBlock Text="{Binding NumberValue, StringFormat=\{0:N2\}}" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Date:" FontWeight="Bold" Margin="0,0,4,0" />
<TextBlock Text="{Binding DateValue, StringFormat=\{0:d\}}" />
</StackPanel>
<i:Interaction.Behaviors>
<interactivity:SetLanguageBehavior/>
</i:Interaction.Behaviors>
</StackPanel>
</ToolTip>
Příště se podíváme na opravu a rozšíření některých standartních Silverlight controlů.