How to conditionally format axis values in Silverlight Toolkit LineSeries - silverlight

I am trying to conditionally format the numbers that appear in a NumericAxis axis for a LineSeries (from Silverlight 4 Toolkit). To be more specific, I want numbers that are >=10000 and <=0.0001 to display in scientific notation, but I can't seem to make this work.
I can override the NumericAxisLabel template like this:
<Style x:Key="NumericAxisLabelStyle" TargetType="chartingToolkit:NumericAxisLabel">
<Setter Property="IsTabStop" Value="False"/>
<Setter Property="StringFormat" Value="{}{0:0.0E+00}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="chartingToolkit:NumericAxisLabel">
<TextBlock Text="{TemplateBinding FormattedContent}"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
But this will apply the scientific notation format to ALL labels in the axis. What I want is for the string format expression to "kick in" only when the condition I mentioned above occurs.
I was able to accomplish this in the LineDataPoint tooltip template fairly easy by using a binding with a custom value converter, like this:
<ControlTemplate TargetType="chartingToolkit:LineDataPoint">
<Grid x:Name="Root" Opacity="0">
<ToolTipService.ToolTip>
<StackPanel Margin="2,2,2,2">
<StackPanel Orientation="Horizontal">
<TextBlock Text="X:" />
<ContentControl Content="{Binding objResultValueX, Converter={StaticResource ToCustomStringFormat}}"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Y:" />
<ContentControl Content="{Binding dblResultValueY, Converter={StaticResource ToCustomStringFormat}}"/>
</StackPanel>
</StackPanel>
</ToolTipService.ToolTip>
...
</Grid>
</ControlTemplate>
If only I could specify a converter for the "FormattedContent" in the NumericAxisLabelStyle like I do for LineDataPoint template...surely there must be a way!
Any ideas?
Thanks in advance for the help!

Try setting the DataContext of the TextBlock to FormattedContent. Then apply the converter to the Text property as so:
<Style x:Key="NumericAxisLabelStyle" TargetType="chartingToolkit:NumericAxisLabel">
<Setter Property="IsTabStop" Value="False"/>
<Setter Property="Template">
<Setter.Value >
<ControlTemplate TargetType="chartingToolkit:NumericAxisLabel">
<TextBlock DataContext="{TemplateBinding FormattedContent}" Text ="{Binding Converter={StaticResource ToCustomStringFormat}}"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

It's also possible to override the PrepareAxisLabel() method from the Toolkit's DisplayAxis class.
The source code for the original method (found here) is:
protected virtual void PrepareAxisLabel(Control label, object dataContext)
{
label.DataContext = dataContext;
label.SetStyle(AxisLabelStyle);
}
So, you can override it with something like:
public class MyLinearAxis : LinearAxis
{
protected override void PrepareAxisLabel(Control label, object dataContext)
{
(label as AxisLabel).StringFormat = "{0:c}"; // currency format, for example
dataContext = 10.0; // your own custom numeric value
base.PrepareAxisLabel(label, dataContext);
}
}
In this way, you can get total control over the label as it's created.

Related

Excess border selection in WPF's Lisbox [duplicate]

I have a ListBox in which each item is a StackPanel. The StackPanel consist of an Image and a TextBlock below it:
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Margin="10">
<Image>
<Image.Source>
<BitmapImage UriSource="{Binding Path=ImageFilePath}"/>
</Image.Source>
</Image>
<TextBlock Text="Title" TextAlignment="Center"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
It looks like this:
When the user select an item, I get the default blue rectangle that surround the StackPanel:
Now, I want to make a different border for the selected-item, but I want it to surround only the image.
I know how to make a control template and put a custom border around the ContentPresenter, but this, of course, will surround the whole StackPanel, not only the Image.
I don’t know if making changes to the ContentPresenter is possible, and if it is a good idea at all. If there is other way to achieve the look I want, it will be fine as well.
Right, the ListBox's own ContentPresenter isn't helpful for what you're doing. You want to a) eliminate the ListBox's own selection visuals and b) replace them with something more suitable in the DataTemplate for your items.
The default selection visual is applied by the default template for ListBoxItem. So replace that template. Using a Style in the resources for your ListBox, apply your own control template to ListBoxItem. Not much to it, just present the content and don't provide a selection background. Then you handle the selection visuals with a trigger in your data template, where your image and your label are defined and you can apply changes to one and not the other. The below example works for me.
Note that there's some fiddling with the HorizontalAlignment on the Border element to make it cling to the Image element within it. Also, I wrote a quickie test viewmodel whose Items property is called Items; I assume this is not the name of the collection member you're using to populate your own ListBox.
<ListBox
Margin="8"
ItemsSource="{Binding Items}"
>
<ListBox.Resources>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<Grid>
<ContentPresenter />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.Resources>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Border
x:Name="HighlightBorder"
BorderThickness="4"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Margin="10"
>
<Border.Style>
<Style TargetType="Border">
<!-- MUST set default BorderBrush via a style, if you set it at all.
As an attribute on the Border tag, it would override the effects of
the trigger below.
-->
<Setter Property="BorderBrush" Value="Transparent" />
</Style>
</Border.Style>
<Image Source="{Binding ImageFilePath}" />
</Border>
</Grid>
<DataTemplate.Triggers>
<DataTrigger
Binding="{Binding IsSelected, RelativeSource={RelativeSource AncestorType=ListBoxItem}}"
Value="True">
<Setter TargetName="HighlightBorder" Property="BorderBrush" Value="Orange" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>

