数据绑定到每个 KeyValuePair<string, List<T>> 中的最后一个条目
Posted
技术标签:
【中文标题】数据绑定到每个 KeyValuePair<string, List<T>> 中的最后一个条目【英文标题】:Databinding to last entry in each KeyValuePair<string, List<T>> 【发布时间】:2021-11-03 21:21:44 【问题描述】:我有一个实现 INotifyChanged 的简单 OHLC 对象。每个 OHLC 对象都与特定的蜡烛相关,并且是列表的一部分。每个 List 都与特定股票相关,并存储在 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”的示例。
如果能提供任何帮助,我将不胜感激。
【问题讨论】:
唯一重要的是你如何在你的ConcurrentDictionary<string, List<OHLC>>
中设置List<OHLC>
,这似乎是一个辅助线程。如果您不完全替换 List<OHLC>
,而是创建一个新对象,而只是更新现有成员,则 Binding 也可以跨线程工作。如果您改为替换字典中的List<OHLC>
,创建一个新对象,数据绑定当然不起作用,只是因为您已经销毁了它。 -- 您可以将 BindingList 设置为,例如,LastCandles.Add(Candles["First"].Last()); LastCandles.Add(Candles["Second"].Last()); [...]
...当您第一次初始化 BindingList 时,可能在您设置 DataGridView 的 DataSource 之前。 -- 如果你在字典中添加了新的 Keys,还要将新的Last()
对象添加到 BindingList 中(这个对象稍后会更新,从辅助线程)。 -- 这:form1.dataGridView1.[...]
根本不是什么好东西。另外,不要BeginInvoke()
一个Control,总是使用父Form容器作为marshaller。
我可以尝试在初始化期间填充 ConcurrentDictionary,然后用每个键的最后一个 OHLC 对象填充 BindingList,然后将数据源设置为 BindingList。但是当一个新的蜡烛到达并且一个新的 OHLC 对象被添加到 ConcurrentDictionary 中特定键的 ListLast()
内部列表更改的条目时使用 IProgress<T>
委托来更新 UI(如果您修改列表成员的属性值则不能)。委托方法可以是:private void UpdateLastCandles() LastCandles.Clear(); foreach (var list in Candles.Values) LastCandles.Add(list.Last());
。您只是在添加引用。Last()
条目的属性值进行更新收藏。在这种情况下,您无需执行任何操作,数据绑定将永远有效,无需直接更新LastCandles
BindingList 的内容。
【参考方案1】:
注意:这将是 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>
根据需要触发更新。
【讨论】:
感谢您的帮助。我在 WinForms 中执行此操作,但将切换到 WPF,这样我就不需要重新排列 ViewModel 以适应我想要显示的内容。 啊,抱歉,我看到了代码,还以为是 WPF 的。 WinForms 有类似的机制,但我不记得它们了。我会回到这个来寻找合适的 WinForms 解决方案。但我相信您仍然可以使用相同的转换器。【参考方案2】:Windows 窗体的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;
【讨论】:
以上是关于数据绑定到每个 KeyValuePair<string, List<T>> 中的最后一个条目的主要内容,如果未能解决你的问题,请参考以下文章