ViewMakerで生成するWPF/Silverlightコントロール(2)TextBox編

前回に引き続き今回もViewMakerで利用できるコントロールを説明します。今回はTextBoxです。

TextBoxコントロール

TextBoxは一般的なコントロールなのでWPFSilverlightいずれにも標準で含まれています。ViewMakerでは以下のような項目を設定できるようになっています。

    1. StringFormat(出力フォーマット)
    2. IsReadOnly(読み取り専用)
    3. IsMultiline(複数行)
    4. IsEmptyAsNull(空文字をnull扱いするか)
    5. TextAlignment(テキストの配置位置)
    6. LostFocusCommand(ロストフォーカス時に実行するコマンド)
    7. EnterKeyCommand(Enterキーを押下した時に実行するコマンド)

個人的な趣味に依存しているかもしれませんが、業務アプリでよく利用するものを用意しました。ロストフォーカスやEnterキー押下のタイミングでコマンド実行する仕組みも比較的必要とされることがあるので含めておきました。

WPFサンプルイメージとXAMLコード

    <StackPanel Name="TextBox" Orientation="Vertical" DataContext="{Binding Path=TextBox,Mode=OneWay}" 
      Margin="5" VerticalAlignment="Top" Grid.Row="0" Grid.Column="0">
      <TextBox Name="TextBox1" Text="{Binding Path=TextBox1, StringFormat=[\{0\}], Mode=TwoWay, 
         ValidatesOnExceptions=True, ValidatesOnDataErrors=True}" VerticalAlignment="Center" />
      <TextBox Name="TextBox2" Text="{Binding Path=TextBox2, StringFormat=yyyy年MM月dd日(ddd), Mode=TwoWay, 
         ValidatesOnExceptions=True, ValidatesOnDataErrors=True}" VerticalAlignment="Center" />
      <TextBox Name="TextBox3" IsReadOnly="True" Text="{Binding Path=TextBox3, StringFormat=c, Mode=TwoWay, 
         ValidatesOnExceptions=True, ValidatesOnDataErrors=True}" TextAlignment="Right" VerticalAlignment="Center" />
      <TextBox Name="TextBox4" TextWrapping="Wrap" AcceptsReturn="True" VerticalScrollBarVisibility="Auto" 
         Text="{Binding Path=TextBox4, Mode=TwoWay, ValidatesOnExceptions=True, ValidatesOnDataErrors=True}" 
         Foreground="Blue" Background="Azure" VerticalAlignment="Center" />
      <TextBox Name="TextBox5" Text="{Binding Path=TextBox5, TargetNullValue='', Mode=TwoWay, ValidatesOnExceptions=True, 
         ValidatesOnDataErrors=True}" vg:LostFocusBehavior.Command="{Binding Path=LostFocusCommand, Mode=OneWay}" 
         VerticalAlignment="Center" xmlns:vg="clr-namespace:ViewMaker.Core.Wpf;assembly=ViewMaker.Core" />
      <TextBox Name="TextBox6" Text="{Binding Path=TextBox6, Mode=TwoWay, ValidatesOnExceptions=True, 
         ValidatesOnDataErrors=True}" vg:EnterKeyBehavior.Command="{Binding Path=EnterKeyCommand, Mode=OneWay}" 
         VerticalAlignment="Center" xmlns:vg="clr-namespace:ViewMaker.Core.Wpf;assembly=ViewMaker.Core" />
    </StackPanel>

SilverlightサンプルイメージとXAMLコード

    <StackPanel Name="TextBox" Orientation="Vertical" DataContext="{Binding Path=TextBox,Mode=OneWay}" Margin="5" 
        VerticalAlignment="Top" Grid.Row="0" Grid.Column="0">
      <TextBox Name="TextBox1" Text="{Binding Path=TextBox1, StringFormat=[\{0\}], Mode=TwoWay, 
        ValidatesOnExceptions=True, ValidatesOnDataErrors=True}" VerticalAlignment="Center" />
      <TextBox Name="TextBox2" Text="{Binding Path=TextBox2, StringFormat=yyyy年MM月dd日(ddd), Mode=TwoWay, 
        ValidatesOnExceptions=True, ValidatesOnDataErrors=True}" VerticalAlignment="Center" />
      <TextBox Name="TextBox3" IsReadOnly="True" Text="{Binding Path=TextBox3, StringFormat=c, Mode=TwoWay, 
        ValidatesOnExceptions=True, ValidatesOnDataErrors=True}" TextAlignment="Right" VerticalAlignment="Center" />
      <TextBox Name="TextBox4" TextWrapping="Wrap" AcceptsReturn="True" VerticalScrollBarVisibility="Auto" 
        Text="{Binding Path=TextBox4, Mode=TwoWay, ValidatesOnExceptions=True, ValidatesOnDataErrors=True}" 
        Foreground="Blue" Background="Azure" VerticalAlignment="Center" />
      <TextBox Name="TextBox5" Text="{Binding Path=TextBox5, TargetNullValue='', Mode=TwoWay, ValidatesOnExceptions=True, 
        ValidatesOnDataErrors=True}" VerticalAlignment="Center">
        <i:Interaction.Behaviors>
          <vg:LostFocusBehavior vg:Command="{Binding Path=LostFocusCommand, Mode=OneWay}" 
            xmlns:vg="clr-namespace:ViewMaker.Core.Silverlight;assembly=SilverlightViewMaker.Core" />
        </i:Interaction.Behaviors>
      </TextBox>
      <TextBox Name="TextBox6" Text="{Binding Path=TextBox6, Mode=TwoWay, ValidatesOnExceptions=True, 
        ValidatesOnDataErrors=True}" VerticalAlignment="Center">
        <i:Interaction.Behaviors>
          <vg:EnterKeyBehavior vg:Command="{Binding Path=EnterKeyCommand, Mode=OneWay}" 
             xmlns:vg="clr-namespace:ViewMaker.Core.Silverlight;assembly=SilverlightViewMaker.Core" />
        </i:Interaction.Behaviors>
      </TextBox>
    </StackPanel>

