【问题标题】:'System.Windows.Data.Binding' is not a valid value for property 'SelectedIndex'“System.Windows.Data.Binding”不是属性“SelectedIndex”的有效值
【发布时间】:2013-03-20 21:27:18
【问题描述】:

有人可以解释一下这个错误吗?

一开始我以为 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 Message='设置属性 '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 baseUri) 在 System.Windows.Markup.WpfXamlLoader.LoadBaml(XamlReader xamlReader,布尔型 skipJournaledProperties,对象 rootObject, XamlAccessLevel accessLevel, Uri baseUri) 在 System.Windows.Markup.XamlReader.LoadBaml(流流,ParserContext parserContext,对象父,布尔 closeStream) 在 System.Windows.Application.LoadComponent(对象组件,Uri 资源定位器) 在 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 行
内部异常:System.ArgumentException H结果=-2147024809 Message='System.Windows.Data.Binding' 不是属性“SelectedIndex”的有效值。 源=WindowsBase 堆栈跟踪: 在 System.Windows.DependencyObject.SetValueCommon(DependencyProperty dp, 对象值、PropertyMetadata 元数据、布尔值 coerceWithDeferredReference,布尔 coerceWithCurrentValue, OperationType operationType, Boolean isInternal) 在 System.Windows.DependencyObject.SetValue(DependencyProperty dp,对象 价值) 在 System.Windows.Baml2006.WpfMemberInvoker.SetValue(对象实例, 对象值) 在 MS.Internal.Xaml.Runtime.ClrObjectRuntime.SetValue(XamlMember 成员, 对象 obj,对象值) 在 MS.Internal.Xaml.Runtime.ClrObjectRuntime.SetValue(对象实例, XamlMember 属性,对象值) 内部异常:

【问题讨论】:

  • SelectedIndex 是一个int。您不能将 System.Windows.Data.Binding 放在 int 属性中。你的MarkupExtension 是错误的。您正在返回 Binding 本身,而不是评估它并返回 Binding Source
  • @HighCore - 但是普通的{Binding xxx} 也不是int。我不明白。如果我使用{Binding SelectedIndex} 而不是{src:ValidatedBinding SelectedIndex} 那么它会起作用,并且基本上都返回一个绑定。我在哪里错过了船?
  • Binding 标记扩展不返回 Binding 实例,它创建绑定并返回另一端(源)中的值
  • @HighCore - 谢谢你的解释。这是有道理的,除了标记扩展适用于SelectedItemSelectedItem 是否忽略绑定并改为分配值?
  • SelectedItem 的类型为object,这就是为什么您没有收到异常,但这并不意味着它正在工作。不是,您只是将Binding 类的一个实例分配给SelectedItem 属性。

标签: c# wpf combobox selectedindex markup-extensions


【解决方案1】:

好的,如果有人感兴趣,这是一个代理绑定。

感谢@HighCore 为我指明了正确的方向。

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

这些是默认值:

  • NotifyOnValidationError = True,
  • ValidatesOnExceptions = True,
  • 模式 = 双路,
  • 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
    }
}

【讨论】:

    猜你喜欢
    • 2011-07-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-24
    相关资源
    最近更新 更多