1
\$\begingroup\$

This is a practice application that I built to learn Rx.Net & RxUI. It follows the MVVM pattern and is written in C# with WPF. Here is a link to a question I asked during the development, which includes some information about this application. Now that it finally runs, I want to improve it by eliminating any potential flaws in the code, which is presented as follows:

public class MainViewModel : ReactiveObject
{
    #region Constructors

    public MainViewModel()
    {
        _currentState = new BehaviorSubject<MainViewModelState>(MainViewModelState.Idle);

        _status = _currentState.Select(s => s.ToString()).ObserveOn(RxApp.MainThreadScheduler)
            .ToProperty(this, nameof(Status));
        _progress = _currentState.Select(s => ((int) s).ToString()).ObserveOn(RxApp.MainThreadScheduler)
            .ToProperty(this, nameof(Progress));

        var canSearch = this.WhenAnyValue(x => x.TargetUrl, x => !string.IsNullOrEmpty(x))
            .CombineLatest(_currentState, (b, state) => b && _currentState.Value == MainViewModelState.Idle);

        _searchCommand = ReactiveCommand.Create(() =>
        {
            _currentState.OnNext(MainViewModelState.FetchingHtmlDoc);
            return TargetUrl;
        }, canSearch);

        _searchCommand.SelectMany(HtmlDownloadService.GetHtmlDocument)
            .Zip(_searchCommand, Tuple.Create)
            .Do(_ => _currentState.OnNext(MainViewModelState.ExtractingImageUrl))
            .Select(tuple => ImageExtractService.ExtractAllImageAddress(tuple.Item1, tuple.Item2).Distinct())
            .SubscribeOn(RxApp.TaskpoolScheduler)
            .ObserveOn(RxApp.MainThreadScheduler)
            .Subscribe(
                urls =>
                {
                    _currentState.OnNext(MainViewModelState.PopulatingList);
                    urls.ToObservable().ObserveOn(RxApp.MainThreadScheduler).Subscribe(url =>
                        ImageList.Add(new ScrappedWebImageViewModel {ImageUrl = url}));
                    _currentState.OnNext(MainViewModelState.Idle);
                },
                ex => this.Log().Error(ex));

        var canClear = _currentState.Select(x => x == MainViewModelState.Idle).ObserveOn(RxApp.MainThreadScheduler);

        _clearCommand = ReactiveCommand.Create(() =>
        {
            _currentState.OnNext(MainViewModelState.ClearingList);
            ImageList.Clear();
            _currentState.OnNext(MainViewModelState.Idle);
        }, canClear, RxApp.MainThreadScheduler);
    }

    #endregion

    #region Properties

    private IHtmlDownloadService _htmlDownloadService;

    private IHtmlDownloadService HtmlDownloadService =>
        _htmlDownloadService ?? (_htmlDownloadService = Locator.Current.GetService<IHtmlDownloadService>());

    private IImageExtractService _imageExtractService;

    private IImageExtractService ImageExtractService =>
        _imageExtractService ?? (_imageExtractService = Locator.Current.GetService<IImageExtractService>());

    private readonly BehaviorSubject<MainViewModelState> _currentState;

    public ReactiveList<ScrappedWebImageViewModel> ImageList =
        new ReactiveList<ScrappedWebImageViewModel>();

    private readonly ObservableAsPropertyHelper<string> _status;

    public string Status => _status.Value;

    private readonly ObservableAsPropertyHelper<string> _progress;

    public string Progress => _progress.Value;

    private string _targetUrl;

    public string TargetUrl
    {
        get => _targetUrl;
        set => this.RaiseAndSetIfChanged(ref _targetUrl, value);
    }

    #endregion

    #region Commands

    private readonly ReactiveCommand<Unit, string> _searchCommand;

    public ICommand SearchCommand => _searchCommand;

    private readonly ReactiveCommand<Unit, Unit> _clearCommand;

    public ICommand ClearCommand => _clearCommand;

    #endregion

    #region Methods


    #endregion

    #region Types

    private enum MainViewModelState
    {
        Idle = 0,
        FetchingHtmlDoc,
        ExtractingImageUrl,
        PopulatingList,
        ClearingList
    }

    #endregion
}

During my test runs, I noticed that when extracting Urls, the UI became unresponsive, but I can't figure out a better way to do it. Also I wish to add cancellation functionalities but it seems quite difficult to implement with the current code, perhaps I'm not following the best practice?

\$\endgroup\$
  • \$\begingroup\$ Because we don't deal with code not yet implemented, don't expect someone to show you how to add your cancellation. But it's quite appropriate to ask how changes to your code would make it easier to add that yourself. So your question is welcome here. \$\endgroup\$ – Toby Speight Feb 8 '18 at 11:16

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.