2012-07-03 7 views
3

У меня есть форма с двумя полями пароля - одна, где пользователь вводит пароль, а другой, где пользователь должен повторно ввести этот пароль для подтверждения. Валидация используется для подтверждения того, что оба пароля совпадают - если они делают, кнопка включена, чтобы позволить пользователю перейти:Проверка нескольких элементов управления одновременно в WPF

<PasswordBox Name="P1Box" src:PasswordBoxAssistant.BindPassword="True"> 
    <src:PasswordBoxAssistant.BoundPassword> 
     <Binding Source="{StaticResource mybinding}" Path="Password.P1" UpdateSourceTrigger="PropertyChanged" Mode="TwoWay"> 
      <Binding.ValidationRules> 
       <DataErrorValidationRule ValidatesOnTargetUpdated="True"/> 
      </Binding.ValidationRules> 
     </Binding> 
    </src:PasswordBoxAssistant.BoundPassword> 
</PasswordBox> 

Кнопка Стиль:

<Setter Property="IsEnabled" Value="False"/> 
<Style.Triggers> 
    <MultiDataTrigger> 
     <MultiDataTrigger.Conditions> 
      <Condition Binding="{Binding ElementName=P1Box, Path=(Validation.HasError)}" Value="false"/> 
     </MultiDataTrigger.Conditions> 
     <Setter Property="IsEnabled" Value="true"/> 
    </MultiDataTrigger> 
</Style.Triggers> 

Моя проблема заключается в том, что пользователь может изменить пароль в первом поле, и он не заставит второе поле пароля повторить проверку. Например, если первый пароль вводится как «пароль», а пользователь вводит «пароль» во второй блок, проверка проходит и кнопка включается. Если пользователь затем изменит исходное поле пароля на «ПАРОЛЬ», оба окна остаются проверенными - оригинал, потому что нет ограничений на непустой пароль, второй - потому что ничто не заставило проверку обновить.

В моих блоках ввода пароля используется связанное свойство here, чтобы разрешить привязку к паролю. Из-за этого я не могу найти способ получить доступ к нему в коде (поскольку PasswordBox.Password не является свойством зависимостей сам по себе) способом, выраженным в this solution. Или, возможно, он просто не работает для прикрепленных свойств привязка объекта PasswordData и коробки паролей устанавливаются на PasswordData -

P2Box.GetBindingExpression(PasswordBoxAssistant.BoundPassword).UpdateSource(); 

у меня есть пользовательский класс, который наследует IDataErrorInfo, чтобы для проверки между двумя элементами управления: - код ниже ничего не делать. P1 и PasswordData.P2:

public class PasswordData : IDataErrorInfo 
{ 
    public string P1 { get; set; } 
    public string P2 { get; set; } 
    public string Error { get { return string.Empty; } } 
    public string this[string propertyName] 
    { 
     get 
     { 
      string ret; 
      if (propertyName == "P1") 
      { 
      if (P1 == null || P1 == "") 
       ret = "Password cannot be null or empty."; 
      else 
       ret = ""; 
      } 
      else if (propertyName == "P2") 
      { 
      if (P2 == null || P2 == "") 
       ret = "Password cannot be null or empty."; 
      else if (P2 != P1) 
       ret = "The passwords do not match."; 
      else 
       ret = ""; 
      } 
      return ret; 
     } 
    } 
} 

Я попытался скачкообразной в ходе PasswordChanged события, создавая новые PasswordData и переназначение привязки. Это решает проблему проверки, но каретка в пароле всегда находится в самом начале, разрушая введенные данные.

Я хотел бы иметь решение xaml, но код позади вполне приемлем. Я использую .Net 4.0, если это имеет значение.

EDIT:

ОК, так получается, что я неправильно набранный обработчик события в XAML, и решение действительно работает:

private void PasswordChanged(object sender, RoutedEventArgs e) 
{ 
    binding.Pass.P1 = ((PasswordBox)sender).Password; 
    P2Box.GetBindingExpression(PasswordBoxAssistant.BoundPassword).UpdateSource(); 
} 

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

