ListBox未在UWP中使用ReactiveUI填充

时间:2016-04-15 15:44:13

标签: win-universal-app reactiveui

只是为了解决这个问题 - 我是XAML,Univeral Apps和ReactiveUI的完全新手,所以我可能在这里错过了一些简单的东西。

要获得ReactiveUI的句柄,我想我会尝试将文档(Reactive UI 101)中的ReactiveUI WPF Flickr示例移植到Windows Universal App。我正在使用Visual Studio 2015,ReactiveUI 6.5和更新的Splat。

我复制了代码并进行了必要的更改,以便为UWP编译。当前代码确实编译并打开,但无法正常工作。

我有两个问题:

  1. 我必须在“搜索:”框外单击以启动Flickr照片搜索。从我的理解,我不应该点击,它应该只是拿起我输入的东西,并在800ms的超时后启动搜索。调试照片搜索确实有效,并且我的SearchResults将返回一组对象。

  2. 第二个问题是即使我得到结果列表,也从不填充ListBox。我已尝试使用示例中所示的Binding,并使用x:Bind,因为我学习了如何在所有UWP教程中做 - 没有骰子。

  3. 我下载了另一个UWP示例(RxUI and UWP .Net Native Sample),我正在研究它的结构和设置,看看我错过了什么,但现在对我来说有点复杂。

    如果有人能指出我正确的方向,那将会有很大的帮助。

    这是我的XAML:

    <Page
    x:Class="ReactiveUIFlickerApp.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:ReactiveUIFlickerApp"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:data="using:ReactiveUIFlickerApp.Models"
    x:Name="MainPageView"
    mc:Ignorable="d">
    
    <Page.Resources>
        <DataTemplate x:Key="PhotoDataTemplate"
                      x:DataType="data:FlickrPhoto">
            <Grid MaxHeight="100">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="Auto" />
                    <ColumnDefinition Width="*" />
                </Grid.ColumnDefinitions>
    
                <Image Source="{x:Bind Url, Mode=OneWay}" Margin="6" MaxWidth="128"
                       HorizontalAlignment="Center" VerticalAlignment="Center" />
    
                <StackPanel Grid.Column="1" Margin="6">
                    <TextBlock FontSize="14" FontWeight="Bold" Text="{x:Bind Title}" />
                    <TextBlock FontStyle="Italic" Text="{x:Bind Description}"
                               TextWrapping="Wrap" Margin="6" />
                </StackPanel>
            </Grid>
        </DataTemplate>
    </Page.Resources>
    
    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
          Margin="12">
    
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto" />
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="Auto" />
        </Grid.ColumnDefinitions>
    
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
    
        <TextBlock FontSize="16"
                   FontWeight="Bold" 
                   VerticalAlignment="Center">Search For:</TextBlock>
    
        <TextBox Grid.Column="1"
                 Margin="6,0,0,0"
                 Text="{x:Bind PageViewModel.SearchTerm, Mode=TwoWay}"/>
    
        <TextBlock Grid.Column="2"
                   Margin="6,0,0,0"
                   FontSize="16"
                   FontWeight="Bold"
                   Text="..."
                   Visibility="{x:Bind PageViewModel.SpinnerVisibility}" />
    
        <ListBox Grid.ColumnSpan="3"
                 Grid.Row="1"
                 Margin="0,6,0,0" 
                 ScrollViewer.HorizontalScrollBarVisibility="Disabled"
                 ItemsSource="{x:Bind PageViewModel.SearchResults, Mode=OneWay}"
                 ItemTemplate="{StaticResource PhotoDataTemplate}"  />
    </Grid>
    

    XAML代码隐藏:

    public sealed partial class MainPage : Page
    {
        public MainPageViewModel PageViewModel { get; private set;}
    
        public MainPage()
        {
            PageViewModel = new MainPageViewModel();
    
            this.InitializeComponent();
        }
    }
    

    MainPageViewModel(在解决方案中自己的View文件夹中):

        public class MainPageViewModel : ReactiveObject
    {
        string _SearchTerm;
        public string SearchTerm
        {
            get { return _SearchTerm; }
            set { this.RaiseAndSetIfChanged(ref _SearchTerm, value); }
        }
    
        public ReactiveCommand<List<FlickrPhoto>> ExecuteSearch { get; protected set; }
    
        ObservableAsPropertyHelper<List<FlickrPhoto>> _SearchResults;
        public List<FlickrPhoto> SearchResults => _SearchResults.Value;
    
        ObservableAsPropertyHelper<Visibility> _SpinnerVisibility;
        public Visibility SpinnerVisibility => _SpinnerVisibility.Value;
    
    
    
        public MainPageViewModel()
        {
            ExecuteSearch =
                ReactiveCommand.CreateAsyncTask(parameter => GetSearchResultsFromFlickr(this.SearchTerm));
    
    
            this.WhenAnyValue(x => x.SearchTerm)
                .Throttle(TimeSpan.FromMilliseconds(800), RxApp.MainThreadScheduler)
                .Select(x => x?.Trim()) // null conditional operator
                .DistinctUntilChanged()
                .Where(x => !String.IsNullOrWhiteSpace(x))
                .InvokeCommand(ExecuteSearch);
    
            _SpinnerVisibility = ExecuteSearch.IsExecuting
                .Select(x => x ? Visibility.Visible : Visibility.Collapsed)
                .ToProperty(this, x => x.SpinnerVisibility, Visibility.Collapsed/*Hidden*/);
    
            ExecuteSearch.ThrownExceptions.Subscribe(ex => { Debug.WriteLine(ex.ToString());/* Handle errors here */});
    
            _SearchResults = ExecuteSearch.ToProperty(this, x => x.SearchResults, new List<FlickrPhoto>());
        }
    
        public static async Task<List<FlickrPhoto>> GetSearchResultsFromFlickr(string searchTerm)
        {
    
            var client = new HttpClient();
            var stream = await client.GetStreamAsync(String.Format(CultureInfo.InvariantCulture,
                "http://api.flickr.com/services/feeds/photos_public.gne?tags={0}&format=rss_200",
                WebUtility.UrlEncode(searchTerm)));
    
            //var doc = await Task.Run(() => XDocument.Load(String.Format(CultureInfo.InvariantCulture,
            //    "http://api.flickr.com/services/feeds/photos_public.gne?tags={0}&format=rss_200",
            //    WebUtility.UrlEncode(searchTerm)))); 
    
            var doc = await Task.Run( () => XDocument.Load(stream) );
    
            if (doc.Root == null)
                return null;
    
            var titles = doc.Root.Descendants("{http://search.yahoo.com/mrss/}title")
                .Select(x => x.Value);
    
            var tagRegex = new Regex("<[^>]+>", RegexOptions.IgnoreCase);
            var descriptions = doc.Root.Descendants("{http://search.yahoo.com/mrss/}description")
                .Select(x => tagRegex.Replace(WebUtility.HtmlDecode(x.Value), ""));
                //.Select(x => tagRegex.Replace(HttpUtility.HtmlDecode(x.Value), ""));
    
            var items = titles.Zip(descriptions,
                (t, d) => new FlickrPhoto { Title = t, Description = d }).ToArray();
    
            var urls = doc.Root.Descendants("{http://search.yahoo.com/mrss/}thumbnail")
                .Select(x => x.Attributes("url").First().Value);
    
            var ret = items.Zip(urls, (item, url) => { item.Url = url; return item; }).ToList();
            return ret;
        }
    }
    

1 个答案:

答案 0 :(得分:0)

正如我在回复your issue on Github时提到的,UWP在控件失去焦点之前不会更新TextBox绑定。您必须使用RxUI Bind系列来解决这个问题。

至于结果列表,我不是完全确定为什么它不起作用,除了我将绑定到ReactiveList<T>(或更可能,{{1} })。这样,IReadOnlyReactiveList<T>List<T>使用ListBox.ItemsSource,而不是销毁ReactiveList<T>所绑定的INotifyPropertyChanged

一旦你开始工作,尝试将列表填充代码移动到ReactiveCommand工作者的主体中,这通常会更好。