ObservableCollection上的IDataErrorInfo

时间:2010-06-24 21:30:55

标签: wpf mvvm observablecollection idataerrorinfo

我有一个实现IDataError的viewmodel。在viewmodel中我有一个ObservableCollection。 ObservableCollection在我的视图中填充数据网格:

    // the list that populates the datagrid
    public ObservableCollection<ProjectExpenseItemsDto> ListOfProjectExpenseItems
    {
        get { return listOfProjectExpenseItems; }
        set
        {
            if (listOfProjectExpenseItems != value)
            {
                listOfProjectExpenseItems = value;
                NotifyPropertyChanged("ListOfProjectExpenseItems");
            }
        }
    }

我还有一个属性来表示数据网格中的选定项目(它基于Dto):

    // the selected row in the datagrid
    public ProjectExpenseItemsDto SelectedProjectExpenseItem
    {
        get { return selectedProjectExpenseItem; }
        set
        {
            if (selectedProjectExpenseItem != value)
            {
                selectedProjectExpenseItem = value;
                NotifyPropertyChanged("SelectedProjectExpenseItem");
            }
        }
    }

这是Dto:

namespace ProjectExpense.Model.Dto
{
    [DataContract]
    public class ProjectExpenseItemsDto
    {
        [DataMember]
        public int RowID { get; set; }
        [DataMember]
        public int ProjectExpenseID { get; set; }
        [DataMember]
        public string ItemNumber { get; set; }
        [DataMember]
        public string ItemDescription { get; set; }
        [DataMember]
        public decimal ItemUnitPrice { get; set; }
        [DataMember]
        public decimal ItemQty { get; set; }
        [DataMember]
        public string SupplierName { get; set; }
        [DataMember]
        public DateTime CreateDate { get; set; }
    }
}

我想使用IDataError来验证datagrid(SelectedProjectExpenseItem)的选定行中的值,但由于我的网格绑定到ObservableCollection,我的viewmodel中没有任何属性;因此,我只能对ObservableCollection使用IDataError,而不是集合中的单个项目,这对我没有帮助,因为我已经知道如何看到集合中的“内部”。我也不能将IDataError用于我的SelectedProjectExpenseItem。例如:

    string IDataErrorInfo.this[string propertyName]
    {
        get
        {
            string result = string.Empty;
            propertyName = propertyName ?? string.Empty;
            if (propertyName == string.Empty || propertyName == "ItemNumber")
            {
                if (string.IsNullOrEmpty(SelectedProjectExpenseItem.ItemNumber))
                {
                    result = "Name cannot be blank!";
                }
            }
            return result;
        }
    }

这不会触发,因为我的datagrid列未绑定到SelectedProjectExpenseItem.ItemNumber,它绑定到ObservableCollection中的ItemNumber。

我正在寻找任何指导,因为这让我很困惑。

----------------------------编辑:-------------- --------------

好的,我为我的DTO创建了一个单独的viewmodel:

namespace ProjectExpense.ViewModels
{
    public class ProjectExpenseItemsDtoViewModel : ProjectExpenseItemsDto, IDataErrorInfo
    {
        public ProjectExpenseItemsDtoViewModel()
        {
            Initialize();
        }

        private void Initialize()
        {
        }

        #region Validation

        // string method
        static bool IsStringMissing(string value)
        {
            return String.IsNullOrEmpty(value) || value.Trim() == String.Empty;
        }

        #endregion

        #region IDataErrorInfo Members

        public string Error
        {
            get
            {
                return this[string.Empty];
            }
        }

        public string this[string propertyName]
        {
            get
            {
                string result = string.Empty;
                if (propertyName == "ItemNumber")
                {
                    if (IsStringMissing(this.ItemNumber))
                        result = "Item number cannot be empty!";
                    if (this.ItemNumber.Length > 50)
                        return "Item number exceeds 50 characters";
                }
                return result;
            }
        }

        #endregion
    }
}

现在,我在主vm中的以下行遇到了问题:

IList<ProjectExpenseItemsDtoViewModel> iList = projectExpenseItemsRepository.GetProjectExpenseItems(ProjectExpenseID);
foreach (ProjectExpenseItemsDtoViewModel item in iList)
   ListOfProjectExpenseItems.Add(item);

它说:

无法将类型'System.Collections.Generic.IList'隐式转换为'System.Collections.Generic.IList'。存在显式转换(您是否错过了演员?)

有什么想法吗?

----------------------------编辑:-------------- --------------

我找到了这个链接,看看我是否可以复制这个人在做什么:

Validation-in-a-WPF-DataGrid

4 个答案:

答案 0 :(得分:0)

不是使用ObservableCollection ProjectExpenseItemsDto,而是为ProjectExpenseItemsDto类型(IE:ProjectExpenseItemsDtoViewModel)创建一个ViewModel,并使其实现IDataErrorInfo接口,然后在主ViewModel中使用ObservableCollection<ProjectExpenseItemsDtoViewModel>作为属性。

