|
| 1 | +# ReactiveUI |
| 2 | + |
| 3 | +ReactiveUI is a compelling combination of MVVM and Reactive Extensions (Rx). |
| 4 | +Combining these two make managing concurrency as well as expressing complicated |
| 5 | +interactions between objects possible in a declarative, functional way. Put |
| 6 | +simply, if you’ve ever had to chain events / callbacks together and declare |
| 7 | +state ints/booleans to keep track of what’s going on, Reactive Extensions |
| 8 | +provides a sane alternative. |
| 9 | + |
| 10 | +## What’s in this library |
| 11 | +- **ReactiveObject** - a ViewModel object based on Josh Smith’s implementation, |
| 12 | + that also implements IObservable as a way to notify property changes. It also |
| 13 | + allows a straightforward way to observe the changes of a single property. |
| 14 | +- **ReactiveCommand** - an implementation of ICommand that is also a Subject |
| 15 | + whose OnNext is raised when Execute is executed. Its CanExecute can also be |
| 16 | + defined by an IObservable which means the UI will instantly update instead of |
| 17 | + implementations which rely on RequerySuggested in WPF. ReactiveCommand |
| 18 | + encapsulates the common pattern of “Fire asynchronous command, then marshal |
| 19 | + result back onto dispatcher thread”. It also allows you to control if |
| 20 | + concurrency is allowed. |
| 21 | +- **ObservableAsPropertyHelper<T>** - a class that easily lets you convert an |
| 22 | + IObservable into a property that stores its latest value, as well as fires |
| 23 | + NotifyPropertyChanged when the property changes. This is really useful for |
| 24 | + combining existing properties together and replacing IValueConverters, since |
| 25 | + your ViewModels will also be IObservables. |
| 26 | +- **ReactiveList<T>** - a custom implementation of an ObservableCollection which |
| 27 | + allows you to see changes in the collection as observables. |
| 28 | +- **MessageBus** - a reactive implementation of the Publish/Subscribe pattern, |
| 29 | + usefull to decouple your objects, while still being able to communicate. |
| 30 | +- **MemoizingMRUCache** - a cache that only remembers a specified number of |
| 31 | + recently used items. |
| 32 | +- **ObservableAsyncMRUCache** - a thread-safe, asynchronous MemoizingMRUCache. |
| 33 | +- **ReactiveBinding** - a powerful and flexible cross-platform binding framework |
| 34 | + as an alternative for Xaml bindings. |
| 35 | + |
| 36 | +## Organization |
| 37 | + |
| 38 | +This library is organized into several high-level assemblies: |
| 39 | + |
| 40 | +- **ReactiveUI** - Core library that doesn't rely on any particular UI |
| 41 | + framework. `ReactiveObject`, the base ViewModel object, as well as |
| 42 | + `ReactiveList<T>`, a more awesome ObservableCollection, `ReactiveCommand`, an |
| 43 | + implementation of ICommand, and the Binding framework are in here. |
| 44 | + |
| 45 | +- **ReactiveUI.Platforms** - Classes that require references to a Xaml'ly |
| 46 | + framework, like WPF or WinRT. This assembly also contains the Xaml part of the |
| 47 | + Binding framework and a screens and navigation framework usefull for |
| 48 | + navigating back and forward between views based on ViewModels. |
| 49 | + |
| 50 | +- **ReactiveUI.Blend** - This class has several Blend Behaviors and Triggers |
| 51 | + that make attaching ViewModel changes to Visual State Manager states. |
| 52 | + |
| 53 | +- **ReactiveUI.Mobile** - Useful classes when developing for a mobile platforms |
| 54 | + such as Windows Phone or the Windows Runtime. These classes handle things |
| 55 | + like persisting state and reacting to application lifetime events. |
| 56 | + |
| 57 | +## ReactiveObject |
| 58 | + |
| 59 | +Like any other MVVM framework, ReactiveUI has an object designed as a ViewModel |
| 60 | +class. This object is based on Josh Smith’s ObservableObject implementation in |
| 61 | +MVVM Foundation (actually, many of the classes’ inspiration come from MVVM |
| 62 | +Foundation, Josh does awesome work!). The Reactive version as you can imagine, |
| 63 | +implements INotifyPropertyChanged as well as IObservable so that you can |
| 64 | +subscribe to object property changes. |
| 65 | + |
| 66 | +ReactiveObject also does a few nice things for you: first, when you compile |
| 67 | +ReactiveUI in Debug mode, it will print debug messages using its logging |
| 68 | +framework whenever a property changes. Another example is, implementing the |
| 69 | +standard pattern of a property that raises the changed event is a few lines |
| 70 | +shorter and makes effective use of the new CallerMemberName attribute: |
| 71 | + |
| 72 | +```cs |
| 73 | +int _someProp; |
| 74 | +public int SomeProp { |
| 75 | + get { return _someProp; } |
| 76 | + set { this.RaiseAndSetIfChanged(ref _someProp, value); } |
| 77 | +} |
| 78 | +``` |
| 79 | + |
| 80 | +## ReactiveCommand |
| 81 | + |
| 82 | +ReactiveCommand is an ICommand implementation that is simultaneously a |
| 83 | +RelayCommand implementation, as well as some extra bits that are pretty |
| 84 | +motivating. We can provide an IObservable as our CanExecute. For example, here’s |
| 85 | +a command that can only run when the mouse is up: |
| 86 | + |
| 87 | +```cs |
| 88 | +var mouseIsUp = Observable.Merge( |
| 89 | + Observable.FromEvent<MouseButtonEventArgs>(window, ”MouseDown”).Select(_ => false), |
| 90 | + Observable.FromEvent<MouseButtonEventArgs>(window, ”MouseUp”).Select(_ => true), |
| 91 | +).StartWith(true); |
| 92 | + |
| 93 | +var cmd = new ReactiveCommand(mouseIsUp); |
| 94 | +cmd.Subscribe(x => Console.WriteLine(x)); |
| 95 | +``` |
| 96 | + |
| 97 | +Or, how about a command that can only run if two other commands are disabled: |
| 98 | + |
| 99 | +```cs |
| 100 | +// Pretend these were already initialized to something more interesting |
| 101 | +var cmd1 = new ReactiveCommand(); |
| 102 | +var cmd2 = new ReactiveCommand(); |
| 103 | + |
| 104 | +var can_exec = cmd1.CanExecuteObservable.CombineLatest(cmd2.CanExecuteObservable, (lhs, rhs) => !(lhs && rhs)); |
| 105 | +var new_cmd = new ReactiveCommand(can_exec); |
| 106 | +new_cmd.Subscribe(Console.WriteLine); |
| 107 | +``` |
| 108 | + |
| 109 | +One thing that’s important to notice here, is that the command’s CanExecute |
| 110 | +updates immediately, instead of relying on CommandManager.RequerySuggested. If |
| 111 | +you’ve ever had the problem in WPF or Silverlight where your buttons don’t |
| 112 | +reenable themselves until you switch focus or click them, you’ve seen this bug. |
| 113 | +Using an IObservable means that the Commanding framework knows exactly when the |
| 114 | +state changes, and doesn’t need to requery every command object on the page. |
| 115 | + |
| 116 | +### What about Execute? |
| 117 | + |
| 118 | +This is where ReactiveCommand’s IObservable implementation comes in. |
| 119 | +ReactiveCommand itself can be observed, and it provides new items whenever |
| 120 | +Execute is called (the items being the parameter passed into the Execute call). |
| 121 | +This means, that Subscribe can act the same as the Execute Action, or we can |
| 122 | +actually get a fair bit more clever. For example: |
| 123 | + |
| 124 | +```cs |
| 125 | +var cmd = new ReactiveCommand(); |
| 126 | +cmd.Where(x => ((int)x) % 2 == 0).Subscribe(x => Console.WriteLine(”Even numbers like {0} are cool!”, x)); |
| 127 | +cmd.Where(x => ((int)x) % 2 != 0).Timestamps().Subscribe(x => Console.WriteLine(”Odd numbers like {0} are even cooler, especially at {1}!”, x.Value, x.Timestamp)); |
| 128 | + |
| 129 | +cmd.Execute(2); |
| 130 | +>>> ”Even numbers like 2 are cool!” |
| 131 | + |
| 132 | +cmd.Execute(5); |
| 133 | +>>> ”Odd numbers like 5 are even cooler, especially at (the current time)!” |
| 134 | +``` |
| 135 | + |
| 136 | +### Running commands async. |
| 137 | +If you’ve done any C#/Xaml programming that does any sort of interesting work, |
| 138 | +you know that one of the difficult things is that if you do things in an event |
| 139 | +handler that take a lot of time, like reading a large file or downloading |
| 140 | +something over a network, you will quickly find that you have a problem: you |
| 141 | +either block the UI, or when you can’t even do blocking operations at all, |
| 142 | +you’ll just run it on another thread. Then, you find the 2nd tricky part that WPF |
| 143 | +and Silverlight objects have thread affinity. Meaning, that you can only access |
| 144 | +objects from the thread that created them. So, at the end of the computation |
| 145 | +when you go to runtextBox.Text = results;, you suddenly get an Exception. |
| 146 | +Dispatcher.BeginInvoke solves this So, once you dig around on the Internet a |
| 147 | +bit, you find out the pattern to solve this problem involves the Dispatcher: |
| 148 | + |
| 149 | +```cs |
| 150 | +void SomeUIEvent(object o, EventArgs e) { |
| 151 | + var some_data = this.SomePropertyICanOnlyGetOnTheUIThread; |
| 152 | + var t = new Task(() => { |
| 153 | + var result = doSomethingInTheBackground(some_data); |
| 154 | + Dispatcher.BeginInvoke(new Action(() => { this.UIPropertyThatWantsTheCalculation = result; })); |
| 155 | + } |
| 156 | + |
| 157 | + t.Start(); |
| 158 | +} |
| 159 | +``` |
| 160 | + |
| 161 | +We use this pattern a lot, so when we run a command, we often are just: |
| 162 | +1. The command executes, we kick off a thread |
| 163 | +2. We calculate something that takes a long time |
| 164 | +3. We take the result, and set a property on the UI thread, using Dispatcher |
| 165 | + |
| 166 | +ReactiveCommand encapsulates this pattern by allowing you to register a Task or |
| 167 | +IObservable to execute. It also gives you other thing for free. For example, you |
| 168 | +often only want one async instance running, and the Command should be disabled |
| 169 | +while we are still processing. Another common thing you would want to do is, |
| 170 | +display some sort of UI while an async action is running - something like a |
| 171 | +spinner control or a progress bar being displayed. |
| 172 | + |
| 173 | +Here’s a simple use of a Command, who will run a task in the background, and |
| 174 | +only allow one at a time (i.e. its CanExecute will return false until the action |
| 175 | +completes) |
| 176 | + |
| 177 | +```cs |
| 178 | +var cmd = new ReactiveCommand(null, false, /* do not allow concurrent requests */ null); |
| 179 | +cmd.RegisterAsyncAction(i => { |
| 180 | + Thread.Sleep((int)i * 1000); // Pretend to do work |
| 181 | +}; |
| 182 | + |
| 183 | +cmd.Execute(5 /*seconds*/); |
| 184 | +cmd.CanExecute(5); // False! We’re still chewing on the first one. |
| 185 | +``` |
| 186 | + |
| 187 | +## Learn more |
| 188 | + |
| 189 | +For more information on how to use ReactiveUI, check out |
| 190 | +[ReactiveUI](http://www.reactiveui.net). |
0 commit comments