65.9K
CodeProject is changing. Read more.
Home

How to Embed Arbitrary Content in a WPF Control with Binding

starIconstarIconstarIconstarIconstarIcon

5.00/5 (7 votes)

Sep 27, 2016

CPOL

1 min read

viewsIcon

13130

downloadIcon

100

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.