Adding columns in WPF but only for certain rows [duplicate] - wpf

This question already has answers here:
Grid inside Grid in XAML
(4 answers)
Closed 5 months ago.
I'm working on understanding the Grid control in WPF.
I have a really basic setup here:
What I would like is, instead of having three rows and three columns that divide up each row, I'd like to have the columns only dividing up the last row, so that the top two rows have no columns, and only the last (third) row has columns.

You can change the template of the DataGridRow for the rows that you want to make them look different..
Assume you've set the ItemsSource like this..
var allItems = new ObservableCollection<DataItem>();
// ..
// fill the collection with data
// ..
DataGridName.ItemsSource = allItems;
First, Add new property to DataItem class
public bool LooksDifferent { get; set; }
Next, In xaml, we will change the DataGridRow template when LooksDifferent is true..
<DataGrid ItemsSource="{Binding Items}">
<DataGrid.ItemContainerStyle>
<Style TargetType="{x:Type DataGridRow}">
<Style.Triggers>
<DataTrigger Binding="{Binding LooksDifferent}" Value="True">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type DataGridRow}">
<Border
Background="White"
BorderBrush="Black"
BorderThickness="0,0,1,1">
<TextBlock Text="I AM DIFFERENT" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.ItemContainerStyle>
<DataGrid.Columns>
<DataGridTextColumn
Width="*"
Binding="{Binding Property1}"
Header="Header1" />
<DataGridTextColumn
Width="*"
Binding="{Binding Property2}"
Header="Header2" />
</DataGrid.Columns>
</DataGrid>
Now, You are all set! Give it a try..

Related

Set the cell as ReadOnly in Datagrid in code-behind - Cell has different value [duplicate]

