2016-08-31 6 views
0

Ich versuche, ein sehr flexibles benutzerdefiniertes Steuerelement zu erstellen. Die Flexibilität, die ich versuche, in der Lage zu erreichen, um UserControl zum ExpanderContentDependency Eigenschaft, Code-behind-Schnipsel zu binden:So binden Sie UserControl an den Inhalt des benutzerdefinierten Steuerelements

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

Ich habe versucht, ein ContentPresenter in der XAML verwenden, aber es funktioniert nicht. Ich kann natürlich mit Tasten füllen und es funktioniert, aber das schlägt den dynamischen Inhalt durch Bindung.

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

Was noch verwirrender ist, das ich tun kann,

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

und es funktioniert sowie den Rückruf ausgelöst.

Letztendlich ist UIElement in einer Abhängigkeitseigenschaft und mit einem ContentPresenter der richtige Weg, dies zu tun?

+1

[Wie Inhalt Benutzersteuerung hinzufügen] (http://stackoverflow.com/q/10427133/1997232). – Sinatr

+0

Ich habe diesen Link angeschaut, aber die Lösung bestand darin, typeof (Objekt) zu verwenden, was ich nicht erreichen wollte. Ich wollte UIElement oder FrameworkElement verwenden. Fehle ich etwas (sehr möglich)? – Jason

+0

'ContentPresenter' soll in einer' ControlTemplate' verwendet werden, und Sie füllen seinen Inhalt nicht, indem Sie seine 'Content' -Eigenschaft binden, sondern indem Sie seine' ContentSource' -Eigenschaft auf den Namen einer Eigenschaft auf der Vorlage setzen ('ContentSource 'hat einen Standardwert von' "Content" ', weshalb man oft einen Eintrag ohne Attribute einfügen kann und das magisch was du meinst). Wenn diese bestimmte Verwendung nicht für das gilt, was Sie tun, tun Sie dies, wie @ibebbs vorschlägt, und verwenden Sie stattdessen "ContentControl". –

Antwort

0

So würde ich das machen. Es beruht auf der SecondaryContent entweder UI-Zeug (wie das zweite Beispiel in "Verwendung" unten), oder ein Viewmodel mit einem impliziten DataTemplate. Ich könnte leicht eine SecondaryDataTemplateSelector Eigenschaft hinzufügen, um dem Verbraucher eine explizitere Kontrolle darüber zu geben, wie das Templating geschieht.

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, sonst App.xaml, innerhalb <Application.Resources> oder ein anderes XAML-Ressourcenverzeichnis in der einen oder anderen enthält.

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

Verbrauch:

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

Ok. Option A ist viel mehr von dem, was ich erreichen möchte. Die Buttons A-F waren einfach Platzhalter, um sicherzustellen, dass der Expander korrekt funktionierte und diese Platzhalter werden in Ihrem "Verwendungs" -Fall durch ein 'UserControl' ersetzt (eine Ansicht mit einem entsprechenden Viewmodel). Der leere '' ist mein Anliegen. – Jason

+0

@Jason Ist dieser sekundäre View/ViewModel-Inhalt immer gleich, oder würden Sie das auch parametrisieren? –

+0

Das Ansichtsmodell wird basierend auf dem, an dem dieser benutzerdefinierte Expander angehängt ist (OHLC-Diagramm, Indikatorfenster usw.), dynamisch über IoC aufgelöst. Deshalb wollte ich, dass der Inhalt des Expanders so flexibel wie möglich ist, da er verschiedene Benutzersteuerelemente mit unterschiedlichem Inhalt hostet – Jason

1

Versuchen Sie, den ContentPresenter zu einem ContentControl zu ändern.

Darüber hinaus könnten Sie das UserControl in ein DataTemplate umbrechen und es als ContentControl.ContentTemplate festlegen, sodass Sie Datenkontext über die ContentControl.Content-Eigenschaft fließen lassen können.

+0

Ich werde es versuchen. Vielen Dank. – Jason

+0

Es spielte keine Rolle, ob ich 'ContentPresenter' oder' ContentControl' benutzte, um die Bindung zu erhalten, aber beide würden den gebundenen Inhalt nicht anzeigen. – Jason

Verwandte Themen