I have a ListView with a binding to a ObservableCollection. Further I am listing out all items in the ObservableCollection. Now, Is there a good way to check if the ObservableCollection is empty, and the display an alternative xaml?
You can use the HasItems dependency property of the ListView. With a trigger, when the property is false, you can change the ControlTemplate. Here is as example:
<ListView ItemsSource="{Binding Items}">
<ListView.Style>
<Style TargetType="{x:Type ListView}">
<Style.Triggers>
<Trigger Property="HasItems" Value="False">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListView}">
<Border SnapsToDevicePixels="true"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<TextBlock Text="No items"
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</ListView.Style>
</ListView>
Related
I've noticed that TextBoxes are very slow and create performance issues when the Text is changed dynamically by code (I need to change the Text continuosly to 10-15 TextBoxes at the same time), so, as a workaround, I've created a custom control with a TextBlock and a TextBox:
The TextBlock is used in almost all time.
The TextBox is used only when I need to edit the Text inside the control with keyboard.
My solution is to change the template and use the TextBox when the control is focused:
(Value is a string Dependency Property)
<Style TargetType="{x:Type local:CustomControl1}">
<Setter Property="Value" Value="Val"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:CustomControl1}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Text="{TemplateBinding Value}"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsFocused" Value="True">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:CustomControl1}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<TextBox HorizontalContentAlignment="Center" VerticalContentAlignment="Center"
Text="{Binding Path=Value, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
But when I click on the control nothing happens.
I think that the problem is that the "focus state" is passed to the internal TextBox, and the control loses the "focus state".
There is a better way to create a custom "TextBox" control like this, or a way to resolve this problem?
You don't need a custom control for this, that's just adding unnecessary overhead. What you're trying to create is still a TextBox, with all the usual behavior of a TextBox (focus etc). All you need to do is change the template to a TextBlock when it's not in focus:
<Window.Resources>
<Style TargetType="{x:Type TextBox}">
<Style.Triggers>
<Trigger Property="IsFocused" Value="False">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TextBox}">
<TextBlock Text="{TemplateBinding Text}" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<StackPanel>
<TextBox Text="Hello World" />
<TextBox Text="Goodbye World" />
</StackPanel>
Here is the intention,
When items get added to ItemsControl and if the content is more than the available size I wish to see scrolling in ItemsControl. I bet this is the default behavior for any ItemsControl type.
So I generated a sample replicates my intention. Suggest me what I need to fix
<StackPanel>
<StackPanel.Resources>
<Style TargetType="{x:Type ItemsControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ItemsControl}">
<Border SnapsToDevicePixels="true" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Padding="{TemplateBinding Padding}">
<ScrollViewer VerticalScrollBarVisibility="Visible">
<ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</ScrollViewer>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</StackPanel.Resources>
<ToggleButton Name="SomeToggle" Content="Show/Hide"/>
<Button Content="Add Item" Click="ButtonBase_OnClick" />
<TextBlock Text="Hide/Show on toggle" >
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsChecked,ElementName=SomeToggle}" Value="True">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
<ItemsControl Name="ItemsCollection" >
</ItemsControl>
</StackPanel>
and Code behind:
private int counter = 0;
private ObservableCollection<string> coll;
public MainWindow()
{
coll= new ObservableCollection<string>();
//ItemsCollection.ItemsSource = coll;
InitializeComponent();
}
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
ItemsCollection.Items.Add(string.Format("Item {0}", ++counter));
}
This is basically occurs each time you put a ScrollViewer inside a Stackpanel, and this is due to the way the Stackpanel works, take a look here
you could either put the Stackpanel inside a ScrollView, or better change your main panel to a Grid
<Grid >
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.Resources>
<Style TargetType="{x:Type ItemsControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ItemsControl}">
<Border SnapsToDevicePixels="true" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Padding="{TemplateBinding Padding}">
<ScrollViewer VerticalScrollBarVisibility="Visible">
<ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</ScrollViewer>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Grid.Resources>
<ToggleButton Name="SomeToggle" Content="Show/Hide" Grid.Row="0"/>
<Button Content="Add Item" Click="ButtonBase_OnClick" Grid.Row="1"/>
<TextBlock Text="Hide/Show on toggle" Grid.Row="2">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsChecked,ElementName=SomeToggle}" Value="True">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
<ItemsControl Name="ItemsCollection" Grid.Row="3">
</ItemsControl>
</Grid>
The only reason you are not able to see the scrollbars cause you have not set the height of your ItemsControl.
Every time you add a item it's height will expand and StackPanel will expand with it to contain the content. You have already defined ScrollViewer to your ItemsControl template you have to also decide at what point it has to work.
<ItemsControl Name="ItemsCollection" Height="200" >
</ItemsControl>
Then it will also not matter either you containing it in a StackPanel or any other control.
initial scroll bar
After adding items
with code
And just from understanding point of view. this will also work:
Remove style for item control & set the height of scroll viewer.
To handle the height of all elements:
<ScrollViewer MaxHeight="{Binding RelativeSource={RelativeSource AncestorType={x:Type Border}},Path=ActualHeight,Mode=OneWay}">
<StackPanel>
<StackPanel.Resources>
<Style TargetType="{x:Type ItemsControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ItemsControl}">
<Border SnapsToDevicePixels="true" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Padding="{TemplateBinding Padding}">
<ScrollViewer VerticalScrollBarVisibility="Visible">
<ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</ScrollViewer>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</StackPanel.Resources>
<ToggleButton Name="SomeToggle" Content="Show/Hide"/>
<Button Content="Add Item" Click="Button_Click" />
<TextBlock Text="Hide/Show on toggle" >
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsChecked,ElementName=SomeToggle}" Value="True">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
<ItemsControl Name="ItemsCollection" ItemsSource="{Binding list}"
MaxHeight="{Binding RelativeSource={RelativeSource AncestorType={x:Type Border}},Path=ActualHeight,Mode=OneWay}">
</ItemsControl>
</StackPanel>
</ScrollViewer>
I have a ListBoxItem Style that I am trying to modify so that it will show character ellipsis when the list box is made to small. To do that I've had to get rid of the ContentPresenter in our code and replace it with a TextBlock. The ListBoxes that this is applied to are all bound via the ItemSource property.
Here is my code.
<Style x:Key="ListBoxItemStyle" TargetType="{x:Type ListBoxItem}">
<Setter Property="Background" Value="White"/>
<Setter Property="Margin" Value="0,0,0,0"/>
<Setter Property="Padding" Value="0,0,0,0"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<Grid>
<Border x:Name="Bd" SnapsToDevicePixels="true">
<!-- Before this used to be ContentPresenter but I switched it to TextBlock to get it the TextTrimming property. I can't find the right way to bind the data though.-->
<TextBlock Text="{TemplateBinding DisplayMemberPath}" TextTrimming="CharacterEllipsis" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Border>
<Rectangle x:Name="HoverRectangle"
Stroke="{StaticResource Gold}"
StrokeDashCap="Square"
StrokeThickness="0"
SnapsToDevicePixels="True" />
<Rectangle x:Name="KeyboardFocusRectangle"
Height="Auto"
SnapsToDevicePixels="True"
Stroke="{StaticResource BrightBlue}"
StrokeDashCap="Square"
StrokeThickness="0" />
</Grid>
<ControlTemplate.Triggers>
<!-- Bunch of Triggers in here -->
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
My current TextBlock Text binding (Text="{TemplateBinding DisplayMemberPath}") is not working. What should the binding be in order to work correctly?
Your only reasonable choice here is to assume the data context of the ListBoxItem is a string, or can be displayed as such:
<TextBlock Text="{Binding}" .../>
The default scrollbar for a listview appears at the right. I designed a new style and would like it to appear separate from the listview control. What you see in the image is how I have it set up now using blend.
So how do I link the custom scroll bar to the listview? I'm using Blend 4.
Listview Image located here:
http://i141.photobucket.com/albums/r69/thebirdbath/scroll.jpg
I'm not sure if you want to add your Style to the ScrollViewer inside the Template of the ListView or if you want to disable that ScrollViewer and place the ListView in a separate ScrollViewer.
To apply the Style to the ScrollViewer in the Template and place the ScrollBar to the left you can modify the default Template when using GridView. It will require a reference to PresentationFramework.Aero
Set FlowDirection="RightToLeft" on the ScrollViewer to place it on the left side
Set FlowDirection="LeftToRight" on the ItemsPresenter and GridViewHeaderRowPresenter since they will inherit RightToLeft otherwise
To get the transparent space between the ScrollViewer and the content, set Background="Transparent" for the ListView and set the desired Background on the ItemsPanel instead
Control the transparent space with Padding, e.g. Padding="0,0,10,0"
Looks like this
<ListView xmlns:MS_Themes="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero"
Padding="0,0,10,0"
Background="Transparent"
BorderThickness="0"
ScrollViewer.VerticalScrollBarVisibility="Visible">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel Background="White"/>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.Template>
<ControlTemplate TargetType="{x:Type ListView}">
<MS_Themes:ListBoxChrome x:Name="Bd" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" RenderMouseOver="{TemplateBinding IsMouseOver}" RenderFocused="{TemplateBinding IsKeyboardFocusWithin}" SnapsToDevicePixels="true">
<ScrollViewer Padding="{TemplateBinding Padding}"
Style="{YourStyle...}"
FlowDirection="RightToLeft">
<ScrollViewer.Resources>
<Style TargetType="GridViewHeaderRowPresenter">
<Setter Property="FlowDirection" Value="LeftToRight"/>
</Style>
</ScrollViewer.Resources>
<ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
FlowDirection="LeftToRight"/>
</ScrollViewer>
</MS_Themes:ListBoxChrome>
<ControlTemplate.Triggers>
<Trigger Property="IsGrouping" Value="true">
<Setter Property="ScrollViewer.CanContentScroll" Value="false"/>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</ListView.Template>
<!--...-->
</ListView>
Ok, pretty close. Its working but I've lost my column headers. Also I can't get the transparency in between the bar and the listview. Its like the bar is still attached and I want them to appear like they are separate. (See the link to my original image)
This has been very helpful. Almost there!
My code is below.
<ListView
AlternationCount="2"
Padding="0,0,10,0"
Shared:GridViewSort.AutoSort="True"
Shared:GridViewSort.ShowSortGlyph="True"
ItemsSource="{Binding Contents}"
SelectionMode="Extended"
Background="Transparent"
Foreground="White"
SelectionChanged="ListBoxSelectionChanged"
BorderThickness="0"
ItemContainerStyle="{DynamicResource ListViewItemStyle}"
Style="{DynamicResource ListViewStyle1}" Margin="0" VerticalAlignment="Top">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel Background="Transparent"/>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.Template>
<ControlTemplate TargetType="{x:Type ListView}">
<MS_Themes:ListBoxChrome x:Name="Bd" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" RenderMouseOver="{TemplateBinding IsMouseOver}" RenderFocused="{TemplateBinding IsKeyboardFocusWithin}" SnapsToDevicePixels="true">
<ScrollViewer Padding="{TemplateBinding Padding}"
Style="{DynamicResource ScrollViewerKey}"
FlowDirection="RightToLeft">
<ScrollViewer.Resources>
<Style TargetType="GridViewHeaderRowPresenter">
<Setter Property="FlowDirection" Value="LeftToRight"/>
</Style>
</ScrollViewer.Resources>
<ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
FlowDirection="LeftToRight"/>
</ScrollViewer>
</MS_Themes:ListBoxChrome>
<ControlTemplate.Triggers>
<Trigger Property="IsGrouping" Value="true">
<Setter Property="ScrollViewer.CanContentScroll" Value="false"/>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</ListView.Template>
I am trying to figure out how to bind a WPF DataGrid's column header and main data to a data source using an MVVM pattern. The result I'm looking for would look like this:
(source: vallelunga.com)
I've successfully styled the headers here, but I'm unsure how to bind the values in the headers. Specifically, the IsChecked property of the check-box, the selected index of the combo box and the value of the text box.
I was previously using a simple DataTable to populate the main grid data, but I'm going to need something more complex to hold both the grid data and the values for each column. Or perhaps I can store them as separate entities entirely.
So, does anyone have any idea of how I might pull off this binding? One limitation is that the columns must be auto-generated since I have no idea what they will be until runtime. The application simply loads the data form an Excel spreadsheet and there may be any number of columns present.
Thanks,
Brian
Here's what I ended up doing to use this with the MVVM pattern:
I have two sets of data for binding on my view model: one for the actual grid data and one for the column headers. Currently these are exposed as two properties:
// INotifyPropertyChanged support not shown for brevity
public DataTable GridData { get; set; }
public BindingList<ImportColumnInfo> ColumnData { get; set; }
The trick to working with two differing sets of data is in the grid. I have subclassed the DataGrid and given the grid an additional data source called ColumnSource, as a dependency property. This is what is bound to the ColumnData on my view model. I then set the header of each auto-generated column to the appropriately indexed data in the ColumnSource data source. The code is as follows:
public class ImporterDataGrid : DataGrid
{
protected override void OnAutoGeneratingColumn(DataGridAutoGeneratingColumnEventArgs e)
{
base.OnAutoGeneratingColumn(e);
int columnIndex = this.Columns.Count;
var column = new ImporterDataGridColumn();
column.Header = ColumnSource[columnIndex];
column.Binding = new Binding(e.PropertyName) { Mode = BindingMode.OneWay };
e.Column = column;
}
public IList ColumnSource
{
get { return (IList)GetValue(ColumnSourceProperty); }
set { SetValue(ColumnSourceProperty, value); }
}
public static readonly DependencyProperty ColumnSourceProperty = DependencyProperty.Register("ColumnSource", typeof(IList), typeof(ImporterDataGrid), new FrameworkPropertyMetadata(null));
}
I can now perform normal data binding in the templated header of my columns, which will all bind against the data in the ColumnData property of my view model.
UPDATE: I was asked to show the XAML for my grid. It's really basic, but here it is:
<Controls:ImporterDataGrid
AutoGenerateColumns="True" x:Name="previewDataGrid"
VerticalScrollBarVisibility="Visible"
HorizontalScrollBarVisibility="Visible"
IsReadOnly="True"
SelectionMode="Extended"
HeadersVisibility="Column"
ItemsSource="{Binding PreviewData}"
ColumnSource="{Binding PreviewColumnData}"
Style="{StaticResource ImporterDataGridStyle}"
Background="White" CanUserReorderColumns="False" CanUserResizeRows="False"
CanUserSortColumns="False" AlternatingRowBackground="#FFFAFAFA" AllowDrop="True" />
And here is the ImporterColumnHeaderStyle:
<Style x:Key="ImporterDataGridColumnHeaderStyle" TargetType="{x:Type toolkit:DataGridColumnHeader}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type toolkit:DataGridColumnHeader}">
<Grid>
<toolkit:DataGridHeaderBorder Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Padding="{TemplateBinding Padding}" IsClickable="{TemplateBinding CanUserSort}" IsHovered="False" IsPressed="False" SortDirection="{TemplateBinding SortDirection}">
<Grid>
<CheckBox Height="16" Margin="6,6,16,0" Name="importCheckBox" IsChecked="{Binding Path=Import}" VerticalAlignment="Top">Import Column</CheckBox>
<StackPanel IsEnabled="{Binding Path=Import}">
<ComboBox Height="24" Margin="6,29,6,0" Name="columnTypeComboBox" VerticalAlignment="Top" SelectedValue="{Binding ColumnType}" ItemsSource="{Binding Source={local:EnumList {x:Type Models:ImportColumnType}}}">
</ComboBox>
<TextBox Height="23" Margin="6,6,6,33" Name="customHeadingTextBox" VerticalAlignment="Bottom" Text="{Binding Path=CustomColumnName}" IsEnabled="{Binding ColumnType, Converter={StaticResource ColumnTypeToBooleanConverter}}" />
</StackPanel>
<TextBlock Height="20" Margin="6,0,6,7" Name="originalHeadingTextBlock" Text="{Binding Path=OriginalColumnName}" VerticalAlignment="Bottom" Foreground="Gray" />
</Grid>
</toolkit:DataGridHeaderBorder>
<Thumb x:Name="PART_LeftHeaderGripper" HorizontalAlignment="Left">
<Thumb.Style>
<Style TargetType="{x:Type Thumb}">
<Setter Property="Width" Value="8"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Cursor" Value="SizeWE"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Thumb}">
<Border Background="{TemplateBinding Background}" Padding="{TemplateBinding Padding}"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Thumb.Style>
</Thumb>
<Thumb x:Name="PART_RightHeaderGripper" HorizontalAlignment="Right">
<Thumb.Style>
<Style TargetType="{x:Type Thumb}">
<Setter Property="Width" Value="8"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Cursor" Value="SizeWE"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Thumb}">
<Border Background="{TemplateBinding Background}" Padding="{TemplateBinding Padding}"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Thumb.Style>
</Thumb>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I am definitely a WPF / MVVM / databinding noob, but have been working hard on this stuff lately. I don't know what you have wired up so far, but first you'll want to set the DataContext for your View. Since you're using MVVM, I assume you have a ViewModel, so that should be the DataContext for your View.
i.e. if you have your View create / own your ViewModel, it could look something like this:
MyViewModel vm = new MyViewModel();
this.DataContext = vm;
You can easily databind your CheckBox, ComboBox, and TextBox to properties in your ViewModel. I have found the easiest way is to make your ViewModel inherit from a base viewmodel class, like the one that Josh Smith wrote. This will give you a method to call internally when you want the ViewModel to notify the GUI of any changes in values.
Assuming you have properties like ImportColumn, LastName, and LastNameText (all C# properties, not fields that call OnPropertyChanged accordingly), then your XAML would look something like this:
<CheckBox IsChecked="{Binding ImportColumn}" />
<ComboBox SelectedItem="{Binding LastName}" />
<TextBox Text="{Binding LastName Text, Mode=TwoWay}" />
I hope this helps you out. If not, please comment and I'll try to do as best as I can to try other things out.
We do something similar in our app.
What i have done is derived my own column type (DataGridSearchableBooleanColumn), then i replace the DataGridColumnHeader template, i put two content presenters in there. the first i bind to the content (the same as the default template) the second i bind to the column. I use a data template for the column (i have a few of them for different search types (text, combo, boolean). then i add the extra properties to the column so i can bind to them. See if this code makes sense.
<!--Style for the datagrid column headers, contains a text box for searching-->
<Style
x:Key="columnHeaderStyle"
TargetType="dg:DataGridColumnHeader">
<Setter
Property="Foreground"
Value="#FF000000" />
<Setter
Property="HorizontalContentAlignment"
Value="Left" />
<Setter
Property="VerticalContentAlignment"
Value="Center" />
<Setter
Property="IsTabStop"
Value="False" />
<Setter
Property="Padding"
Value="1,2,1,2" />
<Setter
Property="Template">
<Setter.Value>
<ControlTemplate
TargetType="dg:DataGridColumnHeader">
<Grid
x:Name="Root">
<dg:DataGridHeaderBorder
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Padding="{TemplateBinding Padding}"
IsClickable="{TemplateBinding CanUserSort}"
IsHovered="{TemplateBinding IsMouseOver}"
IsPressed="{TemplateBinding IsPressed}"
SeparatorBrush="{TemplateBinding SeparatorBrush}"
SeparatorVisibility="{TemplateBinding SeparatorVisibility}"
SortDirection="{TemplateBinding SortDirection}">
<Grid
HorizontalAlignment="Stretch"
Margin="{TemplateBinding Padding}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}">
<Grid.Resources>
<DataTemplate
DataType="{x:Type local:DataGridSearchableBooleanColumn}">
<CheckBox
Margin="0,5,0,0"
IsThreeState="True"
IsChecked="{Binding Path=IsChecked}" />
</DataTemplate>
</Grid.Resources>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition
Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition
Height="19" />
<RowDefinition
Height="Auto" />
</Grid.RowDefinitions>
<ContentPresenter
Content="{Binding RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay, Path=Content}" />
<Path
x:Name="SortIcon"
Fill="#FF444444"
Stretch="Uniform"
HorizontalAlignment="Left"
Margin="4,0,0,0"
VerticalAlignment="Center"
Width="8"
Opacity="0"
RenderTransformOrigin=".5,.5"
Grid.Column="1"
Data="F1 M -5.215,6.099L 5.215,6.099L 0,0L -5.215,6.099 Z ">
<Path.RenderTransform>
<ScaleTransform
ScaleX=".9"
ScaleY=".9" />
</Path.RenderTransform>
</Path>
<ContentPresenter
x:Name="columnHeaderContentPresenter"
Content="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Column}"
Grid.Row="1"
Grid.ColumnSpan="2"
Margin="0,0,0,0" />
</Grid>
</dg:DataGridHeaderBorder>
<Thumb
x:Name="PART_LeftHeaderGripper"
HorizontalAlignment="Left">
<Thumb.Style>
<Style
TargetType="{x:Type Thumb}">
<Setter
Property="Width"
Value="8" />
<Setter
Property="Background"
Value="Transparent" />
<Setter
Property="Cursor"
Value="SizeWE" />
<Setter
Property="Template">
<Setter.Value>
<ControlTemplate
TargetType="{x:Type Thumb}">
<Border
Background="{TemplateBinding Background}"
Padding="{TemplateBinding Padding}" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Thumb.Style>
</Thumb>
<Thumb
x:Name="PART_RightHeaderGripper"
HorizontalAlignment="Right">
<Thumb.Style>
<Style
TargetType="{x:Type Thumb}">
<Setter
Property="Width"
Value="8" />
<Setter
Property="Background"
Value="Transparent" />
<Setter
Property="Cursor"
Value="SizeWE" />
<Setter
Property="Template">
<Setter.Value>
<ControlTemplate
TargetType="{x:Type Thumb}">
<Border
Background="{TemplateBinding Background}"
Padding="{TemplateBinding Padding}" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Thumb.Style>
</Thumb>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>