ViewModelコード

        [View(ViewControlType.StackPanel)]
        [ViewProperty(StackPanelViewControl.Properties.HeaderPosition, LayoutHeaderPosition.Hidden)]
        public class TextBoxSample : ViewModel
        {

            [View(ViewControlType.TextBox)]
            [ViewProperty(TextBoxViewControl.Properties.StringFormat, "[{0}]")]
            public string TextBox1 { get; set; }

            [View(ViewControlType.TextBox)]
            [ViewProperty(TextBoxViewControl.Properties.StringFormat, "yyyy年MM月dd日(ddd)")]
            public DateTime TextBox2 { get; set; }

            [View(ViewControlType.TextBox)]
            [ViewProperty(TextBoxViewControl.Properties.StringFormat, "c")]
            [ViewProperty(TextBoxViewControl.Properties.IsReadOnly, true)]
            [ViewProperty(TextBoxViewControl.Properties.TextAlignment, LayoutHorizontalAlignment.Right)]
            public int TextBox3 { get; set; }

            [View(ViewControlType.TextBox)]
            [ViewProperty(TextBoxViewControl.Properties.IsMultiline, true)]
            [ViewProperty(TextBoxViewControl.Properties.Foreground, "Blue")]
            [ViewProperty(TextBoxViewControl.Properties.Background, "Azure")]
            public string TextBox4 { get; set; }

            [View(ViewControlType.TextBox)]
            [ViewProperty(TextBoxViewControl.Properties.IsEmptyAsNull, true)]
            [ViewProperty(TextBoxViewControl.Properties.LostFocusCommand, "LostFocusCommand")]
            public string TextBox5 { get; set; }

            [ViewProperty(TextBoxViewControl.Properties.EnterKeyCommand, "EnterKeyCommand")]
            public string TextBox6
            {
                get { return _textBox6; }
                set { _textBox6 = value; OnPropertyChanged("TextBox6"); }
            }
            private string _textBox6;

            [Browsable(false)]
            public ICommand LostFocusCommand 
            { 
                get { return this.CreateCommand(()=> TextBox6 = (TextBox5 ?? "(null)")); } 
            }

            [Browsable(false)]
            public ICommand EnterKeyCommand
            {
                get { return this.CreateCommand(() => TextBox6 = TextBox6.ToUpper()); }
            }

            public TextBoxSample()
            {
                TextBox1 = "メッセージ1";
                TextBox2 = DateTime.Now;
                TextBox3 = 100;
                TextBox4 = @"1行目
2行目";
            }
        }

WPF/Silverlight 実装TIPS

TextBoxのコンテンツの配置位置はWHorizontalContentAlignmentではなくTextAlignmentを利用します。
あとEnterKeyCommandに関連してTextBoxのソース更新はフォーカスアウトのタイミングなのでEnterキーを押下されてもソース更新は行われていません。このためEnterKeyBehaviorでは強制的にUpdateSourceを行った後にコマンドを実行するようにしています。(以下はSilverlight版のビヘイビアの抜粋です)

    public class EnterKeyBehavior : Behavior<FrameworkElement>
    {
        public static readonly DependencyProperty CommandProperty = DependencyProperty.Register("Command", typeof(ICommand), typeof(EnterKeyBehavior), new PropertyMetadata(CommandCallback));
        protected override void OnAttached()
        {
            base.OnAttached();
            AssociatedObject.KeyDown += new KeyEventHandler(AssociatedObject_KeyDown);
        }
        void AssociatedObject_KeyDown(object sender, KeyEventArgs e)
        {
            if (Command == null) return;
            if (e.Key != Key.Enter) return;
            var txt = e.OriginalSource as TextBox;
            if (txt != null)
            {
                var be = txt.GetBindingExpression(TextBox.TextProperty);
                if (be != null) be.UpdateSource();
            }
            Command.Execute(null);
        }
        ...