【问题标题】:WPF datagrid live-update with button-column带有按钮列的 WPF 数据网格实时更新
【发布时间】:2010-06-01 19:35:34
【问题描述】:

我有一个数据网格,其中包含货币符号的买价和卖价。数据每秒钟更新一次。我通过创建我的视图模型实体的新集合来更新数据,并每秒将此集合绑定到数据网格。

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

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

【问题讨论】:

    标签: wpf datagrid


    【解决方案1】:

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

    即使您更新所有项目,重要的区别是您应该更新项目,而不是完全清除当前绑定到数据网格的集合。与仅更新现有集合的内容相比,将 ItemsSource 更改为新的将导致数据网格需要做更多的工作。如果您使用的是 ObservableCollection,这可能意味着使您的视图模型项可变,以便您可以简单地更新出价/要价。如果您的视图模型项是可变的并实现 INotifyPropertyChanged,则出价/要价更新将反映在数据网格或与对象的这些属性的任何其他绑定中。这样做的好处是相同的对象保持绑定到 ItemsControl 中的相同容器,因此在每次更新期间,您的按钮绝对不会发生任何事情。现在,如果您的包含出价/要价的视图模型对象是不可变的,您应该仍然能够实现这一点。每秒钟,您只需遍历您的项目集合并使用 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));
            }
        }
    }
    

    第二,视图,在这个例子中就是我的主窗口。

    <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 按钮的点击并在视图中初始化一个视图模型(请注意,除了如何更新对集合的出价/询价之外的其他做法items 可能不是处理事情的最佳方式,具体取决于您的应用程序将如何增长)。

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

    希望这个例子有帮助!

    【讨论】:

    • 非常感谢! 这确实帮助我更好地理解了事情,虽然我正在从事的实际项目已经完成,但您的帖子包含了很多信息,我可以在以后的项目中使用!
    【解决方案2】:

    你查看过ObservableCollection吗?

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

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

    【讨论】:

    • 感谢您的建议!我已经尝试使用 ObservableCollection 而不是简单的列表。结果是一样的,因为每个买卖对每秒都会更新一次......所以我需要的是能够只刷新单元格而不是整行的东西。
    • @dlang - 也许您需要确保只更新更改的项目而不是整个列表。
    • 问题是每次更新时每一行都会发生变化......我只想刷新一列......
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-08-09
    • 2015-12-20
    • 2011-12-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-23
    相关资源
    最近更新 更多