I understand you can make the whole DataGrid or a whole column readyonly (IsReadOnly = true). However, at cell level this property is ready only. But I do need this level of granularity. There is blog about adding IsReadOnly to a row by changing the source code in old days when DataGrid was public domain, but now I don't have source code for DataGrid. What's workaround?
Making cell disabled (IsEnabled=false) almost meets my need. But the problem is that you can't even click the disabled cell to select the row (I have full row selection mode).
EDIT: Since nobody has responded to this question, so I guess it's not an easy fix. Here is a possible workaround: Make the cell uneditable. The only problem is that clicking the cell doesn't select the row. I just noticed that MouseDown or MouseUp event of the DataGrid is still fired when the disabled cell is clicked. In this event handler, if I could figure out the row it clicked, I could select the row programmatically. However, I couldn't figure out how to find the underlying row from DataGrid.InputHitTest. Can somebody please give me some tip?
After much searching and experimentation using IsTabStop = False and Focusable = False works best for me.
<DataGridTextColumn Header="My Column" Binding="{Binding Path=MyColumnValue}">
<DataGridTextColumn.CellStyle>
<Style TargetType="DataGridCell">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=ReadOnly}" Value="True">
<Setter Property="IsTabStop" Value="False"></Setter>
<Setter Property="Focusable" Value="False"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</DataGridTextColumn.CellStyle>
</DataGridTextColumn>
I've encountered the same problem, the cell should be read-only in some rows but not in the others. Here is a workaround solution:
The idea is to dynamically switch the CellEditingTemplate between two templates, one is the same as the one in the CellTemplate, the other is for editing. This makes the edit mode acts exactly the same as the non-editing cell although it is in edit mode.
The following is some sample code for doing this, notice that this approach requires DataGridTemplateColumn:
First, define two templates for read-only and editing cells:
<DataGrid>
<DataGrid.Resources>
<!-- the non-editing cell -->
<DataTemplate x:Key="ReadonlyCellTemplate">
<TextBlock Text="{Binding MyCellValue}" />
</DataTemplate>
<!-- the editing cell -->
<DataTemplate x:Key="EditableCellTemplate">
<TextBox Text="{Binding MyCellValue}" />
</DataTemplate>
</DataGrid.Resources>
</DataGrid>
Then define a data template with additional ContentPresenter layer and use Trigger to switch the ContentTemplate of the ContentPresenter, so the above two templates can be switched dynamically by the IsEditable binding:
<DataGridTemplateColumn CellTemplate="{StaticResource ReadonlyCellTemplate}">
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<!-- the additional layer of content presenter -->
<ContentPresenter x:Name="Presenter" Content="{Binding}" ContentTemplate="{StaticResource ReadonlyCellTemplate}" />
<DataTemplate.Triggers>
<!-- dynamically switch the content template by IsEditable binding -->
<DataTrigger Binding="{Binding IsEditable}" Value="True">
<Setter TargetName="Presenter" Property="ContentTemplate" Value="{StaticResource EditableCellTemplate}" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
HTH
There is a property on DataGridCell.IsReadOnly that you might think you can bind to,
e.g. using XAML like this:
<!-- Won't work -->
<DataGrid Name="myDataGrid" ItemsSource="{Binding MyItems}">
<DataGrid.Resources>
<Style TargetType="DataGridCell">
<Setter Property="IsReadOnly" Value="{Binding MyIsReadOnly}" />
</Style>
</DataGrid.Resources>
<!-- Column definitions... -->
</DataGrid>
Unfortunantly this won't work because this property is not writable.
Next you might attempt to intercept and stop mouse events, but this won't prevent the user from entering edit mode using the F2 key.
The way I sloved this was by listening for the PreviewExecutedEvent on the DataGrid and then conditionally flagging it as handled.
E.g. by adding code similar to this to the constructor of my Window or UserControl (or another more suitable place):
myDataGrid.AddHandler(CommandManager.PreviewExecutedEvent,
(ExecutedRoutedEventHandler)((sender, args) =>
{
if (args.Command == DataGrid.BeginEditCommand)
{
DataGrid dataGrid = (DataGrid) sender;
DependencyObject focusScope = FocusManager.GetFocusScope(dataGrid);
FrameworkElement focusedElement = (FrameworkElement) FocusManager.GetFocusedElement(focusScope);
MyRowItemModel model = (MyRowItemModel) focusedElement.DataContext;
if (model.MyIsReadOnly)
{
args.Handled = true;
}
}
}));
By doing it like this the cells are still focusable and selectable.
But the user will not be able to enter edit mode unless your model items allow it for the given row.
And you will not suffer the performance costs or complexities by using the DataGridTemplateColumn.
I've solved this problem in my application by setting the underlying object in the cell (eg. CheckBox) - IsHitTestVisible = false; Focusable = false;
var cb = this.dataGrid.Columns[1].GetCellContent(row) as CheckBox;
cb.IsHitTestVisible = false;
cb.Focusable = false;
"row" is a DataGridRow. IsHitTestVisible=false means that you can't click/select/manipulate the underlying object via mouse, but you can still select the DataGridCell. Focusable=false means that you can't select/manipulate the underlying object with the keyboard. This gives the illusion of a ReadOnly cell, but you can still select the cell and I'm sure if the DataGrid is set up to SelectionMode=FullRow then clicking the "read only" cell will select the entire row.
My solution is to use binding to the DataGridTemplateColumn with converter.
<UserControl.Resources>
<c:isReadOnlyConverter x:Key="isRead"/>
</UserControl.Resources>
<DataGridTemplateColumn x:Name="exampleTemplate" Header="example:" Width="120" IsReadOnly="True">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox x:Name="exampleCheckBox" VerticalAlignment="Center" IsEnabled="{Binding ElementName=exmpleTemplate, Path=IsReadOnly, Converter={StaticResource isRead}}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
and the converter:
class isReadOnlyConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
try
{
return !(bool)value;
}
catch (Exception)
{
return false;
}
}
This is a bit late but, I was looking into this as well, these solutions work well but I needed something a little different, I did the following and it works exactly like I wanted and what the question is looking for.
I essentially I wanted to be able to enter edit mode for the cell and have all that other templates and command logic the same while not being able to edit the cell.
The solution for all this is to set the TextBox.IsReadOnly property to true in the DataGridCell Style and to handle the initial keydown event
<Style TargetType="DataGridCell">
<Setter Property="TextBox.IsReadOnly" Value="True"/>
<EventSetter Event="PreviewKeyDown" Handler="cell_PreviewKeyDown"/>
</Style>
and the following code behind to stop the initial edit
protected void cell_PreviewKeyDown(object sender, KeyEventArgs e)
{
DataGridCell cell = sender as DataGridCell;
if (cell.IsEditing == false &&
((Keyboard.Modifiers & ModifierKeys.Control) != ModifierKeys.Control)) //So that Ctrl+C keeps working
{
cell.IsEditing = true;
e.Handled = true;
}
}
Hopefully this is helpful.
Based on #sohum comment, here you can use simplified version of the response marked as answer.
dataGrid.BeginningEdit += DataGrid_BeginningEdit;
(...)
private static void DataGrid_BeginningEdit(object sender, DataGridBeginningEditEventArgs e)
{
//Actual content of the DataGridCell
FrameworkElement content = e.Column.GetCellContent(e.Row);
MyObject myObject = (MyObject)content.DataContext;
if (!myObject.CanEdit)
{
e.Cancel = true;
}
}
You can use it later as Attached Property Behaviour.
One way of getting selectable, read-only text cells for DataGrid is to use template and style like this:
<DataGrid>
<DataGrid.CellStyle>
<Style TargetType="{x:Type DataGridCell}">
<Setter Property="BorderThickness" Value="0" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type DataGridCell}">
<Border Padding="0" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="True">
<ContentPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
<TextBox BorderThickness="0" MouseDoubleClick="DataGrid_TextBox_MouseDoubleClick" IsReadOnly="True" Padding="5" Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Content.Text}"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</DataGrid.CellStyle>
And for CS backend:
private void DataGrid_TextBox_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
(sender as TextBox).SelectAll();
}
You can do this with a simpler data template.
<DataGrid.Resources>
<DataTemplate x:Key="MyTemplate" DataType="MyRowDataType">
<TextBox Text="{Binding Value}" IsReadOnly="{Binding IsReadOnly}" />
</DataTemplate>
</DataGrid.Resources>
...
<DataGridTemplateColumn CellTemplate="{StaticResource MyTemplate}" />
For me, the most simple solution was to style the TextBox inside the EditingElementStyle.
<DataGridTextColumn Binding="{Binding MyValueProperty}">
<DataGridTextColumn.EditingElementStyle>
<Style TargetType="{x:Type TextBox}">
<Style.Triggers>
<DataTrigger Binding="{Binding MyReadonlyProperty}" Value="True">
<Setter Property="IsReadOnly" Value="True"/>
</DataTrigger>
</Style.Triggers>
</Style>
</DataGridTextColumn.EditingElementStyle>
</DataGridTextColumn>
In my case I was using DataGridTextColumn. I set the IsEnabled property on ContentPresenter in Style as follows and it works fine
<DataGrid ItemsSource="{Binding Items}" AutoGenerateColumns="False">
<DataGrid.Resources>
<Style TargetType="{x:Type DataGridCell}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type DataGridCell}">
<Grid Background="{TemplateBinding Background}" >
<ContentPresenter IsEnabled="{Binding Path=IsEditable}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</DataGrid.Resources>
<DataGridTextColumn Header="A"
Binding="{Binding Path=A}"/>
</DataGrid>

