使用WPF在F#中使用INotifyPropertyChanged和INotifyCollectionChanged

时间:2021-03-01 22:34:10

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?

这看起来有点矫枉过正吗?以前有人这样做过吗?任何替代方案?