如何将 UserControl 绑定到自定义控件的内容

How to bind UserControl to content of custom control

我正在尝试创建一个非常灵活的自定义控件。我正在尝试实现的灵活性,以便能够将 UserControl 绑定到 ExpanderContent Dependency 属性,代码片段后面:

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();
    }
    .
    .
    .

我试过在 XAML 中使用 ContentPresenter 但它不起作用。我显然可以填充按钮并且它可以工作,但这会通过绑定破坏动态内容。

<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>

更迷茫的是我能做到

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

它的工作原理以及触发回调。

最终,UIElement 在依赖项 属性 中并使用 ContentPresenter 是正确的方法吗?

尝试将 ContentPresenter 更改为 ContentControl。

此外,您可以将 UserControl 包装在 DataTemplate 中并将其设置为 ContentControl.ContentTemplate 允许您通过 ContentControl.Content 属性.

流动数据上下文

以下是我的操作方法。它依赖于 SecondaryContent 要么是 UI 东西(就像下面 "Usage" 中的第二个例子),要么是一个带有隐式 DataTemplate 的视图模型。我可以很容易地添加一个 SecondaryDataTemplateSelector 属性 来让消费者更明确地控制模板是如何发生的。

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,或者 App.xaml,在 <Application.Resources> 内,或者其他一些。xaml 包含在一个或另一个中的资源字典。

<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>

用法:

<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>