答案 1 :(得分:0)

据我所知,问题不在你的viewmodel中,而是在你视图中的标记中。你必须在你的DTO上实现它,你只需要告诉你的观点注意IDataErrorInfo并做点什么。

您可以执行以下操作:

<dg:DataGrid ItemsSource="{StaticResource ListOfProjectExpenseItems}"/> >
<dg:DataGrid.Columns>
    <dg:DataGridTextColumn Header="ItemNumber" Binding="{Binding ItemNumber,ValidatesOnDataErrors=true}"/>
    <dg:DataGridTextColumn Header="ItemDescription" Binding="{Binding ItemDescription" />
</dg:DataGrid.Columns>
</dg:DataGrid>

请注意ValidatedOnDataErrors属性。看看你的viewmodel必须能够告诉视图有什么问题,所以IDataErrorInfo是一个WPF固有地监听的接口,如果它被询问的话。如果你对该属性的绑定告诉wpf听它,那么它会。

并且有默认的错误模板,但是如果我们想要添加工具提示来告诉用户您需要执行类似这样的实际错误

<Style x:Key="textBoxInError" TargetType="{x:Type TextBox}">
  <Style.Triggers>
    <Trigger Property="Validation.HasError" Value="true">
      <Setter Property="ToolTip"
        Value="{Binding RelativeSource={x:Static RelativeSource.Self},
                        Path=(Validation.Errors)[0].ErrorContent}"/>
    </Trigger>
  </Style.Triggers>
</Style>

Google wpf error templates阅读有关this example.等错误模板的内容。以上内容会向有错误的文本框添加工具提示。

WPF有几个移动部件,因此很容易丢失,但你会习惯它,只需要时间:)

希望这可以帮助您完成旅程。

答案 2 :(得分:0)

好的,我发现了部分问题。 IDataErrorInfo没有被触发的原因是因为我在dg的SelectedItem绑定上没有ValidatesOnDataErrors = True:

            <DataGrid ItemsSource="{Binding Path=ListOfProjectExpenseItems, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" AutoGenerateColumns="False" 
                Name="dgProjectExpenseItems" SelectionMode="Single" SelectionUnit="FullRow" CanUserResizeColumns="True" 
                RowStyle="{StaticResource RowStyle}" SelectedItem="{Binding Path=SelectedProjectExpenseItem, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" GridLinesVisibility="Horizontal" CanUserDeleteRows="True" CanUserAddRows="True">
            <DataGrid.RowValidationRules>
                <DataErrorValidationRule ValidationStep="UpdatedValue" />
                <ExceptionValidationRule ValidationStep="UpdatedValue" />
            </DataGrid.RowValidationRules>

            <DataGrid.Columns>
                <DataGridTextColumn Header="ID" Width="SizeToCells"  MinWidth="50" Binding="{Binding RowID}" />
                <DataGridTextColumn Header="Project Expense ID" Width="SizeToCells" Visibility="Hidden" MinWidth="0" Binding="{Binding ProjectExpenseID, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
                <DataGridTextColumn Header="Item Number" EditingElementStyle="{StaticResource CellEditStyle}" Width="SizeToCells" MinWidth="140" Binding="{Binding ItemNumber, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True, ValidatesOnExceptions=True, NotifyOnValidationError=true }" />
                <DataGridTextColumn Header="Item Description" Width="SizeToCells" MinWidth="250" Binding="{Binding ItemDescription, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" />
                <DataGridTextColumn Header="Unit Price" Width="SizeToCells" MinWidth="90" Binding="{Binding ItemUnitPrice, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
                <DataGridTextColumn Header="Qty" Width="SizeToCells" MinWidth="65" Binding="{Binding ItemQty, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
                <DataGridTextColumn Header="Supplier Name" Width="SizeToCells" MinWidth="200" Binding="{Binding SupplierName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
            </DataGrid.Columns>
        </DataGrid>

我的IDataErroIno看起来像这样:

    string IDataErrorInfo.this[string propertyName]
    {
        get
        {
            string result = string.Empty;
            propertyName = propertyName ?? string.Empty;
            if (propertyName == string.Empty || propertyName == "SelectedProjectExpenseItem")
            {
                if (SelectedProjectExpenseItem != null)
                {
                    if (IsStringMissing(SelectedProjectExpenseItem.ItemNumber))
                    {
                        result = "Item number cannot be blank!";
                        IsValid = false;
                    }
                }
            }
            return result;
        }
    }

现在我只需要弄清楚如何突出显示错误的单元格。我定义的样式似乎没有完成这项工作。我仍然认为这与vm中没有个别属性有关。

哇,这个wpf / mvvm的东西可以让一个人匆忙失去。

答案 3 :(得分:0)

问题与在我的实现中使用Dto有关,或者至少在其中没有实现IDataErrorInfo。我决定抛弃它们并使用实现IDataErroInfo和wa-la的直接业务对象,现在一切都很美妙。