2009-01-23 5 views
3

J'ai une fenêtre WPF qui utilise la validation. J'ai créé un modèle d'erreur qui place une bordure rouge autour d'un élément qui échoue à la validation et affiche le message d'erreur ci-dessous. Cela fonctionne correctement, mais le message d'erreur est affiché au-dessus de tous les contrôles sous le contrôle avec l'erreur. Le mieux que je puisse dire, cela se produit parce que le modèle d'erreur rend sur Adorner Layer, qui est au-dessus de tout le reste. Ce que j'aimerais voir se produire, c'est que tout le reste descende pour faire place au message d'erreur. Y a-t-il un moyen de faire cela? Tous les exemples sur le Web semblent utiliser une info-bulle et utilisent un indicateur simple comme un astérisque ou un point d'exclamation qui n'utilise pas beaucoup d'espace.Comment développer un contrôle dans WPF pour faire de la place pour les messages d'erreur dans un ErrorTemplate?

Voici le modèle:

<ControlTemplate x:Key="ValidationErrorTemplate"> 
    <StackPanel> 
     <Border BorderBrush="Red" BorderThickness="2" CornerRadius="2"> 
      <AdornedElementPlaceholder x:Name="placeholder"/> 
     </Border> 
     <TextBlock Foreground="Red" FontSize="10" Text="{Binding ElementName=placeholder, Path=AdornedElement.(Validation.Errors)[0].ErrorContent, FallbackValue=Error!}"></TextBlock> 
    </StackPanel> 
</ControlTemplate> 

Voici les commandes en utilisant le modèle (je tapais une partie de ceci, donc ignorer les erreurs de syntaxe):

<Grid> 
    <Grid.RowDefinitions> 
    <RowDefinition Height="Auto"/> 
    <RowDefinition Height="Auto"/> 
    </Grid.RowDefinitions> 
    <TextBox Name="Account" Grid.Row="0" Validation.ErrorTemplate="{StaticResource ValidationErrorTemplate}" Width="200"> 
    <TextBox.Text> 
     <Binding Path="AccountNumber"> 
     <Binding.ValidationRules> 
      <validators:RequiredValueValidationRule/> 
      <validators:NumericValidationRule/> 
     </Binding.ValidationRules> 
     </Binding> 
    </TextBox.Text> 
    </TextBox> 
    <TextBox Name="Expiration" Grid.Row="1" Validation.ErrorTemplate="{StaticResource ValidationErrorTemplate}" Width="100" Margin="0,2,5,2"> 
    <TextBox.Text> 
     <Binding Path="ExpirationDate"> 
     <Binding.ValidationRules> 
      <validators:ExpirationDateValidationRule/> 
     </Binding.ValidationRules> 
     </Binding> 
    </TextBox.Text> 
    </TextBox> 
</Grid> 

Répondre

5

EDIT: D'accord, je ne suis pas positif que c'est la meilleure solution (j'espère que quelqu'un peut fournir un meilleur), mais ici il va:

Au lieu d'utiliser le Validation.ErrorTeplate, qui présentera tous les visu als dans AdornerLayer, vous pouvez ajouter des TextBlocks et les lier à Validation.HasError et (Validation.Errors) [0] .ErrorContent, en utilisant un client IValueConverter pour convertir le booléen Validation.HasError en valeur de visibilité. Il ressemblerait à quelque chose comme ce qui suit:

Window1.cs:

<Window x:Class="WpfApplicationTest.Window1" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:sys="clr-namespace:System;assembly=mscorlib" 
    xmlns:local="clr-namespace:WpfApplicationTest" 
    Title="Window1" Height="300" Width="300"> 
    <Grid Margin="10"> 
     <Grid.Resources> 
      <!-- The person we are binding to --> 
      <local:Person x:Key="charles" Name="Charles" Age="20" /> 
      <!-- The convert to use--> 
      <local:HasErrorToVisibilityConverter x:Key="visibilityConverter" /> 
     </Grid.Resources> 
     <Grid.RowDefinitions> 
      <RowDefinition Height="Auto" /> 
      <RowDefinition Height="Auto" /> 
      <RowDefinition Height="Auto" /> 
      <RowDefinition Height="Auto" /> 
     </Grid.RowDefinitions> 
     <!-- The name --> 
     <TextBox Name="NameTextBox" Grid.Row="0" Text="{Binding Source={StaticResource charles}, Path=Name, ValidatesOnDataErrors=true}" /> 
     <TextBlock Grid.Row="1" 
        Foreground="Red" 
        Text="{Binding ElementName=NameTextBox, Path=(Validation.Errors)[0].ErrorContent}" 
        Visibility="{Binding ElementName=NameTextBox, Path=(Validation.HasError), Converter={StaticResource visibilityConverter}}" /> 

     <!-- The age --> 
     <TextBox Name="AgeTextBox" Grid.Row="2" Text="{Binding Source={StaticResource charles}, Path=Age, ValidatesOnExceptions=true}" /> 
     <TextBlock Grid.Row="3" 
        Foreground="Red" 
        Text="{Binding ElementName=AgeTextBox, Path=(Validation.Errors)[0].ErrorContent}" 
        Visibility="{Binding ElementName=AgeTextBox, Path=(Validation.HasError), Converter={StaticResource visibilityConverter}}" /> 
    </Grid> 
</Window> 

Person.cs:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.ComponentModel; 
using System.Text.RegularExpressions; 

namespace WpfApplicationTest 
{ 
    public class Person : IDataErrorInfo 
    { 
     public string Name { get; set; } 
     public int Age { get; set; } 

     #region IDataErrorInfo Members 

     string IDataErrorInfo.Error 
     { 
      get { throw new NotImplementedException(); } 
     } 

     string IDataErrorInfo.this[string columnName] 
     { 
      get 
      { 
       switch (columnName) 
       { 
        case ("Name"): 
         if (Regex.IsMatch(this.Name, "[^a-zA-Z ]")) 
         { 
          return "Name may contain only letters and spaces."; 
         } 
         else 
         { 
          return null; 
         } 
        default: 
         return null; 
       } 
      } 
     } 

     #endregion 
    } 
} 

HasErrorToVisibilityConverter.cs:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Windows.Data; 
using System.Windows; 

namespace WpfApplicationTest 
{ 
    [ValueConversion(typeof(bool), typeof(Visibility))] 
    public class HasErrorToVisibilityConverter : IValueConverter 
    { 
     #region IValueConverter Members 

     object IValueConverter.Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 
     { 
      bool hasError = (bool)value; 
      return hasError ? Visibility.Visible : Visibility.Collapsed; 
     } 

     object IValueConverter.ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 
     { 
      throw new NotImplementedException(); 
     } 

     #endregion 
    } 
} 

Il n'échelle ainsi que d'avoir un seul ControlTemplate que vous pouvez référencer dans tous vos contrôles, mais c'est la seule solution que je J'ai trouvé. Je ressens votre douleur - à peu près tous les exemples que je peux trouver sur le sujet de la validation WPF est très simple, et utilise presque toujours '!' ou '*' précédant le contrôle, avec une info-bulle liée à (Validation.Errors) [0] .ErrorContent ...

Bonne chance à toi! Si je trouve une meilleure solution, je vais mettre à jour ce;)

+0

Très cool! J'aimerais pouvoir vous donner plusieurs votes. ;) –

+0

Merci, j'espère que cela a aidé :)! Si vous trouvez un meilleur moyen, faites le moi savoir. J'ai essayé de créer une solution en utilisant les propriétés de dépendance attachées, mais je ne me souviens pas de ce qui est arrivé ... – Charles

Questions connexes