wpf DataGrid change wrapping on cells

I have a WPF DataGrid with AutoGenerate Columns. I have been able to override the column headers using code and also force wrapping on the column headers when I shrink the columns. When I try to force text wrapping on the cells my binding breaks... it shows the same value in every column.
Here is the XAML I am using to format
<DataGrid.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock TextWrapping="Wrap" Text="{Binding}"></TextBlock>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</DataGrid.CellStyle>
<DataGrid.ColumnHeaderStyle>
<Style TargetType="DataGridColumnHeader">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock TextWrapping="Wrap" Text="{Binding}"></TextBlock>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</DataGrid.ColumnHeaderStyle>
Again, the ColumnHeaderStyle works fine, but the CellStyle is not working.
Suggestions?
Update:
Column headers are set as follows:
if (e.Column.Header.ToString() == "Product_Description")
e.Column.Header = "Product";
if (e.Column.Header.ToString() == "Original_Gross_Weight")
e.Column.Header = "Orig. Net Wt.";
The wrapping of the headers works well. Just the wrapping of the content does not work.
On the binding it seems that once one replaces the DataGridCell data style, the full object for for the row is placed into the content presenter instead of the current property of the column.
It appears you override AutoGeneratingColumn so why not simply turn of auto generate and define the columns by hand?
Here is a working version where the text is wrapped for the data:
<Window.Resources>
<model:People x:Key="People">
<model:Person First="Joe" Last="Smith" Phone="303-555 5555" />
<model:Person First="Mary" Last="Johnson" Phone="720-555 5555" />
<model:Person First="Frank" Last="Wright" Phone="202-555 5555" />
</model:People>
</Window.Resources>
<DataGrid AutoGenerateColumns="False"
ItemsSource="{StaticResource People}">
<DataGrid.Columns>
<DataGridTextColumn Header="First" Binding="{Binding First}" />
<DataGridTextColumn Header="The Name" Binding="{Binding Last}" />
<DataGridTextColumn Header="Phone Number" Binding="{Binding Phone}">
<DataGridTextColumn.ElementStyle>
<Style TargetType="TextBlock">
<Setter Property="TextWrapping"
Value="Wrap" />
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
Result

Show "No record found" message on a WPF DataGrid when it's empty

