Отключение кнопки на основе нескольких свойств.Я использую MultiDataTrigger и MultiBinding - PullRequest
1 голос
/ 15 февраля 2012

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

Кажется, я на пути к включению кнопки в зависимости от видимости, но аспект IsValid доставляет мне горе.

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

Мои конкретные вопросы: 1) Каков правильный шаблон для решения этой проблемы? Есть примеры? Я должен сказать, что я упростил проблему. Например, проверка может быть больше, чем просто «должно быть восемь символов», и может быть задействовано несколько полей редактирования (например, «адрес» и «почтовый индекс» ИЛИ «адрес» и «состояние». Таким образом, я думаю, что мне, вероятно, нужно Идея MultiBinding Converter, но я открыт для других идей! 2) Как мне обработать Validation.HasError внутри моего метода Converter? Я рассматриваю это как 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, "");
    }
}
}

1 Ответ

1 голос
/ 15 февраля 2012

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

В файле с выделенным кодом объявите новый статический класс Commands и объявите новую 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"/>

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

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

    }

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

EDIT

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

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