ListView的绑定项上的ValidationRules

时间:2010-07-22 23:23:01

标签: c# wpf validation data-binding listview

我的实际情况是这样的:我在主 - 详细信息中设置了ListView和自定义UserControl。通过菜单项,可以添加最初无效的多个项目。

如果列表中的任何项目无效,我最终会阻止提交。短期内,我试图给出无效项目的视觉线索。我的想法是,在ListView的附加ListViewItem属性上为ListViewItem定位Validation.HasError触发器引入一种样式,以触发整行的背景变为红色。

为了执行此操作,我当然添加了样式,并引入了一个简单的验证规则,我在DisplayMemberBinding的{​​{1}}中使用了该规则。我已经通过调试器验证了规则正在调用,并且规则按预期运行,但我没有看到样式更改。

我已将以下所有相关部分包含在复制品中。我很感激这里有任何帮助。我应该注意,按钮总是生成一个“有效!”的消息框。尽管调试器显示失败的规则被击中,但作为文本也是如此。

我也在使用.Net 3.5 SP1。

Person.cs:

GridViewColumn

RequiredStringValidator.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ListViewItemValidation
{
    public class Person
    {
        public string Name { get; set; }
        public int Age { get; set; }

        static Person[] _Data;
        public static Person[] Data
        {
            get
            {
                if (_Data == null)
                {
                     _Data =new[]{
                        new Person() { Name="John", Age=30},
                        new Person() { Name="Mary", Age=40},
                        new Person() { Name="", Age=20},
                        new Person() { Name="Tim", Age=-1},
                    };
                }
                return _Data;
            }
        }
    }
}

Window1.xaml:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Data;
using System.Windows.Controls;

namespace ListViewItemValidation
{
    public class RequiredStringValidator : ValidationRule
    {
        public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
        {
            if (string.IsNullOrEmpty(value as string))
                return new ValidationResult(false, "String cannot be empty.");

            return ValidationResult.ValidResult;
        }
    }
}

Window1.xaml.cs:

<Window
    x:Class="ListViewItemValidation.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:l="clr-namespace:ListViewItemValidation"  
    Title="Window1" Height="300" Width="300">
    <DockPanel>
        <Button Content="Validate"
                DockPanel.Dock="Bottom"
                Click="ValidateClicked"
                />
        <ListView 
            HorizontalAlignment="Stretch"        
            VerticalAlignment="Stretch"
            ItemsSource="{x:Static l:Person.Data}"
            >
            <ListView.Resources>
                <Style TargetType="ListViewItem">
                    <Style.Triggers>
                        <Trigger Property="Validation.HasError" Value="True">
                            <Setter Property="Background" Value="Red" />
                        </Trigger>
                    </Style.Triggers>
                </Style>
            </ListView.Resources>
            <ListView.View>
                <GridView>
                    <GridView.Columns>
                        <GridViewColumn Header="Name">
                            <GridViewColumn.DisplayMemberBinding>
                                <Binding Path="Name">
                                    <Binding.ValidationRules>
                                        <l:RequiredStringValidator
                                            ValidatesOnTargetUpdated="True"
                                            ValidationStep="RawProposedValue"
                                            />
                                    </Binding.ValidationRules>
                                </Binding>
                            </GridViewColumn.DisplayMemberBinding>
                        </GridViewColumn>
                        <GridViewColumn 
                            Header="Age"
                            DisplayMemberBinding="{Binding Path=Age}"
                            />
                    </GridView.Columns>
                </GridView>
            </ListView.View>
        </ListView>
    </DockPanel>
</Window>

更新:最终解决方案 我添加了以下类,为ListViewItem提供附加属性,以检查是否有任何子项包含已失败的验证规则的绑定属性:

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;

namespace ListViewItemValidation
{
    /// <summary>
    /// Interaction logic for Window1.xaml
    /// </summary>
    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();
        }

        private void ValidateClicked(object sender, RoutedEventArgs e)
        {
            if (Validation.GetHasError(this))
                MessageBox.Show("invalid!");
            else
                MessageBox.Show("valid!");
        }
    }
}

