Mostra di Errore di Convalida in UserControl

Io non sono sicuro perché lo stato di convalida non ottenere riscontro nel mio controllo utente.
Sto lanciando un’eccezione, ma per qualche motivo il controllo non mostra lo stato di convalida…Quando uso standard Textbox (Che è commentata proprio ora nel mio esempio) sulla mia pagina principale mostra lo stato di errore, non certo perché il suo non quando il suo avvolto.

Ho dimagrito questo modo, in pratica il suo un controllo utente che esegue il wrapping di una TextBox.
Che cosa mi manca??

MyUserControl XAML:

<UserControl x:Class="ValidationWithUserControl.MyUserControl"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400">

    <Grid x:Name="LayoutRoot" Background="White">
        <TextBox x:Name="TextBox"/>
    </Grid>
</UserControl>

MyUserControl Codice Dietro:

public partial class MyUserControl : UserControl
{
    public MyUserControl()
    {
        InitializeComponent();

        this.Loaded += new RoutedEventHandler(MyUserControl_Loaded);
        this.TextBox.Unloaded += new RoutedEventHandler(TextBox_Unloaded);
    }

    public string Value
    {
        get { return (string)base.GetValue(ValueProperty); }
        set { base.SetValue(ValueProperty, value); }
    }

    public static DependencyProperty ValueProperty =
        DependencyProperty.Register(
        "Value",
        typeof(string),
        typeof(MyUserControl),
        new PropertyMetadata(null));

    private void MyUserControl_Loaded(object sender, RoutedEventArgs e)
    {
        this.TextBox.SetBinding(TextBox.TextProperty, new Binding()
        {
            Source = this,
            Path = new PropertyPath("Value"),
            Mode = BindingMode.TwoWay,
            ValidatesOnExceptions = true,
            NotifyOnValidationError= true
        });  
    }

    private void TextBox_Unloaded(object sender, RoutedEventArgs e)
    {
        this.TextBox.ClearValue(TextBox.TextProperty);
    }
}

Mio MainPage XAML:

<Grid x:Name="LayoutRoot" Background="LightBlue">
    <StackPanel>
        <uc:MyUserControl x:Name="UC" Value="{Binding Path=Value, Mode=TwoWay}" Height="20" Width="100" />
        <!--TextBox x:Name="MS" Text="{Binding Path=Value, Mode=TwoWay, ValidatesOnExceptions=True, NotifyOnValidationError=True}" Height="20" Width="100" /-->
    </StackPanel>
</Grid>

Mio MainPage Codice Dietro:

public partial class MainPage : UserControl
{
    private Model model;
    //private Model model2;

    public MainPage()
    {
        InitializeComponent();
        this.model = new Model("UC");
        //this.model2 = new Model("MS");
        this.UC.DataContext = this.model;
        //this.MS.DataContext = this.model2;
    }
}

Mio Modello:

public class Model
{
    public Model(string answer)
    {
        this.answer = answer;
    }

    private string answer;
    public string Value
    {
        get
        {
            return this.answer;
        }
        set
        {
            if (!String.IsNullOrEmpty(value))
                this.answer = value;
            else
                throw new Exception("Error");
        }
    }
}
InformationsquelleAutor Gabe | 2010-11-12