If there is no record available, I want to add a TextBlock on data grid, below the header, showing the message "No Record Found."
Consider the attached image for reference.
Its been a long time since the question has been posted. But I thought this might be useful to someone else.
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
</Window.Resources>
<DataGrid Name="dgProjects" ItemsSource="{Binding Projects}" AutoGenerateColumns="True" />
<TextBlock Text="Employee has no projects" Visibility="{Binding Items.IsEmpty, Converter={StaticResource BooleanToVisibilityConverter}, ElementName=dgProjects}" />
For simplicity purpose I have set AutoGenerateColumns="True". Please define the columns. This way when a empty datasource is bound, the column names will be shown along with 'Empty row' message.
Finally I am able to findout the way.
When the grid in empty, add a default row on grid
Create a RowDetailTemplate which contain a text block with a message "No Record Found"
<DataGrid.RowDetailsTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="No Record Found" Width="400"></TextBlock>
</StackPanel>
</DataTemplate>
</DataGrid.RowDetailsTemplate>
Set the style on datagrid
<DataGrid.Style>
<Style TargetType="DataGrid">
<Setter Property="RowDetailsVisibilityMode" Value="Collapsed"></Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding DataContext.IsRecordExists,
RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType={x:Type local:MainWindow}}}" Value="false">
<Setter Property="RowHeight" Value="0"></Setter>
<Setter Property="RowDetailsVisibilityMode" Value="Visible"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.Style>
By default (record available on datagrid) row detail template will be collapsed.
DataTrigger that checks CLR poperty, if it is false then show the row detail template.
The reason for setting the rowheight as 0 to hide the default row which we haved added on 1st step.
Add the grid inside the stackpanel
Place below border code next to datagrid
<Border HorizontalAlignment="Stretch" VerticalAlignment="Center"
BorderThickness="1,0,1,1" BorderBrush="Black" Height="35">
<Border.Style>
<Style TargetType="Border">
<Setter Property="Visibility" Value="Collapsed" />
<Style.Triggers>
<DataTrigger Binding="{Binding YourListName.Count}" Value="0">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<TextBlock Text="No record fount" HorizontalAlignment="Center"
VerticalAlignment="Center" />
</Border>
It will show/hide based on your collection/list count.
I find that it is easy to center a text block over the grid and set its visibility based on the number of rows. I am usually using MVVM and will bind the visibility to a View Model property:
<Grid>
<toolkit:DataGrid>
<toolkit:DataGrid.Columns>
.
.
.
</toolkit:DataGrid.Columns>
</toolkit:DataGrid>
<TextBlock Text="No Records Found" HorizontalAlignment="Center" VerticalAlignment="Center" Visibility="{Binding EmptyMessageVisibility, Mode=OneWay, FallbackValue=Visible}" />
</Grid>

Set row properties for a data bound datagrid

I have a DataGrid bound to a object (using MVVM pattern).
<DataGrid ItemsSource="{Binding Path=RecordSet}"
AutoGenerateColumns="False"
IsReadOnly="True"
Name="ResultGrid">
<DataGrid.Columns>
<DataGridTextColumn Header="ID" Binding="{Binding Path=Id}" Width="Auto"/>
<DataGridTextColumn Header="Foo" Binding="{Binding Path=Foo}" Width="*"/>
</DataGrid.Columns>
</DataGrid>
Here:
RecordSet is a List<Record>.
This works fine. The data loads up fine and everything. I wanted to know if there was away for me to set some properties on individual rows i.e. bound a row property to a value in my ViewModel (set each row's Row.IsEnable based on the Record.Enable)
I am a newbie at WPF.
Thanks
This should work. Put this style inside your DataGrid.
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Enabled}" Value="false">
<Setter Property="Visibility" Value="Hidden"/>
<Setter Property="Height" Value="0"/>
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.RowStyle>
This will make a row invisble and have no height if the record bound this row is not enabled. The Enabled property in the "{Binding Path=Enabled}" belongs to your Record. I don't know if you have that already, but you'll need something like it.
To follow up on wangburger's answer.
To bind the IsEnabled Property for each DataGridRow to Enabled in Record you can do this
<DataGrid ItemsSource="{Binding Path=RecordSet}"
AutoGenerateColumns="False"
IsReadOnly="True"
Name="ResultGrid">
<DataGrid.Resources>
<Style TargetType="DataGridRow">
<Setter Property="IsEnabled" Value="{Binding Enabled}"/>
</Style>
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridTextColumn Header="ID" Binding="{Binding Path=Id}" Width="Auto"/>
<DataGridTextColumn Header="Foo" Binding="{Binding Path=Foo}" Width="*"/>
</DataGrid.Columns>
</DataGrid>
Note: if you set a row style to IsEnabled=false then that row cannot actually be selected at all, neither will the cursor keys skip over it. This may or may not be what you want.
if you want specifically to set IsEnabled=false (and I realize you just gave that as an example), but still allow the row to be selectable then you'll need (AFAIK) to set IsEnabled=false for each column template.

