How to change the custom content of a ToggleButton in XAML - wpf

I have changed the style of a ToggleButton in a ResourceDictionary. In my style I'm changing the ControlTemplate of the ToggleButton and I place an image before the Content.
Now here is my question: The image needs to be changed for different ToggleButtons I use in XAML, should I define different styles for each ToggleButton with different image or is there some way I can change its image in XAML?
<Style x:Key="BaseToggleButton" TargetType="ToggleButton">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToggleButton">
<StackPanel Orientation="Horizontal" x:Name="Border">
<Image Width="13" Height="13" Source="{StaticResource ColumnsLayoutMiniIcon}"/>
<TextBlock>
<ContentPresenter/>
</TextBlock>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Is there a way I can change the image here instead of in style?
<RadioButton Content="Plan View"
GroupName="View"
Style="{StaticResource BaseToggleButton}">
</RadioButton>

Sure, you can piggy back in on an unused property that comes in real handy for this sort of situation called Tag which you can you use to pass in your image path or resource declaration etc once we go bind it to the template like;
<Style x:Key="BaseToggleButton" TargetType="ToggleButton">
<!-- Let's give it a default -->
<Setter Property="Tag" Value="{StaticResource ColumnsLayoutMiniIcon}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToggleButton">
<StackPanel Orientation="Horizontal" x:Name="Border">
<Image Width="13" Height="13"
Source="{TemplateBinding Tag}"/>
<ContentPresenter/>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Now we can either leave as is and it should display that mini icon as your default, you can specify a new one at the instance level like;
<ToggleButton Content="Plan View"
Tag="{StaticResource ADifferentImagePathOrResourcePointingToOne}"
Style="{StaticResource BaseToggleButton}"/>
Hope this helps, cheers.

Related

How do i Style my ComboBox as well as its items in WPF?

I have a ComboBox which i want to style with some custom style. I was successful in providing the required style. In the style i have the following elements :
Template for a toggle button
A pop-up which holds items of the ComboBox
Almost all the links i referred to, used the same approach. But with this approach i am not able to provide a template for the items in the ComboBox. For some reason the item template i am defining is not being used for rendering the items. Could someone help me out?
I am pasting a sample of code to make my problem statement clear(There might be mistakes in the code, i just want an idea to proceed).
<Window.Resources>
<Style x:Key="{x:Type ComboBox}" TargetType="{x:Type ComboBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid>
<ToggleButton Template="{StaticResource MyToggleButton}"/>
<Popup >
<StackPanel>
<Border/>
<ItemsPresenter/>
</StackPanel>
</Popup>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<ComboBox Style="{StaticResource MyStyle}">
<ComboBox.ItemTemplate>
<DataTemplate>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</Grid>
If you're trying to provide a different ControlTemplate for your ComboBoxItem controls you need to set the ItemContainerStyle property on your ComboBox to a Style similar to what you've already done with the parent control, but for ComboBoxItem. ItemTemplate defines a DataTemplate to apply to the data for each item which is then injected into that ComboBoxItem ControlTemplate.
<ComboBox Style="{StaticResource MyStyle}">
<ComboBox.ItemContainerStyle>
<Style TargetType="{x:Type ComboBoxItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ComboBoxItem}">
<Border>
<ContentPresenter/> <!--ItemTemplate is injected here-->
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ComboBox.ItemContainerStyle>
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=SomePropertyOnMyDataObject}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>

How can you enhance a TabControl to dock and float TabItems or documents?

