2010-10-04 11 views
10

Utilizzo di Silverlight 4 & WPF 4, Sto tentando di creare uno stile di pulsante che modifichi il colore del testo di qualsiasi testo contenuto quando il pulsante viene spostato con il mouse. Dal momento che sto cercando di fare questo compatibile sia con Silverlight & WPF, sto usando il manager stato visivo:Nel modello di controllo di un pulsante, come posso impostare il colore del testo contenuto?

<Style TargetType="{x:Type Button}"> 
<Setter Property="Template"> 
    <Setter.Value> 
     <ControlTemplate TargetType="Button"> 
      <Border x:Name="outerBorder" CornerRadius="4" BorderThickness="1" BorderBrush="#FF757679"> 
       <VisualStateManager.VisualStateGroups> 
        <VisualStateGroup x:Name="CommonStates"> 
         <VisualState x:Name="Normal" /> 
         <VisualState x:Name="MouseOver"> 
          <Storyboard> 
           <ColorAnimation Duration="0" To="#FFFEFEFE" 
               Storyboard.TargetProperty="(TextElement.Foreground).(SolidColorBrush.Color)" 
               Storyboard.TargetName="contentPresenter"/> 
          </Storyboard> 
         </VisualState> 
        </VisualStateGroup> 
       </VisualStateManager.VisualStateGroups> 
       <Grid> 
        <Border x:Name="Background" CornerRadius="3" BorderThickness="1" BorderBrush="Transparent"> 
         <Grid> 
          <ContentPresenter x:Name="contentPresenter" ContentTemplate="{TemplateBinding ContentTemplate}"/> 
         </Grid> 
        </Border> 
       </Grid> 
      </Border> 
     </ControlTemplate> 
    </Setter.Value> 
</Setter> 

Dal momento che questo è un modello per un pulsante regolare vecchi, so che non c'è Garantisco che c'è persino un blocco di testo all'interno di esso, e all'inizio non ero sicuro che fosse possibile. Curiosamente, il colore del testo fa cambiamento se il pulsante viene dichiarato come:

<Button Content="Hello, World!" /> 

ma non si cambiamento se il pulsante viene dichiarato come:

<Button> 
    <TextBlock Text="Hello, World!" /> <!-- Same result with <TextBlock>Hello, World </TextBlock> --> 
</Button> 

Anche se l'albero visuale (identico (Button -> ContentPresenter -> TextBlock), con l'avvertenza che il blocco di testo creato nella prima versione ha il suo contesto di dati impostato su "Ciao, Mondo", mentre il blocco di testo nella seconda versione ha semplicemente il suo set di proprietà del testo. Presumo che questo abbia qualcosa a che fare con l'ordine di creazione del controllo (la prima versione del pulsante crea TextBlock, nella seconda versione il blocco di testo potrebbe essere creato per primo? Davvero non sono sicuro su questo).

Nel corso della ricerca di questo, ho visto alcune soluzioni che funzionano in Silverlight (come la sostituzione del ContentPresenter con un ContentControl), ma che non funzionerà in WPF (il programma si blocca effettivamente).

Poiché questo è nel modello di controllo del pulsante e mi piacerebbe utilizzare il VSM se possibile, penso che anche escluda esplicitamente la modifica della proprietà in primo piano del pulsante (non so come accedo a questo da all'interno del modello?)

Apprezzerei molto qualsiasi aiuto, consiglio che qualcuno potrebbe dare.

+0

Sono sorpreso che abbia funzionato a tutti visto che TextElement non ha nulla a che fare con TextBlock. – Denis

risposta

5

Così, dopo un po 'di pensiero, la soluzione che ho in ultima analisi, arrivato a è quello di aggiungere una proprietà associati all'elemento ContentPresenter nel modello di controllo del pulsante. La proprietà allegata accetta un Colore e quando impostato esamina l'albero visivo del presentatore di contenuto per qualsiasi TextBlocks e, a sua volta, imposta le proprietà del Primo piano sul valore passato. Questo potrebbe ovviamente essere espanso/creato per gestire elementi aggiuntivi ma per ora funziona per quello di cui ho bisogno.

public static class ButtonAttachedProperties 
    { 
     /// <summary> 
     /// ButtonTextForegroundProperty is a property used to adjust the color of text contained within the button. 
     /// </summary> 
     public static readonly DependencyProperty ButtonTextForegroundProperty = DependencyProperty.RegisterAttached(
      "ButtonTextForeground", 
      typeof(Color), 
      typeof(FrameworkElement), 
      new FrameworkPropertyMetadata(Color.FromArgb(255, 0, 0, 0), FrameworkPropertyMetadataOptions.AffectsRender, OnButtonTextForegroundChanged)); 

     public static void OnButtonTextForegroundChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) 
     { 
      if (e.NewValue is Color) 
      { 
       var brush = new SolidColorBrush(((Color) e.NewValue)) as Brush; 
       if (brush != null) 
       { 
        SetTextBlockForegroundColor(o as FrameworkElement, brush); 
       } 
      } 
     } 

     public static void SetButtonTextForeground(FrameworkElement fe, Color color) 
     { 
      var brush = new SolidColorBrush(color); 
      SetTextBlockForegroundColor(fe, brush); 
     } 

     public static void SetTextBlockForegroundColor(FrameworkElement fe, Brush brush) 
     { 
      if (fe == null) 
      { 
       return; 
      } 

      if (fe is TextBlock) 
      { 
       ((TextBlock)fe).Foreground = brush; 
      } 

      var children = VisualTreeHelper.GetChildrenCount(fe); 
      if (children > 0) 
      { 
       for (int i = 0; i < children; i++) 
       { 
        var child = VisualTreeHelper.GetChild(fe, i) as FrameworkElement; 
        if (child != null) 
        { 
         SetTextBlockForegroundColor(child, brush); 
        } 
       } 
      } 
      else if (fe is ContentPresenter) 
      { 
       SetTextBlockForegroundColor(((ContentPresenter)fe).Content as FrameworkElement, brush); 
      } 
     } 
    } 