How to specify a ToolTip for a control in a Style from XAML?

I'm using a the WPF datagrid from the Microsoft CodePlex project. I have a custom control that I want to databind to a field from the row of the datagrid. I can't for the life of me figure out how to specify a tooltip on a datagrid row.
The closest I've come is to use a RowStyle with a Setter to set the tooltip, but this only seems to work for text. When I try to put a ControlTempalte in as the Value for the ToolTip, it displays the result of calling ToString on the ControlTemplate type.
I think I need to set the "Template" property of the ToolTip, but I can't seem to figure out how to do that...
<dg:DataGrid Name="dgResults" AutoGenerateColumns="True">
<dg:DataGrid.RowStyle >
<Style TargetType="{x:Type dg:DataGridRow}">
<Setter Property="ToolTip" >
<Setter.Value>
<ControlTemplate TargetType="{x:Type ToolTip}">
<StackPanel>
<TextBlock>txt1</TextBlock><TextBlock>txt2</TextBlock>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</dg:DataGrid.RowStyle>
</dg:DataGrid>
Figured it out... took me about 6 hours...
For some reason, I can't set the value directly using Value.Setter. If I define the content for the tooltip as a static resource though, and then set it in the Style property of the DataGrid.RowStyle it works.
So, the datagrid row style looks like:
<Style TargetType="{x:Type dg:DataGridRow}">
<Setter Property="ToolTip" Value="{StaticResource resKWIC}">
</Setter>
</Style>
</dg:DataGrid.RowStyle>
And the resource is
<Window.Resources>
<StackPanel x:Key="resKWIC">
<TextBlock>f1</TextBlock>
<TextBlock>f2></TextBlock>
</StackPanel>
</Window.Resources>
Thanks!
The key is to use the Property ToolTipService.ToolTip, instead of ToolTip - like this:
<Setter Property="ToolTipService.ToolTip" Value="My Tooltip"/>
I also got this working with a couple of changes; included incase it helps someone.
My Datadrid is bound to a list of custom objects, I wanted to display the string "Name" as a column and the string "text" in the tooltip. The trick for me (newbe) was that I had to include the Text Column and hide it for it to show up in the tooltip, i.e.:
<DataGrid AutoGenerateColumns="False" CanUserAddRows="False" EnableRowVirtualization="False" HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch" IsReadOnly="True" IsSynchronizedWithCurrentItem="True" ItemsSource="{Binding}" Name="dgrTextGroupText" VerticalContentAlignment="Stretch" Grid.Column="3" Grid.Row="1" Grid.RowSpan="6" CanUserReorderColumns="False" CanUserSortColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Name}" Width="*" />
<DataGridTextColumn Binding="{Binding Text}" Width="0" Visibility="Hidden" />
</DataGrid.Columns>
<DataGrid.RowStyle>
<Style TargetType="{x:Type DataGridRow}">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Self}, Path=DataContext.text}" />
</Trigger>
</Style.Triggers>
</Style>
</DataGrid.RowStyle>
</DataGrid>
Not sure you can do it through XAML.
A easier way might be to just handle the LoadingRow event. In xaml have something like:
<dg:DataGrid Name="dgResults" AutoGenerateColumns="True"
LoadingRow="dgResults_LoadingRow"
ItemsSource="{Binding ListOfStrings}" />
Then in code behind
void dgResults_LoadingRow(object sender, DataGridRowEventArgs e)
{
DataGridRow row = e.Row;
row.ToolTip = row.DataContext as string;
}
Obviously you will have to change the code depending on how you are populating the data in the datagrid. This is also untested =)
I needed to set the tooltip dynamically based on the cell content. I'm using the tooltip to display text overflow text from the cell. The binding below is from a c# class property named CellText. Thanks to the posts above for allowing me to avoid figuring out the entire thing myself.
<DataGridTextColumn Header="HeaderText" Binding="{Binding DisplayText, Mode=OneWay}" Width="33*">
<DataGridTextColumn.CellStyle>
<Style>
<Setter Property="ToolTipService.ToolTip" Value="{Binding DisplayText, Mode=OneWay}"/>
</Style>
</DataGridTextColumn.CellStyle>
</DataGridTextColumn>
There's no need for the ControlTemplate. If you want the StackPanel in the ToolTip, just set it as:
<Setter Property="ToolTip">
<Setter.Value>
<StackPanel>
<TextBlock>txt1</TextBlock><TextBlock>txt2</TextBlock>
</StackPanel>
</Setter.Value>
</Setter>

Resources