使用 WPF 的 F# 中的 INotifyPropertyChanged 和 INotifyCollectionChanged

发布于 2024-07-27 13:24:05 字数 357 浏览 6 评论 0原文

我有一组示例信息,这些信息在后台线程中不断刷新。

目前,我不断使用 DispatcherTimer 将此数组分配给数据网格的 ItemsSource 属性。 这是可行的,但它会重置任何视觉位置,例如,如果用户将光标放在数据网格的中间,执行计时器将撤消该位置。

是否可以使用 INotifyPropertyChanged 或 INotifyCollectionChanged 事件来防止此类情况发生? 如果是这样,这在 F# 中如何工作?

我想每次数组更新时我都必须执行一些通知数据网格的函数。 数组的更新不在STAThread部分。

我正在使用包含 WPF 数据网格的最新 WPF 工具包运行 VS2010。

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

烟酉 2024-08-03 13:24:05

您可以使用 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)

不幸的是 Reflector 显示 System.Windows.Controls.ItemsControl.OnItemCollectionChanged 方法在调用时会删除选择,因此您可能需要解决此默认行为。

您还可以像这样实现 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"))

实现 INotifyCollectionChanged 的​​工作原理类似。

祝你好运,

丹尼

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

无法言说的痛 2024-08-03 13:24:05

ObservableCollection 可以工作,但不幸的是存在问题。

由于 ObservableCollection 仅在 STAThread 中修改时才起作用,因此我必须使用调度程序并基本上重写或至少检查整个数组,因为我无法判断哪些条目已更改或未更改。

可能的一件事是使用 F# 邮箱。 后台线程可以放置更改消息,这些消息可以由 STAThread 中的调度程序拾取。 该解决方案还消除了同步的需要。

这看起来是不是太过分了? 以前有人这样做过吗? 还有其他解决方案吗?

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?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文