I have a TabControl that allows users to manage documents such as the following:
At some point, I want to add a feature that allows users to float TabItems and dock them back into the TabControl much along the lines of what you can do in Visual Studio. This feature will allow users to more easily compare documents and copy/paste between them, etc.
I have some general ideas on how to go about doing this. The TabControl has its ItemsSource bound to a list of document view models.
To float the tab:
Add a Thumb control to the tab strip area of the TabItem.
When the user drags the Thumb, the associated document view model is removed from the TabControl list.
A separate document Window is brought up, bound with the document view model, to display/edit that document.
To dock the tab:
Add a DragOver event handler in the TabControl to recognise a document Window dragging over the tab strip area.
The associated document view model is added to the TabControl list.
The document Window is closed.
Are there any examples out there on how to do this, or do you have an approach to do this?
Thanks.
If you can't find or don't want to use a pre-existing control, I would highly recommend Bea Stollnitz's article about dragging and dropping between databound controls. You will probably have to alter it a bit to work with a DockPanel to identify what DockPanel.Dock the databound object should use, however I've found the code easy to alter in the past.
You would then setup two databound controls, such as a TabControl and a DockPanel, and when dragging/dropping between the two you are actually dragging/dropping the databound items between the ItemsSources.
I finally got around to implementing this feature, and I used AvalonDock 2.0, which is MVVM friendly. All I needed to do was to replace the TabControl with a DockingManager and modify a few Styles.
The DockingManager setup (I only have documents, not tools, etc.):
<avalonDock:DockingManager x:Name="tabDesigner" DocumentsSource="{Binding Items}">
<avalonDock:DockingManager.LayoutItemContainerStyle>
<Style TargetType="{x:Type avalonDockControls:LayoutItem}" BasedOn="{StaticResource DocumentItem}"/>
</avalonDock:DockingManager.LayoutItemContainerStyle>
<avalonDock:DockingManager.DocumentPaneControlStyle>
<Style TargetType="{x:Type avalonDockControls:LayoutDocumentPaneControl}" BasedOn="{StaticResource DocumentPane}"/>
</avalonDock:DockingManager.DocumentPaneControlStyle>
<avalonDockLayout:LayoutRoot>
<avalonDockLayout:LayoutPanel Orientation="Horizontal">
<avalonDockLayout:LayoutDocumentPane/>
</avalonDockLayout:LayoutPanel>
</avalonDockLayout:LayoutRoot>
</avalonDock:DockingManager>
I didn't need to use AvalonDock's template selectors, I was able to use the DataTemplates that were already set up for the previous TabControl.
I modified the LayoutItem, LayoutDocumentPaneControl, and LayoutDocumentTabItem Styles to do the extra binding to the view models and other layout differences (it took a little while to figure out how to bind to the view models that are within AvalonDock's model):
<Style x:Key="DocumentItem" TargetType="{x:Type avalonDockControls:LayoutItem}">
<Setter Property="Title" Value="{Binding Model.TabTitle}"/>
<Setter Property="CloseCommand" Value="{Binding Model.CloseConfirmCommand}"/>
<Setter Property="IsSelected" Value="{Binding Model.IsSelected, Mode=TwoWay}"/>
</Style>
<Style x:Key="DocumentPane" TargetType="{x:Type avalonDockControls:LayoutDocumentPaneControl}">
...
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type avalonDockControls:LayoutDocumentPaneControl}">
<Grid ClipToBounds="true" SnapsToDevicePixels="true" KeyboardNavigation.TabNavigation="Local">
...
<Grid Panel.ZIndex="1" Background="{DynamicResource TabControlHeaderBrush}" >
...
<avalonDockControls:DocumentPaneTabPanel x:Name="HeaderPanel" Grid.Column="0" IsItemsHost="true" Margin="4,0,16,0" Grid.Row="0" KeyboardNavigation.TabIndex="1"/>
<avalonDockControls:DropDownButton
...
Style="{DynamicResource ToolBarHorizontalOverflowButtonStyle}"
Grid.Column="1">
...
</avalonDockControls:DropDownButton>
</Grid>
<Border x:Name="ContentPanel"
...
CornerRadius="3">
<Border
...
>
<Border
...
>
<ContentPresenter x:Name="PART_SelectedContentHost"
ContentSource="SelectedContent"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</Border>
</Border>
</Border>
</Grid>
<ControlTemplate.Triggers>
...
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="ItemContainerStyle">
<Setter.Value>
<Style TargetType="{x:Type TabItem}">
...
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabItem}">
<Grid>
<ContentPresenter
x:Name="Content"
ContentSource="Header"
...
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Setter.Value>
</Setter>
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate>
<avalonDockControls:LayoutDocumentTabItem Model="{Binding}"/>
</DataTemplate>
</Setter.Value>
</Setter>
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<avalonDockControls:LayoutDocumentControl Model="{Binding}"/>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type avalonDockControls:LayoutDocumentTabItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type avalonDockControls:LayoutDocumentTabItem}">
<ControlTemplate.Resources>
...
</ControlTemplate.Resources>
<Grid x:Name="grid" Margin="8,1,8,0">
...
<Grid RenderTransformOrigin="0.5,0.5">
...
<StackPanel Orientation="Horizontal" Margin="3,0,2,0">
<ContentPresenter x:Name="TabContent" Content="{Binding Model, RelativeSource={RelativeSource TemplatedParent}}" TextBlock.Foreground="{DynamicResource UnselectedTabText}"
ContentTemplate="{Binding DocumentHeaderTemplate, Mode=OneWay, RelativeSource={RelativeSource AncestorType={x:Type avalonDock:DockingManager}, Mode=FindAncestor}}"
ContentTemplateSelector="{Binding DocumentHeaderTemplateSelector, Mode=OneWay, RelativeSource={RelativeSource AncestorType={x:Type avalonDock:DockingManager}, Mode=FindAncestor}}"
Margin="5,2,5,2"/>
<Button
x:Name="TabItemButton"
Command="{Binding Path=Model.Content.CloseConfirmCommand, RelativeSource={RelativeSource TemplatedParent}}"
Content="X"
...
/>
<StackPanel.ContextMenu>
<ContextMenu>
<MenuItem Header="{Binding Model.Content.CloseTabLabel, RelativeSource={RelativeSource TemplatedParent}}" Command="{Binding Model.Content.CloseTab, RelativeSource={RelativeSource TemplatedParent}}" ToolTip="{Binding Model.Content.CloseTabToolTipLabel, RelativeSource={RelativeSource TemplatedParent}}"></MenuItem>
<MenuItem Header="{Binding Model.Content.CloseOtherTabsLabel, RelativeSource={RelativeSource TemplatedParent}}" Command="{Binding Model.Content.CloseOtherTabs, RelativeSource={RelativeSource TemplatedParent}}" ToolTip="{Binding Model.Content.CloseOtherTabsToolTipLabel, RelativeSource={RelativeSource TemplatedParent}}"></MenuItem>
<MenuItem Header="{Binding Model.Content.NextTabLabel, RelativeSource={RelativeSource TemplatedParent}}" Command="{Binding Model.Content.NextTab, RelativeSource={RelativeSource TemplatedParent}}" ToolTip="{Binding Model.Content.NextTabToolTipLabel, RelativeSource={RelativeSource TemplatedParent}}"></MenuItem>
</ContextMenu>
</StackPanel.ContextMenu>
</StackPanel>
</Grid>
</Grid>
<ControlTemplate.Triggers>
...
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
This is an example of the end result:

getting validation into a style

I use IDataErrorInfo on my viewmodels and I have a style (below) for a TextBox with an error template that works ok. I know that "ValidatesOnDataErrors=True" used like:
<TextBox Text="{Binding Path=LastName, ValidatesOnDataErrors=True}"
Style="{StaticResource TextBoxStyle}" />
will force WPF to use IDataErrorInfo but am wondering how to get that baked into my style.
Cheers,
Berryl
<Style x:Key="TextBoxStyle" TargetType="{x:Type TextBox}">
...
<!--
Error handling
-->
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<DockPanel LastChildFill="True">
<TextBlock DockPanel.Dock="Right" Text=" *"
Foreground="Red"
FontWeight="Bold" FontSize="16"
ToolTip="{Binding ElementName=placeholder, Path=AdornedElement.(Validation.Errors).CurrentItem.ErrorContent}"/>
<Border BorderBrush="Red" BorderThickness="1">
<AdornedElementPlaceholder Name="placeholder"></AdornedElementPlaceholder>
</Border>
</DockPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="True">
<Setter Property="Background" Value="LightYellow"/>
</Trigger>
</Style.Triggers>
</Style>
If I understand what you're asking, you want to be able to be able to use the ValidatesOnDataError=True in your style, that so you don't have to repeat it every time.
If that's the case you can't, because that is a property of the data binding and not the style; and you can't template the data binding.
I just wonder if you use a Label instead of a TextBox, then in the style of the Label you can probably do something like this,
<ControlTemplate TargetType="sdk:Label">
<TextBlock x:Name="textBlock" Text="{Binding Content, RelativeSource={RelativeSource TemplatedParent}, ValidatesOnDataErrors=True}">
You can't because that is part of the definition of the binding to your property, not how the error is visualized.

