Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

How to Embed Arbitrary Content in a WPF Control with Binding

0.00/5 (No votes)
27 Sep 2016 1  
This is an alternative for How to Embed Arbitrary Content in a WPF Control

Background

I was looking at how to embed content in a Control, and found this article, and used the CustomControl that was included in the How to Embed Arbitrary Content in a WPF Control by , I had one issue with this particular solution and that was Binding. Ivan Krivyokov did not broach this subject in his article and sample.

Introduction

I needed a ScrollViewer with some buttons in the Horizontal ScrollBar. Therefore this article will focus on creating embedded content in a ScrollViewer.

The Control

The control consists of two parts, the C# class derived from a ScrollViewer, and the associated XAML. The C# code is:

public class ScrollBarToolsScrollViewer : ScrollViewer
{
    static ScrollBarToolsScrollViewer()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(ScrollBarToolsScrollViewer),
    new FrameworkPropertyMetadata(typeof(ScrollBarToolsScrollViewer)));
    }

    public object ScrollBarTools
    {
        get { return (object)GetValue(ScrollBarToolsProperty); }
        set { SetValue(ScrollBarToolsProperty, value); }
    }

    // Using a DependencyProperty as the backing store for ScrollBarTools.
// This enables animation, styling, binding, etc...
    public static readonly DependencyProperty ScrollBarToolsProperty=
        DependencyProperty.Register("ScrollBarTools", typeof(object),
    typeof(ScrollBarToolsScrollViewer),
    new UIPropertyMetadata(null, OnScrollBarToolsChanged));

    private static void OnScrollBarToolsChanged(DependencyObject d,
    DependencyPropertyChangedEventArgs e)
    {
        var scrollViewer = (ScrollBarToolsScrollViewer)d;
        var control = e.NewValue as FrameworkElement;
        if (control != null)
            if (scrollViewer.ScrollBarToolsDataContext == null) control.DataContext
        = scrollViewer.DataContext;
            else control.DataContext = scrollViewer.ScrollBarToolsDataContext;
    }

    public object ScrollBarToolsDataContext
    {
        get { return (object)GetValue(ViewBarToolsDataContextProperty); }
        set { SetValue(ViewBarToolsDataContextProperty, value); }
    }

    // Using a DependencyProperty as the backing store for ScrollBarToolsDataContext.
    public static readonly DependencyProperty ViewBarToolsDataContextProperty =
        DependencyProperty.Register("ViewBarToolsDataContext", typeof(object),
    typeof(ScrollBarToolsScrollViewer), new PropertyMetadata(null,
    OnScrollBarToolsDataContextChanged));

    private static void OnScrollBarToolsDataContextChanged(DependencyObject d,
    DependencyPropertyChangedEventArgs e)
    {
        var scrollViewer = (ScrollBarToolsScrollViewer)d;
        var control = scrollViewer.ScrollBarTools as FrameworkElement;
        if (control != null)
            if (scrollViewer.ScrollBarToolsDataContext == null) control.DataContext
        = scrollViewer.DataContext;
            else control.DataContext = scrollViewer.ScrollBarToolsDataContext;
    }
}

This code adds two DependencyProperties to the ScrollViewer. One is for the content to be to the right of the horizontal ScrollBar, the horizontal ScrollBar width being adjusted to accomodate the UIElement and its content. The UIElement can contain a Panel such as a StackPanel, which can contain controls, such as Button controls. The second allows the DataContext for the content specified in these tools.

Whenever the ScrollBarTools or ScrollBarToolsDataContext is changed, the DataContext for the ScrollBarTools is set. If a ScrollBarToolsDataContext is specified, that is set as the DataContext, otherwise the DataContext of the ScrollViewer is set as the DataContext of the ScrollBarTools. Interestingly, if the DataContext for the ScrollBarTools was not set, then the DataContext would be the ScrollBarToolsScrollViewer.

I pulled the Style for ScrollViewer that is in the Generic.xaml file in the Themes folder from existing code:

<Style x:Key="{x:Type local:ScrollBarToolsScrollViewer}"
    TargetType="{x:Type local:ScrollBarToolsScrollViewer}">
    <Setter Property="FocusVisualStyle" Value="{x:Null}" />
    <Setter Property="VerticalScrollBarVisibility" Value="Auto" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type local:ScrollBarToolsScrollViewer}">
                <Grid Background="Transparent">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="*" />
                        <ColumnDefinition Width="Auto" />
                    </Grid.ColumnDefinitions>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="*" />
                        <RowDefinition Height="Auto" />
                    </Grid.RowDefinitions>

                    <ScrollContentPresenter x:Name="PART_ScrollContentPresenter"
                                            Margin="{TemplateBinding Padding}"
                                            CanContentScroll="{TemplateBinding CanContentScroll}"
                                            Content="{TemplateBinding Content}"
                                            ContentTemplate="{TemplateBinding ContentTemplate}" />

                    <ScrollBar x:Name="PART_VerticalScrollBar"
                               Grid.Column="1"
                               AutomationProperties.AutomationId="VerticalScrollBar"
                               Cursor="Arrow"
                               Maximum="{TemplateBinding ScrollableHeight}"
                               Minimum="0.0"
                               ViewportSize="{TemplateBinding ViewportHeight}"
                               Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}"
                               Value="{Binding RelativeSource={RelativeSource TemplatedParent},
                       Path=VerticalOffset, Mode=OneWay}" />
                    <Grid Grid.Row="1" Grid.Column="0">
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="*" />
                            <ColumnDefinition Width="Auto" />
                        </Grid.ColumnDefinitions>
                        <ScrollBar x:Name="PART_HorizontalScrollBar"
                                   AutomationProperties.AutomationId="HorizontalScrollBar"
                                   Cursor="Arrow"
                                   Maximum="{TemplateBinding ScrollableWidth}"
                                   Minimum="0.0"
                                   Orientation="Horizontal"
                                   ViewportSize="{TemplateBinding ViewportWidth}"
                                   Visibility="{TemplateBinding
                           ComputedHorizontalScrollBarVisibility}"
                                   Value="{Binding RelativeSource={RelativeSource TemplatedParent},
                           Path=HorizontalOffset, Mode=OneWay}" />
                        <ContentPresenter Grid.Column="1"
                      ContentSource="ScrollBarTools"
                      DataContext="{Binding \
                    RelativeSource={RelativeSource TemplatedParent}}"/>
                    </Grid>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

The location for the ScrollBarTools is in an added Grid element that contains the horizontal ScrollBar and the new ScrollBarTools. Since this Style depends on existing default Style defined for the ScrollBar, it should look very similar to the other ScrollViewer elements in the design.

History

2016/09/27: Initial Version.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here