Показать ошибку проверки в UserControl - PullRequest
13 голосов
/ 12 ноября 2010

Я не уверен, почему состояние проверки не отражается в моем пользовательском элементе управления. Я выбрасываю исключение, но по какой-то причине элемент управления не показывает состояние проверки ... Когда я использую стандарт Textbox (который закомментирован прямо сейчас в моем примере) на моей главной странице, он показывает состояние ошибки, а не уверен, почему его нет, когда его завернут.

Я уменьшил это, так что в основном это пользовательский элемент управления, который оборачивает TextBox. Чего мне не хватает ??

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 позади:

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);
    }
}

Моя основная страница 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>

Код моей главной страницы:

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;
    }
}

Моя модель:

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");
        }
    }
}

Ответы [ 4 ]

8 голосов
/ 05 мая 2011

Хорошо, я наконец-то понял, как с этим справиться.

Здесь нужно скопировать подтверждение из исходной привязки и отправить его в привязку Textbox.

Первое, что вам нужно сделать для достижения этой цели, - это реализовать интерфейс INotifyDataErrorInfo в вашем пользовательском элементе управления.Затем вам нужно будет проверить пользовательский элемент управления, чтобы получить точный текст проверки с помощью функции GetErrors (это можно сделать с помощью Validation.GetErrors ).

Это базовая реализация иэто в VB, но я уверен, что вы поняли.

    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

Следующее, что нужно сделать, это уведомить вас, что контроль становится недействительным.Вам придется сделать это в 2 местах.

Первый будет в событии BindingValidationError .Второй будет в функции Value PropertyChangedCallback (должен быть указан при регистрации 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

Большая часть работы уже выполнена, но вам все еще нужно выполнитьнекоторые корректировки привязок.

При создании привязки TextBox необходимо установить для NotifyOnValidationError значение False, чтобы избежать цикла уведомлений между исходной привязкой и привязкой Textbox. ValidatesOnExceptions , ValidatesOnDataErrors и ValidatesOnNotifyDataErrors необходимо установить в значение 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)

В заключение вам необходимо установить NotifyOnroralalid и ValidatesOnNotifyDataErrors свойство True в вашем XAML.

<uc:MyUserControl x:Name="UC" Value="{Binding Path=Value, Mode=TwoWay, NotifyOnValidationError=True, ValidatesOnNotifyDataErrors=True}" Height="20" Width="100" />
3 голосов
/ 19 декабря 2018

Вы должны иметь возможность отображать привязку свойства зависимостей непосредственно к текстовому полю usercontrol. Это приведет к ошибкам проверки так же, как привязки в родительском представлении. В вашей функции MyUserControl_Loaded:

var valueBinding = BindingOperations.GetBindingBase(this, ValueProperty);
if (valueBinding != null) TextBox.SetBinding(TextBox.TextProperty, valueBinding);
3 голосов
/ 30 апреля 2011

Это поведение вызвано добавленным уровнем привязки. Привязки не поддерживают пересылку ошибок проверки.

Что происходит за кадром:

  1. Пользователь вводит текст в TextBox, и привязка, определенная в MyUserControl_Loaded, передает значение в MyUserControl.ValueProperty.
  2. Затем привязка, определенная в MainPage XAML для MyUserControl, передает значение в модель.
  3. Исключение, выданное в Model.Value.set (), обрабатывается привязкой, установленной в MainPage XAML.
  4. Не передается исключение для привязки, связанной с TextBox.
  5. Так как UserControl не имеет ValidatesOnExceptions, установленных на true, визуальная индикация не отображается.

Чтобы решить эту проблему, вы можете привязать текстовое поле непосредственно к модели следующим образом:

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
});  

С тех пор, как прошло 6 месяцев, мне интересно, и как вы преодолели эту проблему?

1 голос
/ 13 декабря 2016

Если кто-то придет в поисках хорошего (читай: «не написано в VBA и завершено») решения этой проблемы, я напишу базовый класс (хотя я тестировал его только с элементами управления без внешнего вида). , не знаю, работает ли он с UserControl s) на основе ответа @ The_Black_Smurf в 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);
        }
    }
}

используйте класс ControlBaseWithValidation в качестве базового класса для элементов управления без внешнего вида вместо класса Control и добавьте обратный вызов ValidatePropertyWhenChangedCallback в качестве PropertyChangedCallback для любых свойств зависимости, которые вы хотите проверить.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...