带按钮列的WPF datagrid实时更新

时间:2010-06-01 19:35:35

标签: wpf datagrid

我有一个datagrid,其中包含bid-ask - 货币符号的价格。数据每秒更新一次。我通过创建一个新的viewmodel-entities集合来更新数据,并每秒将此集合绑定到datagrid。

问题是: 因为我的datagrid包含一个带有“buy”按钮的模板列,所以每秒都会重新创建这个按钮!这意味着,当用户悬停按钮时,悬停样式会闪烁,因为每秒都会重新创建按钮。此外,如果在用户按下鼠标左键的同时重新创建按钮,有时点击事件不会被正确触发。

有任何建议,如何用按钮列解决数据网格的实时更新?

2 个答案:

答案 0 :(得分:4)

如果我理解正确,你有一组项目,你有几个字段(特别是买/卖),所有这些都会每秒更新一次。听起来可能发生的事情是,在更改数据网格的ItemsSource的过程中,您正在丢失一些重要的状态,这会导致按钮上的事件处理程序出现问题。

即使您更新了所有项目,要做的重要区别是您应该更新项目,而不是完全清除当前绑定到数据网格的集合。将ItemsSource更改为新项目将导致数据网格比仅更新现有集合的内容需要做更多的工作。如果您使用的是ObservableCollection,这可能意味着您的viewmodel项目可变,以便您可以简单地更新出价/提问。如果您的viewmodel项是可变的并实现INotifyPropertyChanged,则买/卖更新将反映在datagrid或对象的这些属性的任何其他绑定中。这样做的巧妙之处在于,相同的对象保持绑定到ItemsControl中的相同容器,因此在每次更新期间,您的按钮绝对不会发生任何事情。现在,如果包含bid / ask的viewmodel对象是不可变的,那么您仍然可以将其关闭。每一秒,您只需遍历您的项目集合,并使用SetItem将每个现有项目替换为新项目。在后一种情况下要记住的重要一点是,每一秒,数据网格仍然会收到通知,ObservableCollection发生了变化,因此,每行的绑定将导致行的DataContext /要更新的单元格/按钮。

以下是我如何解决此问题的快速示例。我将假设在.NET 4.0中使用datagrid(如果你使用工具包虽然使用3.5,这应该是相同的)。我将采用第一种方法,我的CurrencyPair对象是可变的。

首先,一些简单的viewmodel代码,带有一个自包含计时器来更新一些货币对bid /每秒询问:

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背后有一些代码来处理BUY按钮点击并在视图中初始化一个viewmodel(注意这个,以及如何更新出价/询问集合之外的其他做法项目可能不是最好的方式,取决于您的应用程序将如何增长)。

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);
    }
}

希望这个例子很有帮助!

答案 1 :(得分:1)

您是否查看了ObservableCollection

  

表示动态数据集合,在添加,删除项目或刷新整个列表时提供通知。

这应该只刷新那些被更改的项目,而不是整个网格。