WPF subclass Control without adding logic, just to be able to customize style and template?

I'm searching for a good way to create style-able, reusable controls in WPF. For example, I've got a twitter feed that could look something like this (much simplified):
<ItemsControl ItemsSource={Binding tweets}>
<ItemsControl.DataTemplate>
<DataTemplate TargetType="tweet">
<StackPanel>
<Image Source="{Binding user.image}" />
<TextBlock Text="{Binding text}" />
</StackPanel>
</DataTemplate>
</ItemsControl.DataTemplate>
</ItemsControl>
To make this a reusable control, I could put this in a UserControl. But doing just that would make it impossible to change the way the image part is displayed for example.
So what I find myself doing now is creating a control for the user's image, like this:
public class UserImage : Control
{
// Empty class..
}
<Style TargetType="{x:Type UserImage}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type UserImage}">
<Image Source="{Binding}" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
// The itemscontrol datatemplate now looks like this
<DataTemplate TargetType="tweet">
<StackPanel>
<UserImage DataContext="{Binding user.image}" />
<TextBlock Text="{Binding text}" />
</StackPanel>
</DataTemplate>
Now I could do something like this to customize the appearance of the user image:
<Style TargetType="{x:Type UserImage}" BasedOn="{StaticResource {x:Type UserImage}}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type UserImage}">
<Ellipse>
<Ellipse.Fill>
<ImageBrush ImageSource="{Binding}">
</Ellipse.Fill>
</Ellipse>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Even though this works, it does feel a bit inefficient and well ... wrong to have to create an "empty" control for each and every component in the custom control. Is this the way to go, or is there a cleaner way?

Style of a TextBox: Why is 'AcceptsReturn=true' not applied?

