为什么在属性getter中返回yield会破坏WPF绑定?

时间:2014-03-07 01:36:44

标签: c# wpf yield-return

我有一个在其get函数中执行yield return的属性:

数据:

public class TestSummary
{
   [Description("Test1")]
   public TestResult Test1 {get; set;}

   [Description("Test2")]
   public TestResult Test2 {get; set;}
}
public enum TestResult
{
   Failed,
   Passed
}

视图模型:

private TestSummary TestResults
{
  get { return new TestSummary() { Test1=Failed, Test2=Passed }; }
}
public int TimePassed
{
   get
   {
       return 2;
   }
}

public IEnumerable<String> FailedTests
{
   get
   {
      PropertyDescriptorCollection attributes = TypeDescriptor.GetProperties(TestResults);
     foreach (PropertyInfo failedResult in typeof(TestSummary).GetProperties(BindingFlags.Instance | BindingFlags.Public)
                                              .Where(p => p.PropertyType == typeof(TestResult)).Where(t => (TestResult)t.GetValue(TestResults) == TestResult.Failed))
     {
        yield return ((DescriptionAttribute)attributes[failedResult.Name].Attributes[typeof(DescriptionAttribute)]).Description;
     }
   }
}

XAML:

<UserControl>
<UserControl.DataContext>
   <local:ViewModel/>
</UserControl.DataContext>
   <StackPanel>
       <ItemsControl ItemsSource="{Binding FailedTests}"/>
       <ProgressBar Value="{Binding TimePassed}"/>
   </StackPanel>
</UserControl>

工作和编译就好(属性是IEnumerable类型)。您甚至可以绑定它,并且UI正确更新。但是,使用此代码会破坏其他控件上的其他(看似随机)绑定,甚至是其他用户控件内部的绑定。

我的问题是,为什么?这是完全有效的C#,如果有的话,它应该打破绑定的UI控件,而不是其他。

更新 更新的源代码更完整。当我使用上面的内容时,从未调用过“TimePassed”的getter,进度条的Value总是为0。

如果有帮助,在函数中使用相同的yield return,调用它并将result.ToList()赋值给属性不会破坏任何内容。我仍然很好奇为什么发布的代码导致“TimePassed”绑定失败(从未调用getter)。

同样有趣的是,如果我使用Snoop来调查绑定,当我尝试查看“已损坏”元素时,会调用getter来调用。

1 个答案:

答案 0 :(得分:1)

一切正常。这是我的测试代码。

<Window x:Class="BindingTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:bindingTest="clr-namespace:BindingTest"
        Title="MainWindow" Height="350" Width="525">
    <Window.DataContext>
        <bindingTest:ViewModel/>
    </Window.DataContext>
    <Grid>
        <StackPanel>
            <ItemsControl ItemsSource="{Binding FailedTests}"/>
            <ProgressBar Value="{Binding TimePassed,Mode=OneWay}"/>
        </StackPanel>
    </Grid>
</Window>

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;

namespace BindingTest
{
    public class TestSummary
    {
        [Description("Test1")]
        public TestResult Test1 { get; set; }

        [Description("Test2")]
        public TestResult Test2 { get; set; }

        [Description("Test3")]
        public TestResult Test3 { get; set; }
    }
    public enum TestResult
    {
        Failed,
        Passed
    }

    public class ViewModel
    {
        private TestSummary TestResults
        {
            get { return new TestSummary()
            {
                Test1 = TestResult.Failed, 
                Test2 = TestResult.Passed,
                Test3 = TestResult.Failed
            }; }
        }

        public int TimePassed
        {
            get
            {
                return 2;
            }
        }

        public IEnumerable<String> FailedTests
        {
            get
            {
                PropertyDescriptorCollection attributes = TypeDescriptor.GetProperties(TestResults);
                foreach (PropertyInfo failedResult in typeof(TestSummary).GetProperties(BindingFlags.Instance | BindingFlags.Public)
                                                         .Where(p => p.PropertyType == typeof(TestResult))
                                                         .Where(t => (TestResult)t.GetValue(TestResults,null) == TestResult.Failed))
                {
                    yield return ((DescriptionAttribute)attributes[failedResult.Name].Attributes[typeof(DescriptionAttribute)]).Description;
                }
            }
        }
    }
}

我会仔细研究并调整您的值以匹配,看看是否有帮助。如果没有,则问题超出了您发布的代码的范围。