2012-03-28 8 views
27

Ho un controllo TextBox con una regola di convalida che si trova in una scheda di un controllo TabControl. L'ErrorTemplate predefinito mostra correttamente (bordo rosso attorno a TextBox) quando la regola di validazione fallisce.
Tuttavia, se si passa a un'altra scheda e si ritorna alla scheda con TextBox, il valore di hightlight ErrorTemplate è esaurito. Se c'è una modifica nel TextBox, la regola di convalida viene ancora chiamata e restituisce false ma l'evidenziazione dell'errore non viene ancora visualizzata.
Solo quando il contenuto del testo viene modificato per essere valido e quindi di nuovo non valido, il ritorno di qualità superiore.
Mi piacerebbe che, se il contenuto del testo non è valido, il passaggio a un'altra scheda e viceversa mantenga l'evidenziazione non valida. Qualche idea per ottenere questo comportamento di benvenuto.
XAML:TextBox con convalida perde ErrorTemplate nella modifica scheda

<TextBox Height="35" > 
    <TextBox.Text> 
    <Binding Path="pan_id" UpdateSourceTrigger="PropertyChanged"> 
     <Binding.ValidationRules> 
     <ps:PanIdValidation /> 
     </Binding.ValidationRules> 
    </Binding> 
    </TextBox.Text> 
</TextBox> 
+0

Stai utilizzando MVVM? – Paparazzi

risposta

47

TabItem dovrebbe essere definita come segue:

<TabItem Header="Foo"> 
    <Border> 
     <AdornerDecorator> 
      <Grid> 
       <TextBox Height="35" > 
        <TextBox.Text> 
         <Binding Path="pan_id" UpdateSourceTrigger="PropertyChanged"> 
          <Binding.ValidationRules> 
           <ps:PanIdValidation /> 
          </Binding.ValidationRules> 
          </Binding> 
         </TextBox.Text> 
        </TextBox> 
       </Grid> 
      </AdornerDecorator> 
     </Border> 
    </TabItem> 

Il problema è, i segnali Validation.Error sono dipinte nello strato Adorner. Quando cambi scheda, quel livello viene scartato.

+0

Perfetto - grazie. – Ricibob

+0

fantastico! funziona anche quando si avviano più controlli che condividono gli stessi attributi di stile. – Salty

+0

Questo mi ha fatto risparmiare un sacco di tempo, grazie! – akagixxer

7

Solo un'aggiunta per casi speciali: stavo avendo un problema simile e ora sto usando una soluzione simile al codice di Dylan.

La differenza è che il mio TabItem contiene GroupBox e il TextBox è al suo interno. In questo caso, AdornerDecorator deve trovarsi nello stesso GroupBox, non un discendente diretto del TabItem.

Quindi questo non ha funzionato:

<TabItem> 
    <AdornerDecorator> 
     <Grid> 
      <GroupBox> 
       <Grid> 
        <TextBox>...<TextBox/> 
       </Grid> 
      </GroupBox> 
     </Grid> 
    </AdornerDecorator> 
</TabItem> 

Ma questo fatto:

<TabItem> 
    <Grid> 
     <GroupBox> 
      <AdornerDecorator> 
       <Grid> 
        <TextBox>...<TextBox/> 
       </Grid> 
      </AdornerDecorator> 
     </GroupBox> 
    </Grid> 
</TabItem> 

sto aggiungendo, perché non riuscivo a trovare la soluzione in modo semplice e anche la documentazione di AdornerLayer.GetAdornerLayer() (anche se non sono sicuro se è applicabile qui) afferma This static method traverses up the visual tree starting at the specified Visual and returns the first adorner layer found. - ma forse si ferma anche a un certo punto, questo non è chiaro dai documenti.

4

Come spiegato da Dylan, questo è dovuto al fatto che il livello Adorner, in cui vengono disegnati gli errori di convalida, viene scartato sul commutatore. Quindi è necessario avvolgere il contenuto con AdornerDecorator.

Ho creato un comportamento che avvolge il Content di TabItem automaticamente in un AdornerDecorator, in modo che esso non deve essere fatto manualmente su tutti i TabItems.

public static class AdornerBehavior 
{ 
    public static bool GetWrapWithAdornerDecorator(TabItem tabItem) 
    { 
     return (bool)tabItem.GetValue(WrapWithAdornerDecoratorProperty); 
    } 
    public static void SetWrapWithAdornerDecorator(TabItem tabItem, bool value) 
    { 
     tabItem.SetValue(WrapWithAdornerDecoratorProperty, value); 
    } 

    // Using a DependencyProperty as the backing store for WrapWithAdornerDecorator. This enables animation, styling, binding, etc... 
    public static readonly DependencyProperty WrapWithAdornerDecoratorProperty = 
     DependencyProperty.RegisterAttached("WrapWithAdornerDecorator", typeof(bool), typeof(AdornerBehavior), new UIPropertyMetadata(false, OnWrapWithAdornerDecoratorChanged)); 

    public static void OnWrapWithAdornerDecoratorChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) 
    { 
     var tabItem = o as TabItem; 
     if (tabItem == null) return; 

     if(e.NewValue as bool? == true) 
     { 
      if (tabItem.Content is AdornerDecorator) return; 
      var content = tabItem.Content as UIElement; 
      tabItem.Content = null; 
      tabItem.Content = new AdornerDecorator { Child = content }; 
     } 
     if(e.NewValue as bool? == false) 
     { 
      if (tabItem.Content is AdornerDecorator) 
      { 
       var decorator= tabItem.Content as AdornerDecorator; 
       var content = decorator.Child; 
       decorator.Child = null; 
       tabItem.Content = content; 
      } 
     } 
    } 
} 

È possibile impostare questo comportamento su tutti TabItems tramite uno stile predefinito:

<Style TargetType="TabItem"> 
    <Setter Property="b:AdornerBehavior.WrapWithAdornerDecorator" Value="True"></Setter> 
</Style> 

b è lo spazio dei nomi in cui si trova il comportamento, qualcosa di simile (sarà diverso per ogni progetto):

xmlns:b="clr-namespace:Styling.Behaviors;assembly=Styling" 
Problemi correlati