数据绑定到每个 KeyValuePair<string, List<T>> 中的最后一个条目

Databinding to last entry in each KeyValuePair<string, List<T>>

我有一个实现 INotifyChanged 的​​简单 OHLC 对象。每个 OHLC 对象都与特定蜡烛相关,并且是列表的一部分。每个列表都与特定股票相关,并存储在 ConcurrentDictionary 中。

我正在尝试将每只股票的最后一根蜡烛数据绑定到数据网格视图。这就是我设置数据绑定的方式。不是很优雅!

    ConcurrentDictionary<string, List<OHLC>> candles= new ConcurrentDictionary<string, List<OHLC>>();
    BindingList<OHLC> LastCandles = new BindingList<OHLC>();

    . . .
    form1.dataGridView1.DataSource = LastCandles;
    . . .
    public void OnUpdate()
    {
        //Update the appropriate candles
        . . .
        //Pull out the last candle by symbol and re-bind
        BindingList<OHLC> lastBySymbol = new BindingList<OHLC>();
        foreach (KeyValuePair<string, List<OHLC>> dict in candles)
        {
           if (dict.Value.Count > 0)
           {
               lastBySymbol.Add(dict.Value.Last());
           }
       }

       LastCandles = lastBySymbol;

       form1.dataGridView1.BeginInvoke((MethodInvoker)delegate
       {
           form1.dataGridView1.DataSource = LastCandles;
           form1.dataGridView1.Refresh();
       });
   }

在这里设置数据绑定的最佳方法是什么,这样我就可以专注于更新集合而不必在每次更新时都重新绑定?我可以限制对 datagridview.Refresh() 的调用,这样它就不会在每次原子更新时都重新绘制,但不想重新绑定。

我知道如何将数据绑定一次到自定义对象的 BindingList 并使用 INotifyPropertyChanged 和对 datagridview.Refresh() 的简单调用将更新传播到 UI。我还可以在 XAML 中找到一些示例,这些示例显示了对 BindingList 中最后一项的数据绑定。但是我找不到任何绑定到列表字典的 'last-per-key' 的示例。

对于这方面的任何帮助,我将不胜感激。

注意:这将是问题的 WPF 解决方案,而不是 WinForms。

不要转换数据以适应视图,创建一个值转换器来处理该转换。

[ValueConversion(typeof(System.Collections.IEnumerable), typeof(object))]
public class LastItemConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture) =>
        ((System.Collections.IEnumerable)value)?.Cast<object>().Last();

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) =>
        throw new NotSupportedException();
}

然后使用数据绑定应用转换:

YourControl.xaml.cs:

public partial class YourControl : UserControl
{
    public ConcurrentDictionary<string, List<OHLC>> Candles { get; } = ...;
}

YourControl.xaml:

<UserControl x:Class="YourNamespace.YourControl"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:local="clr-namespace:YourNamespace"
  DataContext="{Binding RelativeSource={RelativeSource Self}}">

  <UserControl.Resources>
    <local:LastItemConverter x:Key="lastItemConverter" />
  </UserControl.Resources>

  <Grid>
    <DataGrid ItemsSource="{Binding Candles}" AutoGenerateColumns="False">
      <DataGrid.Columns>
        <DataGridTextColumn Header="Key"
          Binding="{Binding Key}" />
        <DataGridTextColumn Header="Value"
          Binding="{Binding Value, Converter={StaticResource lastItemConverter}}" />
      </DataGrid.Columns>
    </DataGrid>
  </Grid>
</UserControl>

根据需要触发更新。

Windows Forms' DataGridView 有一种方法可以通过 CellFormatting 事件覆盖它格式化数据的方式。处理该事件,以便您可以更改将用于该列的值。如果你愿意,你也可以从这里设置单元格的样式。

form1.dataGridView1.CellFormatting += this.dataGridView1_CellFormatting;

//...

private void dataGridView1_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
{
    switch (dataGridView1.Columns[e.ColumnIndex].Name)
    {
    case "Value": // assuming this is the corresponding column name
        if (e.Value is List<OHLC> asList)
        {
            var lastValue = asList.Last();
            e.Value = lastValue;
            // example of additional styling
            if (lastValue.CostSavings > 0)
                e.CellStyle.ForeColor = Color.Green;
        }
        return;
    }
}