TabItem HeaderTemplate - multiple TextBlocks
-
Friday, May 25, 2012 3:28 PM
Hi,
I have an application that dynamically creates new TabItems based on messages it receives. Each TabItem displays a DataGrid with rows of data. Currently I have the header text bound to a property called country and it shows the text correctly.
However, I want to change the design of my TabItem header so that it shows the country text in the center but on either side it has a dynamic "Block" that acts almost like a bar chart where the height of it will raise and lower based on another integer variable that it is bound to. (much like a vertical bar that represents the volume on a stereo).
public int VolOutsideParamCount { get { return _volOutsideParmCount; } set { _volOutsideParmCount= value; OnPropertyChange("VolOutsideParamCount"); } } public int ToneOutsideParamCount { get { return _toneOutsideParamCount; } set { _toneOutsideParamCount= value; OnPropertyChange("ToneOutsideParamCount"); } }I have the 2 properties exposed (one for the left hand side of the Header text, and one for the right) but I cannot get my template to work to show the dymanic "bars". My plan is to use a simple TextBlock with a background colour set to say Red and adjust the height of the TextBlock based on the property above - therefore I want to bind the height property to the property.
My current HeaderTemplate is as follows:
<DataTemplate x:Key="TabItem_HeaderTemplate"> <Grid> <Border Name="Border" Background="{StaticResource TabBackgoundBrush}" BorderBrush="#f5f5f5" BorderThickness="2" Width="Auto"> <Grid> <TextBlock x:Name="_header" Text="{Binding}"/> </Grid> </Border> </Grid> <DataTemplate.Triggers> <DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type TabItem}},Path=IsSelected}" Value="True"> <Setter Property="Panel.ZIndex" Value="100" /> <Setter TargetName="Border" Property="Background" Value="#003366" /> <Setter TargetName="Border" Property="BorderThickness" Value="2" /> <Setter TargetName="_header" Property="Foreground" Value="White"/> <Setter TargetName="_header" Property="FontFamily" Value="Arial"/> <Setter TargetName="_header" Property="FontWeight" Value="Bold"/> <Setter TargetName="_header" Property="HorizontalAlignment" Value="Center"/> <Setter TargetName="_header" Property="FontSize" Value="12"/> <Setter TargetName="_header" Property="Margin" Value="2"/> </DataTrigger> <DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type TabItem}},Path=IsSelected}" Value="False"> <Setter TargetName="Border" Property="BorderThickness" Value="1" /> <Setter TargetName="Border" Property="CornerRadius" Value="3,3,0,0" /> <Setter TargetName="Border" Property="Background" Value="{StaticResource TabBackgoundBrush}" /> <Setter TargetName="Border" Property="BorderBrush" Value="#f5f5f5" /> <Setter TargetName="_header" Property="Foreground" Value="Black" /> <Setter TargetName="_header" Property="FontFamily" Value="Arial"/> <Setter TargetName="_header" Property="FontWeight" Value="Bold"/> <Setter TargetName="_header" Property="HorizontalAlignment" Value="Center"/> <Setter TargetName="_header" Property="FontSize" Value="12"/> </DataTrigger> <!-- When TabItem is unavailable Disabled Style Below--> <DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type TabItem}},Path=IsEnabled}" Value="False"> <Setter TargetName="Border" Property="Background" Value="#fff" /> <Setter TargetName="Border" Property="BorderBrush" Value="#f5f5f5" /> <Setter TargetName="Border" Property="BorderThickness" Value="0" /> <Setter TargetName="_header" Property="Foreground" Value="#ccc" /> <Setter TargetName="_header" Property="FontFamily" Value="Arial"/> </DataTrigger> </DataTemplate.Triggers> </DataTemplate>Should I be able to add 2 more TextBlocks to the Grid in the template? And if so how do I bind the height to one of the properties above?
Hopefully you can help with my query. Thanks a lot!!
All Replies
-
Friday, May 25, 2012 9:31 PM
Here's the basic idea (will explain in more detail):
<Window x:Class="TabItem_dynamic_binding.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:binding="clr-namespace:TabItem_dynamic_binding" Title="MainWindow" Height="350" Width="525"> <Grid> <TabControl> <TabItem Header="TabItem"> <TabItem.HeaderTemplate> <DataTemplate> <DataTemplate.Resources> <ObjectDataProvider ObjectType="{x:Type binding:BindingTest}" MethodName="getLeftHeight" x:Key="leftSideHeight" /> <ObjectDataProvider ObjectType="{x:Type binding:BindingTest}" MethodName="getRightHeight" x:Key="rightSideHeight" /> </DataTemplate.Resources> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto" /> <ColumnDefinition /> <ColumnDefinition Width="Auto" /> </Grid.ColumnDefinitions> <TextBlock Height="{Binding Source={StaticResource ResourceKey=leftSideHeight}}" Text="{Binding Source={StaticResource ResourceKey=leftSideHeight}}" /> <TextBlock Text="{Binding}" Grid.Column="1" Margin="3, 0, 3, 0" /> <TextBlock Grid.Column="2" VerticalAlignment="Top" Height="{Binding Source={StaticResource ResourceKey=rightSideHeight}}" Text="{Binding Source={StaticResource ResourceKey=rightSideHeight}}" /> </Grid> </DataTemplate> </TabItem.HeaderTemplate> </TabItem> </TabControl> </Grid> </Window>Basically, you have to create an ObjectDataProvider which will point to the class that has the method that you want to use. I created a sample project that works using this method. You also need to add two columns to the grid and put TextBlocks in each column, with the middle column holding the general binding for the TabItem header text. I bound the text for the two TextBlocks on the left and right side to the same ODP as their heights just to verify that it works correctly.
Here is the class that I used to bind the heights of the TextBlocks to:
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace TabItem_dynamic_binding { class BindingTest { public int getLeftHeight() { return 72; } public int getRightHeight() { return 22; } } }
Simple, nothing fancy. I just return some constants to demonstrate how you can bind the value of an XAML object to a method in your code file. Hope this helps
- Marked As Answer by wallaceoc Monday, May 28, 2012 5:24 PM
-
Monday, May 28, 2012 7:21 AMModerator
Hi wallaceoc,
How about your issue, if your issue persists, please let me know.
Best regards,
Sheldon _Xiao[MSFT]
MSDN Community Support | Feedback to us
Microsoft
Please remember to mark the replies as answers if they help and unmark them if they provide no help.
-
Monday, May 28, 2012 8:15 AM
Hi Snake,
thanks for your response. This looks like what I need minus one important thing. I need the value of the height to update dyamically as the value in the bound class updates. I was hoping I could bind the height to a property in a class that implements INotifyPropertyChanged and therefore the changes are passed up automatically to the UI.
Is there a way I can adapt your solution to do this?
Regards
-
Monday, May 28, 2012 10:52 AM
I have found a way to bind to a property as follows...
<ObjectDataProvider x:Key="LeftHeight" ObjectType="{x:Type Data:BindingClass}" /> <ObjectDataProvider x:Key="RightHeight" ObjectType="{x:Type Data:BindingClass}" /> ... ... <TextBlock x:Name="_left" Background="LightPink" Grid.Column="0" Height="{Binding Source={StaticResource ResourceKey=LeftHeight}, Path=LeftOutsideCountHeight}" Text="{Binding Source={StaticResource ResourceKey=LeftHeight}, Path=LeftOutsideCountHeight}" Width="15" HorizontalAlignment="Stretch" />And my class BindingClass is has property
public double LeftOutsideCountHeight { get { Random rnd = new Random(); return rnd.Next(1, 20); } }
public int Counter
{
get { return _value;}
set { _value = value; OnPropertyChanged("LeftOutsideCountHeight"); }
}
However, the text or the height are not updating. Is it possible that the update notification does not get sent up to the UI using the ObjectDataProvider?
Thanks
-
Monday, May 28, 2012 5:24 PM
I found a problem was with the DataContext of the TextBlock was not being set correctly. I had to set use the DataContext of the parent TabItem to make sure the updates worked correctly and now all is good.
Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Border}, AncestorLevel=2}, Path=DataContext.LefctCountHeight}"
Thanks for your help guys.

