【问题标题】:Binding custom data structures to DataGrid ItemsSource in WPF在 WPF 中将自定义数据结构绑定到 DataGrid ItemsSource
【发布时间】:2012-03-16 21:45:11
【问题描述】:

我正在尝试从 c# 将自定义类绑定到 WPF DataGrid 的 itemssource 成员。我实现了 IList,但由于某种原因,我无法让列表显示任何成员。

我通过调用在 C# 中设置 ItemsSource 属性

    dataGridObject.ItemsSource = switchHolderObject

这是我的价值的定义:

    public class AssetHolder<T> : Dictionary<string,T>, IList<T>, INotifyCollectionChanged, INotifyPropertyChanged where T : Asset
    {
        ... // lots of code here
        new public IEnumerator<T> GetEnumerator()
        {
            var enumerator = base.GetEnumerator();
            while (enumerator.MoveNext())
            {
                var pair = enumerator.Current;
                yield return pair.Value;
            }
        }
    }
    public class NonGenericAssetHolder : AssetHolder<Asset> {}
    public class SwitchHolder : NonGenericAssetHolder {}

我在这里设置三个类的原因是为了解决 C# 中的字典不是协变的这一事实。

我已经验证了我实现的所有 IList 方法都正常工作,并且列表确实展开并具有适当的数据,但 DataGrid 仍然没有显示任何成员。奇怪的是,当我将此方法添加到 AssetHolder 类时,它会起作用:

    public List<T> Hack()
    {
        List<T> result = new List<T>();
        foreach (T asset in this)
        {
            result.Add(asset);
        }
        return result;
    }

并像这样不断地重新绑定 DataGrid:

    SwitchGrid.ItemsSource = _tempLocation.Switches.Hack();

但是,这会对性能造成巨大影响,我觉得它应该按原样工作。有谁知道发生了什么?为什么 DataGrid 不显示我的数据?

【问题讨论】:

  • 你实现了GetEnumerator() 对吗?你在用吗?附带说明:我总是在代码隐藏中设置ItemsControlDataContext,并在Xaml 中将ItemsSource 设置为ItemsSource="{Binding}",这样您在输出窗口中就会出现更多的BindingErrors 变化,所以您可以更好地诊断出了什么问题。
  • 我猜,它取决于您在 AssetHolder 类中填充数据的位置。您必须在构造函数中填写它才能以这样的方式使用它。或者,您可以将 ItemsSource 绑定到 Hack 方法,但您应该使用 AssetHolder 作为返回类型,IMO:public AssetHolder Hack() { return this; }
  • @Silvermind Dictionary 实现了 IEnumerable,所以我只是修改了 GetEnumerator 函数以仅返回值。我已经确认它可以正常工作,并将其添加到我上面的帖子中。
  • 您是否还通过在 GetEnumerator 中放置断点确认它在不由代码访问而仅由 Xaml 访问时可以正常工作?这样,如果 Xaml 触发它,我们现在将这样做,抱歉,如果您已经指出这一点,请确保。
  • @Silvermind 按照您的建议修改了绑定,但不幸的是在这种情况下我仍然没有收到任何 BindingErrors。

标签: c# wpf binding ilist itemssource


【解决方案1】:

编辑

我刚刚实现了 IDictionary 接口并添加了 Dictionary 的私有变量。

出于演示目的,我只在界面中使用了 Add 和 GetEnumerator。 这样我们就可以使用 testclass Person 为初学者做到这一点:

AssetHolder<Person> persons = new AssetHolder<Person>( );
persons.Add("One", new Person( ) { FirstName = "Jack" , LastName = "Jobs" , Age = 63 } );
persons.Add( "Two" , new Person( ) { FirstName = "Peter" , LastName = "Lastname" , Age = 33 } );
persons.Add( "Three" , new Person( ) { FirstName = "Wally" , LastName = "Breakfast" , Age = 33 } );
dataGrid1.DataContext = persons;

XAML:

<DataGrid AutoGenerateColumns="False" ItemsSource="{Binding}" Name="dataGrid1">
  <DataGrid.Columns>
    <DataGridTextColumn Width="1*" Header="FirstName" Binding="{Binding Path=FirstName}"/>
  </DataGrid.Columns>
</DataGrid>

只是一个快速测试,但这是AssetHolder,看看GetEnumerator():

public class AssetHolder<T> : IDictionary<string , T>
  {

    Dictionary<string , T> dictionary;

    public AssetHolder()
    {
      dictionary = new Dictionary<string , T>( );
  }

    public void Add( string key , T value )
    {
      dictionary.Add( key , value );
    }

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

    public ICollection<string> Keys
    {
      get { throw new NotImplementedException( ); }
    }

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

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

    public ICollection<T> Values
    {
      get { throw new NotImplementedException( ); }
    }

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

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

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

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

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

    public int Count
    {
      get { throw new NotImplementedException( ); }
    }

    public bool IsReadOnly
    {
      get { throw new NotImplementedException( ); }
    }

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

    IEnumerator<KeyValuePair<string , T>> IEnumerable<KeyValuePair<string , T>>.GetEnumerator( )
    {
      throw new NotImplementedException( );
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator( )
    {
      return dictionary.Values.GetEnumerator( );
    }
  }

人物类:

  public class Person
  {
    public string FirstName { get; set; }
    public string LastName{get;set;}
    public int Age { get; set; }
  }

【讨论】:

  • 我有一些在 AssetHolder 上调用 foreach 的 c# 代码,但现在它告诉我它无法从 KeyValuePair 转换为 AssetObject。如何指定使用哪个 GetEnumerator?
  • 但是耶!有用!我不确定为什么我最初的解决方案不起作用,但我很高兴这个解决方案起作用。
  • 我做了更多的测试,这就是我学到的:正确发送通知事件(或删除 INotify 接口)对于在 DataGrid 中显示任何内容是必要的。但仅仅这样做是不够的,因为我的代码仍然返回 KeyValuePairs 作为 foreach 结果。需要您的无类型 GetEnumerator 方法才能使每个结果显示为单个 Asset 对象。
  • @hypehuman 我猜想实现 IList 不会覆盖派生字典的 GetEnumerator。很高兴它对你有用。
【解决方案2】:

我想通了!我的实现不起作用的原因是因为我包含了 INotifyCollectionChanged 和 INotifyPropertyChanged 接口,但我没有明确告诉它何时通知。当我停止实施它们时,它神奇地起作用了!

【讨论】:

    猜你喜欢
    • 2011-12-17
    • 2023-03-07
    • 2011-07-21
    • 2020-01-30
    • 2011-07-10
    • 2010-12-16
    • 2013-01-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多