ObservableCollection 上的 IDataErrorInfo

发布于 2024-09-07 03:08:10 字数 4715 浏览 4 评论 0原文

我有一个实现 IDataError 的视图模型。在视图模型中我有一个 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,所以我的视图模型中没有任何属性;因此,我只能对 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;
        }
    }

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

我正在寻找任何指导,因为这真的让我感到困惑。

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

好的,我为我的 DTO 创建了一个单独的视图模型:

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
    }
}

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

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

I have a viewmodel that implements IDataError. In the viewmodel I have an ObservableCollection. The ObservableCollection populates a datagrid in my view:

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

I also have a property that represents the selected item in the datagrid (it is based off a Dto):

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

Here is the 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; }
    }
}

I want to use IDataError to validate values in the selected row of the datagrid (SelectedProjectExpenseItem), but since my grid is bound to the ObservableCollection, I don't have any properties in my viewmodel; therefore, I can only use IDataError against the ObservableCollection, not the individual items in the collection, which doesn't help me because I have know way to see "inside" the collection. I cannot use IDataError for my SelectedProjectExpenseItem either. For example:

    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;
        }
    }

this doesn't fire because my datagrid column is not bound to the SelectedProjectExpenseItem.ItemNumber, it is bound to the ItemNumber in the ObservableCollection.

I am looking for any guidance as this is really confusing me.

---------------------------- EDIT: ----------------------------

Ok, I created a separate viewmodel for my DTO:

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
    }
}

Now, I am having problems with the following line in my main vm:

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

It says:

Cannot implicitly convert type 'System.Collections.Generic.IList' to 'System.Collections.Generic.IList'. An explicit conversion exists (are you missing a cast?)

Any ideas?

---------------------------- EDIT: ----------------------------

I found this link, gonna see if I can copy what the person is doing:

Validation-in-a-WPF-DataGrid

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

太傻旳人生 2024-09-14 03:08:10

不要使用 ProjectExpenseItemsDtoObservableCollection ,而是为 ProjectExpenseItemsDto 类型(IE:ProjectExpenseItemsDtoViewModel)创建一个 ViewModel 并制作它实现了 IDataErrorInfo 接口,然后使用 ObservableCollection作为主 ViewModel 中的属性。

Instead of using an ObservableCollection of ProjectExpenseItemsDto , create a ViewModel for the ProjectExpenseItemsDto Type (I.E. : ProjectExpenseItemsDtoViewModel) and make it implements the IDataErrorInfoInterface, then use an ObservableCollection<ProjectExpenseItemsDtoViewModel> as a property in the main ViewModel.

黒涩兲箜 2024-09-14 03:08:10

据我所知,问题不在于您的视图模型,而在于您视图中的标记。你必须在你的 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 属性。请注意,您的视图模型必须能够告诉视图出现了问题,因此 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 的操作来阅读有关错误模板的信息,例如 < a href="http://japikse.blogspot.com/2009/07/idataerrorinfo-error-templates-and-wpf.html" rel="nofollow noreferrer">这个例子。上面会添加一个工具提示到有错误的文本框。

WPF 有多个活动部分,因此很容易迷失,但您会习惯它,只是需要时间:)

希望这对您的旅程有所帮助。

As far as I can tell the problem is not in your viewmodel, but in the markup in your view. You have to implement it on your DTO, you just have to tell your view to pay attention to IDataErrorInfo and do something about it.

you could do the following:

<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>

Note the ValidatedOnDataErrors property. See your viewmodel has to be able to tell the view that something is wrong, so IDataErrorInfo is an interface that WPF inherently listens to, if it's asked. If your binding on the property tells wpf to listen to it then it will.

And there are default error templates, but if lets say you want to add a tooltip to tell the user the actual error you would need to do something like this

<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 to read about error templates like this example. The above will add a tooltip to textboxes that have an error.

WPF has several moving parts, so its easy to get lost, but you'll get used to it, it just takes time :)

Hope this helps you in your journey.

冷清清 2024-09-14 03:08:10

好吧,我发现了部分问题。 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;
        }
    }

现在我只需要弄清楚如何突出显示错误的单元格。我定义的样式似乎不起作用。我仍然认为这与虚拟机中没有单独的属性有关。

哇,这个 wpf/mvvm 东西会让一个人很快迷失方向。

Ok, I found part of the problem. The reason IDataErrorInfo wasn't firing was because I didn't have ValidatesOnDataErrors=True on the SelectedItem binding of the dg:

            <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>

My IDataErroIno looks like so:

    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;
        }
    }

Now I just need to figure out how to highlight the cell in error. My defined styles don't seem to be doing the job. I still think it has to do with not having the individual properties in the vm.

wow, this wpf/mvvm stuff can get a guy lost in a hurry.

臻嫒无言 2024-09-14 03:08:10

这些问题与在我的实现中使用 Dto 有关,或者至少与其中缺少实现 IDataErrorInfo 有关。我决定转储它们并使用实现 IDataErroInfo 和 wa-la 的直接业务对象,现在一切都运行良好。

The problems had to do with using Dto's in my implementation, or at least the lack of implementing IDataErrorInfo in them. I decided to dump them and go with straight business objects that implement IDataErroInfo and wa-la, everything works beautifully now.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文