1

I want to create a custom control that acts like a ToolBar control but contains pre-built elements in addition to those provided by the user.

As an example, with the code below I need a tool bar that contains predefined elements then the two user provided buttons:

<MyToolBar>
    <Button>User button 1</Button>
    <Button>User button 2</Button>
</MyToolBar>

Which would then be equivalent to:

<ToolBar>
    <Button>Predefined button 1</Button>
    <Button>Predefined button 2</Button>
    <Button>User button 1</Button>
    <Button>User button 2</Button>
</ToolBar>

I have tried inheriting ToolBar class and specify a CompositeCollection as ToolBar.ItemSource, however it fails, throwing

Cannot find governing FrameworkElement or FrameworkContentElement for target element

<!-- Wrong Code -->
<Style TargetType="{x:Type local:MyToolBar}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type local:MyToolBar}">
                <ToolBar>
                    <ToolBar.ItemsSource>
                        <CompositeCollection>
                            <CollectionContainer Collection="{Binding ItemsSource,RelativeSource={RelativeSource TemplatedParent}}" />
                            <Button>Predefined button 1</Button>
                            <Button>Predefined button 2</Button>
                        </CompositeCollection>
                    </ToolBar.ItemsSource>
                </ToolBar>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>
Orace
  • 7,822
  • 30
  • 45
Kidie
  • 21
  • 4

2 Answers2

0

Based on this question, it appear that the binding on CollectionContainer.Collection do not update the container content when the source content change.
You have to use a CollectionViewSource has a proxy:

<Style x:Key="MyToolBarStyle" TargetType="{x:Type ToolBar}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type ToolBar}">
                <ToolBar>
                    <ToolBar.Resources>
                        <CollectionViewSource x:Key="TemplateItems" Source="{TemplateBinding ItemsSource}" />
                    </ToolBar.Resources>
                    <ToolBar.ItemsSource>
                        <CompositeCollection>
                            <Button>Predefined button 1</Button>
                            <Button>Predefined button 2</Button>
                            <CollectionContainer Collection="{Binding Source={StaticResource TemplateItems}}" />
                        </CompositeCollection>
                    </ToolBar.ItemsSource>
                </ToolBar>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>
Orace
  • 7,822
  • 30
  • 45
0

Define a read-only collection-type dependency property and add the default items to it in your class:

[ContentProperty(nameof(ItemsSource))]
public class MyToolBar : Control
{
    private static readonly DependencyPropertyKey s_itemsSourcePropertyKey =
        DependencyProperty.RegisterReadOnly(
          name: nameof(ItemsSource),
          propertyType: typeof(List<FrameworkElement>),
          ownerType: typeof(MyToolBar),
          typeMetadata: new FrameworkPropertyMetadata()
        );

    public MyToolBar()
    {
        SetValue(s_itemsSourcePropertyKey, new List<FrameworkElement>()
        {
            new Button(){ Content = "Predefined button 1" },
            new Button(){ Content = "Predefined button 2" }
        });
    }

    public List<FrameworkElement> ItemsSource =>
        (List<FrameworkElement>)GetValue(s_itemsSourcePropertyKey.DependencyProperty);
}

Template:

<Style TargetType="{x:Type local:MyToolBar}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type local:MyToolBar}">
                <ToolBar ItemsSource="{Binding ItemsSource,
                            RelativeSource={RelativeSource AncestorType=local:MyToolBar}}" />
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

Usage:

<local:MyToolBar>
    <Button>User button 1</Button>
    <Button>User button 2</Button>
</local:MyToolBar>

Result:

enter image description here

mm8
  • 163,881
  • 10
  • 57
  • 88