読者です 読者をやめる 読者になる 読者になる

Silverlight の DataForm が強力すぎる

.net

Kay Framework や Django の ModelForm を使っていると、「これ .NET にも欲しい」と良く思う。そういえば、Silverlight Tools には DataForm っていうコントロールがあったな。

試しに使ってみた。

MainPage.xaml
<UserControl x:Class="DataFormSample.MainPage"
    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:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data.DataForm.Toolkit"
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400">
    <Grid x:Name="LayoutRoot" Background="White">
        <data:DataForm x:Name="_dataForm"
                       Header="タスク登録"
                       LabelPosition="Left"
                       CommitButtonContent="保存"
                       CancelButtonContent="キャンセル"
                       AutoEdit="False"
                       AutoCommit="False"
                       AutoGenerateFields="True"
                       DescriptionViewerPosition="BesideLabel"/>
    </Grid>
</UserControl>
MainPage.xaml.cs
using System;
using System.Windows.Controls;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel;
using System.Collections.ObjectModel;

namespace DataFormSample
{
    public partial class MainPage : UserControl
    {
        public MainPage()
        {
            InitializeComponent();

            this._dataForm.ItemsSource = new ObservableCollection<Task>() {
                new Task(){
                    Name="DataFormを試す",
                    Completed=false,
                    Deadline=DateTime.Today.AddDays(1),
                }
            };
        }
    }

    public class Task : INotifyPropertyChanged
    {
        // 入力必須項目なので RequiredAttribute を付与
        // StringLengthAttribute で文字数も制限
        private string _name;
        [Display(Name = "名称", Description = "タスクの名称")]
        [Required(ErrorMessage = "タスクの名称を入力して下さい。")]
        [StringLength(255, MinimumLength = 1,
            ErrorMessage = "名称は1文字以上、255文字以内で入力して下さい。")]
        public string Name
        {
            get { return _name; }
            set
            {
                if (_name != value)
                {
                    Validate(value, "Name");
                    _name = value;
                    OnPropertyChanged("Name");
                }
            }
        }

        private bool _completed;
        [Display(Name = "完了済み", Description = "完了済みかどうか")]
        public bool Completed
        {
            get { return _completed; }
            set
            {
                if (_completed != value)
                {
                    _completed = value;
                    OnPropertyChanged("Completed");
                }
            }
        }

        private DateTime _deadline;
        [Display(Name = "期限", Description = "タスクの期限")]
        public DateTime Deadline
        {
            get { return _deadline; }
            set
            {
                if (_deadline != value)
                {
                    _deadline = value;
                    OnPropertyChanged("Deadline");
                }
            }
        }

        /// <summary>
        /// 値を検証します。
        /// </summary>
        /// <param name="value">検証する値</param>
        /// <param name="propertyName">プロパティ名</param>
        private void Validate(object value, string propertyName)
        {
            Validator.ValidateProperty(value, new ValidationContext(this, null, null)
            {
                MemberName = propertyName,
            });
        }

        public event PropertyChangedEventHandler PropertyChanged;

        private void OnPropertyChanged(string propertyName)
        {
            var h = PropertyChanged;
            if (h != null)
            {
                h(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
}

実行画面がこちら。
f:id:griefworker:20100612100237j:image
最初は入力できないようになっている。右上の鉛筆ボタンを押すと入力モードに移動。
f:id:griefworker:20100612100238j:image
必須項目の名称を空欄にしてみる。
f:id:griefworker:20100612100239j:image
入力が検証されて、ページ下にエラーメッセージが表示された。

DataForm の使い方は簡単だった。編集したいクラスのコレクションを ItemsSource にバインドするだけ。デフォルトだと、クラスのプロパティをもとに、ラベルや入力欄を生成してくれた。

ObservableCollection をセットしておけば、データの追加や削除もできる。

また、System.CompolentModel.DataAnnotations 名前空間内に定義されている属性を使えば、ラベルの内容を変更したり、保存時に入力検証を行える。

久しぶりに「スゴイ!」って思えるコントロールに出会えた。多分、チャート以来だ。データを個別に入力するようなプロジェクト(例えば得意先入力とか)は、DataForm をカスタマイズして使えば十分じゃないかな。

作成したプロジェクト

下のリンクからダウンロードできます。