I have an array of sample information that is constantly refreshed in a background thread.
我有一个示例信息数组,在后台线程中不断刷新。
Currently I am constantly assigning this array to a datagrid's ItemsSource property using a DispatcherTimer. That works but it resets any visual locations, for instance if the user places his cursor in the middle of the datagrid the execution timer will undo such position.
目前,我不断使用DispatcherTimer将此数组分配给datagrid的ItemsSource属性。这有效,但它会重置任何可视位置,例如,如果用户将光标放在数据网格的中间,执行计时器将撤消此位置。
Is it possible to use a INotifyPropertyChanged or INotifyCollectionChanged event for this instead to prevent such situations to occur? If so, how does this work with F#?
是否可以使用INotifyPropertyChanged或INotifyCollectionChanged事件来防止此类情况发生?如果是这样,这与F#有什么关系?
I suppose I have to execute some function notifying the datagrid every time when there is an update of the array. The updating of the array is not in the STAThread section.
我想每次有更新数组时我都必须执行一些通知datagrid的函数。数组的更新不在STAThread部分中。
I am running VS2010 with the latest WPF toolkit containing the WPF datagrid.
我正在使用包含WPF数据网格的最新WPF工具包运行VS2010。
2 个解决方案
#1
You can use an ObservableCollection which will implements INotifyCollectionChanged for you. The F# looks something like this:
您可以使用ObservableCollection为您实现INotifyCollectionChanged。 F#看起来像这样:
open System
open System.Collections.ObjectModel
open System.Windows
open System.Windows.Controls
open System.Windows.Threading
[<EntryPoint; STAThread>]
let Main args =
let data = ObservableCollection [0 .. 9]
let list = ListBox(ItemsSource = data)
let win = Window(Content = list, Visibility = Visibility.Visible)
let rnd = Random()
let callback =
EventHandler(fun sender args ->
let idx = rnd.Next(0, 10)
data.[idx] <- rnd.Next(0, 10)
)
let ts = TimeSpan(1000000L)
let dp = DispatcherPriority.Send
let cd = Dispatcher.CurrentDispatcher
let timer = DispatcherTimer(ts, dp, callback, cd) in timer.Start()
let app = Application() in app.Run(win)
Unfortunately Reflector shows that System.Windows.Controls.ItemsControl.OnItemCollectionChanged method removes the selection when it is called, so you may need to work around this default behaviour.
不幸的是,Reflector显示System.Windows.Controls.ItemsControl.OnItemCollectionChanged方法在调用时删除了选择,因此您可能需要解决此默认行为。
You can also implement INotifyPropertyChanged like so:
你也可以像这样实现INotifyPropertyChanged:
open System.ComponentModel
type MyObservable() =
let mutable propval = 0.0
let evt = Event<_,_>()
interface INotifyPropertyChanged with
[<CLIEvent>]
member this.PropertyChanged = evt.Publish
member this.MyProperty
with get() = propval
and set(v) = propval <- v
evt.Trigger(this, PropertyChangedEventArgs("MyProperty"))
Implementing INotifyCollectionChanged would work similarly.
实现INotifyCollectionChanged将起到类似的作用。
best of luck,
祝你好运,
Danny
#2
The ObservableCollection works but unfortunately with issues.
ObservableCollection有效,但不幸的是有问题。
Since the ObservableColection only works when modified in the STAThread, I have to use a dispatcher and basically rewrite, or at least inspect, the complete array as I cannot tell which entries are changed or not.
由于ObservableColection仅在STAThread中修改时才有效,因此我必须使用调度程序并基本上重写或至少检查整个数组,因为我无法分辨哪些条目被更改。
One thing that is a possibility is to use a F# Mailbox. The background thread could place change messages which could be picked up by a dispatcher in the STAThread. This solution also would remove the need for synchronization.
有一种可能性是使用F#邮箱。后台线程可以放置可以由STAThread中的调度程序拾取的更改消息。该解决方案还将消除同步的需要。
Does that look like overkill? Anybody done that before? Any alternative solutions?
这看起来有点矫枉过正吗?以前有人这样做过吗?任何替代方案?
#1
You can use an ObservableCollection which will implements INotifyCollectionChanged for you. The F# looks something like this:
您可以使用ObservableCollection为您实现INotifyCollectionChanged。 F#看起来像这样:
open System
open System.Collections.ObjectModel
open System.Windows
open System.Windows.Controls
open System.Windows.Threading
[<EntryPoint; STAThread>]
let Main args =
let data = ObservableCollection [0 .. 9]
let list = ListBox(ItemsSource = data)
let win = Window(Content = list, Visibility = Visibility.Visible)
let rnd = Random()
let callback =
EventHandler(fun sender args ->
let idx = rnd.Next(0, 10)
data.[idx] <- rnd.Next(0, 10)
)
let ts = TimeSpan(1000000L)
let dp = DispatcherPriority.Send
let cd = Dispatcher.CurrentDispatcher
let timer = DispatcherTimer(ts, dp, callback, cd) in timer.Start()
let app = Application() in app.Run(win)
Unfortunately Reflector shows that System.Windows.Controls.ItemsControl.OnItemCollectionChanged method removes the selection when it is called, so you may need to work around this default behaviour.
不幸的是,Reflector显示System.Windows.Controls.ItemsControl.OnItemCollectionChanged方法在调用时删除了选择,因此您可能需要解决此默认行为。
You can also implement INotifyPropertyChanged like so:
你也可以像这样实现INotifyPropertyChanged:
open System.ComponentModel
type MyObservable() =
let mutable propval = 0.0
let evt = Event<_,_>()
interface INotifyPropertyChanged with
[<CLIEvent>]
member this.PropertyChanged = evt.Publish
member this.MyProperty
with get() = propval
and set(v) = propval <- v
evt.Trigger(this, PropertyChangedEventArgs("MyProperty"))
Implementing INotifyCollectionChanged would work similarly.
实现INotifyCollectionChanged将起到类似的作用。
best of luck,
祝你好运,
Danny
#2
The ObservableCollection works but unfortunately with issues.
ObservableCollection有效,但不幸的是有问题。
Since the ObservableColection only works when modified in the STAThread, I have to use a dispatcher and basically rewrite, or at least inspect, the complete array as I cannot tell which entries are changed or not.
由于ObservableColection仅在STAThread中修改时才有效,因此我必须使用调度程序并基本上重写或至少检查整个数组,因为我无法分辨哪些条目被更改。
One thing that is a possibility is to use a F# Mailbox. The background thread could place change messages which could be picked up by a dispatcher in the STAThread. This solution also would remove the need for synchronization.
有一种可能性是使用F#邮箱。后台线程可以放置可以由STAThread中的调度程序拾取的更改消息。该解决方案还将消除同步的需要。
Does that look like overkill? Anybody done that before? Any alternative solutions?
这看起来有点矫枉过正吗?以前有人这样做过吗?任何替代方案?