How to set GroupStyle for custom control - wpf

I have created a custom control library project and did the followings:
The custom control is derived from ComboBox
Add a resource dictionary file rd.xaml under Themes folder
Define some styles in the rd.xaml file
<Style x:Key="GroupComboBoxStyle" TargetType="{x:Type local:GroupComboBox}">
<Setter Property="ItemContainerStyle" >
<Setter.Value>
<Style TargetType="{x:Type ComboBoxItem}">
<Setter Property="IsEnabled" Value="{Binding Available}"/>
</Style>
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:GroupComboBox}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<WrapPanel IsItemsHost="True" Orientation="Horizontal" Width="150" Height="Auto" >
<!-- add scroll bar -->
</WrapPanel>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock Text="{Binding Item}" Width="40"/>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
<CollectionViewSource x:Key="groupedData" Source="{Binding Items}">
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="Category"/>
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
<Style x:Key="groupComboBoxItemStyle" TargetType="{x:Type ComboBoxItem}">
<Setter Property="Width" Value="50" />
</Style>
<GroupStyle x:Key="groupStyle">
<GroupStyle.HeaderTemplate>
<DataTemplate>
<Border BorderBrush="Black" BorderThickness="2">
<TextBlock Text="{Binding Name}" HorizontalAlignment="Stretch" Background="YellowGreen"/>
</Border>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
Then I want to set the group style to my custom control at runtime
But the groupstyle cannot be found, how to get it from the resource dictionary file?
public GroupComboBox()
{
GroupStyle style = new GroupStyle();
// get the groupstyle
style.HeaderTemplate = (DataTemplate)this.FindResource("groupStyle");
this.GroupStyle.Add(style);
}

