Если я правильно понимаю, у вас есть коллекция предметов, и у вас есть пара полей (в частности, ставка / спрос), которые будут обновляться каждую секунду. Может показаться, что в процессе изменения ItemsSource вашей сетки данных вы теряете какое-то важное состояние, которое вызывает проблемы для обработчиков событий на ваших кнопках.
Даже если вы обновляете все элементы, важное различие заключается в том, что вы должны обновить элементы, а не полностью очистить коллекцию, которая в настоящее время связана с вашей сеткой данных. Если изменить ItemsSource на новый, сетке данных придется выполнять гораздо больше работы, чем если бы вы просто обновляли содержимое существующей коллекции. Если вы используете ObservableCollection, это может означать, что элементы вашей модели представления могут быть изменяемыми, чтобы вы могли просто обновить bid / ask. Если ваши элементы модели представления являются изменяемыми и реализуют INotifyPropertyChanged, обновления bid / ask будут отражены в сетке данных или в любых других привязках к этим свойствам объектов. Преимущество такого способа в том, что одни и те же объекты остаются привязанными к одним и тем же контейнерам в ItemsControl, поэтому при каждом обновлении с вашими кнопками абсолютно ничего не происходит. Теперь, если ваши объекты viewmodel, которые содержат bid / ask, являются неизменяемыми, вы все равно сможете выполнить это. Каждую секунду вы просто перебираете свою коллекцию элементов и используете SetItem, чтобы заменить каждый существующий элемент новым. В последнем случае важно помнить, что каждую секунду сетка данных все еще получает уведомление о том, что в ObservableCollection произошли изменения, и из-за этого привязки в каждой строке будут вызывать DataContext строки / ячейки / кнопка для обновления.
Вот краткий пример того, как я могу решить эту проблему. Я собираюсь предположить использование сетки данных в .NET 4.0 (если вы используете инструментарий, хотя с 3.5, это должно быть то же самое). Я собираюсь использовать первый подход, когда мой объект CurrencyPair является изменяемым.
Во-первых, простой код модели представления с автономным таймером для обновления ставки / запроса нескольких валютных пар каждую секунду:
public class CurrencyPairsViewModel
{
private readonly Dispatcher _dispatcher = Dispatcher.CurrentDispatcher;
private readonly ObservableCollection<string> _orders = new ObservableCollection<string>();
private readonly ObservableCollection<CurrencyPair> _pairs = new ObservableCollection<CurrencyPair>();
private readonly Random _rand = new Random();
private readonly System.Timers.Timer _timer = new System.Timers.Timer(1000);
private readonly Action _update;
public CurrencyPairsViewModel()
{
this._timer.Elapsed += OnIntervalElapsed;
this._update = new Action(this.Update);
this._pairs.Add(new CurrencyPair("USD/GBP"));
this._pairs.Add(new CurrencyPair("AUD/USD"));
this._pairs.Add(new CurrencyPair("WOW/CAD"));
this._timer.Start();
}
public ObservableCollection<string> Orders { get { return this._orders; } }
public ObservableCollection<CurrencyPair> Pairs { get { return this._pairs; } }
public void Buy(CurrencyPair pair)
{
this._orders.Add(string.Format("Buy {0} at {1}", pair.Name, pair.Ask));
}
private void OnIntervalElapsed(object sender, System.Timers.ElapsedEventArgs e)
{
this._dispatcher.Invoke(this._update);
}
private void Update()
{
foreach (var pair in this._pairs)
{
pair.Bid = this._rand.NextDouble();
pair.Ask = pair.Bid + 0.01;
}
this._timer.Start();
}
}
public class CurrencyPair : INotifyPropertyChanged
{
private readonly string _name;
private double _ask;
private double _bid;
public CurrencyPair(string name)
{
this._name = name;
}
public event PropertyChangedEventHandler PropertyChanged;
public double Ask
{
get { return this._ask; }
set
{
this._ask = value;
this.OnPropertyChanged("Ask");
}
}
public double Bid
{
get { return this._bid; }
set
{
this._bid = value;
this.OnPropertyChanged("Bid");
}
}
public string Name { get { return this._name; } }
protected void OnPropertyChanged(string name)
{
if (null != this.PropertyChanged)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
}
Во-вторых, представление, которое в этом примере является просто моим MainWindow.
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="4"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<DataGrid Grid.Row="0"
ItemsSource="{Binding Pairs}"
AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding Name}" Width="*"/>
<DataGridTextColumn Header="Bid" Binding="{Binding Bid}" Width="*"/>
<DataGridTextColumn Header="Ask" Binding="{Binding Ask}" Width="*"/>
<DataGridTemplateColumn Header="Buy">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Content="BUY"
Click="OnBuyClicked"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
<GridSplitter Grid.Row="1" Height="4" HorizontalAlignment="Stretch" VerticalAlignment="Center"/>
<ListBox Grid.Row="2"
ItemsSource="{Binding Orders}"/>
</Grid>
</Window>
И, наконец, у меня есть немного кода за этим XAML для обработки нажатий кнопки «КУПИТЬ» и инициализации модели представления прямо в представлении (обратите внимание, что это и другие практики, помимо того, как обновлять ставку / спрос в коллекции Предметы могут быть не лучшим способом решения проблем, в зависимости от того, как будет расти ваше приложение).
public partial class MainWindow : Window
{
private readonly CurrencyPairsViewModel _model = new CurrencyPairsViewModel();
public MainWindow()
{
InitializeComponent();
this.DataContext = this._model;
}
private void OnBuyClicked(object sender, RoutedEventArgs e)
{
var pair = (CurrencyPair)((Button)sender).DataContext;
this._model.Buy(pair);
}
}
Надеюсь, пример полезен!