e ho modificato il template in questo modo:

<ContentPresenter x:Name="contentPresenter" 
        ContentTemplate="{TemplateBinding ContentTemplate}" 
        local:ButtonAttachedProperties.ButtonTextForeground="{StaticResource ButtonTextNormalColor}" /> 
+0

Questa è anche una buona soluzione. Sono curioso di sapere quali storyboard di animazione saresti in grado di utilizzare dal momento che stai vincolando a una risorsa statica. – Jeremiah

+0

Ho dovuto usare ObjectAnimationUsingKeyFrames, a differenza di ColorAnimation o qualcosa di simile. – Jordan0Day

4

Questo è un problema. Il primo piano è una proprietà del pulsante che viene passato ai controlli creati dal presentatore di contenuti. Poiché è una proprietà di dipendenza e non una proprietà dei controlli disponibili nel modello, è difficile animarla utilizzando puro xaml.

MSDN Ha un paio di esempi su come cambiare il colore di primo piano. Tuttavia, non sembra che tu voglia fare questo lavoro dal codice. (http://msdn.microsoft.com/en-us/library/system.windows.controls.button(VS.95).aspx)

Il modello predefinito del pulsante ti trattiene. Come avrai notato, il pulsante è un controllo senza contenuto; nel senso che il progettista può spingere qualche oggetto visivo casuale al suo interno. Essendo content-less impone la proprietà foreground per essere un membro del modello piuttosto che il controllo perché non c'è un componente garantito per l'impostazione del colore. Ecco perché c'è un presentatore di contenuti al suo interno.

Quindi ora avete due opzioni. 1. Semplice ma non flessibile (puro xaml), 2. Crea il tuo controllo personale che fa tutto ciò che fa un pulsante (richiede codice e molti test)

Implementerò il numero 1 per te.

Se si modifica il modello del pulsante e rimuovere il presentatore di contenuto è possibile inserire al suo interno due blocchi di testo. Uno con il colore normale, l'altro con il mouse sul colore.

<TextBlock x:Name="RedBlock" Text="{TemplateBinding Content}"  HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" Foreground="Red" Visibility="Collapsed"/>  
<TextBlock x:Name="BlueBlock" Text="{TemplateBinding Content}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" Foreground="#FF0027FF"/> 

Nota come le proprietà di testo dei blocchi di testo sono associate al contenuto del pulsante. Questo diventa un problema se c'è mai bisogno di legarsi a QUALSIASI altro che al testo. TextBlocks semplicemente non può mostrare altro che valori AlphaNumeric.

Si noti inoltre che, per impostazione predefinita, ho ridotto la visibilità su RedBlock.

Poi, nel mio MouseOver VisualState's Storyboard posso animare la RedBlock di essere visibile e la BlueBlock di essere invisibile:

<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="RedBlock"> 
    <DiscreteObjectKeyFrame KeyTime="0"> 
     <DiscreteObjectKeyFrame.Value> 
      <Visibility>Visible</Visibility> 
     </DiscreteObjectKeyFrame.Value> 
    </DiscreteObjectKeyFrame> 
</ObjectAnimationUsingKeyFrames> 
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="BlueBlock"> 
    <DiscreteObjectKeyFrame KeyTime="0"> 
     <DiscreteObjectKeyFrame.Value> 
      <Visibility>Collapsed</Visibility> 
     </DiscreteObjectKeyFrame.Value> 
    </DiscreteObjectKeyFrame> 
</ObjectAnimationUsingKeyFrames> 

Ci si sente come un hack, e probabilmente non avrebbe applicato questo pulsante in un sacco di posti . Vorrei creare il mio pulsante con buone proprietà di dipendenza da associare. Uno per HighlightForeground e Text. Vorrei anche nascondere o almeno lanciare un'eccezione se qualcuno ha tentato di impostare il contenuto su valori diversi da quelli AlphaNumeric. Tuttavia, XAML sarebbe lo stesso, avrei diversi blocchi di testo per diversi stati visivi.

Spero che questo aiuti.

Buona giornata.

Geremia

+0

Ciao Jeremiah, questa è una soluzione davvero carina e funziona bene in una situazione in cui il pulsante conterrebbe solo testo. Nel mio caso mi piacerebbe avere una soluzione più generica, in modo che tutti i bambini del blocco di testo abbiano i loro colori in primo piano impostati. (Qualsiasi cosa interessante da notare è che il pulsante funziona quando si imposta la proprietà Foreground in modo esplicito - qualsiasi TextBlocks figlio riceve il colore appropriato, indipendentemente da come siano nidificati all'interno di pannelli/controlli che si trovano nel contenuto del pulsante. Alla fine sono arrivato ad usare le proprietà associate in questa discussione – Jordan0Day

0

Io uso questo nel mio modello e funziona benissimo

<ControlTemplate TargetType="Button"> 
       <Border 
        Margin="{TemplateBinding Margin}" 
        BorderBrush="{StaticResource BorderBrush}" 
        BorderThickness="{TemplateBinding BorderThickness}" 
        CornerRadius="2" 
        > 

        <TextBlock 
         Margin="{TemplateBinding Padding}" 
         Text="{TemplateBinding Content}" 
         Foreground="White" 
         HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" 
         VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/> 
       </Border> 

È necessario inserire questo codice nel tuo creazione del modello di pulsante ma lo uso quando inserisco del testo nel contenuto del pulsante e se lo desideri per aggiungere altri controlli su di esso utilizzare lo stile normale

Problemi correlati