Мне любопытно, существует ли правильный способ XAML, используя правила проверки, IDataErrorInfo или некоторые другие средства для привязки между элементами управления без необходимости подключаться к событиям и вручную обновлять.

+0

+1 Очень хорошо написанный вопрос. – JDB

ответ

2

Для более сложной проверки часто бывает необходимо подталкивать вашу проверку в ViewModel или даже Model во многих случаях. IDataErrorInfo - хорошее начало.

Вот ссылка на отличную статью на тему:
http://msdn.microsoft.com/en-us/magazine/ff714593.aspx

+0

Я собираюсь пойти дальше и принять это как ответ - похоже, что это просто ограничение WPF, что проверка не работает между элементами управления. Спасибо Cy! – Tom

+0

Это не ограничение WPF - проблема связана с тем, что вы пытаетесь проверить? Когда вы проверяете, что строка представляет числовое значение, тогда проверка будет узко сфокусирована. Но если вы проверяете, что одно значение в форме равно другому значению в форме, то вы не проверяете отдельные значения, а форму в целом. Форма действительна или недействительна в зависимости от значений внутри этих текстовых полей. Конечно, в WPF «форма» обычно представляет собой модель представления. При проверке модели представления, IDataErrorInfo - хороший маршрут. – JDB

1

Это может быть полезным. Я вижу много сообщений, где Data Annotations используются в ваших классах моделей. Когда вы привязываете модель к контексту в WPF, вы можете заметить, что проверка не срабатывает до того, как данные не будут введены.

Это может быть проблематично, и итерация по выражениям привязки может быть болью. Поэтому я подключил пару расширений для этого.

Это приведет к перечислению вашего типа, как TextBox. DependencyObject может быть вашим окном или сеткой. Чтобы ссылаться на ваше окно, используйте var window = Window.GetWindow (это). Вы также можете использовать элемент управления как сетку по имени.

public static IEnumerable<T> FindVisualChildren<T>(DependencyObject obj) where T : DependencyObject 
    { 
     if (obj != null) 
     { 
      for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++) 
      { 
       DependencyObject child = VisualTreeHelper.GetChild(obj, i); 
       if (child != null && child is T) 
       { 
        yield return (T)child; 
       } 

       foreach (T childOfChild in FindVisualChildren<T>(child)) 
       { 
        yield return childOfChild; 
       } 
      } 
     } 
    } 

После того, как у вас есть список элементов управления мы используем «перепроверить» расширение перебрать их и выстрелить связывание выражений. Мы возвращаем bool, который просто скажет нам, есть ли у формы какие-либо сбои. Обратите внимание, что T - ваш тип, который вы хотите проверить. Такие как TextBox, ListBox и так далее. Заметьте, я показываю один пример ниже в TextBox. Вам нужно будет создать любые другие элементы управления, которые вы собираетесь использовать, а затем применить «obj» к правильному типу.

 public static Boolean Revalidate<T>(this Window depObj) where T : DependencyObject 
    { 
     bool isValid = true; 
     foreach (T obj in FindVisualChildren<T>(depObj)) 
     { 
      var name = typeof(T).Name.ToLower(); 
      BindingExpression exp = null; 

      switch (name) 
      { 
       case "textbox": 
        var tb = obj as TextBox; 
        exp = tb.GetBindingExpression(TextBox.TextProperty); 
        exp.UpdateSource(); 
        if (Validation.GetHasError(tb)) 
         isValid = false; 
        break;   
      }        
     } 

     return isValid; 
    } 

... и назвать его использовать:

valid = this.window.Revalidate<TextBox>(); // where this.window is a reference to our window. 

Оттуда мы можем просто проверить, если это правда или нет, и просто вернуться, если не удалось.

if(!valid){ 
    return; // you could update a message or something as well obviously. 
} 
Смежные вопросы