かずきのBlog@hatena

日本マイクロソフトに勤めています。XAML + C#の組み合わせをメインに、たまにASP.NETやJavaなどの.NET系以外のことも書いています。掲載内容は個人の見解であり、所属する企業を代表するものではありません。

WPFでTextBoxに入力エラーがないときだけ押せるボタンをXAMLで実現したい

WPFでint型をバインドしたTextBoxとかって入力エラーがあると赤色枠がついていい感じにしてくれますよね。ただしViewModelからは、そのエラーは検知できない。 でも、ボタンは押せないようにしたい。そんなときは、こんなXAMLで実現できます。

<Window x:Class="WpfApplication12.MainWindow"
        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"
        xmlns:local="clr-namespace:WpfApplication12"
        mc:Ignorable="d"
        Title="MainWindow"
        Height="350"
        Width="525">
    <Window.Resources>
        <local:IsAllFalseConverter x:Key="IsAllFalseConverter" />
    </Window.Resources>
    <Window.DataContext>
        <local:MainWindowViewModel />
    </Window.DataContext>
    <StackPanel>
        <TextBox x:Name="TextBoxInput1"
                 Text="{Binding Input1, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
        <TextBlock Text="{Binding (Validation.HasError), ElementName=TextBoxInput1}" />
        <TextBox x:Name="TextBoxInput2"
                 Text="{Binding Input2, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
        <TextBlock Text="{Binding (Validation.HasError), ElementName=TextBoxInput2}" />
        <Button Content="OK">
            <Button.IsEnabled>
                <MultiBinding Converter="{StaticResource IsAllFalseConverter}">
                    <Binding ElementName="TextBoxInput1"
                             Path="(Validation.HasError)" />
                    <Binding ElementName="TextBoxInput2"
                             Path="(Validation.HasError)" />
                </MultiBinding>
            </Button.IsEnabled>
        </Button>
    </StackPanel>
</Window>

IsAllFalseConverterは以下のような感じで。

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Data;

namespace WpfApplication12
{
    public class IsAllFalseConverter : IMultiValueConverter
    {
        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            return values.OfType<bool>().All(x => !x);
        }

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

これで、エラーのないときだけ押せるボタンの出来上がり。WPFのXAML強いですねやっぱり。

ここまで書いておいて

ViewModelにstringで受け取るようにしておいて、そこでエラーチェックとかコマンドの実行可否を判断するようにするのがおすすめです。はい。