C# 通过IDictionary进行绑定<;字符串,对象>;属性更改的事件处理程序为空

C# 通过IDictionary进行绑定<;字符串,对象>;属性更改的事件处理程序为空,c#,data-binding,uwp,uwp-xaml,inotifypropertychanged,C#,Data Binding,Uwp,Uwp Xaml,Inotifypropertychanged,我有一个类FormItem,它实现了一个IDictionary,当我绑定FormItem的值属性时,属性更改事件始终为空,但是当我从FormItem中删除IDictionary时,FormItem.PropertyChanged事件不为空。我想知道当我实现IDictionary时为什么它是空的,以及如何修复它 范例 public class FormItem : INotifyPropertyChanged, IDictionary<string, object> { pub

我有一个类
FormItem
,它实现了一个
IDictionary
,当我绑定
FormItem
属性时,
属性更改
事件始终为空,但是当我从
FormItem
中删除
IDictionary
时,
FormItem.PropertyChanged
事件不为空。我想知道当我实现
IDictionary
时为什么它是空的,以及如何修复它

范例

public class FormItem : INotifyPropertyChanged, IDictionary<string, object>
{
    public int _value;
    public int Value
    {
        get { return _value; }
        set
        {
            _value= value;
            OnPropertyChanged("Value");
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));

    }

    public bool CanEdit
    {
        get { return true; }
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }

    public IEnumerator<KeyValuePair<string, object>> GetEnumerator()
    {
        throw new NotImplementedException();
    }

    public void Add(KeyValuePair<string, object> item)
    {
        throw new NotImplementedException();
    }

    public void Clear()
    {
        throw new NotImplementedException();
    }

    public bool Contains(KeyValuePair<string, object> item)
    {
        throw new NotImplementedException();
    }

    public void CopyTo(KeyValuePair<string, object>[] array, int arrayIndex)
    {
        throw new NotImplementedException();
    }

    public bool Remove(KeyValuePair<string, object> item)
    {
        throw new NotImplementedException();
    }

    public int Count { get; }
    public bool IsReadOnly { get; }

    public void Add(string key, object value)
    {
        throw new NotImplementedException();
    }

    public bool ContainsKey(string key)
    {
        throw new NotImplementedException();
    }

    public bool Remove(string key)
    {
        throw new NotImplementedException();
    }

    public bool TryGetValue(string key, out object value)
    {
        throw new NotImplementedException();
    }

    public object this[string key]
    {
        get { throw new NotImplementedException(); }
        set { throw new NotImplementedException(); }
    }

    public ICollection<string> Keys { get; }
    public ICollection<object> Values { get; }
}

您看到的问题是微软拒绝实现原始WPF API和WinRT/UWP API(主要源自Silverlight/Windows Phone API,一种简化的WPF API再发明版本)之间的对等性的不幸后果。我的猜测是,如果你问微软,或者至少问负责UWP的人,他们会说这是一个功能,而不是一个bug

当您的类型实现
IDictionary
接口时,UWP决定支持通过属性路径语法以及通常的索引器语法对字典进行索引。也就是说,在WPF索引中,字典需要编写类似于
Text={Binding FormItem[SomeKey]}
,而在UWP中,您可以编写
Text={Binding FormItem.SomeKey}
。当然,这完全破坏了对类中任何实际属性的绑定。一旦类实现了
IDictionary
接口,您就只能访问字典项

更糟糕的是,当绑定到索引字典项时,UWP不需要订阅
PropertyChanged
事件。在WPF中,这有点不太成熟,但是您可以使用属性名为“Item”来引发
PropertyChanged
事件,并将刷新对索引值的绑定(例如通过字典)。UWP甚至不包括这种黑客行为。索引字典项实际上是一次性绑定

如果是我,我就不会在我想要访问其他属性的同一个对象上实现dictionary接口。相反,请更改模型层次结构,使模型对象严格为模型对象。如果您在某个地方需要字典行为,请使您的模型对象包含字典,而不是字典。这将确保将索引字典项和命名属性分开

然而,在这种情况下还有另一个解决办法。
{x:Bind}
语法更受设计限制,不会将索引器和属性路径语法合并。缺点是它也不依赖于
DataContext
。但是,如果您愿意将属性添加到
页面
类中,可以使用
{x:Bind}
语法,这将按照您的预期工作。例如:

public sealed partial class MainPage : Page
{
    public DataPageViewModel Model { get { return (DataPageViewModel)DataContext; } }

    public MainPage()
    {
        this.InitializeComponent();
        this.DataContext = new DataPageViewModel();
    }

    // I added this to your example so that I had a way to modify
    // the property and observe any binding updates
    private void button_Click(object sender, RoutedEventArgs e)
    {
        Model.FormItem.Value++;
    }
}
注意,我在新属性中包装了
DataContext
。这允许您混合和匹配
{Binding}
{x:Bind}
语法

另外请注意,您需要将
DataPageViewModel
类更改为
public
,或者(我的偏好)将
MainPage
类更改为非
public
。否则,将出现编译时错误,抱怨新的
模型
属性的可访问性不一致

然后,在XAML中:


请注意,
{x:Bind}
默认情况下是一次性绑定。要在属性更改时获取更新,您需要显式地将模式设置为
单向
双向


通过这种方式实现视图,您可以保留混合模型+字典设计,并且仍然可以让UWP observe属性更改。

感谢@PeterDuniho的响应。但是,如果像这样使用x:bind
,则会得到无效路径。其中VisibilityProvider实现IPropertyStateProvider,IPropertyStateProvider具有如下索引器
bool this[string key]{get;}
添加VisibilityProvider类。
公共类VisibilityProvider:IPropertyStateProvider{private FormItem FormItem;public VisibilityProvider(FormItem FormItem){FormItem=FormItem;}public bool this[string key]{get{return GetVisibilityValue(key);}}}private bool GetVisibilityValue(string key){bool result=true;返回结果;}公共事件属性changedEventHandler属性changed;}
请不要试图在评论中添加大量代码。没有足够的空间或格式功能来充分理解这一点。上面发布的答案很有效,并且是根据您在问题中发布的代码进行测试的。如果您更改了场景,例如尝试对索引器使用相同的技术,则您将我已经改变了这个问题,应该把它作为一个新问题发布,而不是试图背上这个问题。
public sealed partial class MainPage : Page
{
    public DataPageViewModel Model { get { return (DataPageViewModel)DataContext; } }

    public MainPage()
    {
        this.InitializeComponent();
        this.DataContext = new DataPageViewModel();
    }

    // I added this to your example so that I had a way to modify
    // the property and observe any binding updates
    private void button_Click(object sender, RoutedEventArgs e)
    {
        Model.FormItem.Value++;
    }
}