【解決方法】WPF データ グリッドとバインディング グループを使用して複数の行を検証する

プログラミングQA


私は WPF/EF を初めて使用しており、シンプルなマスター詳細 UI を持っています。 検証ルールを使用して単純なセル/行検証を実装しました。 詳細データの整数フィールドを検証する必要があります。つまり、すべてのデータグリッド行に対して検証します。 データグリッドレベルとページ(これはXAPAPアプリケーションです)グリッドレベルでバインディンググループを実装しようとしました。 さまざまなプロパティなどを設定します。[保存]ボタンをクリックすると検証ルールが起動しますが、バインディング グループには項目がありません。 インターネットで調べましたが、やりたいことに関する参考資料が 1 つ見つかりました。 解決策は私にはうまくいきませんでした。

1) フィールドの複数の行を検証するにはどうすればよいですか?
2) 誰かが簡単な例を提供してもらえますか?

誰かが見る必要がある場合は、XAML、コードビハインド、バインディンググループ検証ルールを含めるようにします。 注: 私は初心者なので、MVVM アプローチを実装していません。 マスター/詳細 UI へのアプローチは、WPF/EF に関する MSDN オンライン ビデオ (Beth Massie 著) から引用されています。

TIA

— バインディング グループの検証ルール (データ グループ上)

public class UtilizationValidationRule : ValidationRule
     {
         // for Utilization Entry 
        public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
         {
             // the UtilizationAmount total cannot > 100%
             BindingGroup bindingGroup = (BindingGroup)value;
 
            if (bindingGroup != null)
             {
                 // utilization total
                 int utilTotal = 0;
 
                foreach (var bindingSource in bindingGroup.Items)
                 {
                     // get item
                     Utilization util = (Utilization)bindingSource;
                     utilTotal += util.UtilizationAmount;
                 }
 
                if (utilTotal > 100)
                 {
                     return new ValidationResult(false, "Total utilization cannot exceed 100%.");
                 }
             }
 
            return new ValidationResult(true, null);
         }
 
    }

— 確認する必要がある場合は、背後のコードをここに示します。

using System;
 using System.Collections.Generic;
 using System.Linq;
 using System.Text;
 using System.Windows;
 using System.Windows.Controls;
 using System.Windows.Data;
 using System.Windows.Documents;
 using System.Windows.Input;
 using System.Windows.Media;
 using System.Windows.Media.Imaging;
 using System.Windows.Navigation;
 using System.Windows.Shapes;
 using DataModel;
 using System.ComponentModel;
 

