Xamarin Forms - ReactiveUi 할 일 목록 | 아이포 테크노랩

ReactiveUi란?



모든 .Net 프레임워크를 위한 고급 기능적 반응형 MVVM 및 구성 가능한 프레임워크입니다.

ReactiveUi는 애플리케이션에서 MVVM 패턴을 쉽게 구현할 수 있도록 반응형 프로그래밍을 지원합니다.

MVVM 프레임 - 다른 작품에 비해 ReactiveUi는 그다지 독재적이지 않습니다.



여기에서 Reactive Extensions(Rx) 라이브러리는 콜백을 선언하고 코드에서 비동기 실행을 관리할 수 있으며 LinQ 구문을 사용하여 이러한 모든 작업을 수행하는 적절한 용도입니다.

구조화되지 않고 관리하기 어려운 코드를 제거하고 읽기 쉽게 만듭니다.

ReactiveUi를 사용하여 ToDo 목록 생성
프로젝트를 시작하려면 NuGet 패키지를 사용하여 PCL 프로젝트에 3개의 패키지를 설치해야 합니다.
  • ReactiveUi.XamForms 패키지
  • DynamicData 패키지
  • Sextant.XamForms 패키지

  • 그리고 C# 8.0 이상 버전을 사용해야 합니다.

    이제 Managers, Models, ViewModels 및 Views로 4개의 폴더를 만들고 아래와 같이 적절한 파일을 추가해야 합니다.

    HomePage -> HomeViewModel은 홈 페이지인 것처럼 Todo 목록을 처리합니다.

    ItemPage -> ItemVewModel은 단일 항목을 추가하고 편집합니다.

    항목 모델은 Todo 항목을 나타냅니다.

    ItemManagers는 Todo 컬렉션을 처리합니다.

    추가된 모든 파일에 대한 코드:

    IItemManager.cs

    
                    public class ItemManager : IItemManager
    {
        public ItemManager()
        {
          ItemChanges = _itemsCache.Connect().RefCount();
        }
        public Optional<item> Get(string id) => _itemsCache.Lookup(id);
        public IObservable<ichangeset<item, string="">> ItemChanges { get; }
        public void AddOrUpdate(Item item) => _itemsCache.AddOrUpdate(item);
        public void Remove(Item item) => _itemsCache.Remove(item);
        private SourceCache<item, string=""> _itemsCache = new SourceCache<item, string="">(item => item.Id);
    }
    </item,></item,></ichangeset<item,></item>
    
    


    Item.cs

                    public class Item : ReactiveObject
    {
      public Item(string id, string title)
      {
         Id = id;
         Title = title;
      }
      public string Id { get; }
      public string Title { get; }
      public bool IsCompleted
      {
        get => _isCompleted;
        set => this.RaiseAndSetIfChanged(ref _isCompleted, value);
      }
      private bool _isCompleted;
    }
    
    


    HomeViewModel.cs

                    public class HomeViewModel : ViewModelBase
    {
       public HomeViewModel(IParameterViewStackService navigationService, IItemManager 
        itemManager) : base(navigationService)
       {
         DeleteCommand = ReactiveCommand.Create<item>(itemManager.Remove);
         itemManager
           .ItemChanges
           .Bind(out _items)
           .DisposeMany()
           .Subscribe()
           .DisposeWith(Subscriptions);            
        AddCommand = ReactiveCommand.CreateFromObservable(() => 
          NavigationService.PushModal<itemviewmodel>());
        ViewCommand = ReactiveCommand.CreateFromObservable<item, unit="">((item) =>
        {
          SelectedItem = null;
          return NavigationService.PushModal<itemviewmodel>(new NavigationParameter()
          {
             { NavigationParameterConstants.ItemId , item.Id }
          });
        });
        this.WhenAnyValue(x => x.SelectedItem)
          .Where(x => x != null)
          .InvokeCommand(ViewCommand)
          .DisposeWith(Subscriptions);
      }
      public ReactiveCommand<unit, unit=""> AddCommand { get; }
      public ReactiveCommand<item, unit=""> ViewCommand { get; }
      public ReactiveCommand<item, unit=""> DeleteCommand { get; }
      public Item SelectedItem
      {
         get => _selectedItem;
         set => this.RaiseAndSetIfChanged(ref _selectedItem, value);
      }
      public ReadOnlyObservableCollection<item> Items => _items;
      public override string Id => "Reactive ToDo";
      private readonly ReadOnlyObservableCollection<item> _items;
      private Item _selectedItem;
    }
    </item></item></item,></item,></unit,></itemviewmodel></item,></itemviewmodel></item>
    
    


    자세히 보기: Xamarin 앱에 대한 노출 알림 API 지원



    ItemViewModel.cs

    
      public class ItemViewModel:ViewModelBase
    {
      public ItemViewModel(IParameterViewStackService navigationService, IItemManager 
       itemManager) : base(navigationService)
      {
        _itemManager = itemManager;
    var canExecute = this.WhenAnyValue(x => x.Title, (title) => 
            !string.IsNullOrEmpty(title));
           SaveCommand = ReactiveCommand.Create(ExecuteSave, canExecute);
    
       CloseCommand = ReactiveCommand.CreateFromObservable(() =>     
         NavigationService.PopModal());
       SaveCommand
         .InvokeCommand(CloseCommand)
         .DisposeWith(Subscriptions);
       this.WhenAnyValue(x => x.ItemId)
          .Where(x => x != null)
          .Select(x => _itemManager.Get(x))
          .Where(x => x.HasValue)
          .Select(x => x.Value)
          .Subscribe(x =>
           {
               Title = x.Title;
           })
           .DisposeWith(Subscriptions);
      }
      public override IObservable<unit> WhenNavigatingTo(INavigationParameter parameter)
      {
         if (parameter.TryGetValue(NavigationParameterConstants.ItemId, out string itemId))
         {
           ItemId = itemId;
         }
         return base.WhenNavigatedTo(parameter);
      }
      private void ExecuteSave() => _itemManager.AddOrUpdate(new Item(ItemId ??    
       Guid.NewGuid().ToString(), Title));
      public ReactiveCommand<unit, unit=""> SaveCommand { get; }
      public ReactiveCommand<unit, unit=""> CloseCommand { get; }
      public override string Id => string.Empty;
      public string Title
      {
         get => _title;
         set => this.RaiseAndSetIfChanged(ref _title, value);
      }
      private string ItemId
      {
        get => _itemId;
           set => this.RaiseAndSetIfChanged(ref _itemId, value);
      }
      private string _title;
      private string _description;
      private readonly IItemManager _itemManager;
      private string _itemId;
    }
    </unit,></unit,></unit>
    
    


    ViewModelBase.cs

    
      public abstract class ViewModelBase:ReactiveObject,IDisposable,INavigable
    {
       protected ViewModelBase(IParameterViewStackService viewStackService) =>  
         NavigationService = viewStackService;
       public abstract string id { get; }
       public virtual IObservable<unit> WhenNavigatedFrom(INavigationParameter parameter) =>      
         Observable.Return(Unit.Default);
       public virtual IObservable<unit> WhenNavigatedTo(INavigationParameter parameter) =>  
         Observable.Return(Unit.Default);
       public virtual IObservable<unit> WhenNavigatingTo(INavigationParameter parameter) =>  
         Observable.Return(Unit.Default);
      protected IParameterViewStackService NavigationService { get; }
      public void Dispose()
      {
        Dispose(true);
        GC.SuppressFinalize(this);
      }
      protected virtual void Dispose(bool disposing)
      {
        if (disposing)
        {
          Subscriptions?.Dispose();
        }
      }
      protected readonly CompositeDisposable Subscription = new CompositeDisposable();
    }
    </unit></unit></unit>
    
    


    HomePage.xaml

    <xmp>
    <?xml encoding="utf-8" version="1.0">
    <rxui:reactivecontentpage title="ToDo List" x:class="TodoList.Views.HomePage" x:name="homePage" x:typearguments="vm:HomeViewModel" xmlns="http://xamarin.com/schemas/2014/forms" xmlns:rxui="clr-namespace:ReactiveUI.XamForms;assembly=ReactiveUI.XamForms" xmlns:vm="clr-namespace:TodoList.ViewModels" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml">
        <stacklayout padding="20">
            <collectionview itemssource="{Binding Items}" selecteditem="{Binding SelectedItem, Mode=TwoWay}" selectionmode="Single">
                <collectionview.itemslayout>
                    <griditemslayout orientation="Vertical" verticalitemspacing="10" />
                </collectionview.itemslayout>
                <collectionview.itemtemplate>
                    <datatemplate>
                        <frame style="{StaticResource CardFrameStyle}">
                            <frame.triggers>
                                <datatrigger binding="{Binding IsCompleted}" targettype="Frame" value="True">
                                    <setter property="Opacity" value="0.2" />
                                </datatrigger>
                            </frame.triggers>
                            <stacklayout orientation="Horizontal">
                                <checkbox ischecked="{Binding IsCompleted}" />
    
                                <label text="" verticaloptions="EndAndExpand">
                                    <label.gesturerecognizers>
                                        <tapgesturerecognizer command="{Binding 
                                           Source={x:Reference homePage},               
                                           Path=BindingContext.DeleteCommand}" commandparameter="{Binding}" />
                                    </label.gesturerecognizers>
                                </label>
                            </stacklayout><label fontattributes="Bold" horizontaloptions="FillAndExpand" text="{Binding Title}" verticaloptions="Center">
                        </label></frame><label fontattributes="Bold" horizontaloptions="FillAndExpand" text="{Binding Title}" verticaloptions="Center">
                    </label></datatemplate><label fontattributes="Bold" horizontaloptions="FillAndExpand" text="{Binding Title}" verticaloptions="Center">
                </label></collectionview.itemtemplate><label fontattributes="Bold" horizontaloptions="FillAndExpand" text="{Binding Title}" verticaloptions="Center">
            </label></collectionview><label fontattributes="Bold" horizontaloptions="FillAndExpand" text="{Binding Title}" verticaloptions="Center"><button command="{Binding AddCommand}" style="{StaticResource CircularButtonStyle}" text="+"></button></label></stacklayout><label fontattributes="Bold" horizontaloptions="FillAndExpand" text="{Binding Title}" verticaloptions="Center">
    </label></rxui:reactivecontentpage><label fontattributes="Bold" horizontaloptions="FillAndExpand" text="{Binding Title}" verticaloptions="Center">
    </label></?xml></xmp><label fontattributes="Bold" horizontaloptions="FillAndExpand" text="{Binding Title}" verticaloptions="Center">
    </label>
    
    


    ItemPage.xaml

    
    <label fontattributes="Bold" horizontaloptions="FillAndExpand" text="{Binding Title}" verticaloptions="Center">
    <xmp><?xml encoding="utf-8" version="1.0">
    <?xml encoding="utf-8" version="1.0">
    <rxui:reactivecontentpage x:class="TodoList.Views.ItemPage" x:typearguments="vm:ItemViewModel" xmlns="http://xamarin.com/schemas/2014/forms" xmlns:rxui="clr-namespace:ReactiveUI.XamForms;assembly=ReactiveUI.XamForms" xmlns:vm="clr-namespace:TodoList.ViewModels" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml">
        <rxui:reactivecontentpage.toolbaritems>
            <toolbaritem command="{Binding CloseCommand}" text="Close" />
        </rxui:reactivecontentpage.toolbaritems>
        <stacklayout>
            <label fontsize="Title" horizontaloptions="Center" padding="20" text="Item">
            <frame margin="10" style="{StaticResource CardFrameStyle}" verticaloptions="Start">
                <stacklayout horizontaloptions="FillAndExpand">
                    <entry placeholder="Title" text="{Binding Title}" /><button command="{Binding SaveCommand}" style="{StaticResource MainButtonStyle}" text="Save"></button></stacklayout>
            </frame>
        </label></stacklayout><label fontsize="Title" horizontaloptions="Center" padding="20" text="Item">
    </label></rxui:reactivecontentpage><label fontsize="Title" horizontaloptions="Center" padding="20" text="Item">
    </label></?xml></?xml></xmp><label fontsize="Title" horizontaloptions="Center" padding="20" text="Item">
    </label></label>
    
    


    Xamarin 앱 개발 회사를 고용할 계획입니까? 귀하의 검색은 여기서 끝납니다.



    App.xaml:

    
      <label fontattributes="Bold" horizontaloptions="FillAndExpand" text="{Binding Title}" verticaloptions="Center"><label fontsize="Title" horizontaloptions="Center" padding="20" text="Item">
        <xml><!--?xml version="1.0" encoding="utf-8"--><!--?xml version="1.0" encoding="utf-8"-->
        <!--?xml version="1.0" encoding="utf-8" ?-->
        <application x:class="TodoList.App" xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml">
            <application.resources>
                <resourcedictionary><style targettype="Frame" type="text/css" x:key="CardFrameStyle"><Setter Property="HasShadow" Value="False" />
                        <Setter Property="BorderColor"  Value="Black" />
                        <Setter Property="Padding" Value="10" /></style><style targettype="Button" type="text/css" x:key="MainButtonStyle"><Setter Property="TextColor" Value="White" />
                        <Setter Property="BackgroundColor"  Value="Black" />
                        <Setter Property="HorizontalOptions" Value="FillAndExpand" /></style><style targettype="Button" type="text/css" x:key="CircularButtonStyle"><Setter Property="TextColor" Value="White" />
                        <Setter Property="BackgroundColor"  Value="Black" />
                        <Setter Property="HorizontalOptions" Value="Center" />
                        <Setter Property="CornerRadius" Value="40" />
                        <Setter Property="WidthRequest" Value="80" />
                        <Setter Property="HeightRequest" Value="80" />
                        <Setter Property="FontSize" Value="50" /></style>
                </resourcedictionary>
            </application.resources>
        </application>
        <!--?xml--><!--?xml--></xml>
        </label></label>  
    
    


    App.xaml.cs

    
      <label fontattributes="Bold" horizontaloptions="FillAndExpand" text="{Binding Title}" verticaloptions="Center"><label fontsize="Title" horizontaloptions="Center" padding="20" text="Item">
        public partial class App : Application
            {
                public App()
                {
                    InitializeComponent();
                    RxApp.DefaultExceptionHandler = new RxExceptionHandler();
                    Instance.InitializeForms();
                    Locator
                       .CurrentMutable
                       .RegisterConstant<iitemmanager><!--?xml version="1.0" encoding="utf-8"--><!--?xml version="1.0" encoding="utf-8"-->(new ItemManager());
                    Locator
                       .CurrentMutable
                       .RegisterNavigationView(() => new NavigationView(RxApp.MainThreadScheduler, 
                          RxApp.TaskpoolScheduler, ViewLocator.Current))
                       .RegisterParameterViewStackService()
                       .RegisterView<homepage, homeviewmodel="">()
                       .RegisterView<itempage, itemviewmodel="">()
                       .RegisterViewModel(() => new  
                            HomeViewModel(Locator.Current.GetService<iparameterviewstackservice>(),  
                            Locator.Current.GetService<iitemmanager>()))
                       .RegisterViewModel(() => new 
                            ItemViewModel(Locator.Current.GetService<iparameterviewstackservice>(), 
                            Locator.Current.GetService<iitemmanager>()));
                    Locator
                        .Current
                        .GetService<iparameterviewstackservice>()
                        .PushPage<homeviewmodel>(null, true, false)
                        .Subscribe();
                    MainPage = Locator.Current.GetNavigationView("NavigationView");
                }
            }
        </homeviewmodel></iparameterviewstackservice></iitemmanager></iparameterviewstackservice></iitemmanager></iparameterviewstackservice></itempage,></homepage,><!--?xml--><!--?xml--></iitemmanager></label></label>
    
    


    NavigationParameterConstants.cs

      <label fontattributes="Bold" horizontaloptions="FillAndExpand" text="{Binding Title}" verticaloptions="Center"><label fontsize="Title" horizontaloptions="Center" padding="20" text="Item">
        public class NavigationParameterConstants
        {
            public const string ItemId = "ItemId";
        }
        </label></label>
    
    


    RxExceptionHandler.cs

    
      <label fontattributes="Bold" horizontaloptions="FillAndExpand" text="{Binding Title}" verticaloptions="Center"><label fontsize="Title" horizontaloptions="Center" padding="20" text="Item">
        public class RxExceptionHandler : IObserver<exception><!--?xml version="1.0" encoding="utf-8"--><!--?xml version="1.0" encoding="utf-8"-->
            {
                public void OnNext(Exception ex)
                {
                    if (Debugger.IsAttached)
                    {
                        Debugger.Break();
                    }
                    RxApp.MainThreadScheduler.Schedule(() => { throw ex; });
                }
                public void OnError(Exception ex)
                {
                    if (Debugger.IsAttached)
                    {
                        Debugger.Break();
                    }
                    RxApp.MainThreadScheduler.Schedule(() => { throw ex; });
                }
                public void OnCompleted()
                {
                    if (Debugger.IsAttached)
                    {
                        Debugger.Break();
                    }
                    RxApp.MainThreadScheduler.Schedule(() => { throw new NotImplementedException(); });
                }
            }
        </exception></label></label>
    
    




    아이템 추가



    항목 삭제 후 목록

    결론



    ReactiveUi를 사용하면 MVVM 패턴을 사용할 때 반응적이고 테스트 가능하며 구성 가능한 Ui를 만들 수 있습니다. Todo 목록은 따라야 할 일일 시간표를 관리하는 데 사용됩니다. 그리고 이것을 사용하여 빠르고 이해하기 쉬운 방식으로 응답을 제공하는 일부 응용 프로그램을 만들 수도 있습니다.

    좋은 웹페이지 즐겨찾기