2013-03-20 175 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 
    } 
} 
相關問題