【问题标题】:How to use ReactiveList so UI is updated when new items are added如何使用 ReactiveList 以便在添加新项目时更新 UI
【发布时间】:2015-08-21 05:44:45
【问题描述】:

我正在创建一个带有列表的 Xamarin.Forms 应用程序。 itemSource 是一个响应式列表。但是,将新项目添加到列表不会更新 UI。这样做的正确方法是什么?

列表定义

_listView = new ListView();
var cell = new DataTemplate(typeof(TextCell));
cell.SetBinding(TextCell.TextProperty, "name");
cell.SetBinding(TextCell.DetailProperty, "location");
_listView.ItemTemplate = cell;

绑定

this.OneWayBind(ViewModel, x => x.monkeys, x => x._listView.ItemsSource);
this.OneWayBind(ViewModel, x => x.save, x => x._button.Command); //save adds new items

查看模型

public class MyPageModel : ReactiveObject
{
    public MyPageModel()
    {
        var canSave = this.WhenAny(x => x.username, x => !String.IsNullOrWhiteSpace(x.Value) && x.Value.Length>5);
        save = ReactiveCommand.CreateAsyncTask(canSave, async _ =>
        {
            var monkey = new Monkey { name = username, location = "@ " + DateTime.Now.Ticks.ToString("X"), details = "More here" };
            monkeys.Add(monkey);
            username = "";
        });
        monkeys = new ReactiveList<Monkey>{
            new Monkey { name="Baboon", location="Africa & Asia", details = "Baboons are Africian and Arabian Old World..." }
        };
        _monkeys.ChangeTrackingEnabled = true;
    }
    private string _username = "";
    public string username
    {
        get { return _username; }
        set { this.RaiseAndSetIfChanged(ref _username, value); }
    }
    private double _value = 0;
    public double value
    {
        get { return _value; }
        set { this.RaiseAndSetIfChanged(ref _value, value); }
    }
    public ReactiveCommand<Unit> save { get; set; }
    public ReactiveList<Monkey> _monkeys;
    public ReactiveList<Monkey> monkeys
    {
        get { return _monkeys; }
        set { this.RaiseAndSetIfChanged(ref _monkeys, value); }
    }
}
public class Monkey
{
    public string name { get; set; }
    public string location { get; set; }
    public string details { get; set; }
}

尝试将 ReactiveList 属性作为正常的自动属性以及上面代码中我使用 RaiseAndSetIfChanged 方法的属性。

【问题讨论】:

    标签: c# xamarin.forms reactiveui


    【解决方案1】:

    您的问题是您在非 UI 线程上更改 monkeys。在其他框架中,这会抛出异常,但在 AppKit / UIKit 中,这只是做奇怪的事情(通常什么都没有)。

        save = ReactiveCommand.Create(canSave);
        save.Subscribe(_ =>
        {
            // Do the part that modifies UI elements (or things bound to them)
            // in the RxCmd's Subscribe. This is guaranteed to run on the UI thread
            var monkey = new Monkey { name = username, location = "@ " + DateTime.Now.Ticks.ToString("X"), details = "More here" };
            monkeys.Add(monkey);
            username = "";
        });
    

    【讨论】:

    • 我通过两个观察进行了上述更改。我必须将 ReactiveCommand&lt;Unit&gt; save 更改为 ReactiveCommand&lt;object&gt; save 才能编译代码。当我运行列表时,尽管在用户名属性设置为空字符串时清除了文本字段,但列表仍然没有更新。将列表的定义改为public ReactiveList&lt;Monkey&gt; monkeys { get; set; },效果一样。
    • 更改为public ObservableCollection&lt;Monkey&gt; monkeys { get; set; } 工作并且用户界面刷新。我使用的 reactiveList 是不是错了?
    • @ritcoder - 你在这方面有什么进展吗?我仍然在 Xam Forms 项目中遇到同样的问题。
    • 我无法使用 ReactiveList,但对于我正在研究 ObservableCollection 的测试项目来说已经足够了。
    【解决方案2】:

    这种技术看起来非常复杂。这让我觉得你想做一些我认为更复杂的事情。

    这是一个干净的解决方案,通过以纯 MVVM 方式绑定到 ReactiveList&lt;T&gt;,将对象添加到列表框。

    首先,xaml

    <Window x:Class="ReactiveUIListBox.View.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:viewModel="clr-namespace:ReactiveUIListBox.ViewModel"
            Title="MainWindow" Height="350" Width="525">
    
        <Window.Resources>
            <viewModel:MainWindowViewModel x:Key="MainWindowViewModel"/>    
        </Window.Resources>
    
        <Window.DataContext>
            <StaticResource ResourceKey="MainWindowViewModel"/>
        </Window.DataContext>
    
        <Grid DataContext="{StaticResource MainWindowViewModel}">
            <Grid.ColumnDefinitions>
                <ColumnDefinition/>
                <ColumnDefinition/>
            </Grid.ColumnDefinitions>
    
            <Grid Grid.Column="0" Background="Azure">
                <ListBox ItemsSource="{Binding Model.TestReactiveList}"></ListBox>
            </Grid>
    
            <Grid Grid.Column="1">
                <Button x:Name="button" Command="{Binding TestCommand}" Content="Button" HorizontalAlignment="Left" Margin="78,89,0,0" VerticalAlignment="Top" Width="75"/>
            </Grid>
    
        </Grid>
    </Window>
    

    这是视图模型

    using System.Windows.Input;
    using ReactiveUIListBox.Model;
    using SecretSauce.Mvvm.ViewModelBase;
    
    namespace ReactiveUIListBox.ViewModel
    {
        public class MainWindowViewModel : ViewModelBase
        {
            public MainWindowViewModel()
            {
                Model = new ReactiveModel<string>();
            }
            public ReactiveModel<string> Model
            {
                get;
                set;
            }
    
            public ICommand TestCommand
            {
                get { return new RelayCommand(ExecuteTestCommand); }
            }
    
            private void ExecuteTestCommand(object obj)
            {
                Model.TestReactiveList.Add("test string");
            }
        }
    }
    

    最后,这是模型。

    using ReactiveUI;
    
    namespace ReactiveUIListBox.Model
    {
        public class ReactiveModel<T> : ReactiveObject
        {
            public ReactiveModel()
            {
                TestReactiveList= new ReactiveList<T>();
            }
    
            public ReactiveList<T> TestReactiveList
            {
                get;
                set;
            }
        }
    }
    

    按下按钮将填充列表框。希望我没有完全简化您尝试做的事情,但是您的代码似乎到处都是。

    我无法区分适合视图、模型或视图模型的代码。

    干杯。

    【讨论】:

    • 注明。上面的代码确实结构良好。有了以上内容,我的目标是动态生成 ui 的能力。这是样品之一。我确实让它工作了。当我得到零钱时,我会检查一下。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多