7.15.2017

ReactiveUI的ReactiveCommand

RxUI的命令和其它MVVM中實作ICommand的最大不一樣的地方就是 – 它可以有回傳值(當然,是reactive式的IObservable)!

之前剛學Prism(under Xarmin.Forms)的時候,在DelegateCommand的說明那裡找不到回傳值的用法,再看了一次RxUI的API說明,才發覺到正常來說,ICommand介面的實作中基本上就是執行動作,其概念中是不管動作的執行結果的,不管是Prism中的DelegateCommand或MvvmLight中的RelayCommand都是如此。

而在Reactive的世界中,實作時思考的方式是需要改變的,如上一篇blog介紹的範例中,RetrieveWordCommand的定義如下:

public ReactiveCommand<Unit, List<WordOption>> RetrieveWordCommand

ReactiveCommand<Unit, List<WordOption>>表示此命令不需要代入參數(Unit),但會傳回一IObservable<List<WordOption>>,因此,在建立此命令的程式中:

RetrieveWordCommand = ReactiveCommand.CreateFromTask<List<WordOption>>(async (arg) =>
{
    // ... 
    return wordResults.Select(wr => new WordOption{ //...    }
}, canRetrieve);

它回傳了一個可觀察佇列,且在接續的程式中,定義了當其命令完成後的動作:

RetrieveWordCommand
    .ObserveOn(RxApp.MainThreadScheduler)
    .Subscribe(wordOptions =>
    {
        // ...
    });

程式中,ObserveOn表示後續動作會在其指定的執行緒上被執行,此例中是UI主執行緒,因為它會修改被綁定到View上的屬性,而Subscribe中則會收到原先定義的回傳值List<WordOpton>當參數。

Written with StackEdit.

7.10.2017

在Xamarin.Forms上的MVVM

ReactiveUI

最近在看Xamarin.Forms的MVVM framework,個人徧好ReactiveUI,於是找到了這篇介紹 –
A Simple Vocabulary App Using ReactiveUI and Xamarin Forms,它實作了一個猜字的遊戲,如下圖:
enter image description here

基本上它使用的方式和在Windows平台上沒有什麼差別,仍是focus在view和viewmodel的互動,沒有其它的東西,如頁面的routing、Navigation、IoC的使用等。

此程式主要的動作在WordPickViewModel中:

public WordPickViewModel(IWordRepository wordRepository)
{
    _wordRepository = wordRepository;
    WordOptions = new ObservableCollection<WordOption>();
    CorrectPct = "0%";

    // 設定狀態的條件,型別為IObservable<bool>
    var canRetrieve = this.WhenAnyValue(x => x.CanRetrieve).Select(x => x);
    var canSelect = this.WhenAnyValue(x => x.CanRetrieve).Select(x => !x);

    // 原文使用CreateAsyncTask,目前版本v7.4,要改用如下函式
    // 定義命令,此命令完成後會回傳一List<WordOption>>
    RetrieveWordCommand = ReactiveCommand.CreateFromTask<List<WordOption>>(async (arg) =>
    {
        var wordResults = await _wordRepository.GetWords(_rangeFloor, RangeCeiling);

        return wordResults.Select(wr =>
            new WordOption
            {
                Word = wr.Name,
                Definition = wr.Definition,
                WordId = wr.Id
            }).ToList();
    }, canRetrieve);

    // 原文使用CreateAsyncTask,目前版本v7.4,要改用如下函式
    SelectAnswerCommand = ReactiveCommand.CreateFromTask(async arg =>
    {
        await HandleItemSelectedAsync(arg);
    }, canSelect);

    // ObserveOn表示後續動作會在其指定的執行緒上被執行
    // Subscribe定義命令完成後的處理
    RetrieveWordCommand
        .ObserveOn(RxApp.MainThreadScheduler)
        .Subscribe(wordOptions =>
        {
            _timerCancellationToken = new CancellationTokenSource();
            NextRange();
            CanRetrieve = false;
            WordOptions.Clear();

            // randomly determine the word to challenge user with
            var rand = new Random();
            var challengeWord = wordOptions[rand.Next(wordOptions.Count)];
            ChallengeWord = $"\"{challengeWord.Word}\"";

            foreach (var item in wordOptions)
            {
                var isAnswer = item.WordId == challengeWord.WordId;
                item.IsAnswer = isAnswer;
                item.Image = isAnswer ? "check.png" : "x.png";
                WordOptions.Add(item);
            }

            TimerCountdown = 10;
            Device.StartTimer(new TimeSpan(0, 0, 1), () =>
            {
                if (_timerCancellationToken.IsCancellationRequested)
                {
                    return false;
                }

                if (TimerCountdown == 0)
                {
                    ProcessAnswer();
                    return false;
                }
                TimerCountdown--;
                return true;
            });
        });

    //Behaviors
    this.WhenAnyValue(x => x.Begin).InvokeCommand(RetrieveWordCommand);
}

不過程式中對xaml頁面的部份,都是用code behind中的程式碼產生,View和ViewModel的綁定也是,個人比較不建議這個方式,這讓xaml的優勢不再。

xamvvm

這個輕量級的framework填補了ReactiveUI缺少的部份,因此兩個可以一起合用

Prism

Xarmin.Forms上的Prism不像WPF環境下那麼龐大,它精簡了很多,當然也比ReactiveUI完整多了,若是需要可以互相配合使用。

這邊有一個不錯的教學影片可參考.。

MVVMLight

在學Wpf時主要都是使用這一個,在Xamarin.Forms上它也滿適合的,輕量且剛剛好的功能,作者也寫了一個跨平台的範例程式,另在channel9有影片介紹

另有以此為基礎的延伸框架:

Xarch-starter

可以把它當作基楚的樣板來擴充,wiki中也提到了另一個更進階的框架 - Exrin,也可以參考看看…

這些framework都滿足了最基本的需求,不過大部份的說明文件都不甚豐富,想要應用都需要一點時間;當然也可以不依靠這些framework,之前在學wpf時就看過一系列的教學沒有應用任何framework,不過後來程式一步步擴大,不斷的重構後就出來了另一個框架了,就學習層面來說這很不錯,但還是要看個人的取舍了XD。

Written with StackEdit.