A WPF CustomControl is supposed to be lookless. This means, that the code should only contain the control's logic but nothing related to how it looks, styling, etc. This should all be done using the style which is created for you in the Generic.xaml.
Anyway, it is totally valid to wish for a green background in your header... I would suggest to create a bindable dependency property for a DefaultGroupStyle in your control. I've implemented and tested it and it does the trick:
The control GroupComboBox:
public class GroupComboBox : ComboBox
{
public static readonly DependencyProperty DefaultGroupStyleProperty =
DependencyProperty.Register("DefaultGroupStyle", typeof (GroupStyle), typeof (GroupComboBox), new PropertyMetadata(default(GroupStyle), OnDefaultGroupStyleChanged));
private static void OnDefaultGroupStyleChanged(DependencyObject s, DependencyPropertyChangedEventArgs a)
{
var c = (GroupComboBox) s;
if (a.NewValue == null) return;
if (c.GroupStyle.Count == 0)
c.GroupStyle.Add((GroupStyle) a.NewValue);
}
public GroupStyle DefaultGroupStyle
{
get { return (GroupStyle) GetValue(DefaultGroupStyleProperty); }
set { SetValue(DefaultGroupStyleProperty, value); }
}
static GroupComboBox()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(GroupComboBox), new FrameworkPropertyMetadata(typeof(GroupComboBox)));
}
}
and the styles in Generic.xaml (feel free to move the styles to another file but don't forget to merge it into Generic.xaml. Note that I removed the key on the default style for the ComboBox. It won't get applied automatically otherwise...
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfCustomControlLibrary1">
<GroupStyle x:Key="GroupStyle">
<GroupStyle.HeaderTemplate>
<DataTemplate>
<Border BorderBrush="Black" BorderThickness="2">
<TextBlock Text="{Binding Name}" HorizontalAlignment="Stretch" Background="YellowGreen"/>
</Border>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
<Style TargetType="{x:Type local:GroupComboBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:GroupComboBox}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="DefaultGroupStyle" Value="{StaticResource GroupStyle}" />
</Style>
</ResourceDictionary>
Please let me know if this works for you and feel free to ask, if there's anything unclear.

Related

WPF Flexible TabControl Header

I want to have a TabControl with multiple TabItems. These TabItems each have a header text. These texts may vary a lot in length (like 5 chars long and 15 chars long).
I want the TabControl to align the headers in one row only.
All tab headers should use the same width, and when there is enough space available, i want them the to use all the space available, up to a MaxWidth, that is the same for all items.
So if i want to use vMaxWidth` of 100 for 7 items, the tab header should be max 700 in width. If there is more space available, it should be ignored.
If there is less space available, i want that space to be distributed equally between the items. If the text gets cut off, i want to use TextWrapping.
I have tried multiple approaches to this problem now, this is my current setup:
<Style x:Key="Style-TabControl-Main" TargetType="{x:Type TabControl}">
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabControl}">
<Grid KeyboardNavigation.TabNavigation="Local">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Border BorderThickness="0,0,0,1" Margin="13,0,0,0" BorderBrush="{StaticResource Brush-White}">
<StackPanel Panel.ZIndex="1" x:Name="HeaderPanel" IsItemsHost="True" KeyboardNavigation.TabIndex="1" Background="Transparent"
Orientation="Horizontal"/>
</Border>
<Border x:Name="Border"
Grid.Row="1" Grid.ColumnSpan="2"
KeyboardNavigation.TabNavigation="Local"
KeyboardNavigation.DirectionalNavigation="Contained"
KeyboardNavigation.TabIndex="2"
BorderThickness="{TemplateBinding BorderThickness}"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}">
<ContentPresenter x:Name="PART_SelectedContentHost" ContentSource="SelectedContent" />
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
And the TabItem Style
<Style x:Key="Style-TabItem-Main" TargetType="{x:Type TabItem}">
<Setter Property="Height" Value="31"/>
<Setter Property="Width" Value="180" />
<Setter Property="Foreground" Value="{DynamicResource Brush-BrightRegular-Foreground}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabItem}">
<Border x:Name="Border" Cursor="Hand"
Margin="2,0,0,0"
BorderThickness="1,1,1,0"
CornerRadius="4,4,0,0"
BorderBrush="{DynamicResource Brush-BrightRegular-Background}"
Background="{DynamicResource Brush-White}">
<ContentPresenter x:Name="Content" VerticalAlignment="Center" HorizontalAlignment="Stretch" ContentSource="Header" RecognizesAccessKey="True"
TextBlock.TextAlignment="Center" TextBlock.FontSize="16" />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Foreground" Value="{DynamicResource Brush-White}"/>
<Setter TargetName="Border" Property="Background" Value="{DynamicResource Brush-DefaultDark-Background}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I am using a StackPanel instead of a TabPanel to get rid of the "stacking", that occurs, when you resize a default TabControl. However, i cannot get the rest of my requirements to work. I tried applying a MaxWidth (instead of fixed width) to the TabItem headers, but that of course doesn't work, because the item than shrinks to its minimum required size.
Step 1 (first attempt): Put headers in a single row, and give each header the same width.
This can be achieved by using a UniformGrid instead of the standard TabPanel, and lock its row count to 1. Here is a stripped-down version of your TabControl style:
<Style x:Key="Style-TabControl-Main" TargetType="{x:Type TabControl}">
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabControl}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Border>
<UniformGrid x:Name="HeaderPanel" IsItemsHost="True"
Rows="1" />
</Border>
<Border x:Name="Border" Grid.Row="1"
BorderThickness="{TemplateBinding BorderThickness}"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}">
<ContentPresenter x:Name="PART_SelectedContentHost" ContentSource="SelectedContent" />
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Step 2: Restrict headers to a MaxWidth and apply text wrapping.
The MaxWidth can be set in the TabItem style, along with a HeaderTemplate which wraps text (you can still use your custom ControlTemplate here to style the TabItem parts):
<Style x:Key="Style-TabItem-Main" TargetType="{x:Type TabItem}">
<Setter Property="MaxWidth" Value="100" />
<!--https://social.msdn.microsoft.com/forums/vstudio/en-US/df4f7fc3-f0ec-4ed1-a022-a32650e49cb3/how-to-wrap-header-text-in-tabcontrol-->
<Setter Property="HeaderTemplate" >
<Setter.Value>
<DataTemplate>
<TextBlock Text="{Binding}" TextWrapping="Wrap" />
</DataTemplate>
</Setter.Value>
</Setter>
<Setter Property="Template">
...
</Setter>
</Style>
Troubleshooting: Now, if you apply the MaxWidth in Step 2, you'll probably want to left-align the UniformGrid when the TabControl gets too wide..
<UniformGrid x:Name="HeaderPanel" IsItemsHost="True"
Rows="1" HorizontalAlignment="Left" />
..but you don't want that when the MaxWidth hasn't been reached yet, and the items should stretch across the entire width of the TabControl (aka Step 1). So we need a way to switch that HorizontalAlignment depending on whether the items' MaxWidth (if set) has been reached.
Step 1 (revisited): Let's try to make our own UniformGrid:
public class UniformTabPanel : UniformGrid
{
public UniformTabPanel()
{
this.IsItemsHost = true;
this.Rows = 1;
//Default, so not really needed..
this.HorizontalAlignment = HorizontalAlignment.Stretch;
}
protected override Size MeasureOverride(Size constraint)
{
var totalMaxWidth = this.Children.OfType<TabItem>().Sum(tab => tab.MaxWidth);
if (!double.IsInfinity(totalMaxWidth))
{
this.HorizontalAlignment = (constraint.Width > totalMaxWidth)
? HorizontalAlignment.Left
: HorizontalAlignment.Stretch;
}
return base.MeasureOverride(constraint);
}
}
Now, we can replace the UniformGrid in our TabControl style this new panel:
...
<Border>
<mycontrols:UniformTabPanel x:Name="HeaderPanel" />
</Border>
...
...and the TabControl should function as expeced.
Sphinxx answer is correct, however i needed to add the following code to the UniformTabPanel, to make it work like i want (resize the headers to maxwidth when enough space is available)
I added the following code to the UniformTabPanel, and it now does what i need:
protected override Size MeasureOverride(Size constraint)
{
var children = this.Children.OfType<TabItem>();
var totalMaxWidth = children.Sum(tab => tab.MaxWidth);
if (!double.IsInfinity(totalMaxWidth))
{
this.HorizontalAlignment = (constraint.Width > totalMaxWidth)
? HorizontalAlignment.Left
: HorizontalAlignment.Stretch;
foreach (var child in children)
{
child.Width = this.HorizontalAlignment == System.Windows.HorizontalAlignment.Left
? child.MaxWidth
: Double.NaN;
}
}
return base.MeasureOverride(constraint);
}

How to create a custom control which is a combobox with group header

I have a special requirement which need to implement a combobox which list data in groups and I could do it as below
the XAML file
<Window x:Class="GroupComboBox.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<ComboBox x:Name="comboBox" Width="100">
<ComboBox.Resources>
<Style TargetType="{x:Type ComboBox}">
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<WrapPanel IsItemsHost="True" Orientation="Horizontal" Width="150" Height="Auto" >
<!-- add scroll bar -->
</WrapPanel>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type ComboBoxItem}">
<Setter Property="Width" Value="50" />
</Style>
</ComboBox.Resources>
<ComboBox.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<Border BorderBrush="Black" BorderThickness="2">
<TextBlock Text="{Binding Name}" HorizontalAlignment="Stretch" Background="YellowGreen"/>
</Border>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</ComboBox.GroupStyle>
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Item}" Width="40"/>
</DataTemplate>
</ComboBox.ItemTemplate>
<ComboBox.ItemContainerStyle>
<Style TargetType="{x:Type ComboBoxItem}">
<Setter Property="IsEnabled" Value="{Binding Available}"/>
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
</Grid>
</Window>
The code-behind file
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
List<CategoryItem<string>> items = new List<CategoryItem<string>>();
for (int i = 0; i < 18; ++i)
{
items.Add(new CategoryItem<string> { Item = string.Format("{0:D2}", i), Available = (i > 9), Category = "Group A" });
}
for (int i = 0; i < 4; ++i)
{
items.Add(new CategoryItem<string> { Item = string.Format("{0:D2}", i), Available = (i > 2), Category = "Group B" });
}
//Need the list to be ordered by the category or you might get repeating categories
ListCollectionView lcv = new ListCollectionView(items.OrderBy(w => w.Category).ToList());
//Create a group description
lcv.GroupDescriptions.Add(new PropertyGroupDescription("Category"));
this.comboBox.ItemsSource = lcv;
}
}
public class CategoryItem<T>
{
public T Item { get; set; }
public bool Available { get; set; }
public string Category { get; set; }
}
Now I want to make the combobox to be a custom control, then it can be reused easily, but I am new for creating custom control and how should I do?
Currently I have created a WPF custom control library and change the base class of the custom control to be 'ComboBox' , but I do not know how to move the styles and templates to the resource dictionary file correctly
I can offer the following variant. Move all the styles and templates in resources:
<!-- Main style for ComboBox -->
<Style x:Key="MyComboBox" TargetType="{x:Type ComboBox}">
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="Width" Value="100" />
<Setter Property="Height" Value="25" />
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<WrapPanel IsItemsHost="True" Orientation="Horizontal" Width="150" Height="Auto" />
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock Text="{Binding Item}" Width="40"/>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- Style for ComboBoxItem -->
<Style TargetType="{x:Type ComboBoxItem}">
<Setter Property="Width" Value="50" />
</Style>
<!-- Style for ItemContainerStyle -->
<Style x:Key="ComboBoxItemContainerStyle" TargetType="{x:Type ComboBoxItem}">
<Setter Property="IsEnabled" Value="{Binding Available}" />
</Style>
<!-- DataTemplate for HeaderTemplate -->
<DataTemplate x:Key="MyHeaderTemplate">
<Border BorderBrush="Black" BorderThickness="2">
<TextBlock Text="{Binding Name}" HorizontalAlignment="Stretch" Background="YellowGreen" />
</Border>
</DataTemplate>
Using of ComboBox with our styles:
<ComboBox x:Name="MyComboBox1" Style="{StaticResource MyComboBox}" IsSynchronizedWithCurrentItem="False" ItemContainerStyle="{StaticResource ComboBoxItemContainerStyle}">
<ComboBox.GroupStyle>
<GroupStyle HeaderTemplate="{StaticResource MyHeaderTemplate}" />
</ComboBox.GroupStyle>
</ComboBox>
<ComboBox x:Name="MyComboBox2" Style="{StaticResource MyComboBox}" IsSynchronizedWithCurrentItem="False" ItemContainerStyle="{StaticResource ComboBoxItemContainerStyle}">
<ComboBox.GroupStyle>
<GroupStyle HeaderTemplate="{StaticResource MyHeaderTemplate}" />
</ComboBox.GroupStyle>
</ComboBox>
And set the data in code:
this.MyComboBox1.ItemsSource = lcv;
this.MyComboBox2.ItemsSource = lcv;
Set styles for you control need to change Type and write your control name:
<Style x:Key="MyControlComboBox" TargetType="{x:Type local:MyControlComboBox}">
</Style>

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:

Horizontal Scrollbars on a Fluid Listbox

What I want: To get the darn horizontal scrollbar to appear. I will be editing it a bit just so i fits the rest of the app's style scheme, but not too much.
What I have
Here is the code for the listbox as of now. Everything runs perfectly except the scrollbars dont appear. You might say... "well you dont have a scrollviewer anywhere", but I have tried inserting a scrollviewer in numerous places and still no luck.
The Listbox Code:
<ListBox ItemsSource="{Binding Items}" ItemTemplate="{StaticResource itemsdatatemplate}" Background="{x:Null}" BorderBrush="{x:Null}" Foreground="{x:Null}" ItemsPanel="{StaticResource HorizontalListBoxTemplate}" ItemContainerStyle="{StaticResource TransparentListBox}" VerticalAlignment="Center" SelectedItem="{Binding SelectedItem, Mode=TwoWay}" />
The 'TransparentListBox' (to shut-up the selected background color):
<Style x:Key="TransparentListBox" TargetType="ListBoxItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Grid>
<Border x:Name="HoverBorderBackgroundBrush" BorderThickness="1" Margin="0,0,25,0" Background="Transparent"/>
<Border x:Name="SelectedBorderBackgroundBrush" BorderThickness="1" Margin="0,0,25,0" Background="Transparent"/>
<ContentPresenter></ContentPresenter>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Horizontal List Box (to make the listbox Horizontal, rather than standard vertical)
<ItemsPanelTemplate x:Key="HorizontalListBoxTemplate">
<StackPanel Orientation="Horizontal">
</StackPanel>
</ItemsPanelTemplate>
The Datatemplate (to actually show the Items)
<DataTemplate x:Key="itemsdatatemplate">
<local:ListItemControl HorizontalAlignment="Left" VerticalAlignment="Top" DataContext="{Binding}"/>
</DataTemplate>
I have a feeling its going to be a simple addition, but Thanks in advance.
Update
It looks like the scrollbars now do appear with this:
<Style x:Key="ScrollingListBox" TargetType="ListBox">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid>
<ScrollViewer HorizontalScrollBarVisibility="Visible">
<ItemsPresenter></ItemsPresenter>
</ScrollViewer>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
But they do not function accordingly. They feel... broken. However, if one was to define a static width (say 300) of the grid, then the ScrollViewer acts perfectly. Right now I have a completely fluid layout (meaning things stretch to fill), is this not acceptable for scrollviewers?
When you create your own template, you have to define the ScrollViewer in there and use an ItemPresenter instead of a ContentPresenter.
<Style x:Key="TransparentListBox" TargetType="ListBoxItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Grid>
<Border x:Name="HoverBorderBackgroundBrush" BorderThickness="1" Margin="0,0,25,0" Background="Transparent"/>
<Border x:Name="SelectedBorderBackgroundBrush" BorderThickness="1" Margin="0,0,25,0" Background="Transparent"/>
<ScrollViewer x:Name="ScrollViewer" HorizontalScrollBarVisibility="Visible">
<ItemsPresenter />
</ScrollViewer>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

Item Template in the ItemPresenter

In this template ItemPresenter just defines host panel for the Items.
Is it possible to define ItemTemplate?
<ControlTemplate x:Key="ItemsControlTemplate" TargetType="ItemsControl">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<ScrollViewer>
<ItemsPresenter Width="{TemplateBinding Width}" Height="{TemplateBinding Height}"/>
</ScrollViewer>
</Grid>
</ControlTemplate>
To go further, I've created a class:
public class ItemsControlExtended : ItemsControl
{
public ItemsControlExtended()
{
DefaultStyleKey = typeof(ItemsControlExtended);
}
}
And I would like to create a dependency property "ItemsMargin".
After I've done that I'm supposed to bind an Item "Margin" property to "ItemsMargin".
How it would be possible to implement?
GetContainerForItemOverride ?
PrepareContainerForItemOverride ?
OnApplyTemplate ?
You can't define the ItemTemplate from with the ControlTemplate for the control as a whole.
Instead you would create a style that includes your control template and the the other templates as required:-
<Style x:Key="ItemsControlStyle" TargetType="ItemsControl>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ItemsControl">
<!-- your template as above -->
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<!-- An alternative to StackPanel if so desired -->
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
<Setter Property="ItemTemplate">
<Setter.Value>
<DatalTemplate>
<!-- The item template you wanted -->
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
Now you can style the ItemsControl:-
<ItemsControl Style="{StaticResource ItemsControlStyle}">

Resources