Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/25.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 组合反应式框架(Rx)查询以提供正确的UI行为时出现问题_C#_.net_Silverlight_System.reactive - Fatal编程技术网

C# 组合反应式框架(Rx)查询以提供正确的UI行为时出现问题

C# 组合反应式框架(Rx)查询以提供正确的UI行为时出现问题,c#,.net,silverlight,system.reactive,C#,.net,Silverlight,System.reactive,我试图从Silverlight应用程序中删除更传统的事件处理程序,以支持使用大量Rx查询来提供更好、更易于管理的行为抽象 我需要解决的问题是让搜索屏幕的行为正常工作,但我不能完全按照我想要的方式解决它。这是相当标准的东西。这就是它的行为方式: 我有一个文本框,用户可以在其中输入搜索文本 如果没有文本(或仅空白)则搜索按钮被禁用 如果存在非空白文本,则搜索按钮处于启用状态 当用户单击搜索时,文本框和搜索按钮都被禁用 当结果返回时文本框和搜索按钮均已启用 我有这些观测值(通过标准事件上的一些扩展

我试图从Silverlight应用程序中删除更传统的事件处理程序,以支持使用大量Rx查询来提供更好、更易于管理的行为抽象

我需要解决的问题是让搜索屏幕的行为正常工作,但我不能完全按照我想要的方式解决它。这是相当标准的东西。这就是它的行为方式:

  • 我有一个文本框,用户可以在其中输入搜索文本
  • 如果没有文本(或仅空白)搜索按钮被禁用
  • 如果存在非空白文本,则搜索按钮处于启用状态
  • 当用户单击搜索时,文本框和搜索按钮都被禁用
  • 结果返回时文本框搜索按钮均已启用
我有这些观测值(通过标准事件上的一些扩展方法创建)可以使用:

IObservable<IEvent<TextChangedEventArgs>> textBox.TextChangedObservable()
IObservable<IEvent<RoutedEventArgs>> button.ClickObservable()
IObservable<IEvent<LoadingDataEventArgs>> dataSource.LoadingDataObservable()
IObservable<IEvent<LoadedDataEventArgs>> dataSource.LoadedDataObservable()
(我确实保留了对
Subscribe
方法返回的一次性文件的引用。我已将
.ObserveOnDispatcher()
添加到每个可观察文件中,以确保代码在UI线程上运行。)

现在,虽然这样做有效,但有一件事让我很烦。
searchTextReady
中的select语句调用
textBox.Text.Trim()
以获取当前修剪的搜索文本,但我已经在
textBoxText
中完成了此操作。我真的不想重复我自己的话,所以我想创建一个结合了这些观察值的查询,这就是我失败的地方

当我尝试以下查询时,会收到重入调用以执行搜索:

IObservable<string> searchTextReady =
    from text in textBoxText
    from x in button.ClickObservable()
    select text;
IObservable searchTextReady=
从textBoxText中的文本
从x进入按钮。单击可观察()
选择文本;
以下查询似乎适用于第一个查询,但每次我更改文本框中的文本后,搜索将自动执行,而无需单击搜索按钮:

IObservable<string> searchTextReady =
    from text in button.ClickObservable()
        .CombineLatest(textBoxText, (c, t) => t)
    select text;
IObservable searchTextReady=
从按钮中的文本。单击可观察()
.CombineTest(textBoxText,(c,t)=>t)
选择文本;
以下查询需要在单击搜索按钮后进行进一步的文本更改,然后再次运行失败:

IObservable<string> searchTextReady =
    from text in textBoxText
      .SkipUntil(button.ClickObservable())
      .TakeUntil(dataSource.LoadingDataObservable())
    select text;
IObservable searchTextReady=
从textBoxText中的文本
.SkipUntil(button.ClickObservable())
.TakeUntil(dataSource.LoadingDataObservable())
选择文本;
有什么办法可以让这一切顺利进行吗?

你试过了吗