namespace WorkbookPlan
 {
     /// <summary>
     /// Interaction logic for page
     /// </summary>
     public partial class UtilizationEntry2 : Page
     {
 

        // NOTE: The Master-Detail UIs are different than those that use just
         // fields for data entry and use the EF entity IDataErrorInfo interface
         // for validation
         // Seemingly the Detail collection does not fire the EF entity class
         // I am not sure why. Hence for validation must be done by validation rules
         // at a row/column level.
 
        private WorkbookPlanEntities db = new WorkbookPlanEntities();
         private EmpUtilCollection EmpUtilData;
 
        // collections for EF data 
        private CollectionViewSource MasterViewSource;
         private CollectionViewSource DetailViewSource;
         
        // collections wired to UI
         private ListCollectionView MasterView;
         private BindingListCollectionView DetailView;
 
 
 
        public UtilizationEntry2()
         {
             InitializeComponent();
         }
 
        private void Page_Loaded(object sender, RoutedEventArgs e)
         {
 
            try
             {
 
 
 
                // get data from model
                 var employees = from emp in db.Employees.Include("Utilizations")
                                 orderby emp.LastName, emp.FirstName
                                 select emp;
 

                CollectionViewSource organizationLookup = (CollectionViewSource)this.FindResource("OrganizationLookup");
                 // lookup
                 organizationLookup.Source = from org in db.Organizations orderby org.Name select org;
 

                // create obs. collection of data from EF data context
                 this.EmpUtilData = new EmpUtilCollection(employees, db);
 
                // set up Master view: Employees which is read only since we are not changing Employee table
                 this.MasterViewSource = (CollectionViewSource)this.FindResource("MasterView");
                 // set up detail view: Utilization which will be changed
                 this.DetailViewSource = (CollectionViewSource)this.FindResource("DetailView");
                 
                this.MasterViewSource.Source = this.EmpUtilData;
 
                this.MasterView = (ListCollectionView)(MasterViewSource.View);
                 this.MasterView.CurrentChanged += new EventHandler(MasterView_CurrentChanged);
                 this.DetailView = (BindingListCollectionView)(DetailViewSource.View);
             
             
            }
 
            catch (Exception ex)
             {
                 MessageBox.Show("Exception: " + ex.Message);
                 
            }
 
 
 
            
        }
 

         void MasterView_CurrentChanged(object sender, EventArgs e)
         {
             this.DetailView = (BindingListCollectionView)(DetailViewSource.View);
 
            // set navigation buttons
             // this.PreviousClick.IsEnabled = this.MasterView.CurrentPosition > 0;
             //this.NextClick.IsEnabled = this.MasterView.CurrentPosition < this.MasterView.CurrentPostion - 1;
         }
 

    
        private void SaveClick(object sender, RoutedEventArgs e)
         {
             
             try
             {
                 
                 bool okay = this.FormBindingGroup.ValidateWithoutUpdate();
                  if (okay)
                      {
                          db.SaveChanges();
                          MessageBox.Show("Data was saved.", this.Title, MessageBoxButton.OK, MessageBoxImage.Information);
                      }
               
            }
             catch (Exception ex)
             {
                 MessageBox.Show(ex.ToString());
                
            }
 
         
   
        }
 

        private void AddClick(object sender, System.Windows.RoutedEventArgs e)
         {
 
            // for detail only
             this.DetailView.AddNew();
             this.DetailView.CommitNew();
            
           
          
        }
 
        private void DeleteClick(object sender, System.Windows.RoutedEventArgs e)
         {
 
            // for detail only
             if (this.DetailView.CurrentPosition > -1)
             {
                 this.DetailView.RemoveAt(this.DetailView.CurrentPosition);
             }
         }
 
        private void PreviousClick(object sender, RoutedEventArgs e)
         {
             this.MasterView.MoveCurrentToPrevious();
         }
 
        private void NextClick(object sender, RoutedEventArgs e)
         {
             this.MasterView.MoveCurrentToNext();
         }

— XAML

<Page x:Class="WorkbookPlan.UtilizationEntry2"
       xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
       xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
       xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
      xmlns:local="clr-namespace:WorkbookPlan"
       xmlns:localuc="clr-namespace:UserControlLibrary;assembly=UserControlLibrary"
       mc:Ignorable="d" 
        Title="Utilization Data Entry" Name="UtilizationEntry"
          Height="230" Width="600" Loaded="Page_Loaded">
 
    <Page.Resources>
         <CollectionViewSource x:Key="OrganizationLookup"/>
         <CollectionViewSource x:Key="MasterView" />
         <CollectionViewSource x:Key="DetailView" 
             Source="{Binding Source={StaticResource MasterView}, 
                    Path='Utilizations'}"/>
 
        <Style TargetType="{x:Type DataGridColumnHeader}">
             <Setter Property="Background" Value="LightSteelBlue"/>
             <Setter Property="BorderThickness" Value="1"/>
             <Setter Property="BorderBrush" Value="Black"/>
             <Setter Property="HorizontalContentAlignment" Value="Left"  />  
            <Setter Property="Padding" Value="3" /> 
            <Setter Property="FontWeight" Value="Bold"/>
         </Style>
 
        <Style TargetType="{x:Type DataGridCell}">
             <Style.Triggers>
                 <Trigger Property="DataGridCell.IsSelected" Value="True">
                     <Setter Property="Background" Value="LightCyan"   />
                     <Setter Property="Foreground" Value="Black" />
                   </Trigger>
             </Style.Triggers>
         </Style>
 
 
 
        <Style TargetType="{x:Type DataGridRow}">
             <Setter Property="ValidationErrorTemplate">
                 <Setter.Value>
                     <ControlTemplate>
                            <Grid Margin="0,-2,0,-2"
                             ToolTip="{Binding RelativeSource={RelativeSource
                            FindAncestor, AncestorType={x:Type DataGridRow}},
                            Path=(Validation.Errors)[0].ErrorContent}">
                             <Ellipse StrokeThickness="0" Fill="Red" 
                            Width="{TemplateBinding FontSize}" 
                            Height="{TemplateBinding FontSize}" />
                             <TextBlock Text="!" FontSize="{TemplateBinding FontSize}"
                             FontWeight="Bold" Foreground="White" 
                            HorizontalAlignment="Center"  />
                         </Grid>
                     </ControlTemplate>
                 </Setter.Value>
                 
            </Setter>
         </Style>
 
    </Page.Resources>
 

    <Grid Name="FormGrid" >
                  
        <Grid.ColumnDefinitions>
             <ColumnDefinition Width="300" />
             <ColumnDefinition Width="*" />
         </Grid.ColumnDefinitions>
         <Grid.RowDefinitions>
             <RowDefinition Height="30" />
             <RowDefinition Height="5" />
             <RowDefinition Height="25" />
             <RowDefinition Height="5" />
             <RowDefinition Height="100*" />
             <RowDefinition Height="5" />
             <RowDefinition Height="25" />
         </Grid.RowDefinitions>
 
        <!--<Grid.BindingGroup>
             <BindingGroup x:Name="FormBindingGroup" NotifyOnValidationError="True" >
                 <BindingGroup.ValidationRules>
                     <local:UtilizationValidationRule   
                        ValidationStep="RawProposedValue" />
                 </BindingGroup.ValidationRules>
             </BindingGroup>
         </Grid.BindingGroup>-->
 
        <Border Grid.Row="0"  BorderThickness="1" BorderBrush="Black"
                 Grid.RowSpan="7" Grid.ColumnSpan="2" Background="White"></Border>
 

        <Border Grid.Row="0" Grid.ColumnSpan="3" BorderThickness="1" BorderBrush="Black"
                 Background="Gainsboro"></Border>
 
       
        <localuc:PageHeader  Grid.Row="0" Grid.ColumnSpan="2" Content="Utilization Data Entry"/>
 

        <Label Grid.Row="2" Grid.Column="0" 
               Style="{StaticResource ListboxHeaderText}" VerticalContentAlignment="Center" >Employee List</Label>
       
        <localuc:DataEntryButtons x:Name="ucButtons"   Grid.Row="2" Grid.Column="1" 
                            Save="SaveClick"  Add="AddClick" Delete="DeleteClick"/>
        
        <Border Grid.Row="4" Grid.Column="0" Grid.ColumnSpan="1" Style="{StaticResource BorderBlueBackground}"></Border>
         <Grid Grid.Row="4" Grid.Column="0" DataContext="{Binding Source={StaticResource MasterView}}">
             <Grid.ColumnDefinitions>
                 <ColumnDefinition Width="80" />
                 <ColumnDefinition Width="*" />
                 <ColumnDefinition Width="5" />
             </Grid.ColumnDefinitions>
             <Grid.RowDefinitions>
                 <RowDefinition Height="25" />
                 <RowDefinition Height="5" />
                 <RowDefinition Height="30" />
                 <RowDefinition Height="5" />
                 <RowDefinition Height="30" />
                 <RowDefinition Height="5" />
                 <RowDefinition Height="30" />
                
            </Grid.RowDefinitions>
 
            <localuc:NavigationButtons x:Name="ucNavigationButtons" Grid.Row="0" Grid.ColumnSpan ="2" 
                     Previous="PreviousClick"  Next="NextClick"/>
 

            <Label Grid.Row="2" Grid.Column="0">Number:</Label>
             <Label Grid.Row="4" Grid.Column="0">Last Name:</Label>
             <Label Grid.Row="6" Grid.Column="0">First Name:</Label>
 
            <TextBox Grid.Row="2" Grid.Column="1" Padding="5"
                      Name="Number" IsReadOnly="True" 
                         Text="{Binding Path=Number, Mode=OneWay}"/>
             <TextBox Grid.Row="4" Grid.Column="1" Padding="5"
                      Name="LastName" IsReadOnly="True" 
                         Text="{Binding Path=LastName, Mode=OneWay}"/>
             <TextBox Grid.Row="6" Grid.Column="1" Padding="5"
                      Name="FirstName" IsReadOnly="True" 
                         Text="{Binding Path=FirstName, Mode=OneWay}"/>
 
        </Grid>
                     
      
 

        <Border Grid.Row="4" Grid.Column="1" BorderThickness="1" BorderBrush="Black"
                 Background="White">
 
            <DataGrid Grid.Row="4" Grid.Column="1" Name="ListView1" AutoGenerateColumns="false"
                   CanUserAddRows="false" CanUserDeleteRows="false" GridLinesVisibility="All"
                   IsSynchronizedWithCurrentItem="True" 
                  ItemsSource="{Binding Source={StaticResource DetailView}}" 
                  Background="LightGray" BorderBrush="Cornsilk" >
                 <DataGrid.BindingGroup>
                     <BindingGroup x:Name="FormBindingGroup" NotifyOnValidationError="True" >
                         <BindingGroup.ValidationRules>
                             <local:UtilizationValidationRule   
                        ValidationStep="RawProposedValue" />
                         </BindingGroup.ValidationRules>
                     </BindingGroup>
 
                </DataGrid.BindingGroup>
                 
                
                <DataGrid.Resources>
                     <Style x:Key="errorStyle" TargetType="{x:Type TextBox}">
                         <Setter Property="Padding" Value="-2"/>
                         <Style.Triggers>
                             <Trigger Property="Validation.HasError" Value="True">
                                 <Setter Property="BorderThickness" Value="2"/>
                                 <Setter Property="BorderBrush" Value="red"/>
                                 <Setter Property="ToolTip" 
                              Value="{Binding RelativeSource={RelativeSource Self},
                                 Path=(Validation.Errors)[0].ErrorContent}"/>
                             </Trigger>
                         </Style.Triggers>
                     </Style>
                 </DataGrid.Resources>
                 
                <DataGrid.RowValidationRules>
                     <local:UtilizationRowValidationRule ValidationStep="RawProposedValue"/>
                 </DataGrid.RowValidationRules>
 

                <DataGrid.Columns>
                 <DataGridComboBoxColumn Header="Organization"  Width="150"
                          ItemsSource="{Binding Source={StaticResource OrganizationLookup}}"
                          SelectedValueBinding="{Binding Path=Organization,
                             ValidatesOnExceptions=True,ValidatesOnDataErrors=True,
                             NotifyOnValidationError=True,UpdateSourceTrigger=PropertyChanged}"
                          DisplayMemberPath="Name">
                     </DataGridComboBoxColumn>
                    
                                   
                    
                    <DataGridTextColumn Header="Utilization" Width="75" 
                                        EditingElementStyle="{StaticResource errorStyle}">
                         <DataGridTextColumn.Binding>
                             <Binding Path="UtilizationAmount" 
                                     NotifyOnValidationError="true"
                                      UpdateSourceTrigger="PropertyChanged" 
                                     ValidatesOnExceptions="true"
                                      ValidatesOnDataErrors="true">
                                 <Binding.ValidationRules>
                                     <local:PercentValidationRule/>
                                 </Binding.ValidationRules> 
                                
                            </Binding>
                            
                        </DataGridTextColumn.Binding>
                       
                    
                </DataGridTextColumn>
             </DataGrid.Columns>
             </DataGrid>
         </Border>
 
 
 
        <ListView Grid.Row="6" Grid.Column="0" Grid.ColumnSpan="2" 
            ItemsSource="{Binding Path=(Validation.Errors), ElementName=FormGrid}">
             <ListView.ItemTemplate>
                 <DataTemplate>
                     <Label Foreground="Red" Content="{Binding Path=ErrorContent}"/>
                 </DataTemplate>
             </ListView.ItemTemplate>
         </ListView>
 
    </Grid>
 </Page>

コメント

タイトルとURLをコピーしました