かずきのBlog@hatena

すきな言語は C# + XAML の組み合わせ。Azure Functions も好き。最近は Go 言語勉強中。日本マイクロソフトで働いていますが、ここに書いていることは個人的なメモなので会社の公式見解ではありません。

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