我的实际情况是这样的:我在主 - 详细信息中设置了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帮助我解决这个问题。
答案 0 :(得分:3)
Validation.HasError
仅在单个单元格的TextBlock上设置,因为这是应用绑定的位置。这是ListViewItem的子项之一,但不是ListViewItem本身。它也没有在窗口上设置,这就是为什么您的消息框始终显示“有效!”。
当一个单元格验证失败时,您可以使用一种方法突出显示整行,即将单元格的ValidationAdornerSite设置为其行。这将导致应用ListViewItem的ErrorTemplate,默认情况下将为其提供红色边框。尝试添加这样的样式:
<Style TargetType="TextBlock">
<Setter
Property="Validation.ValidationAdornerSite"
Value="{Binding RelativeSource={RelativeSource AncestorType=ListViewItem}}"/>
</Style>