然后,我将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.Media; namespace ListViewItemValidation { public class ListViewItemExtensions { #region ChildrenHaveError Property public bool ChildrenHaveError { get { return (bool)this.ListViewItem.GetValue(ChildrenHaveErrorProperty); } set { this.ListViewItem.SetValue(ChildrenHaveErrorProperty, value); } } public static bool GetChildrenHaveError(ListViewItem obj) { return EnsureInstance(obj).ChildrenHaveError; } public static void SetChildrenHaveError(ListViewItem obj, bool value) { EnsureInstance(obj).ChildrenHaveError = value; } public static readonly DependencyProperty ChildrenHaveErrorProperty = DependencyProperty.RegisterAttached( "ChildrenHaveError", typeof(bool), typeof(ListViewItemExtensions), new PropertyMetadata( new PropertyChangedCallback((o, a) => { EnsureInstance((ListViewItem)o); }) ) ); #endregion #region ValidatesChildren Property public bool ValidatesChildren { get { return (bool)this.ListViewItem.GetValue(ValidatesChildrenProperty); } set { this.ListViewItem.SetValue(ValidatesChildrenProperty, value); } } public static bool GetValidatesChildren(ListViewItem obj) { return EnsureInstance(obj).ValidatesChildren; } public static void SetValidatesChildren(ListViewItem obj, bool value) { EnsureInstance(obj).ValidatesChildren = value; } public static readonly DependencyProperty ValidatesChildrenProperty = DependencyProperty.RegisterAttached( "ValidatesChildren", typeof(bool), typeof(ListViewItemExtensions), new PropertyMetadata( new PropertyChangedCallback((o, a) => { EnsureInstance((ListViewItem)o); }) ) ); #endregion #region Instance Property public static ListViewItemExtensions GetInstance(ListViewItem obj) { return (ListViewItemExtensions)obj.GetValue(InstanceProperty); } public static void SetInstance(ListViewItem obj, ListViewItemExtensions value) { obj.SetValue(InstanceProperty, value); } public static readonly DependencyProperty InstanceProperty = DependencyProperty.RegisterAttached("Instance", typeof(ListViewItemExtensions), typeof(ListViewItemExtensions)); #endregion #region ListViewItem Property public ListViewItem ListViewItem { get; private set; } #endregion static ListViewItemExtensions EnsureInstance(ListViewItem item) { var i = GetInstance(item); if (i == null) { i = new ListViewItemExtensions(item); SetInstance(item, i); } return i; } ListViewItemExtensions(ListViewItem item) { if (item == null) throw new ArgumentNullException("item"); this.ListViewItem = item; item.Loaded += (o, a) => { this.FindBindingExpressions(item); this.ChildrenHaveError = ComputeHasError(item); }; } static bool ComputeHasError(DependencyObject obj) { var e = obj.GetLocalValueEnumerator(); while (e.MoveNext()) { var entry = e.Current; if (!BindingOperations.IsDataBound(obj, entry.Property)) continue; var binding = BindingOperations.GetBinding(obj, entry.Property); foreach (var rule in binding.ValidationRules) { ValidationResult result = rule.Validate(obj.GetValue(entry.Property), null); if (!result.IsValid) { BindingExpression expression = BindingOperations.GetBindingExpression(obj, entry.Property); Validation.MarkInvalid(expression, new ValidationError(rule, expression, result.ErrorContent, null)); return true; } } } for (int i = 0, count = VisualTreeHelper.GetChildrenCount(obj); i < count; ++i) if (ComputeHasError(VisualTreeHelper.GetChild(obj, i))) return true; return false; } void OnDataTransfer(object sender, DataTransferEventArgs args) { this.ChildrenHaveError = ComputeHasError(this.ListViewItem); } void FindBindingExpressions(DependencyObject obj) { var e = obj.GetLocalValueEnumerator(); while (e.MoveNext()) { var entry = e.Current; if (!BindingOperations.IsDataBound(obj, entry.Property)) continue; Binding binding = BindingOperations.GetBinding(obj, entry.Property); if (binding.ValidationRules.Count > 0) { Binding.AddSourceUpdatedHandler(obj, new EventHandler<DataTransferEventArgs>(this.OnDataTransfer)); Binding.AddTargetUpdatedHandler(obj, new EventHandler<DataTransferEventArgs>(this.OnDataTransfer)); } } for (int i = 0, count = VisualTreeHelper.GetChildrenCount(obj); i < count; ++i) { var child = VisualTreeHelper.GetChild(obj, i); this.FindBindingExpressions(child); } } } } 样式修改为:

ListViewItem

非常感谢@Quartermeister帮助我解决这个问题。

1 个答案:

答案 0 :(得分:3)

Validation.HasError仅在单个单元格的TextBlock上设置,因为这是应用绑定的位置。这是ListViewItem的子项之一,但不是ListViewItem本身。它也没有在窗口上设置,这就是为什么您的消息框始终显示“有效!”。

当一个单元格验证失败时,您可以使用一种方法突出显示整行,即将单元格的ValidationAdornerSite设置为其行。这将导致应用ListViewItem的ErrorTemplate,默认情况下将为其提供红色边框。尝试添加这样的样式:

<Style TargetType="TextBlock">
    <Setter
        Property="Validation.ValidationAdornerSite"
        Value="{Binding RelativeSource={RelativeSource AncestorType=ListViewItem}}"/>
</Style>