4 Replies
  1. 8

    Ok, finalmente ho capito come gestire questo.

    Quello che dovete fare qui è quello di copiare la validazione da parte del legatura originale e inviarlo alla casella di testo vincolante.

    La prima cosa che dovrete fare per raggiungere questo obiettivo è quello di implementare il INotifyDataErrorInfo interfaccia di controllo utente. Quindi dovrete convalidare il controllo utente per ottenere l’esatto testo di convalida withon il GetErrors funzione (Questo può essere fatto con il di Convalida.GetErrors).

    Questa è una implementazione di base ed è in VB, ma sono sicuro che si ottiene il punto.

        Public Event ErrorsChanged(ByVal sender As Object, ByVal e As System.ComponentModel.DataErrorsChangedEventArgs) Implements System.ComponentModel.INotifyDataErrorInfo.ErrorsChanged
    
    Public Function GetErrors(ByVal propertyName As String) As System.Collections.IEnumerable Implements System.ComponentModel.INotifyDataErrorInfo.GetErrors
        Dim returnValue As System.Collections.IEnumerable = Nothing
    
        Dim errorMessage As String = Nothing
    
    
        If propertyName = "Value" Then
    
            If Validation.GetErrors(Me).Count = 0 Then
                errorMessage = ""
            Else
                errorMessage = Validation.GetErrors(Me).First.ErrorContent.ToString
            End If
    
            If String.IsNullOrEmpty(errorMessage) Then
                returnValue = Nothing
            Else
                returnValue = New List(Of String)() From {errorMessage}
            End If
    
        End If
    
        Return returnValue
    
    End Function
    
    Public ReadOnly Property HasErrors As Boolean Implements System.ComponentModel.INotifyDataErrorInfo.HasErrors
        Get
            Return Validation.GetErrors(Me).Any()
        End Get
    End Property

    La prossima cosa da fare è avvisare il controllo diventa non valido. Sarà necessario fare questo in 2 posti.

    Il primo si terrà il BindingValidationError evento. Il secondo sarà in Valore PropertyChangedCallback funzione (deve essere specificato al momento della registrazione DependencyProperty)

    Public Shared ValueProperty As DependencyProperty = DependencyProperty.Register("Value", GetType(String), GetType(XDateTimePicker), New PropertyMetadata(Nothing, AddressOf ValuePropertyChangedCallback))
    
    Public Shared Sub ValuePropertyChangedCallback(ByVal dependencyObject As DependencyObject, ByVal dependencyPropertyChangedEventArgs As DependencyPropertyChangedEventArgs)
        DirectCast(dependencyObject, MyUserControl).NotifyErrorsChanged("Value")
    End Sub
    
    Private Sub MyUserControl_BindingValidationError(ByVal sender As Object, ByVal e As System.Windows.Controls.ValidationErrorEventArgs) Handles Me.BindingValidationError
        Me.NotifyErrorsChanged("Value")
    End Sub
    
    Public Sub NotifyErrorsChanged(ByVal propertyName As String)
        RaiseEvent ErrorsChanged(Me, New System.ComponentModel.DataErrorsChangedEventArgs(propertyName))
    End Sub

    La maggior parte del lavoro è fatto, ora, ma è ancora necessario apportare alcune modifiche alle associazioni.

    Quando si crea la casella di testo vincolante, è necessario impostare il NotifyOnValidationError False per evitare le notifiche loop tra l’originale vincolante e la casella di testo vincolante. ValidatesOnExceptions, ValidatesOnDataErrors e ValidatesOnNotifyDataErrors bisogno di essere impostato su True.

            Dim binding As New System.Windows.Data.Binding
    
        binding.Source = Me
        binding.Path = New System.Windows.PropertyPath("Value")
        binding.Mode = Data.BindingMode.TwoWay
        binding.NotifyOnValidationError = False 
        binding.ValidatesOnExceptions = True
        binding.ValidatesOnDataErrors = True
        binding.ValidatesOnNotifyDataErrors = True
    
        Me.TextBox1.SetBinding(TextBox.TextProperty, binding)

    Infine, è necessario impostare il NotifyOnValidationError e ValidatesOnNotifyDataErrors proprietà a True, in XAML.

    <uc:MyUserControl x:Name="UC" Value="{Binding Path=Value, Mode=TwoWay, NotifyOnValidationError=True, ValidatesOnNotifyDataErrors=True}" Height="20" Width="100" />
    • Questo mi ha aiutato molto. Grazie!
    • Io sto avendo lo stesso problema e sto seguendo la tua risposta per risolvere il problema, ma non funziona per me. Che cosa mi manca? Questa è la mia domanda.
    • E ‘ difficile dire senza il codice 🙂
    • Oops dimenticato di collegare alla mia domanda, se avete tempo per il check – out – stackoverflow.com/questions/7349627/…
    • Hai fatto a capire come fare quando si hanno più controlli all’interno di UserControl? Mi piacerebbe mostrare bordo rosso intorno a tutta la UserControl..
    • Se avete solo bisogno di un bordo rosso intorno l’intero controllo, quindi devi solo usare l’ultimo frammento di: <uc:MyUserControl x:Name=”UC” Value=”{Binding Path=Valore, Mode=Reciproca, NotifyOnValidationError=True, ValidatesOnNotifyDataErrors=True}” Height=”20″ Width=”100″ />

  2. 3

    Questo comportamento è causato da un aggiunto associazione di livello.
    Le associazioni non supporta l’inoltro errori di convalida.

    Cosa succede dietro le quinte:

    1. Utente inserisce un testo in una casella di testo, e l’associazione definito nella MyUserControl_Loaded passa il valore per il MyUserControl.ValueProperty.
    2. Prossimo, l’associazione definito nella MainPage XAML per MyUserControl passa il valore del Modello.
    3. Un’Eccezione generata nel Modello.Valore.set() è gestito dall’associazione che è stato impostato in MainPage XAML.
    4. Nessuna eccezione è passato in avanti per l’associazione associato con una casella di testo.
    5. Dal UserControl non hanno ValidatesOnExceptions impostato su true, nessuna indicazione visiva viene visualizzato.

    Per risolvere questo problema potrebbe associare la casella di testo direttamente il Modello come questo:

    this.TextBox.SetBinding(TextBox.TextProperty, new Binding()
    {
        Source = this.DataContext, //bind to the originating source
        Path = new PropertyPath("Value"),
        Mode = BindingMode.TwoWay,
        ValidatesOnExceptions = true,
        NotifyOnValidationError= true
    });  

    Da 6 mesi mi chiedo se e come hai fatto a superare questo problema.

    • Questo dovrebbe funzionare in questo caso particolare, ma è solito essere in grado di riutilizzare stesso controllo utente per modello diverso (o dati di contesto) legato a un nome di proprietà diverso “Valore”.
    • Un buon punto. Come una questione di esporre l’intera casella di testo come una proprietà pubblica? O implementare INofifyPropertyChanged sulla casella di testo.L’evento cambiato (per rimuovere il doppio legame)
    • Mi domando cosa succederebbe se hai gestito l’associazione evento di errore nel controllo utente e rilancio di un’eccezione c’è. Proverò più tardi.
  3. 3

    Si dovrebbe essere in grado di eco la proprietà di dipendenza l’associazione direttamente l’usercontrol textbox. Questo pick up errori di convalida stesso modo le associazioni in vista padre sarebbe. Nel MyUserControl_Loaded funzione:

    var valueBinding = BindingOperations.GetBindingBase(this, ValueProperty);
    if (valueBinding != null) TextBox.SetBinding(TextBox.TextProperty, valueBinding);
    • Perfetto! Questo ha risolto il mio problema di convalida.
    • Questo funziona per me, ma ho anche bisogno di impostare la Convalida ErrorTemplate di controllo utente a null: Validation.ErrorTemplate={x:Null} in XAML o Validation.SetErrorTemplate(this, null); nel code-behind.
  4. 1

    Se a qualcuno viene in giro alla ricerca di un bene (leggi: “non è scritto in VBA e completa”) soluzione per questo problema, ho scritto un classe base (anche se io l’ho testato solo con lookless controlli, non so se funziona con UserControls) basato su @The_Black_Smurf risposta in C#:

    namespace MyApplication.Controls
    {
        using System;
        using System.Collections;
        using System.ComponentModel;
        using System.Linq;
        using System.Windows;
        using System.Windows.Controls;
        using System.Windows.Data;
    
        public abstract class ControlBaseWithValidation : Control, INotifyDataErrorInfo
        {
            public ControlBaseWithValidation()
            {
                //remove the red border that wraps the whole control by default
                Validation.SetErrorTemplate(this, null);
            }
    
            public delegate void ErrorsChangedEventHandler(object sender, DataErrorsChangedEventArgs e);
    
            public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
    
            public bool HasErrors
            {
                get
                {
                    var validationErrors = Validation.GetErrors(this);
                    return validationErrors.Any();
                }
            }
    
            public IEnumerable GetErrors(string propertyName)
            {
                var validationErrors = Validation.GetErrors(this);
                var specificValidationErrors =
                    validationErrors.Where(
                        error => ((BindingExpression)error.BindingInError).TargetProperty.Name == propertyName).ToList();
                var specificValidationErrorMessages = specificValidationErrors.Select(valError => valError.ErrorContent);
                return specificValidationErrorMessages;
            }
    
            public void NotifyErrorsChanged(string propertyName)
            {
                if (ErrorsChanged != null)
                {
                    ErrorsChanged(this, new DataErrorsChangedEventArgs(propertyName));
                }
            }
    
            protected static void ValidatePropertyWhenChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
            {
                ((ControlBaseWithValidation)dependencyObject).NotifyErrorsChanged(dependencyPropertyChangedEventArgs.Property.Name);
            }
        }
    }

    utilizzare ControlBaseWithValidation classe come classe base per il lookless controlli invece di Control classe e aggiungere ValidatePropertyWhenChangedCallbackrichiamata la PropertyChangedCallback su qualsiasi proprietà di Dipendenza che si desidera convalidare.

Lascia un commento