WPF: Problem applying style to custom TabItem Header through ControlTemplate and ContentPresenter.Resources

I am trying to write my own control template for a TabItem Header, and have got the basic layout to work but now I wish to apply styling to the content of the Header, for example to manipulate the size and font of a textblock.
In order to test this, I have put an ellipse in the tabitem header and am attempting to fill that ellipse with the Gold brush through styling. However, it is not working. The ellipse is present, and the control template is being applied, but the fill of the ellipse is not Gold. The style within the ContentPresenter.Resources is being ignored (and Resharper has even greyed it out to prove that). Any ideas what I'm doing wrong? Thanks.
Here is the code:
<TabItem>
<TabItem.Template>
<ControlTemplate x:Name="theTabItemControlTemplate" TargetType="{x:Type TabItem}">
<Border BorderBrush="DarkBlue" BorderThickness="10">
<Grid>
<ContentPresenter ContentSource="Header" RecognizesAccessKey="True">
<ContentPresenter.Resources>
<Style TargetType="{x:Type Ellipse}">
<Setter Property="Ellipse.Fill" Value="Gold"/>
</Style>
</ContentPresenter.Resources>
</ContentPresenter>
</Grid>
</Border>
</ControlTemplate>
</TabItem.Template>
<TabItem.Header>
<Ellipse Stroke="Black" StrokeThickness="2" Width="100" Height="30" Grid.Column="0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
</TabItem.Header>
</TabItem>
Move your style one level upper.ie,move it to ControlTemplate.Resources and it will work fine.I am quite not sure why the code in the question does not work.It may be because the controls in the contentpresenter is already built by the time the style is encountered.
<ControlTemplate x:Name="theTabItemControlTemplate" TargetType="{x:Type TabItem}">
<ControlTemplate.Resources>
<Style TargetType="{x:Type Ellipse}">
<Setter Property="Fill" Value="Red"/>
</Style>
</ControlTemplate.Resources>
<Border BorderBrush="DarkBlue" BorderThickness="10">
<Grid>
<ContentPresenter ContentSource="Header" RecognizesAccessKey="True">
</ContentPresenter>
</Grid>
</Border>
</ControlTemplate>

How can I change the font size of a label in my ControlTemplate

In my WPF ListBox, I have a style with a ControlTemplate for a ListBoxItem. Inside that ControlTemplate I have a label defined. Based on some details, I need to change the font size of the label. So from my code-behind, I need to determine what the font should be and then I need to set it.
Here is my style with the ControlTemplate (I've stripped out some irrelevant controls)
<Style x:Key="RecordTabList" TargetType="{x:Type ListBoxItem}">
<Setter Property="Background" Value="{DynamicResource RecordIndexTabBackcolor}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Label
x:Name="myLabel" Grid.Column="0" Grid.ColumnSpan="1" Grid.Row="0" Grid.RowSpan="1" Margin="3,-2,0,-2" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Foreground="{DynamicResource RecordIndexTabForeground}"
FontSize="10" Height="Auto" BorderThickness="3,0,0,0"
Content="{Binding Path=Name}" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
How can I do this?
If I understand you correctly, you can probably do something similar to the following, and simply change the FontSize property on the ListBoxItem itself; it will be reflected automatically on your Label. Copy this into VS and see it in action!
<Window.Resources>
<Style TargetType="ListBoxItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Label Content="{TemplateBinding Content}" FontSize="{TemplateBinding FontSize}"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<ListBox Margin="12">
<ListBoxItem Content="Test 1" FontSize="14"/>
<ListBoxItem Content="Test 2" FontSize="18"/>
<ListBoxItem Content="Test 3" FontSize="22"/>
</ListBox>
</Grid>
You might be able to use a ValueConverter on the FontSize property.. but I'm not 100% sure if they work inside a ControlTemplate.. I seem to remember Silverlight having issues with it, but I can't remember if it worked in WPF.
If you want to set the FontSize in the code behind, you should remove FontSize from the ControlTemplate, then set it for the ListBoxItem in the code-behind. If you want to set the same size for all the ListBoxItems just set the FontSize of the ListBox in the code-behind.

Resources