I have a custom control in WPF, which consists of a toggle button, a TextBlock and a TextBox. What I basically want to do is to show the TextBox when the toggle button is checked and the TextBlock otherwise. Furthermore I want allow defining to style properties on the control via dependency properties, which are applied to the TextBlock and the TextBox at runtime. The default template looks like this:
<Style TargetType="{x:Type views:EditableLabel}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type views:EditableLabel}">
<Border Background="{TemplateBinding Background}">
<DockPanel Margin="0">
<telerik:RadToggleButton x:Name="PART_Toggle"
DockPanel.Dock="Right"
IsChecked="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=IsInEditMode, Mode=TwoWay}">
<Image Source="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=ToggleImage}" Height="14" />
</telerik:RadToggleButton>
<TextBlock x:Name="PART_TextBlock"
Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Text}" >
</TextBlock>
<TextBox x:Name="PART_TextBox"
Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Text, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" >
</TextBox>
</DockPanel>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The custom control has two dependency properties for styles, one for the PART_TextBlock and one for PART_TextBox. The styles are assigned in the OnApplyTemplate method of the custom control and in the property change callbacks of the two dependency properties:
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
_textBlock = (TextBlock) GetTemplateChild("PART_TextBlock");
_textBox = (TextBox) GetTemplateChild("PART_TextBox");
_toggleButton = (RadToggleButton) GetTemplateChild("PART_Toggle");
ApplyStyles();
UpdateVisibilities();
}
private void ApplyStyles()
{
if (_textBlock != null) _textBlock.Style = TextBlockStyle;
if (_textBox != null) _textBox.Style = TextBoxStyle;
}
(The callbacks are not shown here, as they are trivial, just calling ApplyStyles().
I use the custom control like this:
<views:EditableLabel Text="{Binding SelectedToolbox.Description, Mode=TwoWay}"
CanEdit="{Binding SelectedToolbox.CanEdit}"
ToggleImage="../Resources/Images/edit-26.png">
<views:EditableLabel.TextBlockStyle>
<Style TargetType="TextBlock">
<Setter Property="TextWrapping" Value="Wrap" />
</Style>
</views:EditableLabel.TextBlockStyle>
<views:EditableLabel.TextBoxStyle>
<Style TargetType="TextBox">
<Setter Property="TextWrapping" Value="Wrap" />
<Setter Property="AcceptsReturn" Value="True" />
<Setter Property="VerticalScrollBarVisibility" Value="Visible" />
</Style>
</views:EditableLabel.TextBoxStyle>
</views:EditableLabel>
Everything works as expected, except from the AcceptsReturn setter is not applied, which I find very strange. I've debugged ApplyStyles(): The style is assigned correctly and both setters are contained within the style.
TextWrapping and VerticalScrollBarVisibility are both set correctly:
while AcceptsReturn is not:
Any ideas, what might be the issue here?
The screenshot you posted suggests AcceptsReturn has a local value, i.e., a value set by explicitly calling the property setter or SetValue. Do you have any code in EditableLabel which explicitly sets the AcceptsReturn property? If so, the local value you set will take precedence over any style setters. You can avoid this by using SetCurrentValue to change the value while leaving the value source unchanged.
Secondly, rather assigning the style in your code behind, it is generally easier and more reliable to simply bind the style within the template, e.g.:
<TextBox x:Name="PART_TextBox" Style="{TemplateBinding TextBoxStyle}" ... />
You might try this first and see if you get better results.

Why does an implicit TextBlock style get applied when binding Label.Content to a non-string, but not a string?

I was looking at this question, and discovered that binding Label.Content to a non-string value will apply an implicit TextBlock style, however binding to a string does not.
Here's some sample code to reproduce the problem:
<Window.Resources>
<Style TargetType="Label">
<Setter Property="FontSize" Value="26"/>
<Setter Property="Margin" Value="10"/>
<Setter Property="VerticalAlignment" Value="Center"/>
</Style>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="FontSize" Value="26"/>
<Setter Property="Margin" Value="10"/>
</Style>
</Window.Resources>
<Grid>
<StackPanel Orientation="Horizontal">
<Label Content="{Binding SomeString}" Background="Red"/>
<Label Content="{Binding SomeDecimal}" Background="Green"/>
</StackPanel>
</Grid>
Where the code for the bound values are
SomeDecimal = 50;
SomeString = SomeDecimal.ToString();
And the end result looks like this, with the Margin property from the implicit TextBlock style getting applied to the Label bound to a non-string only:
Both labels get rendered as
<Label>
<Border>
<ContentPresenter>
<TextBlock />
</ContentPresenter>
</Border>
</Label>
When I check out the VisualTree with Snoop, I can see that it looks exactly the same for both elements, except the 2nd TextBlock applies the Margin from the implicit style, while the first does not.
I've used Blend to pull out a copy of the default Label Template, but don't see anything strange there, and when I apply the template to both my labels, the same thing happens.
<Label.Template>
<ControlTemplate TargetType="{x:Type Label}">
<Border BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Background="{TemplateBinding Background}"
Padding="{TemplateBinding Padding}"
SnapsToDevicePixels="True">
<ContentPresenter ContentTemplate="{TemplateBinding ContentTemplate}"
Content="{TemplateBinding Content}"
ContentStringFormat="{TemplateBinding ContentStringFormat}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
RecognizesAccessKey="True"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Label.Template>
It should also be noted that setting a default ContentTemplate to a TextBlock does make both items render without the implicit style, so it must have something to do with when WPF tries to render a non-string value as part of the UI.
<Window.Resources>
<Style TargetType="Label">
<Setter Property="FontSize" Value="26"/>
<Setter Property="Margin" Value="10"/>
<Setter Property="VerticalAlignment" Value="Center"/>
</Style>
<Style x:Key="TemplatedStyle" TargetType="Label" BasedOn="{StaticResource {x:Type Label}}">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock Text="{Binding }"/>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="FontSize" Value="26"/>
<Setter Property="Margin" Value="10"/>
</Style>
</Window.Resources>
<Grid>
<StackPanel Orientation="Horizontal">
<Label Content="{Binding SomeString}" Background="Red"/>
<Label Content="{Binding SomeDecimal}" Background="Green"/>
<Label Content="{Binding SomeString}" Background="Red"
Style="{StaticResource TemplatedStyle}"/>
<Label Content="{Binding SomeDecimal}" Background="Green"
Style="{StaticResource TemplatedStyle}"/>
</StackPanel>
</Grid>
What is the logic that causes a non-string inserted into the UI to be drawn using an implicit TextBlock style, but a string inserted into the UI does not? And where does this occur at?
EDIT: (maybe move this to the bottom?)
And I poked a bit more - and I think I got to the crux of the problem (w/ emphasis on 'I think')
Put this into some Button1_Click or something (again, we need to go 'lazy' on this - as we need the visual tree constructed - we cannot do it on 'Loaded' as we just made the templates - this required better initialization technique true, but it's just a test so who cares)
void Button_Click(object sender, EventArgs e)
{
var insideTextBlock = FindVisualChild<TextBlock>(_labelString);
var value = insideTextBlock.GetProperty<bool>("HasImplicitStyleFromResources"); // false
value = insideTextBlock.GetProperty<bool>("ShouldLookupImplicitStyles"); // true
var boundaryElement = insideTextBlock.TemplatedParent; // ContentPresenter and != null
insideTextBlock = FindVisualChild<TextBlock>(_labelDecimal);
value = insideTextBlock.GetProperty<bool>("HasImplicitStyleFromResources"); // true
value = insideTextBlock.GetProperty<bool>("ShouldLookupImplicitStyles"); // true
boundaryElement = insideTextBlock.TemplatedParent; // == null !!
As mentioned here Implicit styles in Application.Resources vs Window.Resources?
The FindImplicitStyleResource (in FrameworkElement) uses something like...
boundaryElement = fe.TemplatedParent;
And seems that if there is no TemplatedParent (and due to the ways
the TextBlock is constructed within the DefaultTemplate) - there
are no 'boundaries' set - and search for implicit resources / styles -
propagates all the way.
Original Answer: (read this first if you just arrived)
(#dowhilefor and #Jehof already touched on the main things)
I'm not sure this is an 'answer' as such - it's still a guess work - but I needed more space to explain what I think is going on.
You can find the 'ContentPresenter source' code on the web - it's easier than using reflector - just 'google' for it, I'm not posting it here for the obvious reasons :)
It's about the ContentTemplate that is chosen for the ContentPresenter (and in this order)...
ContentTemplate // if defined
ContentTemplateSelector // if defined
FindResource // for typeof(Content) - eg if defined for sys:Decimal takes that one
DefaultTemplate used internally by the presenter
...specific templates are chosen based on typeof(Content)
And indeed it doesn't have anything to do with the Label but any ContentControl or control template that uses ContentPresenter. Or you could bind to resource etc.
Here is a repro of what's going on inside - my goal was to reproduce similar behavior for 'strings' or any type of content.
In XAML just 'name' the labels (and it isn't a typo, a deliberately put strings in both to level the playing field sort of)...
<Label Name="_labelString" Content="{Binding SomeString}" Background="Red"/>
<Label Name="_labelDecimal" Content="{Binding SomeString}" Background="Green"/>
And from code behind (the minimal code that sort of mimics what presenter does):
note: I did it on Loaded as I needed access to the presenter implicitly created
void Window1_Loaded(object sender, RoutedEventArgs e)
{
FrameworkElementFactory factory = new FrameworkElementFactory(typeof(TextBlock));
factory.SetValue(TextBlock.TextProperty, new TemplateBindingExtension(ContentProperty));
var presenterString = FindVisualChild<ContentPresenter>(_labelString);
presenterString.ContentTemplate = new DataTemplate() { VisualTree = factory };
// return;
var presenterDecimal = FindVisualChild<ContentPresenter>(_labelDecimal);
presenterDecimal.ContentTemplate = new DataTemplate();
// just to avoid the 'default' template kicking in
// this is what 'default template' does actually, the gist of it
TextBlock textBlock = new TextBlock();
presenterDecimal.SetProperty(typeof(FrameworkElement), "TemplateChild", textBlock);
textBlock.Text = presenterDecimal.Content.ToString();
First part (for _labelString) does what 'text' template does for strings.
If you return right after that - you'll get the two same looking boxes, no implicit template.
Second part (for _labelDecimal) mimics the 'default template' which is invoked for the 'decimal'.
End result should behave the same as the original example. We
constructed the templates as for the string and decimal - but we
can put anything in the content (if it makes sense of course).
As to why - my guess is something like this (though far from certain - somebody will jump in with something more sensible I guess)...
As per this link FrameworkElementFactory
This class is a deprecated way to programmatically create templates,
which are subclasses of FrameworkTemplate such as ControlTemplate or
DataTemplate; not all of the template functionality is available when
you create a template using this class. The recommended way to
programmatically create a template is to load XAML from a string or a
memory stream using the Load method of the XamlReader class.
And I'm guessing it doesn't invoke any defined styles for the TextBlock.
While the 'other template' (default template) - actually constructs the TextBlock and somewhere along those lines - it actually picks up the implicit style.
Frankly, that's as much as I was able to conclude, short of going through the entire WPF 'internals' and how/where actually styles get applied.
I used this code Finding control within WPF itemscontrol for FindVisualChild.
And the SetProperty is just the reflection - for that one property we need access to to be able to do all this. e.g.
public static void SetProperty<T>(this object obj, string name, T value) { SetProperty(obj, obj.GetType(), name, value); }
public static void SetProperty<T>(this object obj, Type typeOf, string name, T value)
{
var property = typeOf.GetProperty(name, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
property.SetValue(obj, value, null);
}
After going through this question and valuable comments from all, I have done some research on TextBlock Styling.
To my understanding the problem here is not with the Label or TextBlock, it is with the contentpresenter and the controls which use contentpresenter like Label, button and ComboBoxItem.
One of the properties of content presenter from MSDN : http://msdn.microsoft.com/en-us/library/system.windows.controls.contentpresenter.aspx
" If there is a TypeConverter that converts the type of Content to a string, the ContentPresenter uses that TypeConverter and creates a TextBlock to contain that string. The TextBlock is displayed "
In the example above, For SomeString Content presenter is converting it into textblock and applying the TextBlock margin (10) along with Label margin (10) making it 20.
In order to avoid this scenario you need to override the TextBlock style in contentpresenter as shown below
<ContentPresenter >
<ContentPresenter.Resources>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Margin" Value="5" />
</Style>
</ContentPresenter.Resources>
</ContentPresenter>
Following is the changes to your code.
<Window.Resources>
<Style TargetType="Label">
<Setter Property="FontSize" Value="26"/>
<Setter Property="Margin" Value="10"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Label">
<Grid>
<Rectangle Fill="{TemplateBinding Background}" />
<ContentPresenter >
<ContentPresenter.Resources>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Margin" Value="5" />
</Style>
</ContentPresenter.Resources>
</ContentPresenter>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="FontSize" Value="26"/>
<Setter Property="Margin" Value="10"/>
<Setter Property="Foreground" Value="Pink" />
</Style>
</Window.Resources>
<Grid>
<StackPanel Orientation="Horizontal">
<Label Content="{Binding SomeString}" Background="Red" />
<Label Content="{Binding SomeDecimal}" Background="Green"/>
</StackPanel>
</Grid>
</Window>
This explanation is just based on my understanding. Let me know your comments.
Thanks
According to my comment i add more information to the question. Its not a direct answer, but provides additional information to the described problem.
The XAML below will display the described behavior directly in the Designer of Visual Studio and i have narrowed it down to the ContentPresenter, which seems to be source of the problem. The style gets applied to the first both ContentPresenter (intPresenter and boolPresenter), but not the last that uses a string as Content (stringPresenter).
<Window.Resources>
<system:Int32 x:Key="intValue">5</system:Int32>
<system:Boolean x:Key="boolValue">false</system:Boolean>
<system:String x:Key="stringValue">false</system:String>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="FontSize" Value="26" />
<Setter Property="Margin" Value="10" />
</Style>
</Window.Resources>
<Grid>
<StackPanel Orientation="Horizontal">
<ContentPresenter x:Name="intPresenter"
VerticalAlignment="Center"
Content="{StaticResource intValue}" />
<ContentPresenter x:Name="boolPresenter"
VerticalAlignment="Center"
Content="{StaticResource boolValue}" />
<ContentPresenter x:Name="stringPresenter"
VerticalAlignment="Center"
Content="{StaticResource stringValue}" />
</StackPanel>
</Grid>
In the debugger i have analyzed that the stringPresenter uses the DefaultStringTemplate while the intPresenter does not.
Its also interesting that the Language of the intPresenter is set, while by the stringPresenter its not.
And the implementation of the method looks something like that (taken from dotPeek)
private bool IsUsingDefaultStringTemplate
{
get
{
if (this.Template == ContentPresenter.StringContentTemplate || this.Template == ContentPresenter.AccessTextContentTemplate)
return true;
DataTemplate dataTemplate1 = ContentPresenter.StringFormattingTemplateField.GetValue((DependencyObject) this);
if (dataTemplate1 != null && dataTemplate1 == this.Template)
return true;
DataTemplate dataTemplate2 = ContentPresenter.AccessTextFormattingTemplateField.GetValue((DependencyObject) this);
return dataTemplate2 != null && dataTemplate2 == this.Template;
}
}
The StringContentTemplate and AccessTextTemplate are using a FrameworkElementFactory to generate the Visuals.

How can I use a custom TabItem control when databinding a TabControl in WPF?

I have a custom control that is derived from TabItem, and I want to databind that custom TabItem to a stock TabControl. I would rather avoid creating a new TabControl just for this rare case.
This is what I have and I'm not having any luck getting the correct control to be loaded. In this case I want to use my ClosableTabItem control instead of the stock TabItem control.
<TabControl x:Name="tabCases" IsSynchronizedWithCurrentItem="True"
Controls:ClosableTabItem.TabClose="TabClosed" >
<TabControl.ItemTemplate>
<DataTemplate DataType="{x:Type Controls:ClosableTabItem}" >
<TextBlock Text="{Binding Path=Id}" />
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate DataType="{x:Type Entities:Case}">
<CallLog:CaseReadOnlyDisplay DataContext="{Binding}" />
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
EDIT: This is what I ended up with, rather than trying to bind a custom control.
The "CloseCommand" im getting from a previous question.
<Style TargetType="{x:Type TabItem}" BasedOn="{StaticResource {x:Type TabItem}}" >
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabItem}">
<Border
Name="Border"
Background="LightGray"
BorderBrush="Black"
BorderThickness="1"
CornerRadius="25,0,0,0"
SnapsToDevicePixels="True">
<StackPanel Orientation="Horizontal">
<ContentPresenter x:Name="ContentSite"
VerticalAlignment="Center"
HorizontalAlignment="Center"
ContentSource="Header"
Margin="20,1,5,1"/>
<Button
Command="{Binding Path=CloseCommand}"
Cursor="Hand"
DockPanel.Dock="Right"
Focusable="False"
Margin="1,1,5,1"
Background="Transparent"
BorderThickness="0">
<Image Source="/Russound.Windows;component/Resources/Delete.png" Height="10" />
</Button>
</StackPanel>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="FontWeight" Value="Bold" />
<Setter TargetName="Border" Property="Background" Value="LightBlue" />
<Setter TargetName="Border" Property="BorderThickness" Value="1,1,1,0" />
<Setter TargetName="Border" Property="BorderBrush" Value="DarkBlue" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
found a way,
derive a class from TabControl and override this function, in my case I want the items of the tab control (when bound) to be CloseableTabItems
public class CloseableTabControl : TabControl
{
protected override DependencyObject GetContainerForItemOverride()
{
return new CloseableTabItem();
}
}
HTH Someone
Sam
You don't want to set the DataType of the DataTemplate in this case. The value of the ItemTemplate property is used whenever a new item needs to be added, and in the case of a tab control it will be used to create a new TabItem. You should declare an instance of your class within the DataTemplate itself:
<TabControl x:Name="tabCases" IsSynchronizedWithCurrentItem="True" Controls:ClosableTabItem.TabClose="TabClosed">
<TabControl.ItemTemplate>
<DataTemplate>
<Controls:ClosableTabItem>
<TextBlock Text="{Binding Path=Id}" />
</Controls:ClosableTabItem>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate DataType="{x:Type Entities:Case}">
<CallLog:CaseReadOnlyDisplay DataContext="{Binding}" />
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
This will cause a new ClosableTabItem to be created whenever a new tab is added to the TabControl.
Update; From your comment, it sounds like that the ItemTemplate controls what is created within the TabItem, rather than changing the TabItem itself. To do what you want to do, but for a TreeView, you would set the HeaderTemplate. Unfortunately, I don't see a HeaderTemplate property of TabControl.
I did some searching, and this tutorial modifies the contents of the tab headers by adding controls to TabItem.Header. Maybe you could create a Style for your TabItems that would add the close button that your class is currently adding?

Resources