2013-03-20 442 views
0

有人可以澄清一下这个错误吗?'System.Windows.Data.Binding'不是属性'SelectedIndex'的有效值

起初我以为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'。
源= PresentationFramework LineNumber上= 9 LinePosition = 19
堆栈跟踪: 在System.Windows.Markup.WpfXamlLoader.Load(XamlReader xamlReader,IXamlObjectWriterFactory writerFactory,布尔 skipJournaledProperties,对象rootObject,XamlObjectWriterSettings 设置,乌里基本URI) 在系统.Windows.Markup.WpfXamlLoader.LoadBaml(xamlReader xamlReader,布尔skipJournaledProperties,对象rootObject, XamlAccessLevel ACCESSLEVEL,乌里基本URI) 在System.Windows.Markup.XamlReader.LoadBaml(流流,parserContext parserContext,父对象,布尔closeStream) 在System.Windows.Application.LoadComponent(对象组件,Uri resourceLocat或) 在WpfApplication2.MainWindow.InitializeComponent()在C:\用户\管理员\文件\的Visual Studio 2012 \项目\ WpfApplication2 \ MainWindow.xaml:线1 在WpfApplication2.MainWindow..ctor(c)中:\用户\管理员\文件\的Visual Studio 2012 \项目\ WpfApplication2 \ MainWindow.xaml.cs:线12
的InnerException信息:System.ArgumentException 的HResult = -2147024809 消息= 'System.Windows.Data.Binding' 不是属性'SelectedIndex'的有效值为。 源= WindowsBase 堆栈跟踪: 在System.Windows.DependencyObject.SetValueCommon(的DependencyProperty DP, 对象值,PropertyMetadata元数据,布尔 coerceWithDeferredReference,布尔coerceWithCurrentValue, OperationType operationType,布尔isInternal) 在System.Windows.DependencyObject.SetValue (DependencyProperty dp,Object value) at System.Windows.Baml2006.WpfMemberInvoker。的SetValue(Object实例, 对象的值) 在MS.Internal.Xaml.Runtime.ClrObjectRuntime.SetValue(XamlMember构件, 对象OBJ,对象的值) 在MS.Internal.Xaml.Runtime.ClrObjectRuntime.SetValue(对象研究所, XamlMember属性,对象的值) 的InnerException:

+4

'SelectedIndex'是一个'int'。你不能在'int'属性中放置'System.Windows.Data.Binding'。你的'MarkupExtension'是错误的。你正在返回'Binding'本身,而不是评估它并返回'Binding Source'。 – 2013-03-20 21:28:59

+0

@HighCore - 但一个普通的'{Binding xxx}'也不是'int'。我不明白。如果我使用'{Binding SelectedIndex}'而不是'{src:ValidatedBinding SelectedIndex}',那么它就会工作,并且基本上都返回一个绑定。我在哪里错过了船? – 2013-03-20 21:38:12

+1

'Binding'标记扩展不会返回'Binding'实例,它会创建绑定并返回另一端的值(Source) – 2013-03-20 21:50:04

回答

1

好吧,这里是一个代理绑定如果有人有兴趣的作品。

谢谢@HighCore指引我朝着正确的方向前进。

我使用这个绑定代理来设置绑定的非标准默认值,所以我不必在任何地方设置它们。这使得我的xaml更加紧凑,并且让我有一个中心位置,在这里我可以“定制”我的绑定。

这些默认值:

  • NotifyOnValidationError =真,
  • ValidatesOnExceptions =真,
  • 模式=双向,
  • UpdateSourceTrigger =引发LostFocus为 '文本' 属性,否则的PropertyChanged。

如果目标属性不是Text属性的UpdateSourceTrigger将变更为PropertyChanged。 (例如,连击或复选框)

如果我不需要验证,我用的是正常的结合:

<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 
    } 
} 
相关问题