WCF RIA Servicesで公開したDTOに対してDataAnnotationでバリデーション

MetadataType属性でメタデータ用のクラスを指定します。メタデータ用のクラスには、Requiredとかバリデーションの属性を指定します。

    [MetadataType(typeof(EmployeeMetadata))]
    public class Employee
    {
        [Key]
        public int Id { get; set; }
        public string Name { get; set; }
        public int Age { get; set; }

        private class EmployeeMetadata
        {
            [Required(ErrorMessage = "必須です")]
            public string Name { get; set; }
        }
    }

メタデータ用のクラスを作るのがめんどいから、Employeeのプロパティに直接バリデーション用の属性をつけてMetadataType属性で自分自身を指定したらVisual Studioが落ちました。。。

サービスはこんな感じ。DomainServiceを継承します。

    [EnableClientAccess()]
    public class MyDomainService : DomainService
    {
        public Employee GetEmployee()
        {
            return new Employee()
                   {
                       Id = 1,
                       Name = "Hoge",
                       Age = 30
                   };
        }

        public void UpdateEmployee(Employee employee)
        {            
        }
    }

以上が、サーバーサイド。

クライアントでは、ViewModelを作ります。

    public class ViewModelBase : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged(string propertyName)
        {
            var handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
    public class HomeViewModel : ViewModelBase
    {
        public event EventHandler Clicked;

        private readonly MyDomainContext _context = new MyDomainContext();

        private Employee _employee;

        public HomeViewModel()
        {
            _clickCommand = new RelayCommand(OnClicked);

            _context.Load(_context.GetEmployeeQuery(), (op) =>
                                                         {
                                                             if (!op.HasError)
                                                             {
                                                                 Employee = op.Entities.First();
                                                                 
                                                             }
                                                         }, null);            
        }

        private readonly RelayCommand _clickCommand;

        public ICommand ClickCommand
        {
            get { return _clickCommand; }
        }

        private void OnClicked(object parameter)
        {
            var handler = Clicked;
            if (handler != null)
            {
                handler(this, EventArgs.Empty);
            }
        }

        public Employee Employee
        {
            get { return _employee; }
            set
            {
                _employee= value;
                OnPropertyChanged("Employee");
            }
        }

    }

Employeeをサーバから取得してプロパティで公開してます。

ViewのXAMLとコードビハインドは以下のとおり。

<navigation:Page
  x:Class="BASample.Home" 
  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:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
  mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480"  
  Style="{StaticResource PageStyle}">

    <Grid x:Name="LayoutRoot">
        <ScrollViewer x:Name="PageScrollViewer" Style="{StaticResource PageScrollViewerStyle}" >

            <StackPanel x:Name="ContentStackPanel" Style="{StaticResource ContentStackPanelStyle}">

                <TextBlock x:Name="HeaderText" Style="{StaticResource HeaderTextStyle}" 
                           Text="{Binding Path=ApplicationStrings.HomePageTitle, Source={StaticResource ResourceWrapper}}"/>
                <TextBlock x:Name="ContentText" Style="{StaticResource ContentTextStyle}" 
                           Text="Home page content"/>
                    <StackPanel Orientation="Horizontal">
                        <TextBlock>Name:</TextBlock>
                        <TextBox Width="30" Text="{Binding Mode=TwoWay, Path=Employee.Name, ValidatesOnNotifyDataErrors=True}"></TextBox>
                    </StackPanel>
                <Button
                    x:Name="myButton"
                    Content="Button" Height="23" Width="75" Command="{Binding Path= ClickCommand}" />
            </StackPanel>
        </ScrollViewer>
    </Grid>

</navigation:Page>
    public partial class Home : Page
    {
        private HomeViewModel _viewModel = new HomeViewModel();

        /// <summary>
        /// Creates a new <see cref="Home"/> instance.
        /// </summary>
        public Home()
        {
            InitializeComponent();
            this.DataContext = _viewModel;
            _viewModel.Clicked += (e, s) =>
                                      {                                          
                                          NavigationService.Navigate(new Uri("/MyPage", UriKind.Relative));
                                      };
            this.Title = ApplicationStrings.HomePageTitle;
        }

        /// <summary>
        /// Executes when the user navigates to this page.
        /// </summary>
        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
        }
    }

XAMLでは

<TextBox Width="30" Text="{Binding Mode=TwoWay, Path=Employee.Name, ValidatesOnNotifyDataErrors=True}"></TextBox>

コードビハンドでは

this.DataContext = _viewModel;

としておくのが大事。

TextBoxを空にするとこうなります。