2016-04-06 21 views
6

Vorrei creare un UserControl (in questo caso un pulsante quadrato con Backgroundcolors definiti) che può ospitare il proprio contenuto.Come utilizzare un ContentPresenter all'interno di un UserControl

UserControl:

<UserControl x:Class="SGDB.UI.Controls.ModernButton" 
     xmlns:local="clr-namespace:SGDB.UI.Controls" 
     xmlns:converter="clr-namespace:SGDB.UI.Converter" 
     x:Name="_modernButton"> 
<Button> 
    <Button.Resources> 
     <converter:EnumToColorConverter x:Key="ColorConverter"/> 
    </Button.Resources> 
    <Button.Template> 
     <ControlTemplate> 
      <Border Width="{Binding Size, ElementName=_modernButton}" Height="{Binding Size, ElementName=_modernButton}" BorderBrush="Black" BorderThickness="0.8,0.8,3,3"> 
       <Grid Background="{Binding BackgroundColor, ElementName=_modernButton, Converter={StaticResource ColorConverter}}"> 
        <ContentPresenter/> 
       </Grid> 
      </Border> 
     </ControlTemplate> 
    </Button.Template> 
</Button> 

Ora, come ci si potrebbe aspettare che, se uso questo controllo dentro la mia MainView everthing funziona bene fino a che non definisco alcuni contenuti.

Usando:

<control:ModernButton Size="200" BackgroundColor="Light"> 
    TEST 
</control:ModernButton> 

In questo caso "TEST" sovrascrivere l'intero contenuto di UserControl (l'intero pulsante Template). Immagino che ciò accada perché il Button all'interno di UserControl è definito come "Contenuto" stesso e verrà sovrascritto durante la definizione di nuovo Contenuto.

Quindi la domanda finale è: è possibile ottenere quello che sto cercando? se sì: come? Come posso "reindirizzare" il Contenuto che sto definendo nel mio MainView al ContentPresenter auto-definito all'interno del mio Button Template invece del ContentPresenter di UserControls?

Se possibile non voglio creare un nuovo DP-propery che ospita il mio contenuto, ad esempio:

<controls:MordernButton Size="200" BackgroundColor="Light"> 
    <controls:ModernButton.Content> 
     I don't want this, if possible 
    </controls:ModernButton.Content> 
</controls:ModernButton> 
+0

Vuoi dire che non vuoi creare un nuovo dp per questo? – Gopichandar

+0

Corretto - se possibile, ovviamente. – C4p741nZ

+0

@ Chill-X Vedi la mia risposta qui sotto. Fammi sapere se affronti problemi. – Gopichandar

risposta

5

Qui andiamo.

<UserControl x:Class="SGDB.UI.Controls.ModernButton" 
    xmlns:local="clr-namespace:SGDB.UI.Controls" 
    xmlns:converter="clr-namespace:SGDB.UI.Converter" 
    x:Name="_modernButton"> 

    <UserControl.Template> 
     <ControlTemplate TargetType="UserControl"> 
      <Button Content="{TemplateBinding Content}"> 
       <Button.Resources> 
        <converter:EnumToColorConverter x:Key="ColorConverter"/> 
        </Button.Resources> 
      <Button.Template > 
       <ControlTemplate TargetType="Button"> 
        <Border Width="{Binding Size, 
            ElementName=_modernButton}" 
        Height="{Binding Size, 
            ElementName=_modernButton}" 
        BorderBrush="Black" 
        BorderThickness="0.8,0.8,3,3"> 
         <Grid Background="{Binding BackgroundColor, ElementName=_modernButton, Converter={StaticResource ColorConverter}}"> 
          <ContentPresenter /> 
         </Grid> 
        </Border> 
       </ControlTemplate> 
      </Button.Template> 
      </Button> 
     </ControlTemplate> 
    </UserControl.Template> 
</UserControl> 
+0

All'inizio sembrava molto buono - ma quando si passa del Contenuto ("TEST" o ) non compare nulla - il Il controllo rimane vuoto (tranne il proprio colore) – C4p741nZ

+0

Ecco, potresti spiegare perché il TargetType di Button ha avuto un impatto così grande su ContentPresenter? E se ci sei, forse potresti spiegare perché Buttonbinding di Button impedisce a WPF di cancellare tutto il Contenuto quando passa del Contenuto in un Controllo? Grazie in anticipo :) – C4p741nZ

+1

