简体   繁体   中英

How to bind UserControl to content of custom control

I am trying to create a very flexible custom control. The flexibility that I'm trying to achieve to be able to bind UserControl to the ExpanderContent Dependency property, code behind snippet:

public partial class ChartBar : UserControl
{
    public UIElement ExpanderContent
    {
        get { return (UIElement)GetValue(ExpanderContentProperty); }
        set { SetValue(ExpanderContentProperty, value); }
    }

    // Using a DependencyProperty as the backing store for ExpanderContent.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty ExpanderContentProperty =
        DependencyProperty.Register("ExpanderContent", typeof(UIElement), typeof(ChartBar), new PropertyMetadata(null, OnExpanderContentChanged));

    private static void OnExpanderContentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        //throw new NotImplementedException();
    }
    .
    .
    .

I have tried using a ContentPresenter in the XAML but it doesn't work. I obviously can fill with buttons and it works but this defeats the dynamic content via binding.

<Expander x:Name="expander" Header="" VerticalAlignment="Top" d:LayoutOverrides="Width" Style="{DynamicResource ExpanderStyle1}">
    <ContentPresenter Content="{Binding ExpanderContent, ElementName=TestControlWithContent}" />
    <!--<WrapPanel HorizontalAlignment="Center" >
        <Button Content="A" Style="{DynamicResource ButtonStyle1}" />
        <Button Content="B" Style="{DynamicResource ButtonStyle1}" />
        <Button Content="C" Style="{DynamicResource ButtonStyle1}" />
        <Button Content="D" Style="{DynamicResource ButtonStyle1}" />
        <Button Content="E" Style="{DynamicResource ButtonStyle1}" />
        <Button Content="F" Style="{DynamicResource ButtonStyle1}" />
    </WrapPanel>-->
</Expander>

What's even more confusing is I can do

// ChartBarParent is the name of the custom control set in XAML
ChartBarParent.Content = new TestControlWithContent();

and it works as well as fires the callback.

Ultimately, is UIElement in a dependency property and using a ContentPresenter the right way to do this?

Try changing the ContentPresenter to a ContentControl.

Furthermore, you could wrap the UserControl in a DataTemplate and set it as the ContentControl.ContentTemplate allowing you to flow data context through the ContentControl.Content property.

Here's how I would do this. It relies on the SecondaryContent being either UI stuff (like the second example in "Usage" below), or else a viewmodel with an implicit DataTemplate . I could easily add a SecondaryDataTemplateSelector property to give the consumer more explicit control over how that templating happens.

ChartBar.cs

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

    //  Rather than ExpanderContent, we're inheriting ContentControl.Content for the 
    //  main control content. 

    #region SecondaryContent Property
    public Object SecondaryContent
    {
        get { return (Object)GetValue(SecondaryContentProperty); }
        set { SetValue(SecondaryContentProperty, value); }
    }

    public static readonly DependencyProperty SecondaryContentProperty =
        DependencyProperty.Register("SecondaryContent", typeof(Object), typeof(ChartBar),
            new PropertyMetadata(null));
    #endregion SecondaryContent Property

    #region IsExpanded Property
    //  This is optional. I just know I'd end up wanting it. 
    public bool IsExpanded
    {
        get { return (bool)GetValue(IsExpandedProperty); }
        set { SetValue(IsExpandedProperty, value); }
    }

    public static readonly DependencyProperty IsExpandedProperty =
        DependencyProperty.Register("IsExpanded", typeof(bool), typeof(ChartBar),
            new PropertyMetadata(false));
    #endregion IsExpanded Property
}

Themes/Generic.xaml , or else App.xaml, within <Application.Resources> , or some other .xaml resource dictionary included in one or the other.

<ControlTemplate x:Key="ChartBarDefaultTemplate" TargetType="local:ChartBar">
    <!-- 
    Use Binding/RelativeSource TemplatedParent on IsExpanded so it updates both ways, 
    or remove that attribute/binding if you're not bothering with the IsExpanded DP.
    -->
    <Expander 
        x:Name="expander" 
        Header="" 
        VerticalAlignment="Top" 
        Style="{DynamicResource ExpanderStyle1}"
        IsExpanded="{Binding IsExpanded, RelativeSource={RelativeSource TemplatedParent}}"
        >
        <StackPanel Orientation="Vertical">
            <ContentPresenter />
            <ContentControl 
                Content="{TemplateBinding SecondaryContent}" 
                />
        </StackPanel>
    </Expander>
</ControlTemplate>

<Style TargetType="local:ChartBar">
    <Setter 
        Property="Template" 
        Value="{StaticResource ChartBarDefaultTemplate}" 
        />
</Style>

Usage:

<StackPanel Orientation="Vertical" >
    <!-- Control content -->
    <local:ChartBar SecondaryContent="Secondary Content One">
        <StackPanel Orientation="Vertical">
            <Label>Chart Bar</Label>
            <Ellipse Height="30" Width="60" Fill="GreenYellow" Opacity="0.2" />
            <Label>Other stuff, etc. etc.</Label>
        </StackPanel>
    </local:ChartBar>

    <!-- Templated viewmodel content -->
    <local:ChartBar Content="{Binding RandomViewModelProperty}" IsExpanded="True">
        <local:ChartBar.ContentTemplate>
            <DataTemplate>
                <Label Background="Beige" Content="{Binding}" Margin="20" />
            </DataTemplate>
        </local:ChartBar.ContentTemplate>
        <local:ChartBar.SecondaryContent>
            <ComboBox>
                <TextBlock Text="One" />
                <TextBlock Text="Two" />
                <TextBlock Text="Three" />
            </ComboBox>
        </local:ChartBar.SecondaryContent>
    </local:ChartBar>
</StackPanel>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM