2012-02-15 2 views
1

Я поставил ниже простейший пример, который я смог придумать, чтобы продемонстрировать свою проблему. Я пытаюсь, чтобы включить кнопку на основе 1 из 2 условий 1) Textbox1 видно и содержание являются действительными или 2) TextBox2 видна и содержание являются действительнымиКнопка отключения, основанная на нескольких свойствах. Я использую MultiDataTrigger и MultiBinding

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

Для кнопки я представил MultiDataTrigger и MultiBinding с помощью метода MultiBinding Converter, чтобы оценить, должна ли кнопка включаться или нет. Метод (называемый myConverter) вызывается, когда я переключаюсь между editboxes (нажатием на переключатель), но, похоже, не вызывается, когда данные в поле редактирования действительны, недействительны или переходят между ними. Вполне возможно, я неправильно обрабатываю Validation.HasError

Мои конкретные вопросы: 1) Каков правильный шаблон для решения этой проблемы? Любые примеры? Я должен сказать, что я упростил проблему. Например, проверка может быть больше, чем просто «должно быть восемь символов», и может быть задействовано несколько блоков ввода (например, «адрес» и «zip» ИЛИ «адрес» и «состояние». Таким образом, я думаю, что мне, вероятно, понадобится Идея MultiBinding Converter, но я открыт для других идей! 2) Как обрабатывать Validation.HasError внутри моего метода конвертера? Я рассматриваю его как ReadOnlyCollection, который, вероятно, полностью ошибается! 3) Я думаю, что большая часть моих проблем связана с множеством вариантов обработки информации об ошибках. Учитывая, что я использую ValidationRules, должен ли я также бросать Исключения из моих свойств, которые возвращают поля редактирования? Будут ли они когда-нибудь вызваны? Можете ли вы порекомендовать статью, показывающую разные способы проверки?

Я поставил ВСЕ код ниже. Я был бы очень признателен, если бы кто-то мог быстро взглянуть и указать мне в правильном направлении. -Dave XAML код

<Window x:Class="StackOverFlowBindingExample.Window1" 
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
xmlns:local="clr-namespace:StackOverFlowBindingExample" 
Title="Window1" Height="Auto" MinWidth="500" SizeToContent="Manual" WindowStartupLocation="CenterOwner" ResizeMode="CanResizeWithGrip" > 
<Window.Resources> 
    <local:MyConverter x:Key="myConverter" /> 
    <Style x:Key="textStyleTextBox" TargetType="TextBox"> 
     <Setter Property="Foreground" Value="#333333" /> 
     <Setter Property="VerticalAlignment" Value="Top" /> 
     <Setter Property="MinHeight" Value="2" /> 
     <Setter Property="MinWidth" Value="100" /> 
     <Setter Property="Margin" Value="4" /> 
     <Setter Property="MaxLength" Value="23" /> 
     <Setter Property="VerticalContentAlignment" Value="Center" /> 
     <Setter Property="HorizontalAlignment" Value="Left" /> 


     <Style.Triggers> 
      <Trigger Property="Validation.HasError" Value="true"> 
       <Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Self}, 
        Path=(Validation.Errors)[0].ErrorContent}" /> 
      </Trigger> 
     </Style.Triggers> 
    </Style> 



</Window.Resources> 
<Grid> 
    <StackPanel Orientation="Vertical"> 
     <StackPanel Orientation="Horizontal"> 
      <StackPanel Orientation="Vertical"> 
       <RadioButton Name="m_radio1" Margin="4" GroupName="IdInputType" IsChecked="True" Checked="IdInputType_Changed">Use Inputtype1</RadioButton> 
       <RadioButton Name="m_radio2" Margin="4" GroupName="IdInputType" IsChecked="False" Checked="IdInputType_Changed">Use Inputtype2</RadioButton> 
      </StackPanel> 
      <DockPanel Name="Grp8Digit"> 
       <Label MinHeight="25" Margin="4" VerticalAlignment="Top" VerticalContentAlignment="Center" HorizontalAlignment="Left" MinWidth="100" Width="113">Type 1 Id:</Label> 
       <TextBox Height="23" Name="m_textBox8DigitId" MaxLength="8" Width="120" Style="{StaticResource textStyleTextBox}" Validation.Error="TextBox_Error"> 
        <TextBox.Text> 
         <Binding> 
          <Binding.ValidatesOnExceptions>true</Binding.ValidatesOnExceptions> 
          <Binding.ValidatesOnDataErrors>true</Binding.ValidatesOnDataErrors> 
    <Binding.UpdateSourceTrigger>PropertyChanged</Binding.UpdateSourceTrigger> 
          <Binding.Path>EightDigitId</Binding.Path> 
          <Binding.NotifyOnValidationError>true</Binding.NotifyOnValidationError> 
          <Binding.ValidationRules> 
           <local:EightByteStringConvertRule /> 
          </Binding.ValidationRules> 
         </Binding> 
        </TextBox.Text> 
       </TextBox> 
       <Label MinHeight="25" Margin="4" VerticalAlignment="Top" VerticalContentAlignment="Center" HorizontalAlignment="Left" MinWidth="100">Enter 8 digit id</Label> 
      </DockPanel> 
      <DockPanel Name="Grp5Digit" Visibility="Collapsed"> 


       <StackPanel Orientation="Horizontal"> 
        <Label MinHeight="25" Margin="4" VerticalAlignment="Top" VerticalContentAlignment="Center" HorizontalAlignment="Left" MinWidth="100" Width="113">Type 2 id:</Label> 

        <TextBox Name="m_textBox5DigitId" Style="{StaticResource textStyleTextBox}" MinHeight="25" Margin="4" VerticalAlignment="Top" MaxLength="23" VerticalContentAlignment="Center" HorizontalAlignment="Left" MinWidth="100" Width="100" ToolTip="Enter Type 2 id"> 
         <TextBox.Text> 
          <Binding> 
           <Binding.ValidatesOnExceptions>true</Binding.ValidatesOnExceptions> 
           <Binding.Path>FiveDigitId</Binding.Path> 

           <Binding.NotifyOnValidationError>true</Binding.NotifyOnValidationError> 
           <Binding.ValidationRules> 
            <local:FiveByteStringConvertRule /> 
           </Binding.ValidationRules> 
          </Binding> 
         </TextBox.Text> 
        </TextBox> 
        <Label MinHeight="25" Margin="4" VerticalAlignment="Top" VerticalContentAlignment="Center" HorizontalAlignment="Left" MinWidth="100">Enter 5 digit id</Label> 

       </StackPanel> 
      </DockPanel> 
     </StackPanel> 

     <Button Height="27" Name="btnDoSomething" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="4" HorizontalContentAlignment="Center" Click="btnDoSomething_Click" Content="Do Something"> 
      <Button.Style> 
       <Style TargetType="{x:Type Button}"> 
        <Setter Property="IsEnabled" Value="false" /> 
        <Style.Triggers> 
         <MultiDataTrigger> 
          <MultiDataTrigger.Conditions> 
           <Condition Value="true"> 
            <Condition.Binding> 
             <MultiBinding Converter="{StaticResource myConverter}"> 
              <Binding ElementName="Grp8Digit" Path="Visibility" /> 
              <Binding ElementName="m_textBox8DigitId" Path="Validation.HasError" /> 
              <Binding ElementName="Grp5Digit" Path="Visibility" /> 
              <Binding ElementName="m_textBox5DigitId" Path="Validation.HasError" /> 


             </MultiBinding> 
            </Condition.Binding> 
           </Condition> 
          </MultiDataTrigger.Conditions> 
          <Setter Property="IsEnabled" Value="true" /> 
         </MultiDataTrigger> 
        </Style.Triggers> 
       </Style> 
      </Button.Style> 
     </Button> 
    </StackPanel> 
</Grid> 

C# код

using System; 
// lots of usings!!! 
namespace StackOverFlowBindingExample 
{ 
/// <summary> 
/// Interaction logic for Window1.xaml 
/// </summary> 
public partial class Window1 : Window, INotifyPropertyChanged 
{ 
    private static readonly object eightDigitLock = new object(); 
    private string _eightdigitId; 
    public string EightDigitId 
    { 
     get 
     { 
      return _eightdigitId; 
     } 
     set 
     { 
      lock (eightDigitLock) 
      { 
       if (value != _eightdigitId) 
       { 
        if (value.Length == 8) 
         _eightdigitId = value; 
        else 
         throw new Exception("Must be 8 digits");// do I really need to throw Exception here? 

       } 
      } 
     } 
    } 

    private static readonly object fiveDigitLock = new object(); 
    private string _fivedigitId; 
    public string FiveDigitId 
    { 
     get 
     { 
      return _fivedigitId; 
     } 
     set 
     { 
      lock (fiveDigitLock) 
      { 
       if (value != _fivedigitId) 
       { 
        if (value.Length == 5) 
         _fivedigitId = value; 
        else 
         throw new Exception("Must be 5 digits");// do I really need to throw exception? 

       } 
      } 
     } 
    } 
    public Window1() 
    { 
     InitializeComponent(); 
     this.DataContext = this; 
    } 
    private void IdInputType_Changed(object sender, RoutedEventArgs e) 
    { 
     if (m_radio1 != null && Grp8Digit != null && Grp5Digit != null) 
     { 
      if (m_radio1.IsChecked == true) 
      { 
       Grp8Digit.Visibility = Visibility.Visible; 
       Grp5Digit.Visibility = Visibility.Collapsed; 

      } 
      else 
      { 
       Grp8Digit.Visibility = Visibility.Collapsed; 
       Grp5Digit.Visibility = Visibility.Visible; 

      } 
     } 

    } 

    private void TextBox_Error(object sender, ValidationErrorEventArgs e) 
    { 
     try 
     { 
      if (e.Action == ValidationErrorEventAction.Added) 
      { 
       try 
       { 
        if (e.Error.Exception != null && e.Error.Exception.InnerException != null && e.Error.Exception.InnerException.Message.Length > 0) 
        { 
         ((Control)sender).ToolTip = e.Error.Exception.InnerException.Message; 
        } 
        else 
        { 
         ((Control)sender).ToolTip = e.Error.ErrorContent.ToString(); 
        } 
       } 
       catch (Exception ex) 
       { 
        string msg = ex.Message; 
        //Common.ProgramContext.Current.AddSessionLogEntrySync(new LogEntry(LogEntryCategory.Exception, ex.ToString())); 
        ((Control)sender).ToolTip = e.Error.ErrorContent.ToString(); 
       } 
      } 
      else 
      { 
       ((Control)sender).ToolTip = ""; 
      } 
     } 
     catch (Exception) 
     { 
      //Common.ProgramContext.Current.AddSessionLogEntrySync(new LogEntry(LogEntryCategory.Exception, ex.ToString())); 
      ((Control)sender).ToolTip = ""; 
     } 
    } 

    private void btnDoSomething_Click(object sender, RoutedEventArgs e) 
    { 

    } 
    #region INotifyPropertyChanged Members 

    public event PropertyChangedEventHandler PropertyChanged; 
    private void OnPropertyChanged(string name) 
    { 

     PropertyChangedEventHandler handler = PropertyChanged; 

     if (handler != null) 
     { 

      handler(this, new PropertyChangedEventArgs(name)); 

     } 

    } 
    #endregion 
} 

public class MyConverter : IMultiValueConverter 
{ 
    public object Convert(object[] values, Type targetType, object parameter, 
     System.Globalization.CultureInfo culture) 
    { 

     bool valid8Digit = true; 
     ReadOnlyCollection<ValidationError> collection = values[1] as ReadOnlyCollection<ValidationError>; 
     if (collection != null && collection.Count > 0) 
     { 
      valid8Digit = false; 

     } 
     //if ((bool)values[0] == true)//&& (bool)values[1] == false) 
     if ((Visibility)values[0] == Visibility.Visible && valid8Digit) 
     { 

      return true; 
     } 
     else 
      return false; 


    } 

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, 
     System.Globalization.CultureInfo culture) 
    { 
     throw new NotImplementedException(); 
    } 
} 

public class FiveByteStringConvertRule : ValidationRule 
{ 

    public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo) 
    { 
     if ((value as string) != null && (value as string).Length == 5) 
      return new ValidationResult(true, ""); 
     else 
      return new ValidationResult(false, ""); 
    } 
} 

public class EightByteStringConvertRule : ValidationRule 
{ 

    public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo) 
    { 
     if ((value as string) != null && (value as string).Length == 8) 
      return new ValidationResult(true, ""); 
     else 
      return new ValidationResult(false, ""); 
    } 
} 
} 
+0

Я не знаю, если это может относиться к вам, но я нашел эту страницу - http://stackoverflow.com/questions/3120463/databinding-to-validation-haserror. Похоже, может быть проблема, связанная с Validation.HasError, хотя в некоторых других примерах кажется, что это возможно - http://stackoverflow.com/questions/4574304/wpf-validation-how-to-show-tooltips-and -disable-run-button – Dror

+0

@Dave Вы пытаетесь использовать подход ViewModel вместо использования триггеров? – Ankesh

+0

Спасибо за оркестр за ссылки. Вы заметите во второй ссылке, что плакат использует Validation.HasError, но не с концепцией MultiBinding Converter. Думаю, мне это нужно, потому что условие включения кнопки не является тривиальным. Это - «если (A И B) ИЛИ (C И D). MultiDataTrigger.Conditions сами по себе, похоже, не поддаются этому. Он также вводит еще один метод проверки данных, IDataErrorInfo. Не уверен, что это так или нет. Слишком много вариантов! – Dave

ответ

1

Вы должны использовать команды, чтобы отключить/включить кнопки. Это самый простой и чистый способ сделать то, что вы хотите сделать.

В вашем файле кода, объявите новый статический класс, Команды и объявите новый RoutedUICommand.

public static class Commands 
{ 
    public static readonly RoutedUICommand DoSomething = new RoutedUICommand("Do Something", "DoSomething", typeof(MainWindow)); // MainWindow or the name of the usercontrol where you are going to use it. 
} 

Чтобы использовать это, вам необходимо объявить CommandBinding в вашем Window/UserControl.

<Window.CommandBindings> 
    <CommandBinding Command="my:Commands.DoSomething" CanExecute="DoSomethingCanExecute" Executed="DoSomethingExecuted" /> 
</Window.CommandBindings> 

my: это мое местное пространство имен.

Затем вы можете просто установить кнопку для использования этой команды.

<Button Command="my:Commmands.DoSomething"/> 

События CanExecute и Executed CommandBinding - это место, где должна лежать ваша логика. Чтобы отключить/включить кнопку, просто обработайте ее в DoSomethingCanExecute.

private void ShowXRefExecuted(object sender, ExecutedRoutedEventArgs e) 
    { 
     e.CanExecute = (Grp8Digit.Visibility==Visibility.Visible && (...)); 

    } 

И, конечно, событие Executed - это то, что происходит, когда пользователь нажимает кнопку.

EDIT

Событие проверки триггера только тогда, когда привязки обновления. Чтобы принудительно выполнить проверку, вы можете обновить триггеры вручную, как только загрузится окно/usercontrol. В Loaded случае окна:

public void Window_Loaded(object sender, RoutedEventArgs e) 
{ 
    m_textBox8DigitId.GetBindingExpression(TextBox.TextProperty).UpdateSource(); 
} 
+0

Я закодировал ваше предложение, и оно выглядит обещанием, за исключением той части, которую вы отметили (...). Как можно сказать, что поля редактирования находятся в состоянии ошибки? Вспомните, как я его сейчас закодировал, вы не можете просто взглянуть на свойство, поддерживающее поля редактирования. К лучшему или к худшему я использую ValidationRules для каждого окна редактирования. будет выделено красным цветом, но свойство еще не будет записано. Мне нужно что-то вроде textBox8DigitId.HasErrors, но я не вижу такого свойства. Идеи? – Dave

+0

Ahh получил (или очень близко) я добавил: bool result = (Grp8Digit.Visibility == Visibility.Visible) && ! Validation.GetHasError (m_textBox8DigitId) && m_textBox8DigitId.Text.Length == 8; \t e.CanExecute = result; Ключ - Validation.GetHasError. – Dave

+0

Однако я также должен был добавить m_textBox8DigitId.Text.Length == 8. Это было для случая запуска, когда при запуске этот метод вызывается и по какой-то причине Validation.GetHasError (...) возвращает false. Мое решение меньше идей, потому что в моем реальном проекте это не так просто, как просто наличие 8 символов. Идеи? Есть ли способ установить поле ввода как недопустимое при запуске? – Dave

Смежные вопросы