Questo sarà un grande argomento da trattare. Può essere [questo] (http://stackoverflow.com/a/3632926/2819451) risponde alla tua prima domanda. – Gopichandar

2

Supponiamo che tu sei UserControl è:

<UserControl x:Class="QuickAndDirtyAttempt.Decorator" .... 
     <UserControl.Template> 
     <ControlTemplate TargetType="{x:Type local:Decorator}"> 
      <StackPanel Orientation="Vertical"> 
      <Label>Foo</Label> 
      <ContentPresenter/> 
      <Label>Bar</Label> 
      </StackPanel> 
     </ControlTemplate> 
     </UserControl.Template> 
</UserControl> 

Nota la proprietà TargetType sul modello: senza di esso il progetto sarà felicemente compilato, ma ContentPresenter non funzionerà. E poi:

<Window ... > 
    <StackPanel Orientation="Vertical"> 
     <local:Decorator> 
      <Label Background="Wheat">User supplied content here</Label> 
     </local:Decorator> 
    </StackPanel> 
</Window> 

vi consiglio vivamente di read this prima di attuare qualsiasi cosa

+0

Ho letto questo, ma ho letto la parte che dice: "Senza il TargetType il Progetto si compilerà felicemente ma il ContentPresenter non funzionerà". Ti meriti il ​​mio upvote perché CodeProject spiega perché non si dovrebbe usare la mia soluzione :) – C4p741nZ

2

Semplice; Basta eludere e sostituire il modello UserControl.

<UserControl.Template> 
     <ControlTemplate TargetType="{x:Type UserControl}"> 
      <Button Content="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Content}"> 
       <Button.Resources> 
        <converter:EnumToColorConverter x:Key="ColorConverter"/> 
       </Button.Resources> 
       <Button.Template> 
        <ControlTemplate TargetType="{x:Type Button}"> 
         <Border Width="{Binding Size, 
             ElementName=_modernButton}" 
         Height="{Binding Size, 
             ElementName=_modernButton}" 
         BorderBrush="Black" 
         BorderThickness="0.8,0.8,3,3"> 
          <Grid Background="{Binding BackgroundColor, ElementName=_modernButton, Converter={StaticResource ColorConverter}}"> 
           <ContentPresenter /> 
          </Grid> 
         </Border> 
        </ControlTemplate> 
       </Button.Template> 
      </Button> 
     </ControlTemplate> 
    </UserControl.Template> 

Tutto un controllo utente è (almeno termini di XAML e il suo modello), è un bordo con una ContentPresenter suo interno. Il ContentPresenter è l'unica parte importante, davvero.

Quindi tutto ciò che si fa è sventrare il proprio Template e alimentare la proprietà Content di UserControl in qualcosa di leggermente diverso; in questo caso, il tuo pulsante.

Questa è la differenza tra fare un controllo utente su altri controlli, e spingendo alcuni controlli in un controllo utente. Rendere l'usercontrol fuori da altri controlli ti dà molta più energia.

+1

Pensa che questo potrebbe funzionare anche se il Tipo del secondo ControlTemplate fosse "Button". – C4p741nZ

+0

Oh sì, come mi sono perso? - Lo ha modificato. – Logan

10

Utilizzare lo ContentPropertyAttribute per indicare a xaml di impostare questa proprietà anziché la proprietà Contenuto corrente.

[ContentProperty("InnerContent")] 
public partial class ModernButton : UserControl 
{ 
    public ModernButton() 
    { 
     InitializeComponent(); 
    } 

    public static readonly DependencyProperty InnerContentProperty = 
     DependencyProperty.Register("InnerContent", typeof(object), typeof(ModernButton)); 

    public object InnerContent 
    { 
     get { return (object)GetValue(InnerContentProperty); } 
     set { SetValue(InnerContentProperty, value); } 
    } 
} 

Quindi nel proprio xaml, associare Content Presenter per utilizzare invece la proprietà InnerContent.

<ContentPresenter Content="{Binding InnerContent, ElementName=_modernButton}"/> 

In questo modo è possibile effettuare le seguenti operazioni senza sostituire il contenuto effettivo.

<control:ModernButton Size="200" BackgroundColor="Light"> 
    TEST 
</control:ModernButton> 
+2

Funziona anche. Ma volevo sapere come funzionano le cose in XAML - Non mi piace davvero decorare il codice con gli attributi. – C4p741nZ

+0

Suggerimento eccezionale! poiché non è un override del modello, è possibile definire facilmente nomi e collegamenti con il codice –