1

我寫Windows窗體控件擴展像下面從控件的事件(使用Rx和RX-的WinForms 2.2.2)創建觀測量:接收 - 爲Windows可觀察擴展窗體控件

''' <summary> 
''' Returns an IObservable(Of String) that fires when the TextChanged event fires and the text has actually changed. 
''' </summary> 
''' <remarks>It's useful to follow this with .Throttle if you wish to throttle the events.</remarks> 
<Extension()> 
Public Function ObserveTextChanged(textbox As TextBox) As IObservable(Of String) 
    Return Observable _ 
      .FromEventPattern(Of EventArgs)(textbox, "TextChanged", New ControlScheduler(textbox)) _ 
      .Select(Function(a) DirectCast(a.Sender, TextBox).Text) _ 
      .DistinctUntilChanged 
End Function 

''' <summary> 
''' Returns an IObservable(Of Boolean) that fires when the CheckedChanged event fires. 
''' </summary> 
<Extension()> 
Public Function ObserveCheckedChanged(radio As RadioButton) As IObservable(Of Boolean) 
    Return Observable _ 
      .FromEventPattern(Of EventArgs)(radio, "CheckedChanged", New ControlScheduler(radio)) _ 
      .Select(Function(a) DirectCast(a.Sender, RadioButton).Checked) 
End Function 

我目的是爲了再能到我的表格上寫的代碼如下:

Dim myObserver = MyTextBox.ObserveTextChanged.Throttle(TimeSpan.FromSeconds(0.3)) 
myObserver.Subscribe(Function(text) DoSomethingWith(text)) 

我有一些問題:

  1. 上午我以正確的方式去做這件事?那是:
    1. 有沒有人做過類似的事情,並遇到線程/處置問題?
    2. 我是否需要ControlScheduler,或者我可以在最後附加一個.ObserveOn(控件),還是我需要兩者?
    3. 或者,我不需要ObserveOn,而應該使用SubscribeOn嗎?
  2. 這是否已經在WinForms的Rx框架中完成了,我簡單地忽略了它?

更新:

我已經分裂事件捕捉和觀察到兩種不同的擴展方法,以提供用戶更多靈活性的價值。有時候用戶想要改變後的值,有時用戶只想知道值已經改變了

''' <summary> 
''' Returns an IObservable that fires when the TextChanged event fires. 
''' </summary> 
''' <param name="textbox"></param> 
''' <returns></returns> 
''' <remarks>It's useful to follow this with .Throttle if you wish to throttle the events.</remarks> 
<Extension()> 
Public Function ObserveTextChanged(textbox As TextBox) As IObservable(Of Reactive.EventPattern(Of EventArgs)) 
    Return Observable.FromEventPattern(Of EventArgs)(textbox, "TextChanged") 
End Function 

''' <summary> 
''' Returns an IObservable(Of String) that fires when the TextChanged event fires and the text has actually changed. 
''' </summary> 
''' <param name="textbox"></param> 
''' <returns></returns> 
''' <remarks>It's useful to follow this with .Throttle if you wish to throttle the events.</remarks> 
<Extension()> 
Public Function ObserveTextChangedResult(textbox As TextBox) As IObservable(Of String) 
    '** May need to inject a .ObserveOn(textbox) just before the .Select if we get threading issues. 
    Return textbox.ObserveTextChanged.Select(Function(a) textbox.Text).DistinctUntilChanged 
End Function 

''' <summary> 
''' Returns an IObservable that fires when the CheckedChanged event fires. 
''' </summary> 
''' <param name="radio"></param> 
''' <returns></returns> 
''' <remarks></remarks> 
<Extension()> 
Public Function ObserveCheckedChanged(radio As RadioButton) As IObservable(Of Reactive.EventPattern(Of EventArgs)) 
    Return Observable.FromEventPattern(Of EventArgs)(radio, "CheckedChanged") 
End Function 

''' <summary> 
''' Returns an IObservable(Of Boolean) that fires when the CheckedChanged event fires. 
''' </summary> 
''' <param name="radio"></param> 
''' <returns></returns> 
''' <remarks></remarks> 
<Extension()> 
Public Function ObserveCheckedChangedResult(radio As RadioButton) As IObservable(Of Boolean) 
    '** May need to inject a .ObserveOn(radio) just before the .Select if we get threading issues. 
    Return radio.ObserveCheckedChanged.Select(Function(a) radio.Checked) 
End Function 
+0

挖掘到RX源,我看到'.ObserveOn(控制)'內部爲我創造了'ControlScheduler'。 – MCattle

回答

3

我一般會寫你的擴展是這樣的:

<Extension()> 
Public Function ObserveTextChanged(textbox As TextBox) As IObservable(Of String) 
    Return Observable _ 
     .FromEventPattern(Of EventArgs)(textbox, "TextChanged") _ 
     .Select(Function(a) textbox.Text) _ 
     .DistinctUntilChanged() 
End Function 

<Extension()> 
Public Function ObserveCheckedChanged(radio As RadioButton) As IObservable(Of Boolean) 
    Return Observable _ 
     .FromEventPattern(Of ItemCheckedEventArgs)(radio, "CheckedChanged") _ 
     .Select(Function(a) radio.Checked) 
End Function 

我刪除了ControlScheduler完全。現在這只是因爲你想在最後的時刻在UI線程上觀察一個好習慣。所以,你會用你的代碼是這樣的:

Dim myQuery = MyTextBox _ 
    .ObserveTextChanged() _ 
    .Throttle(TimeSpan.FromSeconds(0.3)) 

Dim mySubscription = myQuery _ 
    .ObserveOn(Me) _ 
    .Subscribe(Function(text) DoSomethingWith(text)) 

這背後的原因是,許多運營商的Rx將隱含改變觀察到的調度。

例如:

Dim otherQuery = _ 
    From text in MyTextBox.ObserveTextChanged() _ 
    from choices in Observable.Start(Function() GetSpellingChoices(text)) _ 
    select choices 

將自動更改查詢使用到Scheduler.Default調度。因此,如果您的訂閱者要更新UI,則必須向查詢添加另一個ObserveOn(control)

因此,思考它的基本方法應該是,如果您的訂戶更新UI,則在Subscribe之前執行ObserveOn

另外,在你的擴展方法中,我刪除了DirectCast(a.Sender, TextBox)的代碼。您不需要它,因爲textbox已經在範圍之內。

最後,你應該做的事情從Subscribe方法返回的IDisposable東西。當你關閉你的表單時,你必須處理訂閱,否則你將會連接很多事件處理程序來防止垃圾收集。我通常創建一個級別級別爲CompositeDisposable以容納我的所有訂閱,以便我可以一次性處理所有訂閱。

希望這會給你在正確的方向有幾點建議。

+0

正是我正在尋找的,並感謝'CompositeDisposable'的單挑! – MCattle

+0

您應該做的唯一其他事情應該是從事件參數中取出值,而不是直接從控件屬性中取值。你可能會遇到一些可能會在代碼中引入錯誤的競爭條件。 – Enigmativity

+0

在這兩種情況下,事件僅使用默認的'EventArgs',據我所知,這意味着我無法從中獲取值。 (我最初的問題是錯誤地使用了ItemCheckedEventArgs',我糾正了'EventArgs')的單選按鈕事件。) – MCattle