views:

495

answers:

2

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.

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#?

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.

I am running VS2010 with the latest WPF toolkit containing the WPF datagrid.

+2  A: 

You can use an ObservableCollection which will implements INotifyCollectionChanged for you. The F# looks something like this:

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.

You can also implement INotifyPropertyChanged like so:

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.

best of luck,

Danny

DannyAsher
+1  A: 

The ObservableCollection works but unfortunately with issues.

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.

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.

Does that look like overkill? Anybody done that before? Any alternative solutions?

Moonlight