有人可以对这个错误有所了解吗?
起初我认为SelectedIndex可能不是DependencyProperty而且不能绑定,但我错了。
如果我使用普通绑定而不是标记扩展src:ValidatedBinding
,或者如果我保留标记扩展但绑定SelectedItem
而不是SelectedIndex
,那么它可以正常工作。
这是一个用于演示此问题的小应用程序。
主窗口:
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:src="clr-namespace:WpfApplication2"
Title="MainWindow"
Height="350"
Width="525"
>
<ComboBox SelectedIndex="{src:ValidatedBinding SelectedIndex}"
VerticalAlignment="Center" HorizontalAlignment="Center" Width="100">
<ComboBoxItem>Not Specified</ComboBoxItem>
<ComboBoxItem>First</ComboBoxItem>
<ComboBoxItem>Second</ComboBoxItem>
</ComboBox>
</Window>
主窗口背后的代码:
using System.Windows;
namespace WpfApplication2
{
/// <summary>
/// The main window.
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new Item { Description = "Item 1", SelectedIndex = 0 };
}
}
/// <summary>
/// An object with a string and an int property.
/// </summary>
public sealed class Item
{
int _selectedIndex;
string _description;
public string Description
{
get { return _description; }
set { _description = value; }
}
public int SelectedIndex
{
get { return _selectedIndex; }
set { _selectedIndex = value; }
}
}
}
标记扩展的代码:
using System;
using System.Windows;
using System.Windows.Data;
using System.Windows.Markup;
namespace WpfApplication2
{
/// <summary>
/// Creates a normal Binding but defaults NotifyOnValidationError and
/// ValidatesOnExceptions to True, Mode to TwoWay and UpdateSourceTrigger
/// to LostFocus.
/// </summary>
[MarkupExtensionReturnType(typeof(Binding))]
public sealed class ValidatedBinding : MarkupExtension
{
public ValidatedBinding(string path)
{
Mode = BindingMode.TwoWay;
UpdateSourceTrigger = UpdateSourceTrigger.LostFocus;
Path = path;
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
var Target = (IProvideValueTarget)serviceProvider.GetService(typeof(IProvideValueTarget));
/* on combo boxes, use an immediate update and validation */
DependencyProperty DP = Target.TargetProperty as DependencyProperty;
if (DP != null && DP.OwnerType == typeof(System.Windows.Controls.Primitives.Selector)
&& UpdateSourceTrigger == UpdateSourceTrigger.LostFocus) {
UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
}
return new Binding(Path) {
Converter = this.Converter,
ConverterParameter = this.ConverterParameter,
ElementName = this.ElementName,
FallbackValue = this.FallbackValue,
Mode = this.Mode,
NotifyOnValidationError = true,
StringFormat = this.StringFormat,
ValidatesOnExceptions = true,
UpdateSourceTrigger = this.UpdateSourceTrigger
};
}
public IValueConverter Converter { get; set; }
public object ConverterParameter { get; set; }
public string ElementName { get; set; }
public object FallbackValue { get; set; }
public BindingMode Mode { get; set; }
[ConstructorArgument("path")]
public string Path { get; set; }
public string StringFormat { get; set; }
public UpdateSourceTrigger UpdateSourceTrigger { get; set; }
}
}
运行此应用程序时的异常:
发生了System.Windows.Markup.XamlParseException HResult = -2146233087消息= '设置属性 'System.Windows.Controls.Primitives.Selector.SelectedIndex'扔了一个 例外。'行号'9'和行号'19' Source = PresentationFramework LineNumber = 9 LinePosition = 19
堆栈跟踪: 在System.Windows.Markup.WpfXamlLoader.Load(XamlReader xamlReader,IXamlObjectWriterFactory writerFactory,Boolean skipJournaledProperties,Object rootObject,XamlObjectWriterSettings 设置,Uri baseUri) 在System.Windows.Markup.WpfXamlLoader.LoadBaml(XamlReader xamlReader,Boolean skipJournaledProperties,Object rootObject, XamlAccessLevel accessLevel,Uri baseUri) 在System.Windows.Markup.XamlReader.LoadBaml(Stream stream,ParserContext parserContext,Object parent,Boolean closeStream) 在System.Windows.Application.LoadComponent(对象组件,Uri resourceLocator) 在C:\ Users \ Administrator \ Documents \ Visual Studio中的WpfApplication2.MainWindow.InitializeComponent() 2012 \ Projects \ WpfApplication2 \ MainWindow.xaml:第1行 在C:\ Users \ Administrator \ Documents \ Visual Studio中的WpfApplication2.MainWindow..ctor() 2012 \ Projects \ WpfApplication2 \ MainWindow.xaml.cs:第12行 InnerException:System.ArgumentException 的HResult = -2147024809 Message = 'System.Windows.Data.Binding'不是属性'SelectedIndex'的有效值。 来源= WindowsBase 堆栈跟踪: 在System.Windows.DependencyObject.SetValueCommon(DependencyProperty dp, 对象值,PropertyMetadata元数据,布尔值 coerceWithDeferredReference,Boolean coerceWithCurrentValue, OperationType operationType,Boolean isInternal) 在System.Windows.DependencyObject.SetValue(DependencyProperty dp,Object 值) 在System.Windows.Baml2006.WpfMemberInvoker.SetValue(对象实例, 对象值) 在MS.Internal.Xaml.Runtime.ClrObjectRuntime.SetValue(XamlMember成员, 对象obj,对象值) 在MS.Internal.Xaml.Runtime.ClrObjectRuntime.SetValue(Object inst, XamlMember属性,对象值) 的InnerException:
答案 0 :(得分:1)
好的,这是一个代理绑定,如果有人感兴趣的话。
感谢@HighCore指出我正确的方向。
我使用此绑定代理在绑定上设置非标准默认值,因此我不必在任何地方设置它们。这使得我的xaml更紧凑,并且允许我有一个中心位置,我'我的'绑定'。
这些是默认值:
如果目标属性不是UpdateSourceTrigger
属性,PropertyChanged
将更改为Text
。 (例如Combos或CheckBoxes)
如果我不需要验证,我使用正常绑定:
<TextBlock Text="{Binding FirstName}" />
如果我需要正常的双向绑定,我知道我可能需要验证,所以我使用这个绑定代理:
<TextBox Text="{i:ValidatedBinding FirstName}" />
这意味着我不必写出来:
<TextBox Text="{Binding FirstName
, Mode=TwoWay
, UpdateSourceTrigger=LostFocus
, NotifyOnValidationError=True
, ValidatesOnExceptions=True" />
它适用于SelectedItem
(引用类型)和SelectedIndex
(值类型)。
它将监视DataContext并保持绑定。
如果您在代码中发现漏洞,修复错误或有任何建议,请告知我们。
using ITIS.Reflection /* you can replace this with System.Reflection */;
using System;
using System.Windows;
using System.Windows.Data;
using System.Windows.Markup;
namespace ITIS
{
/// <summary>
/// Creates a Binding with the following defaults:
/// <para>- NotifyOnValidationError = True, </para>
/// <para>- ValidatesOnExceptions = True, </para>
/// <para>- Mode = TwoWay, </para>
/// <para>- UpdateSourceTrigger = LostFocus for 'Text' properties, otherwise PropertyChanged.</para>
/// </summary>
#if !SILVERLIGHT
[MarkupExtensionReturnType(typeof(Binding))]
#endif
public sealed class ValidatedBinding : MarkupExtension
{
#region CONSTRUCTOR
public ValidatedBinding(string path)
{
Mode = BindingMode.TwoWay;
Path = path;
/* possibly changed again in ProvideValue() */
UpdateSourceTrigger = UpdateSourceTrigger.Default;
}
#endregion
#region PROPERTIES
public IValueConverter Converter { get; set; }
public object ConverterParameter { get; set; }
public string ElementName { get; set; }
public object FallbackValue { get; set; }
public BindingMode Mode { get; set; }
#if !SILVERLIGHT
[ConstructorArgument("path")]
#endif
public string Path { get; set; }
public string StringFormat { get; set; }
public UpdateSourceTrigger UpdateSourceTrigger { get; set; }
#endregion
#region FIELDS
bool _bound;
DependencyProperty _property;
FrameworkElement _element;
#endregion
#region OPERATIONS
void ClearBinding()
{
_element.ClearValue(_property);
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
IProvideValueTarget Target = serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget;
if (Target == null) {
throw new InvalidOperationException(
"Cannot resolve the IProvideValueTarget. Are you binding to a property?");
}
#if !SILVERLIGHT
/* on text boxes, use a LostFocus update trigger */
_property = Target.TargetProperty as DependencyProperty;
if (_property != null) {
if (_property.Name.StartsWith("Text")) {
UpdateSourceTrigger = UpdateSourceTrigger.LostFocus;
}
else {
UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
}
}
#endif
_element = Target.TargetObject as FrameworkElement;
if (_element != null) {
_element.DataContextChanged += Element_DataContextChanged_SetBinding;
if (_element.DataContext != null || !string.IsNullOrWhiteSpace(ElementName)) {
SetBinding();
/* can be replaced with normal reflection PropertyInfo.GetValue() */
return FastReflector.GetPropertyValue(/* object = */ _element.DataContext, /* property name = */ Path);
}
/* don't return null for value types */
if (_property.PropertyType.IsValueType) {
return Activator.CreateInstance(_property.PropertyType);
}
return null;
}
return this;
}
void SetBinding()
{
_bound = true;
Binding Binding = new Binding() {
Path = new PropertyPath(this.Path),
Converter = this.Converter,
ConverterParameter = this.ConverterParameter,
FallbackValue = this.FallbackValue,
Mode = this.Mode,
NotifyOnValidationError = true,
StringFormat = this.StringFormat,
ValidatesOnExceptions = true,
UpdateSourceTrigger = this.UpdateSourceTrigger
};
/* only set when necessary to avoid a validation exception from the binding */
if (_element.DataContext != null) { Binding.Source = _element.DataContext; }
if (!string.IsNullOrWhiteSpace(ElementName)) { Binding.ElementName = ElementName; }
_element.SetBinding(_property, Binding);
}
#endregion
#region EVENT HANDLERS
void Element_DataContextChanged_SetBinding(object sender, DependencyPropertyChangedEventArgs e)
{
/* cleanup the old binding */
if (_bound) { ClearBinding(); }
SetBinding();
}
#endregion
}
}