How to set an event handler on the ContentTemplate that is defined in a resource file?
window xaml
<!-- this doesnt get triggered -->
<Style x:Key="TabControlEventSetter" TargetType="{x:Type TabControl}">
<EventSetter Event="SelectionChanged"
Handler="TabControl_SelectionChanged"/>
</Style>
<!-- headered content control containing tab control -->
<HeaderedContentControl
x:Name="WorkspaceTabControl"
Header="Workspaces"
Style="{StaticResource MainHCCStyle}"
Content="{Binding Path=Workspaces}"
ContentTemplate="{StaticResource WorkspacesTemplate}"
/>
resource xaml
<!-- configure content area -->
<Style x:Key="MainHCCStyle" TargetType="{x:Type HeaderedContentControl}">
<Setter Property="HeaderTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock Text="{TemplateBinding Content}" />
</DataTemplate>
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type HeaderedContentControl}">
<DockPanel>
<ContentPresenter
DockPanel.Dock="Top"
ContentSource="Header"
ContentTemplate="{TemplateBinding HeaderTemplate}"
/>
<ContentPresenter
ContentSource="Content"
ContentTemplate="{TemplateBinding ContentTemplate}"
/>
</DockPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- Render a Tab Control -->
<DataTemplate x:Key="TabItemTemplate">
<DockPanel >
<ContentPresenter
Content="{Binding Path=DisplayName}"
VerticalAlignment="Center"
/>
</DockPanel>
</DataTemplate>
<!-- Render Workspace -->
<DataTemplate x:Key="WorkspacesTemplate">
<TabControl
IsSynchronizedWithCurrentItem="True"
ItemTemplate="{StaticResource TabItemTemplate}"
ItemsSource="{Binding}"
>
<TabControl.Resources>
<Style TargetType="TabItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TabItem">
<ContentPresenter
x:Name="ContentSite"
ContentSource="Header"
/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</TabControl.Resources>
</TabControl>
</DataTemplate>
window xaml cs
private void TabControl_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (sender is TabItem tab)
{
// handle tab change event
}
}
tried defining a style as suggested here...
Is there Selected Tab Changed Event in the standard WPF Tab Control
which doesnt seem to work.
so, is there a way in xaml to "attach" an event handler to a "contentTemplate" that is defined in a separate resource xaml?
If you define the Style as an implicit one without an x:Key, it will be applied to all TabControl controls in scope that don't have any specific Style applied to them:
<HeaderedContentControl
x:Name="WorkspaceTabControl"
Header="Workspaces"
Style="{StaticResource MainHCCStyle}"
Content="{Binding Path=Workspaces}"
ContentTemplate="{StaticResource WorkspacesTemplate}">
<HeaderedContentControl.Resources>
<Style TargetType="{x:Type TabControl}">
<EventSetter Event="SelectionChanged"
Handler="TabControl_SelectionChanged"/>
</Style>
</HeaderedContentControl.Resources>
</HeaderedContentControl>
Related
I'm learning WPF and I read an article about Templating. So I wanted to write some code, but i got stuck.
What do I want to do? My Application has A TabControl and I want that all the tabs has the same Layout. A stackpanel and in the stackpanel an Image and a Textblock.
Now i don't know how the content can be set afterwards. Do I need a ContentPresenter?
<ControlTemplate x:Key="TabTemplate">
<StackPanel Orientation="Horizontal">
<Image></Image>
<TextBlock></TextBlock>
</StackPanel>
</ControlTemplate>
In your resource dictionary add a Style with your desired template:
<Style x:Key="CustomTabItemStyle"
TargetType="{x:Type TabItem}">
<Setter Property="FocusVisualStyle" Value="{x:Null}" />
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="VerticalContentAlignment" Value="Stretch" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabItem}">
<Grid x:Name="Root"
Width="180"
Height="45"
Margin="0,0,0,0"
SnapsToDevicePixels="true">
<StackPanel Orientation="Horizontal">
<Image Width="90"
Margin="10"
VerticalAlignment="Center"
Source="pack://Application:,,,/img/myTabImage.png"
Stretch="Uniform" />
<TextBlock x:Name="contentPresenter"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Focusable="False"
FontSize="16"
Foreground="White"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
Text="{TemplateBinding Header}" />
</StackPanel>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Don't forget to edit your Image. If all tabs has same image then just change a Source link, otherwise, you may need another binding, e.g Content.
And then simply use this style in your TabItems:
<TabControl Margin="0,5,0,0"
FocusVisualStyle="{x:Null}">
<TabItem Header="My First Tab"
IsSelected="{Binding FirstTabItemSelected}"
Style="{DynamicResource CustomTabItemStyle}">
...
</TabItem>
<TabItem Header="My Second Tab"
IsSelected="{Binding SecondTabItemSelected}"
Style="{DynamicResource CustomTabItemStyle}">
...
</TabItem>
</TabControl>
I have a ContentControl like this:
<ContentControl>
<userControls:TestControl/>
</ContentControl>
OR like this [when i have PRISM system]:
<ContentControl prism:RegionManager.RegionName="TestView"/>
I see the final UserControl well until this step when i start the program.
In the above samples the Content type is UserControl. Now i want give a ControlTemplate to this ContentControl. Then i created a style named StyleTest and used it in my Xaml:
<ContentControl Style="{StaticResource StyleTest}"> .....
My style:
<Style x:Key="StyleTest" TargetType="ContentControl">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid Margin="10">
<Border CornerRadius="10" BorderBrush="#ffffffff" BorderThickness="5">
<StackPanel>
<ContentPresenter Content="{Binding}"/>
<TextBlock>Some additional text to test template</TextBlock>
</StackPanel>
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
But when i start the program the UserControl can not be seen and i just see this text and a border around it: Some additional text to test template
What i must write instead of above line of code to see my UserControl again with a white border around it?
Why the UserControl not showing with above code (above style)?
There are 3 ways to do this.
- by setting the ContentTemplate
- by setting the Template
- or using the Border directly and apply a style.
In this case I would use the Border and apply a style because it looks like the ContentControl is only used to do add a styled Border.
<StackPanel>
<StackPanel.Resources>
<Style x:Key="BorderStyle" TargetType="{x:Type Border}">
<Setter Property="BorderBrush" Value="Red" />
<Setter Property="BorderThickness" Value="5" />
<Setter Property="CornerRadius" Value="10" />
<Setter Property="Margin" Value="10" />
</Style>
<Style x:Key="ContentTemplateStyle" TargetType="{x:Type ContentControl}">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Border Style="{StaticResource BorderStyle}">
<!-- Bind to the DataContext of the DataTemplate which is the Content of the ContentControl -->
<!-- <ContentPresenter Content="{Binding}" />-->
<!-- TemplateBinding improves performance -->
<ContentPresenter Content="{TemplateBinding Content}" />
<!-- Using the TemplatedParent -->
<!--<ContentPresenter Content="{Binding Content, RelativeSource={RelativeSource TemplatedParent}}"/>-->
</Border>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="ControlTemplateStyle" TargetType="{x:Type ContentControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ContentControl">
<Border Style="{StaticResource BorderStyle}">
<ContentPresenter />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</StackPanel.Resources>
<ContentControl Style="{StaticResource ContentTemplateStyle}">
<Button>ContentTemplateStyle</Button>
</ContentControl>
<ContentControl Style="{StaticResource ControlTemplateStyle}">
<Button>ControlTemplateStyle</Button>
</ContentControl>
<Border Style="{StaticResource BorderStyle}">
<Button>BorderStyle</Button>
</Border>
</StackPanel>
datacontext is a type?. Normally it will be inherited in the visual tree so I think it should be left blank.
Content is the content of the parent. Normally you can use a template binding or set the content source property.
but using this template will only show you a white border around content. The original template is lost. So you should provide the entire template for the control.
Now maybe the control contains margin and border properties and you can set those from your style and leave the original template in place.
I found my mistake place and changed this part of my codes:
<Style x:Key="StyleTest" TargetType="ContentControl">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
To this one:
<Style x:Key="StyleTest" TargetType="ContentControl">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate >
When i play with Template its effects is on inner content of the ContentControl (in this question: template of the UserControl) But when i play with ContentTemplate, its effects is on ContentControl layout.
ContentPresenter tag is same and it works now...
<ContentPresenter Content="{Binding}"/>
I basically have this situation:
<Style x:Key="MyListBoxItem" TargetType="{x:Type ListBoxItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<Grid>
<ContentControl Content="{TemplateBinding Content}">
<!-- style stuff-->
</ContentControl>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
and this ListBox in a separate file
<ListBox ItemsSource="{Binding Path=Users}" ItemsContainerStyle="{DynamicResource MyListBoxItem}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Id}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
But when we run it, the listbox only shows .ToString(). It does not bind properly. How do I get the ItemTemplate to hook up properly with the contentcontrol content property?
I have a suspicion I might be doing something very wrong.
In the ListBoxItem style add ContentTemplate="{TemplateBinding ContentTemplate}"
<ContentControl Content="{TemplateBinding Content}" ContentTemplate="{TemplateBinding ContentTemplate}">
<!-- style stuff-->
</ContentControl>
I have created a listbox of expanders like this question: Expander Auto Open/Close
The solution works with content in the expanders when the listbox is the only item on the window.
But when I try to use the same code with my custom control, I'm getting this error:
System.Windows.Data Error: 4 : Cannot find source for binding with
reference 'RelativeSource FindAncestor,
AncestorType='System.Windows.Controls.ListBoxItem',
AncestorLevel='1''. BindingExpression:Path=IsSelected; DataItem=null;
target element is 'Expander' (Name=''); target property is
'IsExpanded' (type 'Boolean')
I've tried adding the IsSelected Property in the ListBox.ItemContainerStyle as one poster suggested in another thread but that failed.
<ListBox Margin="5"
SelectionMode="Single"
ScrollViewer.VerticalScrollBarVisibility="Auto">
<ListBox.Resources>
<Style TargetType="{x:Type Expander}">
<Setter Property="IsExpanded"
Value="{Binding Path=IsSelected, RelativeSource={RelativeSource AncestorType={x:Type ListBoxItem}}}" />
</Style>
<Style TargetType="{x:Type controls:SelectionCriteriaControl}">
<Setter Property="MaxHeight"
Value="200" />
</Style>
</ListBox.Resources>
<Expander Header="Fund Family" Margin="2">
<StackPanel>
<controls:SelectionCriteriaControl DataContext="{Binding FundFamilySelectionCriteria, Mode=TwoWay}" />
</StackPanel>
</Expander>
<Expander Header="Asset Class" Margin="2">
<StackPanel>
<controls:SelectionCriteriaControl DataContext="{Binding AssetClassSelectionCriteria, Mode=TwoWay}" />
</StackPanel>
</Expander>
<ListBox.Template>
<ControlTemplate TargetType="{x:Type ListBox}">
<ItemsPresenter />
</ControlTemplate>
</ListBox.Template>
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<ContentPresenter Content="{TemplateBinding Content}" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
Fails miserably!!!!!
Any help appreciated :)
It's a bit of a large scenario for me to set up at the moment, but something that comes to mind to try:
Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=IsSelected}" />
I don't think where templates are used that you can define relative sources by type.
Edit: This code worked fine: Based on your original, the TemplatedParent wasn't necessary.
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<ListBox x:Name="testList" Margin="5"
SelectionMode="Single"
ScrollViewer.VerticalScrollBarVisibility="Auto">
<ListBox.Resources>
<Style TargetType="{x:Type Expander}">
<Setter Property="IsExpanded"
Value="{Binding Path=IsSelected, RelativeSource={RelativeSource AncestorType={x:Type ListBoxItem}}}" />
</Style>
</ListBox.Resources>
<Expander Header="Fund Family" Margin="2">
<StackPanel>
<TextBlock Text="First"/>
<TextBlock Text="Second"/>
</StackPanel>
</Expander>
<Expander Header="Asset Class" Margin="2">
<StackPanel>
<TextBlock Text="First"/>
<TextBlock Text="Second"/>
</StackPanel>
</Expander>
<ListBox.Template>
<ControlTemplate TargetType="{x:Type ListBox}">
<ItemsPresenter />
</ControlTemplate>
</ListBox.Template>
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<ContentPresenter Content="{TemplateBinding Content}" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
<Button HorizontalAlignment="Left" Content="Test" Grid.Row="1" Width="60" Click="Button_Click"/>
</Grid>
And a quick code-behind to trigger a programmatic SelectedIndex set...
private void Button_Click(object sender, RoutedEventArgs e)
{
testList.SelectedIndex = 1;
}
Seems to work fine for me. Clicking on a list item expands, and even using the button to set it specifically by setting the selected index it expands. Something very fishy is affecting your specific scenario... :]
I have a style for a button with a ControlTemplate something like this
<Style x:Key="ButtonStyle"
BasedOn="{x:Null}"
TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid>
<Rectangle x:Name="rectangle"
Fill="#FF04822A"
Stroke="{x:Null}" />
<TextBlock HorizontalAlignment="Center"
VerticalAlignment="Center"
FontWeight="Heavy"
Foreground="Black"
x:Name="btnText"
TextAlignment="Center" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Button Grid.Row="3"
Grid.Column="5"
Margin="4,0,4,0"
Command="{Binding ResetCommand}"
Content ="Reset Cells"
Style="{StaticResource ButtonStyle}" />
I want the TextBlock to read from the button Content every time its updated.
Add a template binding to the TextBlock:
Text="{TemplateBinding Content}"
You might just want to use a ContentPresenter instead though (as TextBlocks usually only display text).