比如:

    IObservable<string> searchTextReady = Observable.ForkJoin(textBoxText, button.ClickObservable());
    searchTextReady.Subscribe( ....
IObservable searchtext ready=Observable.ForkJoin(textBoxText,button.ClickObservable());
searchTextReady.Subscribe(。。。。

这类事情本身就很棘手,因此我最终写了一些文章来帮助我解决问题——事实证明,有了这个库,这项任务非常简单;下面是全部代码,详细解释了这些类的工作原理:

public class TextSearchViewModel
{
    public TextSearchViewModel
    {
        // If there is no text (or whitespace only) then the search button is disabled.
        var isSearchEnabled = this.ObservableForProperty(x => x.SearchText)
            .Select(x => !String.IsNullOrWhitespace(x.Value));

        // Create an ICommand that represents the Search button
        // Setting 1 at a time will make sure the Search button disables while search is running
        DoSearch = new ReactiveAsyncCommand(isSearchEnabled, 1/*at a time*/);

        // When the user clicks search the text box and the search button are both disabled.
        var textBoxEnabled = DoSearch.ItemsInflight
            .Select(x => x == 0);

        // Always update the "TextboxEnabled" property with the latest textBoxEnabled IObservable
        _TextboxEnabled = this.ObservableToProperty(textBoxEnabled, 
            x => x.TextboxEnabled, true);

        // Register our search function to run in a background thread - for each click of the Search
        // button, the searchResults IObservable will produce a new OnNext item
        IObservable<IEnumerable<MyResult>> searchResults = DoSearch.RegisterAsyncFunction(textboxText => {
            var client = new MySearchClient();
            return client.DoSearch((string)textboxText);
        });

        // Always update the SearchResults property with the latest item from the searchResults observable
        _SearchResults = this.ObservableToProperty(searchResults, x => x.SearchResults);
    }

    // Create a standard INotifyPropertyChanged property
    string _SearchText;
    public string SearchText {
        get { return _SearchText; }
        set { this.RaiseAndSetIfChanged(x => x.SearchText, value); }
    }

    // This is a property who will be updated with the results of an observable
    ObservableAsPropertyHelper<bool> _TextboxEnabled;
    public bool TextboxEnabled {
        get { return _TextboxEnabled.Value; }
    }

    // This is an ICommand built to do tasks in the background
    public ReactiveAsyncCommand DoSearch { get; protected set; }

    // This is a property who will be updated with the results of an observable
    ObservableAsPropertyHelper<IEnumerable<MyResult>> _SearchResults;
    public IEnumerable<MyResult> SearchResults {
        get { return _SearchResults.Value; }
    }
} 
公共类TextSearchViewModel
{
公共文本搜索视图模型
{
//如果没有文本(或仅空白),则禁用搜索按钮。
var isSearchEnabled=this.ObservableForProperty(x=>x.SearchText)
.Select(x=>!String.IsNullOrWhitespace(x.Value));
//创建表示搜索按钮的ICommand
//每次设置1将确保搜索按钮在搜索运行时禁用
DoSearch=新的ReactiveAsyncCommand(isSearchEnabled,一次1/*);
//当用户单击“搜索”时,文本框和“搜索”按钮均被禁用。
var textBoxEnabled=DoSearch.ItemsInflight
。选择(x=>x==0);
//始终使用最新的TextboxEnabled IObservable更新“TextboxEnabled”属性
_TextboxEnabled=this.ObservableToProperty(TextboxEnabled,
x=>x.TextboxEnabled,true);
//注册我们的搜索功能在后台线程中运行-每次点击搜索
//按钮,searchResults IObservable将生成一个新的OnNext项
IObservable searchResults=DoSearch.RegisterAsyncFunction(textboxText=>{
var client=new MySearchClient();
返回client.DoSearch((string)textboxText);
});
//始终使用可观察的SearchResults中的最新项更新SearchResults属性
_SearchResults=this.ObservableToProperty(SearchResults,x=>x.SearchResults);
}
//创建标准INotifyPropertyChanged属性
字符串搜索文本;
公共字符串搜索文本{
获取{return\u SearchText;}
设置{this.RaiseAndSetIfChanged(x=>x.SearchText,value);}
}
//这是一个属性,将使用可观察到的结果进行更新
ObservablePropertyHelper\u TextboxEnabled;
公共bool TextboxEnabled{
获取{return\u TextboxEnabled.Value;}
}
//这是一个ICommand,用于在后台执行任务
public ReactiveAsyncCommand DoSearch{get;protected set;}
//这是一个属性,将使用可观察到的结果进行更新
ObservablePropertyHelper\u搜索结果;
公共IEnumerable搜索结果{
获取{return\u SearchResults.Value;}
}
} 

DistinctUntilChanged
是您要寻找的

例如,像这样的东西应该可以工作:

// low level observables
var dataSourceLoading = ... // "loading" observable
var dataSourceLoaded = ... // "loaded" observable
var textChange = Observable.FromEvent<TextChangedEventArgs>(MyTextBox, "TextChanged");
var click = Observable.FromEvent<RoutedEventArgs>(MyButton, "Click");

// higher level observables
var text = textChange.Select(_ => MyTextBox.Text.Trim());
var emptyText = text.Select(String.IsNullOrWhiteSpace);
var searchInProgress = dataSourceLoading.Select(_ => true).Merge(dataSourceLoaded.Select(_ => false));

// enable/disable controls
searchInProgress.Merge(emptyText)
    .ObserveOnDispatcher()
    .Subscribe(v => MyButton.IsEnabled = !v);
searchInProgress
    .ObserveOnDispatcher()
    .Subscribe(v => MyTextBox.IsEnabled = !v);

// load data 
click
    .CombineLatest(text, (c,t) => new {c,t})
    .DistinctUntilChanged(ct => ct.c)
    .Subscribe(ct => LoadData(ct.t));
//低级可观测值
var数据源加载=…/“加载”可观察
var dataSourceLoaded=…/“loaded”可观察
var textChange=Observable.FromEvent(MyTextBox,“TextChanged”);
var click=Observable.FromEvent(MyButton,“click”);
//高级可观测
var text=textChange.Select(=>MyTextBox.text.Trim());
var emptyText
public class TextSearchViewModel
{
    public TextSearchViewModel
    {
        // If there is no text (or whitespace only) then the search button is disabled.
        var isSearchEnabled = this.ObservableForProperty(x => x.SearchText)
            .Select(x => !String.IsNullOrWhitespace(x.Value));

        // Create an ICommand that represents the Search button
        // Setting 1 at a time will make sure the Search button disables while search is running
        DoSearch = new ReactiveAsyncCommand(isSearchEnabled, 1/*at a time*/);

        // When the user clicks search the text box and the search button are both disabled.
        var textBoxEnabled = DoSearch.ItemsInflight
            .Select(x => x == 0);

        // Always update the "TextboxEnabled" property with the latest textBoxEnabled IObservable
        _TextboxEnabled = this.ObservableToProperty(textBoxEnabled, 
            x => x.TextboxEnabled, true);

        // Register our search function to run in a background thread - for each click of the Search
        // button, the searchResults IObservable will produce a new OnNext item
        IObservable<IEnumerable<MyResult>> searchResults = DoSearch.RegisterAsyncFunction(textboxText => {
            var client = new MySearchClient();
            return client.DoSearch((string)textboxText);
        });

        // Always update the SearchResults property with the latest item from the searchResults observable
        _SearchResults = this.ObservableToProperty(searchResults, x => x.SearchResults);
    }

    // Create a standard INotifyPropertyChanged property
    string _SearchText;
    public string SearchText {
        get { return _SearchText; }
        set { this.RaiseAndSetIfChanged(x => x.SearchText, value); }
    }

    // This is a property who will be updated with the results of an observable
    ObservableAsPropertyHelper<bool> _TextboxEnabled;
    public bool TextboxEnabled {
        get { return _TextboxEnabled.Value; }
    }

    // This is an ICommand built to do tasks in the background
    public ReactiveAsyncCommand DoSearch { get; protected set; }

    // This is a property who will be updated with the results of an observable
    ObservableAsPropertyHelper<IEnumerable<MyResult>> _SearchResults;
    public IEnumerable<MyResult> SearchResults {
        get { return _SearchResults.Value; }
    }
} 
// low level observables
var dataSourceLoading = ... // "loading" observable
var dataSourceLoaded = ... // "loaded" observable
var textChange = Observable.FromEvent<TextChangedEventArgs>(MyTextBox, "TextChanged");
var click = Observable.FromEvent<RoutedEventArgs>(MyButton, "Click");

// higher level observables
var text = textChange.Select(_ => MyTextBox.Text.Trim());
var emptyText = text.Select(String.IsNullOrWhiteSpace);
var searchInProgress = dataSourceLoading.Select(_ => true).Merge(dataSourceLoaded.Select(_ => false));

// enable/disable controls
searchInProgress.Merge(emptyText)
    .ObserveOnDispatcher()
    .Subscribe(v => MyButton.IsEnabled = !v);
searchInProgress
    .ObserveOnDispatcher()
    .Subscribe(v => MyTextBox.IsEnabled = !v);

// load data 
click
    .CombineLatest(text, (c,t) => new {c,t})
    .DistinctUntilChanged(ct => ct.c)
    .Subscribe(ct => LoadData(ct.t));
Func<string, bool> textIsValid = t => !String.IsNullOrEmpty(t);
IObservable<string> searchTextReady =
    (from text in textBoxText
     select (from x in button.ClickObservable().TakeUntil(textBoxText)
             where textIsValid(text)
             select text)
    ).Switch();
IObservable<Unit> xs= ...;
IObservable<string> ys= ...;

IObservable<IObservable<string>> zss = xs.Select(x => ys);

IObservable<string> zs = zss.Switch();
IObservable<Unit> clicks = ...;
IObservable<string> texts = ...;
Func<string, bool> isValid = ...;

IObservable<IObservable<string>> zss =
    texts.Select(t =>
        clicks
            .Take(1)
            .TakeUntil(texts)
            .Where(x => isValid(t))
            .Select(x => t));

IObservable<string> zs = zss.Switch();