├── .gitignore
├── Droid
├── Assets
│ └── AboutAssets.txt
├── Droid.csproj
├── MainActivity.cs
├── Properties
│ ├── AndroidManifest.xml
│ └── AssemblyInfo.cs
├── Resources
│ ├── AboutResources.txt
│ ├── drawable-hdpi
│ │ └── slideout.png
│ ├── drawable-xxhdpi
│ │ └── slideout.png
│ ├── drawable
│ │ ├── Splash.xml
│ │ ├── icon.png
│ │ └── reactive_logo.png
│ ├── layout
│ │ └── SplashScreen.axml
│ ├── mipmap-hdpi
│ │ └── ic_launcher.png
│ ├── mipmap-mdpi
│ │ └── ic_launcher.png
│ ├── mipmap-xhdpi
│ │ └── ic_launcher.png
│ ├── mipmap-xxhdpi
│ │ └── ic_launcher.png
│ ├── mipmap-xxxhdpi
│ │ └── ic_launcher.png
│ └── values
│ │ ├── colors.xml
│ │ └── styles.xml
└── SplashScreen.cs
├── License.md
├── Presentation
└── Reactive Extensions Examples Presentation.pdf
├── Reactive Extensions Overview.pdf
├── Reactive UI & Xamarin.Forms.pdf
├── ReactiveDotNetCoreApi
├── Controllers
│ ├── ValuesController.cs
│ └── WeatherForecastController.cs
├── Program.cs
├── Properties
│ └── launchSettings.json
├── ReactiveDotNetCoreApi.csproj
├── Startup.cs
├── WeatherForecast.cs
├── appsettings.Development.json
└── appsettings.json
├── ReactiveDotNetCoreConsole
├── Program.cs
└── ReactiveDotNetCoreConsole.csproj
├── ReactiveExtensionExamples.sln
├── ReactiveExtensionExamples
├── App.cs
├── Extensions
│ ├── IObservableExtensions.cs
│ └── StyleExtensions.cs
├── FodyWeavers.xml
├── FodyWeavers.xsd
├── Models
│ └── RssEntry.cs
├── ReactiveExtensionExamples.csproj
├── Services
│ └── Api
│ │ ├── DuckDuckGoServiceModel.cs
│ │ ├── IDuckDuckGoApi.cs
│ │ └── RssDownloader.cs
├── UserInterface
│ ├── Cells
│ │ └── RssEntryCell.cs
│ └── Pages
│ │ ├── Async.cs
│ │ ├── AsyncEvent.cs
│ │ ├── AsyncToObservable.cs
│ │ ├── Buffer.cs
│ │ ├── BufferWithWhere.cs
│ │ ├── CombineLatest.cs
│ │ ├── Delay.cs
│ │ ├── DynamicData
│ │ ├── DynamicDataDashboard.cs
│ │ ├── FilterDynamicData.cs
│ │ ├── SimpleDynamicData.cs
│ │ ├── SortDynamicData.cs
│ │ └── SourceCacheDynamicData.cs
│ │ ├── Merge.cs
│ │ ├── NavigationContainerPage.cs
│ │ ├── NonReactivePage.cs
│ │ ├── PageBase.cs
│ │ ├── ReactiveUiColorSlider.cs
│ │ ├── ReactiveUiEssentials.cs
│ │ ├── ReactiveUiLogin.cs
│ │ ├── ReactiveUiSearch.cs
│ │ ├── Sample.cs
│ │ ├── Scan.cs
│ │ ├── SearchWithReactiveExtensions.cs
│ │ ├── StandardSearch.cs
│ │ ├── Throttle.cs
│ │ └── TimerUpdaterObservableEvents.cs
├── Values
│ └── Styles.cs
└── ViewModels
│ ├── ColorSlider.cs
│ ├── DynamicData
│ ├── FilterDynamicDataViewModel.cs
│ ├── SimpleDynamicDataViewModel.cs
│ ├── SortDynamicData.cs
│ └── SourceCacheDynamicDataViewModel.cs
│ ├── LoginCommand.cs
│ ├── SearchViewModel.cs
│ ├── ViewModelBase.cs
│ └── XamarinEssentials.cs
├── ReadMe.md
└── iOS
├── AppDelegate.cs
├── Entitlements.plist
├── ITunesArtwork
├── ITunesArtwork@2x
├── Info.plist
├── Main.cs
├── Resources
├── Default-568h@2x.png
├── Default-Portrait.png
├── Default-Portrait@2x.png
├── Default.png
├── Default@2x.png
├── Icon-60@3x.png
├── Icon-Small-40@3x.png
├── Icon-Small@3x.png
├── Images.xcassets
│ └── AppIcons.appiconset
│ │ ├── Contents.json
│ │ ├── Icon-60@2x.png
│ │ ├── Icon-76.png
│ │ ├── Icon-76@2x.png
│ │ ├── Icon-Small-40.png
│ │ ├── Icon-Small-40@2x.png
│ │ ├── Icon-Small.png
│ │ ├── Icon-Small@2x.png
│ │ ├── reactive_logo-29.png
│ │ ├── reactive_logo-29@2x.png
│ │ ├── reactive_logo-29@3x.png
│ │ ├── reactive_logo-40.png
│ │ ├── reactive_logo-40@2x.png
│ │ ├── reactive_logo-40@3x.png
│ │ ├── reactive_logo-60@2x.png
│ │ ├── reactive_logo-60@3x.png
│ │ ├── reactive_logo-76.png
│ │ └── reactive_logo-76@2x.png
├── LaunchScreen.storyboard
├── iOS
│ └── AppIcon.appiconset
│ │ ├── Contents.json
│ │ ├── reactive_logo-29.png
│ │ ├── reactive_logo-29@2x.png
│ │ ├── reactive_logo-29@3x.png
│ │ ├── reactive_logo-40.png
│ │ ├── reactive_logo-40@2x.png
│ │ ├── reactive_logo-40@3x.png
│ │ ├── reactive_logo-60@2x.png
│ │ ├── reactive_logo-60@3x.png
│ │ ├── reactive_logo-76.png
│ │ └── reactive_logo-76@2x.png
├── reactive_logo.png
├── reactive_logo@2x.png
├── reactive_logo@3x.png
├── slideout.png
└── slideout@2x.png
└── iOS.csproj
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.userprefs
8 | *.sln.docstates
9 |
10 | # Build results
11 |
12 | [Dd]ebug/
13 | [Rr]elease/
14 | x64/
15 | build/
16 | [Bb]in/
17 | [Oo]bj/
18 |
19 | # Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets
20 | !packages/*/build/
21 |
22 | # MSTest test Results
23 | [Tt]est[Rr]esult*/
24 | [Bb]uild[Ll]og.*
25 |
26 | *_i.c
27 | *_p.c
28 | *.ilk
29 | *.meta
30 | *.obj
31 | *.pch
32 | *.pdb
33 | *.pgc
34 | *.pgd
35 | *.rsp
36 | *.sbr
37 | *.tlb
38 | *.tli
39 | *.tlh
40 | *.tmp
41 | *.tmp_proj
42 | *.log
43 | *.vspscc
44 | *.vssscc
45 | .builds
46 | *.pidb
47 | *.log
48 | *.scc
49 |
50 | # Visual C++ cache files
51 | ipch/
52 | *.aps
53 | *.ncb
54 | *.opensdf
55 | *.sdf
56 | *.cachefile
57 |
58 | # Visual Studio profiler
59 | *.psess
60 | *.vsp
61 | *.vspx
62 |
63 | # Guidance Automation Toolkit
64 | *.gpState
65 |
66 | # ReSharper is a .NET coding add-in
67 | _ReSharper*/
68 | *.[Rr]e[Ss]harper
69 |
70 | # TeamCity is a build add-in
71 | _TeamCity*
72 |
73 | # DotCover is a Code Coverage Tool
74 | *.dotCover
75 |
76 | # NCrunch
77 | *.ncrunch*
78 | .*crunch*.local.xml
79 |
80 | # Installshield output folder
81 | [Ee]xpress/
82 |
83 | # DocProject is a documentation generator add-in
84 | DocProject/buildhelp/
85 | DocProject/Help/*.HxT
86 | DocProject/Help/*.HxC
87 | DocProject/Help/*.hhc
88 | DocProject/Help/*.hhk
89 | DocProject/Help/*.hhp
90 | DocProject/Help/Html2
91 | DocProject/Help/html
92 |
93 | # Click-Once directory
94 | publish/
95 |
96 | # Publish Web Output
97 | *.Publish.xml
98 | *.pubxml
99 |
100 | # NuGet Packages Directory
101 | ## TODO: If you have NuGet Package Restore enabled, uncomment the next line
102 | packages/
103 |
104 | # Windows Azure Build Output
105 | csx
106 | *.build.csdef
107 |
108 | # Windows Store app package directory
109 | AppPackages/
110 |
111 | # Others
112 | sql/
113 | *.Cache
114 | ClientBin/
115 | [Ss]tyle[Cc]op.*
116 | ~$*
117 | *~
118 | *.dbmdl
119 | *.[Pp]ublish.xml
120 | *.pfx
121 | *.publishsettings
122 |
123 | # RIA/Silverlight projects
124 | Generated_Code/
125 |
126 | # Backup & report files from converting an old project file to a newer
127 | # Visual Studio version. Backup files are not needed, because we have git ;-)
128 | _UpgradeReport_Files/
129 | Backup*/
130 | UpgradeLog*.XML
131 | UpgradeLog*.htm
132 |
133 | # SQL Server files
134 | App_Data/*.mdf
135 | App_Data/*.ldf
136 |
137 | # =========================
138 | # Windows detritus
139 | # =========================
140 |
141 | # Windows image file caches
142 | Thumbs.db
143 | ehthumbs.db
144 |
145 | # Folder config file
146 | Desktop.ini
147 |
148 | # Recycle Bin used on file shares
149 | $RECYCLE.BIN/
150 |
151 | # Mac crap
152 | .DS_Store
153 |
154 | \.vs/
155 |
156 | Droid/Resources/Resource\.designer\.cs
157 |
--------------------------------------------------------------------------------
/Droid/Assets/AboutAssets.txt:
--------------------------------------------------------------------------------
1 | Any raw assets you want to be deployed with your application can be placed in
2 | this directory (and child directories) and given a Build Action of "AndroidAsset".
3 |
4 | These files will be deployed with your package and will be accessible using Android's
5 | AssetManager, like this:
6 |
7 | public class ReadAsset : Activity
8 | {
9 | protected override void OnCreate (Bundle bundle)
10 | {
11 | base.OnCreate (bundle);
12 |
13 | InputStream input = Assets.Open ("my_asset.txt");
14 | }
15 | }
16 |
17 | Additionally, some Android functions will automatically load asset files:
18 |
19 | Typeface tf = Typeface.CreateFromAsset (Context.Assets, "fonts/samplefont.ttf");
20 |
--------------------------------------------------------------------------------
/Droid/Droid.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Debug
5 | AnyCPU
6 | {EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
7 | {3B2BD6CA-BFCA-4640-940E-624FABAEF8C7}
8 | Library
9 | EightBot.ReactiveExtensionExamples.Droid
10 | Assets
11 | Resources
12 | Properties\AndroidManifest.xml
13 | Resource
14 | Resources\Resource.designer.cs
15 | True
16 | EightBot.ReactiveExtensionExamples.Droid
17 | v11.0
18 |
19 |
20 | true
21 | full
22 | false
23 | bin\Debug
24 | DEBUG;
25 | prompt
26 | 4
27 | None
28 | false
29 | Xamarin.Android.Net.AndroidClientHandler
30 |
31 |
32 | full
33 | true
34 | bin\Release
35 | prompt
36 | 4
37 | false
38 | false
39 | Xamarin.Android.Net.AndroidClientHandler
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 | 5.0.0.2401
56 |
57 |
58 | 18.0.7
59 |
60 |
61 | 18.0.7
62 |
63 |
64 | 1.7.2
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 | {B54BEC10-F397-45B0-B080-E8E0292B2667}
106 | ReactiveExtensionExamples
107 |
108 |
109 |
110 |
--------------------------------------------------------------------------------
/Droid/MainActivity.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | using Android.App;
4 | using Android.Content;
5 | using Android.Content.PM;
6 | using Android.Runtime;
7 | using Android.Views;
8 | using Android.Widget;
9 | using Android.OS;
10 |
11 | namespace ReactiveExtensionExamples.Droid
12 | {
13 | [Activity (
14 | Label = "Rx Examples",
15 | Icon = "@android:color/transparent",
16 | ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation,
17 | Theme = "@android:style/Theme.Holo.Light.DarkActionBar")]
18 | public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsApplicationActivity
19 | {
20 | protected override void OnCreate (Bundle bundle)
21 | {
22 | base.OnCreate (bundle);
23 |
24 | global::Xamarin.Forms.Forms.Init (this, bundle);
25 |
26 | LoadApplication (new App ());
27 | }
28 | }
29 | }
30 |
31 |
--------------------------------------------------------------------------------
/Droid/Properties/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/Droid/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using Android.App;
4 |
5 | // Information about this assembly is defined by the following attributes.
6 | // Change them to the values specific to your project.
7 |
8 | [assembly: AssemblyTitle ("ReactiveExtensionExamples.Droid")]
9 | [assembly: AssemblyDescription ("")]
10 | [assembly: AssemblyConfiguration ("")]
11 | [assembly: AssemblyCompany ("")]
12 | [assembly: AssemblyProduct ("")]
13 | [assembly: AssemblyCopyright ("Eight-Bot, Inc.")]
14 | [assembly: AssemblyTrademark ("")]
15 | [assembly: AssemblyCulture ("")]
16 |
17 | // The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}".
18 | // The form "{Major}.{Minor}.*" will automatically update the build and revision,
19 | // and "{Major}.{Minor}.{Build}.*" will update just the revision.
20 |
21 | [assembly: AssemblyVersion ("1.0.0")]
22 |
23 | // The following attributes are used to specify the signing key for the assembly,
24 | // if desired. See the Mono documentation for more information about signing.
25 |
26 | //[assembly: AssemblyDelaySign(false)]
27 | //[assembly: AssemblyKeyFile("")]
28 |
29 |
--------------------------------------------------------------------------------
/Droid/Resources/AboutResources.txt:
--------------------------------------------------------------------------------
1 | Images, layout descriptions, binary blobs and string dictionaries can be included
2 | in your application as resource files. Various Android APIs are designed to
3 | operate on the resource IDs instead of dealing with images, strings or binary blobs
4 | directly.
5 |
6 | For example, a sample Android app that contains a user interface layout (main.axml),
7 | an internationalization string table (strings.xml) and some icons (drawable-XXX/icon.png)
8 | would keep its resources in the "Resources" directory of the application:
9 |
10 | Resources/
11 | drawable/
12 | icon.png
13 |
14 | layout/
15 | main.axml
16 |
17 | values/
18 | strings.xml
19 |
20 | In order to get the build system to recognize Android resources, set the build action to
21 | "AndroidResource". The native Android APIs do not operate directly with filenames, but
22 | instead operate on resource IDs. When you compile an Android application that uses resources,
23 | the build system will package the resources for distribution and generate a class called "R"
24 | (this is an Android convention) that contains the tokens for each one of the resources
25 | included. For example, for the above Resources layout, this is what the R class would expose:
26 |
27 | public class R {
28 | public class drawable {
29 | public const int icon = 0x123;
30 | }
31 |
32 | public class layout {
33 | public const int main = 0x456;
34 | }
35 |
36 | public class strings {
37 | public const int first_string = 0xabc;
38 | public const int second_string = 0xbcd;
39 | }
40 | }
41 |
42 | You would then use R.drawable.icon to reference the drawable/icon.png file, or R.layout.main
43 | to reference the layout/main.axml file, or R.strings.first_string to reference the first
44 | string in the dictionary file values/strings.xml.
45 |
--------------------------------------------------------------------------------
/Droid/Resources/drawable-hdpi/slideout.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TheEightBot/Reactive-Examples/203a224f276091ba599fc710a162a9b2efc2c62f/Droid/Resources/drawable-hdpi/slideout.png
--------------------------------------------------------------------------------
/Droid/Resources/drawable-xxhdpi/slideout.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TheEightBot/Reactive-Examples/203a224f276091ba599fc710a162a9b2efc2c62f/Droid/Resources/drawable-xxhdpi/slideout.png
--------------------------------------------------------------------------------
/Droid/Resources/drawable/Splash.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | -
7 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/Droid/Resources/drawable/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TheEightBot/Reactive-Examples/203a224f276091ba599fc710a162a9b2efc2c62f/Droid/Resources/drawable/icon.png
--------------------------------------------------------------------------------
/Droid/Resources/drawable/reactive_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TheEightBot/Reactive-Examples/203a224f276091ba599fc710a162a9b2efc2c62f/Droid/Resources/drawable/reactive_logo.png
--------------------------------------------------------------------------------
/Droid/Resources/layout/SplashScreen.axml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
16 |
--------------------------------------------------------------------------------
/Droid/Resources/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TheEightBot/Reactive-Examples/203a224f276091ba599fc710a162a9b2efc2c62f/Droid/Resources/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/Droid/Resources/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TheEightBot/Reactive-Examples/203a224f276091ba599fc710a162a9b2efc2c62f/Droid/Resources/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/Droid/Resources/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TheEightBot/Reactive-Examples/203a224f276091ba599fc710a162a9b2efc2c62f/Droid/Resources/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/Droid/Resources/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TheEightBot/Reactive-Examples/203a224f276091ba599fc710a162a9b2efc2c62f/Droid/Resources/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/Droid/Resources/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TheEightBot/Reactive-Examples/203a224f276091ba599fc710a162a9b2efc2c62f/Droid/Resources/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/Droid/Resources/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | #909DA5
5 |
--------------------------------------------------------------------------------
/Droid/Resources/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
--------------------------------------------------------------------------------
/Droid/SplashScreen.cs:
--------------------------------------------------------------------------------
1 |
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 |
7 | using Android.App;
8 | using Android.Content;
9 | using Android.OS;
10 | using Android.Runtime;
11 | using Android.Views;
12 | using Android.Widget;
13 | using System.Threading.Tasks;
14 |
15 | namespace ReactiveExtensionExamples.Droid
16 | {
17 | [Activity (
18 | Label = "Rx Examples",
19 | Icon = "@mipmap/ic_launcher",
20 | Theme = "@style/Theme.Splash",
21 | MainLauncher = true, NoHistory = true)]
22 | public class SplashScreen : Activity
23 | {
24 | protected override void OnCreate (Bundle savedInstanceState)
25 | {
26 | base.OnCreate (savedInstanceState);
27 |
28 | this.StartActivity (typeof(MainActivity));
29 | this.Finish ();
30 | }
31 | }
32 | }
33 |
34 |
--------------------------------------------------------------------------------
/Presentation/Reactive Extensions Examples Presentation.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TheEightBot/Reactive-Examples/203a224f276091ba599fc710a162a9b2efc2c62f/Presentation/Reactive Extensions Examples Presentation.pdf
--------------------------------------------------------------------------------
/Reactive Extensions Overview.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TheEightBot/Reactive-Examples/203a224f276091ba599fc710a162a9b2efc2c62f/Reactive Extensions Overview.pdf
--------------------------------------------------------------------------------
/Reactive UI & Xamarin.Forms.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TheEightBot/Reactive-Examples/203a224f276091ba599fc710a162a9b2efc2c62f/Reactive UI & Xamarin.Forms.pdf
--------------------------------------------------------------------------------
/ReactiveDotNetCoreApi/Controllers/ValuesController.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 | using Microsoft.AspNetCore.Mvc;
6 | using System.Reactive.Linq;
7 | using System.Net.Http;
8 | using System.Reactive.Threading.Tasks;
9 | using System.Reactive.Concurrency;
10 | using Microsoft.Extensions.Logging;
11 | using Microsoft.AspNetCore.Http;
12 | using Newtonsoft.Json.Linq;
13 | using Newtonsoft.Json;
14 | using System.IO;
15 |
16 | namespace ReactiveDotNetCoreApi.Controllers
17 | {
18 | [Route("api/[controller]")]
19 | [ApiController]
20 | public class ValuesController : ControllerBase
21 | {
22 | private readonly ILogger _logger;
23 |
24 | static readonly HttpClient _client = new HttpClient();
25 |
26 | public ValuesController(ILogger logger)
27 | {
28 | _logger = logger;
29 | }
30 |
31 | // GET api/values
32 | [HttpGet]
33 | public Task Get()
34 | {
35 | return Observable
36 | .FromAsync(() => _client.GetAsync("https://jsonplaceholder.typicode.com/todos"))
37 | .SubscribeOn(TaskPoolScheduler.Default)
38 | .Retry(5)
39 | .Timeout(TimeSpan.FromMilliseconds(80))
40 | .Do(x => _logger?.LogInformation($"Message Successful? :{x.IsSuccessStatusCode}"))
41 | .SelectMany(
42 | async x =>
43 | {
44 | JArray parsedTodos = null;
45 |
46 | using(var stream = await x.Content.ReadAsStreamAsync().ConfigureAwait(false))
47 | using(var sr = new StreamReader(stream))
48 | using(var jtr = new JsonTextReader(sr))
49 | {
50 | parsedTodos = await JArray.LoadAsync(jtr).ConfigureAwait(false);
51 | }
52 |
53 | return parsedTodos.Select(pt => pt.SelectToken("title")).ToList();
54 | })
55 | .Select(x => new JsonResult(x))
56 | .Catch(ex => Observable.Return(StatusCode(StatusCodes.Status503ServiceUnavailable)))
57 | .Catch(ex => Observable.Return(StatusCode(StatusCodes.Status500InternalServerError)))
58 | .ToTask();
59 | }
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/ReactiveDotNetCoreApi/Controllers/WeatherForecastController.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 | using Microsoft.AspNetCore.Mvc;
6 | using Microsoft.Extensions.Logging;
7 |
8 | namespace ReactiveDotNetCoreApi.Controllers
9 | {
10 | [ApiController]
11 | [Route("[controller]")]
12 | public class WeatherForecastController : ControllerBase
13 | {
14 | private static readonly string[] Summaries = new[]
15 | {
16 | "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
17 | };
18 |
19 | private readonly ILogger _logger;
20 |
21 | public WeatherForecastController (ILogger logger)
22 | {
23 | _logger = logger;
24 | }
25 |
26 | [HttpGet]
27 | public IEnumerable Get ()
28 | {
29 | var rng = new Random();
30 | return Enumerable.Range(1, 5).Select(index => new WeatherForecast
31 | {
32 | Date = DateTime.Now.AddDays(index),
33 | TemperatureC = rng.Next(-20, 55),
34 | Summary = Summaries[rng.Next(Summaries.Length)]
35 | })
36 | .ToArray();
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/ReactiveDotNetCoreApi/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 | using Microsoft.AspNetCore.Hosting;
6 | using Microsoft.Extensions.Configuration;
7 | using Microsoft.Extensions.Hosting;
8 | using Microsoft.Extensions.Logging;
9 |
10 | namespace ReactiveDotNetCoreApi
11 | {
12 | public class Program
13 | {
14 | public static void Main (string[] args)
15 | {
16 | CreateHostBuilder(args).Build().Run();
17 | }
18 |
19 | public static IHostBuilder CreateHostBuilder (string[] args) =>
20 | Host.CreateDefaultBuilder(args)
21 | .ConfigureWebHostDefaults(webBuilder =>
22 | {
23 | webBuilder.UseStartup();
24 | });
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/ReactiveDotNetCoreApi/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "http://json.schemastore.org/launchsettings.json",
3 | "iisSettings": {
4 | "windowsAuthentication": false,
5 | "anonymousAuthentication": true,
6 | "iisExpress": {
7 | "applicationUrl": "http://localhost:10052",
8 | "sslPort": 44373
9 | }
10 | },
11 | "profiles": {
12 | "IIS Express": {
13 | "commandName": "IISExpress",
14 | "launchBrowser": true,
15 | "launchUrl": "weatherforecast",
16 | "environmentVariables": {
17 | "ASPNETCORE_ENVIRONMENT": "Development"
18 | }
19 | },
20 | "ReactiveDotNetCoreApi": {
21 | "commandName": "Project",
22 | "launchBrowser": true,
23 | "launchUrl": "weatherforecast",
24 | "applicationUrl": "https://localhost:52006;http://localhost:22473",
25 | "environmentVariables": {
26 | "ASPNETCORE_ENVIRONMENT": "Development"
27 | }
28 | }
29 | }
30 | }
--------------------------------------------------------------------------------
/ReactiveDotNetCoreApi/ReactiveDotNetCoreApi.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.1
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/ReactiveDotNetCoreApi/Startup.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 | using Microsoft.AspNetCore.Builder;
6 | using Microsoft.AspNetCore.Hosting;
7 | using Microsoft.AspNetCore.HttpsPolicy;
8 | using Microsoft.AspNetCore.Mvc;
9 | using Microsoft.Extensions.Configuration;
10 | using Microsoft.Extensions.DependencyInjection;
11 | using Microsoft.Extensions.Hosting;
12 | using Microsoft.Extensions.Logging;
13 |
14 | namespace ReactiveDotNetCoreApi
15 | {
16 | public class Startup
17 | {
18 | public Startup (IConfiguration configuration)
19 | {
20 | Configuration = configuration;
21 | }
22 |
23 | public IConfiguration Configuration { get; }
24 |
25 | // This method gets called by the runtime. Use this method to add services to the container.
26 | public void ConfigureServices (IServiceCollection services)
27 | {
28 | services.AddControllers();
29 | }
30 |
31 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
32 | public void Configure (IApplicationBuilder app, IWebHostEnvironment env)
33 | {
34 | if (env.IsDevelopment())
35 | {
36 | app.UseDeveloperExceptionPage();
37 | }
38 |
39 | app.UseHttpsRedirection();
40 |
41 | app.UseRouting();
42 |
43 | app.UseAuthorization();
44 |
45 | app.UseEndpoints(endpoints =>
46 | {
47 | endpoints.MapControllers();
48 | });
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/ReactiveDotNetCoreApi/WeatherForecast.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace ReactiveDotNetCoreApi
4 | {
5 | public class WeatherForecast
6 | {
7 | public DateTime Date { get; set; }
8 |
9 | public int TemperatureC { get; set; }
10 |
11 | public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
12 |
13 | public string Summary { get; set; }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/ReactiveDotNetCoreApi/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft": "Warning",
6 | "Microsoft.Hosting.Lifetime": "Information"
7 | }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/ReactiveDotNetCoreApi/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft": "Warning",
6 | "Microsoft.Hosting.Lifetime": "Information"
7 | }
8 | },
9 | "AllowedHosts": "*"
10 | }
11 |
--------------------------------------------------------------------------------
/ReactiveDotNetCoreConsole/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 | using System.Reactive;
4 | using System.Reactive.Concurrency;
5 | using System.Reactive.Linq;
6 | using System.Threading.Tasks;
7 | using Spectre.Console;
8 |
9 | namespace ReactiveDotNetCoreConsole
10 | {
11 | class Program
12 | {
13 | private readonly static object matlock = new object();
14 |
15 | static async Task Main (string[] args)
16 | {
17 | Console.WriteLine("Select an Example");
18 | Console.WriteLine("1a: Publishing With Refcount - Bad");
19 | Console.WriteLine("1b: Publishing With Refcount - Good");
20 | Console.WriteLine("1c: Publishing With Refcount - Good");
21 | Console.WriteLine("2a: Async with Ordering - Bad");
22 | Console.WriteLine("2b: Async with Ordering - Good");
23 | Console.WriteLine("3a: Limit Concurrency - Bad");
24 | Console.WriteLine("3b: Limit Concurrency - Good");
25 | Console.WriteLine("4a: Scheduler - Bad");
26 | Console.WriteLine("4b: Scheduler - Good");
27 |
28 | var selection = Console.ReadLine();
29 |
30 | switch (selection)
31 | {
32 | case "1a":
33 | await PublishAndRefCountBadExample();
34 | break;
35 | case "1b":
36 | await PublishAndRefCountGoodExample();
37 | break;
38 | case "1c":
39 | await PublishAndRefCountAlternativeGoodExample();
40 | break;
41 | case "2a":
42 | await AsyncOrderingBadExample();
43 | break;
44 | case "2b":
45 | await AsyncOrderingGoodExample();
46 | break;
47 | case "3a":
48 | await LimitConcurrencyBadExample();
49 | break;
50 | case "3b":
51 | await LimitConcurrencyGoodExample();
52 | break;
53 | case "4a":
54 | await SchedulerExampleBad();
55 | break;
56 | case "4b":
57 | await SchedulerExampleGood();
58 | break;
59 | default:
60 | break;
61 | }
62 |
63 |
64 | Console.WriteLine();
65 | Console.WriteLine("Example Finished. Press Any key to continue...");
66 |
67 | Console.ReadLine();
68 | }
69 |
70 | public static async Task PublishAndRefCountBadExample ()
71 | {
72 | var intervalObs =
73 | Observable
74 | .Interval(TimeSpan.FromSeconds(2))
75 | .Do(i => WriteToConsoleWithColor((ConsoleColor)i, $"Processing Interval {i}"));
76 |
77 | var firstListener =
78 | intervalObs
79 | .Do(val => WriteToConsoleWithColor((ConsoleColor)val, $"First Listener Received: {val}"))
80 | .Subscribe();
81 |
82 | var secondListener =
83 | intervalObs
84 | .Do(val => WriteToConsoleWithColor((ConsoleColor)val, $"Second Listener Received: {val}"))
85 | .Subscribe();
86 |
87 | await Task.Delay(TimeSpan.FromSeconds(5));
88 |
89 | firstListener.Dispose();
90 | secondListener.Dispose();
91 | }
92 |
93 | public static async Task PublishAndRefCountGoodExample ()
94 | {
95 | var intervalObs =
96 | Observable
97 | .Interval(TimeSpan.FromSeconds(2))
98 | .Do(i => WriteToConsoleWithColor((ConsoleColor)i, $"Processing Interval {i}"))
99 | .Publish()
100 | .RefCount();
101 |
102 | var firstListener =
103 | intervalObs
104 | .Do(val => WriteToConsoleWithColor((ConsoleColor)val, $"First Listener Received: {val}"))
105 | .Subscribe();
106 |
107 | var secondListener =
108 | intervalObs
109 | .Do(val => WriteToConsoleWithColor((ConsoleColor)val, $"Second Listener Received: {val}"))
110 | .Subscribe();
111 |
112 | await Task.Delay(TimeSpan.FromSeconds(5));
113 |
114 | firstListener.Dispose();
115 | secondListener.Dispose();
116 | }
117 |
118 | public static async Task PublishAndRefCountAlternativeGoodExample ()
119 | {
120 | var intervalObs =
121 | Observable
122 | .Interval(TimeSpan.FromSeconds(2))
123 | .Do(i => WriteToConsoleWithColor((ConsoleColor)i, $"Processing Interval {i}"))
124 | .Publish();
125 |
126 | var firstListener =
127 | intervalObs
128 | .Do(val => WriteToConsoleWithColor((ConsoleColor)val, $"First Listener Received: {val}"))
129 | .Subscribe();
130 |
131 | var secondListener =
132 | intervalObs
133 | .Do(val => WriteToConsoleWithColor((ConsoleColor)val, $"Second Listener Received: {val}"))
134 | .Subscribe();
135 |
136 | intervalObs.Connect();
137 |
138 | await Task.Delay(TimeSpan.FromSeconds(5));
139 |
140 | firstListener.Dispose();
141 | secondListener.Dispose();
142 | }
143 |
144 | public static async Task AsyncOrderingBadExample ()
145 | {
146 | var rng = new Random();
147 |
148 | await Observable
149 | .Range(1, 10, TaskPoolScheduler.Default)
150 | .SelectMany(
151 | async range =>
152 | {
153 | var delay = rng.Next(10, 300);
154 | await Task.Delay(delay);
155 |
156 | WriteToConsoleWithColor((ConsoleColor)range, $"{range} - Finished after {delay}ms");
157 |
158 | return range;
159 | })
160 | .Do(
161 | range =>
162 | {
163 | WriteToConsoleWithColor((ConsoleColor)range, $"{range} - Received Notification at {DateTimeOffset.Now}");
164 | });
165 | }
166 |
167 | public static async Task AsyncOrderingGoodExample()
168 | {
169 | var rng = new Random();
170 |
171 | await Observable
172 | .Range(1, 10, TaskPoolScheduler.Default)
173 | .Select(
174 | async range =>
175 | {
176 | var delay = rng.Next(10, 300);
177 | await Task.Delay(delay);
178 |
179 | WriteToConsoleWithColor((ConsoleColor)range, $"{range} - Finished after {delay}ms");
180 |
181 | return range;
182 | })
183 | .Concat()
184 | .Do(
185 | range =>
186 | {
187 | WriteToConsoleWithColor((ConsoleColor)range, $"{range} - Received Notification at {DateTimeOffset.Now}");
188 | });
189 | }
190 |
191 | public static async Task LimitConcurrencyBadExample ()
192 | {
193 | var rng = new Random();
194 |
195 | await Observable
196 | .Range(1, 10, TaskPoolScheduler.Default)
197 | .SelectMany(
198 | async range =>
199 | {
200 | WriteToConsoleWithColor((ConsoleColor)range, $"{range} - Started at {DateTimeOffset.Now.ToUnixTimeMilliseconds()}");
201 | var delay = rng.Next(10, 300);
202 | await Task.Delay(delay);
203 |
204 | WriteToConsoleWithColor((ConsoleColor)range, $"{range} - Finished after {delay}ms");
205 |
206 | return range;
207 | })
208 | .Do(
209 | range =>
210 | {
211 | WriteToConsoleWithColor((ConsoleColor)range, $"{range} - Received Notification at {DateTimeOffset.Now.ToUnixTimeMilliseconds()}");
212 | });
213 | }
214 |
215 | public static async Task LimitConcurrencyGoodExample ()
216 | {
217 | var rng = new Random();
218 |
219 | await Observable
220 | .Range(1, 10, TaskPoolScheduler.Default)
221 | .Select(
222 | range =>
223 | Observable
224 | .DeferAsync(
225 | async ct =>
226 | {
227 | WriteToConsoleWithColor((ConsoleColor)range, $"{range} - Started at {DateTimeOffset.Now.ToUnixTimeMilliseconds()}");
228 | var delay = rng.Next(10, 300);
229 | await Task.Delay(delay);
230 |
231 | WriteToConsoleWithColor((ConsoleColor)range, $"{range} - Finished after {delay}ms");
232 |
233 | return Observable.Return(range);
234 | }))
235 | .Merge(1)
236 | .Do(
237 | range =>
238 | {
239 | WriteToConsoleWithColor((ConsoleColor)range, $"{range} - Received Notification at {DateTimeOffset.Now.ToUnixTimeMilliseconds()}");
240 | });
241 | }
242 |
243 | public static Task SchedulerExampleBad ()
244 | {
245 | var rng = new Random();
246 |
247 | var subscription = Observable
248 | .Range(1, 10)
249 | .Repeat()
250 | .Do(
251 | range =>
252 | {
253 | WriteToConsoleWithColor(
254 | (ConsoleColor)range,
255 | $"{range} - Received Notification at {DateTimeOffset.Now}");
256 | })
257 | .Subscribe();
258 |
259 | subscription.Dispose();
260 |
261 | return Task.CompletedTask;
262 | }
263 |
264 | public static Task SchedulerExampleGood ()
265 | {
266 | var rng = new Random();
267 |
268 | var subscription = Observable
269 | .Range(1, 10, TaskPoolScheduler.Default)
270 | .Repeat()
271 | .Do(
272 | range =>
273 | {
274 | WriteToConsoleWithColor(
275 | (ConsoleColor)range,
276 | $"{range} - Received Notification at {DateTimeOffset.Now}");
277 | })
278 | .Subscribe();
279 |
280 | subscription.Dispose();
281 |
282 | return Task.CompletedTask;
283 | }
284 |
285 | private static void WriteToConsoleWithColor(ConsoleColor color, string value)
286 | {
287 | lock(matlock)
288 | {
289 | Console.ForegroundColor = color;
290 | Console.WriteLine(value);
291 | }
292 | }
293 | }
294 | }
295 |
--------------------------------------------------------------------------------
/ReactiveDotNetCoreConsole/ReactiveDotNetCoreConsole.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | netcoreapp3.1
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/ReactiveExtensionExamples.sln:
--------------------------------------------------------------------------------
1 | Microsoft Visual Studio Solution File, Format Version 12.00
2 | # Visual Studio 15
3 | VisualStudioVersion = 15.0.27130.2010
4 | MinimumVisualStudioVersion = 10.0.40219.1
5 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ReactiveExtensionExamples", "ReactiveExtensionExamples\ReactiveExtensionExamples.csproj", "{B54BEC10-F397-45B0-B080-E8E0292B2667}"
6 | EndProject
7 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "iOS", "iOS\iOS.csproj", "{ECB1239A-8DAD-4E69-A4D2-D879D46491F7}"
8 | EndProject
9 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Droid", "Droid\Droid.csproj", "{3B2BD6CA-BFCA-4640-940E-624FABAEF8C7}"
10 | EndProject
11 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ReactiveDotNetCoreApi", "ReactiveDotNetCoreApi\ReactiveDotNetCoreApi.csproj", "{AFB538F3-98AD-479F-93F5-1DF2937F4D06}"
12 | EndProject
13 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Mobile", "Mobile", "{E9773837-BF86-46C1-AA2E-C1ECE465C855}"
14 | EndProject
15 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ReactiveDotNetCoreConsole", "ReactiveDotNetCoreConsole\ReactiveDotNetCoreConsole.csproj", "{0C92C783-D4CA-405C-9DA8-DF7A7B2AD562}"
16 | EndProject
17 | Global
18 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
19 | Debug|Any CPU = Debug|Any CPU
20 | Release|Any CPU = Release|Any CPU
21 | Debug|iPhoneSimulator = Debug|iPhoneSimulator
22 | Release|iPhoneSimulator = Release|iPhoneSimulator
23 | Debug|iPhone = Debug|iPhone
24 | Release|iPhone = Release|iPhone
25 | Ad-Hoc|iPhone = Ad-Hoc|iPhone
26 | AppStore|iPhone = AppStore|iPhone
27 | EndGlobalSection
28 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
29 | {3B2BD6CA-BFCA-4640-940E-624FABAEF8C7}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
30 | {3B2BD6CA-BFCA-4640-940E-624FABAEF8C7}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU
31 | {3B2BD6CA-BFCA-4640-940E-624FABAEF8C7}.AppStore|iPhone.ActiveCfg = Release|Any CPU
32 | {3B2BD6CA-BFCA-4640-940E-624FABAEF8C7}.AppStore|iPhone.Build.0 = Release|Any CPU
33 | {3B2BD6CA-BFCA-4640-940E-624FABAEF8C7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
34 | {3B2BD6CA-BFCA-4640-940E-624FABAEF8C7}.Debug|Any CPU.Build.0 = Debug|Any CPU
35 | {3B2BD6CA-BFCA-4640-940E-624FABAEF8C7}.Debug|iPhone.ActiveCfg = Debug|Any CPU
36 | {3B2BD6CA-BFCA-4640-940E-624FABAEF8C7}.Debug|iPhone.Build.0 = Debug|Any CPU
37 | {3B2BD6CA-BFCA-4640-940E-624FABAEF8C7}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
38 | {3B2BD6CA-BFCA-4640-940E-624FABAEF8C7}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
39 | {3B2BD6CA-BFCA-4640-940E-624FABAEF8C7}.Release|Any CPU.ActiveCfg = Release|Any CPU
40 | {3B2BD6CA-BFCA-4640-940E-624FABAEF8C7}.Release|Any CPU.Build.0 = Release|Any CPU
41 | {3B2BD6CA-BFCA-4640-940E-624FABAEF8C7}.Release|iPhone.ActiveCfg = Release|Any CPU
42 | {3B2BD6CA-BFCA-4640-940E-624FABAEF8C7}.Release|iPhone.Build.0 = Release|Any CPU
43 | {3B2BD6CA-BFCA-4640-940E-624FABAEF8C7}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
44 | {3B2BD6CA-BFCA-4640-940E-624FABAEF8C7}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
45 | {B54BEC10-F397-45B0-B080-E8E0292B2667}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
46 | {B54BEC10-F397-45B0-B080-E8E0292B2667}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
47 | {B54BEC10-F397-45B0-B080-E8E0292B2667}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
48 | {B54BEC10-F397-45B0-B080-E8E0292B2667}.AppStore|iPhone.Build.0 = Debug|Any CPU
49 | {B54BEC10-F397-45B0-B080-E8E0292B2667}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
50 | {B54BEC10-F397-45B0-B080-E8E0292B2667}.Debug|Any CPU.Build.0 = Debug|Any CPU
51 | {B54BEC10-F397-45B0-B080-E8E0292B2667}.Debug|iPhone.ActiveCfg = Debug|Any CPU
52 | {B54BEC10-F397-45B0-B080-E8E0292B2667}.Debug|iPhone.Build.0 = Debug|Any CPU
53 | {B54BEC10-F397-45B0-B080-E8E0292B2667}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
54 | {B54BEC10-F397-45B0-B080-E8E0292B2667}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
55 | {B54BEC10-F397-45B0-B080-E8E0292B2667}.Release|Any CPU.ActiveCfg = Release|Any CPU
56 | {B54BEC10-F397-45B0-B080-E8E0292B2667}.Release|Any CPU.Build.0 = Release|Any CPU
57 | {B54BEC10-F397-45B0-B080-E8E0292B2667}.Release|iPhone.ActiveCfg = Release|Any CPU
58 | {B54BEC10-F397-45B0-B080-E8E0292B2667}.Release|iPhone.Build.0 = Release|Any CPU
59 | {B54BEC10-F397-45B0-B080-E8E0292B2667}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
60 | {B54BEC10-F397-45B0-B080-E8E0292B2667}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
61 | {ECB1239A-8DAD-4E69-A4D2-D879D46491F7}.Ad-Hoc|iPhone.ActiveCfg = Ad-Hoc|iPhone
62 | {ECB1239A-8DAD-4E69-A4D2-D879D46491F7}.Ad-Hoc|iPhone.Build.0 = Ad-Hoc|iPhone
63 | {ECB1239A-8DAD-4E69-A4D2-D879D46491F7}.AppStore|iPhone.ActiveCfg = AppStore|iPhone
64 | {ECB1239A-8DAD-4E69-A4D2-D879D46491F7}.AppStore|iPhone.Build.0 = AppStore|iPhone
65 | {ECB1239A-8DAD-4E69-A4D2-D879D46491F7}.Debug|Any CPU.ActiveCfg = Debug|iPhoneSimulator
66 | {ECB1239A-8DAD-4E69-A4D2-D879D46491F7}.Debug|Any CPU.Build.0 = Debug|iPhoneSimulator
67 | {ECB1239A-8DAD-4E69-A4D2-D879D46491F7}.Debug|iPhone.ActiveCfg = Debug|iPhone
68 | {ECB1239A-8DAD-4E69-A4D2-D879D46491F7}.Debug|iPhone.Build.0 = Debug|iPhone
69 | {ECB1239A-8DAD-4E69-A4D2-D879D46491F7}.Debug|iPhoneSimulator.ActiveCfg = Debug|iPhoneSimulator
70 | {ECB1239A-8DAD-4E69-A4D2-D879D46491F7}.Debug|iPhoneSimulator.Build.0 = Debug|iPhoneSimulator
71 | {ECB1239A-8DAD-4E69-A4D2-D879D46491F7}.Release|Any CPU.ActiveCfg = Release|iPhoneSimulator
72 | {ECB1239A-8DAD-4E69-A4D2-D879D46491F7}.Release|Any CPU.Build.0 = Release|iPhoneSimulator
73 | {ECB1239A-8DAD-4E69-A4D2-D879D46491F7}.Release|iPhone.ActiveCfg = Release|iPhone
74 | {ECB1239A-8DAD-4E69-A4D2-D879D46491F7}.Release|iPhone.Build.0 = Release|iPhone
75 | {ECB1239A-8DAD-4E69-A4D2-D879D46491F7}.Release|iPhoneSimulator.ActiveCfg = Release|iPhoneSimulator
76 | {ECB1239A-8DAD-4E69-A4D2-D879D46491F7}.Release|iPhoneSimulator.Build.0 = Release|iPhoneSimulator
77 | {AFB538F3-98AD-479F-93F5-1DF2937F4D06}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
78 | {AFB538F3-98AD-479F-93F5-1DF2937F4D06}.Debug|Any CPU.Build.0 = Debug|Any CPU
79 | {AFB538F3-98AD-479F-93F5-1DF2937F4D06}.Release|Any CPU.ActiveCfg = Release|Any CPU
80 | {AFB538F3-98AD-479F-93F5-1DF2937F4D06}.Release|Any CPU.Build.0 = Release|Any CPU
81 | {AFB538F3-98AD-479F-93F5-1DF2937F4D06}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
82 | {AFB538F3-98AD-479F-93F5-1DF2937F4D06}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
83 | {AFB538F3-98AD-479F-93F5-1DF2937F4D06}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
84 | {AFB538F3-98AD-479F-93F5-1DF2937F4D06}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
85 | {AFB538F3-98AD-479F-93F5-1DF2937F4D06}.Debug|iPhone.ActiveCfg = Debug|Any CPU
86 | {AFB538F3-98AD-479F-93F5-1DF2937F4D06}.Debug|iPhone.Build.0 = Debug|Any CPU
87 | {AFB538F3-98AD-479F-93F5-1DF2937F4D06}.Release|iPhone.ActiveCfg = Release|Any CPU
88 | {AFB538F3-98AD-479F-93F5-1DF2937F4D06}.Release|iPhone.Build.0 = Release|Any CPU
89 | {AFB538F3-98AD-479F-93F5-1DF2937F4D06}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
90 | {AFB538F3-98AD-479F-93F5-1DF2937F4D06}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU
91 | {AFB538F3-98AD-479F-93F5-1DF2937F4D06}.AppStore|iPhone.ActiveCfg = Release|Any CPU
92 | {AFB538F3-98AD-479F-93F5-1DF2937F4D06}.AppStore|iPhone.Build.0 = Release|Any CPU
93 | {0C92C783-D4CA-405C-9DA8-DF7A7B2AD562}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
94 | {0C92C783-D4CA-405C-9DA8-DF7A7B2AD562}.Debug|Any CPU.Build.0 = Debug|Any CPU
95 | {0C92C783-D4CA-405C-9DA8-DF7A7B2AD562}.Release|Any CPU.ActiveCfg = Release|Any CPU
96 | {0C92C783-D4CA-405C-9DA8-DF7A7B2AD562}.Release|Any CPU.Build.0 = Release|Any CPU
97 | {0C92C783-D4CA-405C-9DA8-DF7A7B2AD562}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
98 | {0C92C783-D4CA-405C-9DA8-DF7A7B2AD562}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
99 | {0C92C783-D4CA-405C-9DA8-DF7A7B2AD562}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
100 | {0C92C783-D4CA-405C-9DA8-DF7A7B2AD562}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
101 | {0C92C783-D4CA-405C-9DA8-DF7A7B2AD562}.Debug|iPhone.ActiveCfg = Debug|Any CPU
102 | {0C92C783-D4CA-405C-9DA8-DF7A7B2AD562}.Debug|iPhone.Build.0 = Debug|Any CPU
103 | {0C92C783-D4CA-405C-9DA8-DF7A7B2AD562}.Release|iPhone.ActiveCfg = Release|Any CPU
104 | {0C92C783-D4CA-405C-9DA8-DF7A7B2AD562}.Release|iPhone.Build.0 = Release|Any CPU
105 | {0C92C783-D4CA-405C-9DA8-DF7A7B2AD562}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
106 | {0C92C783-D4CA-405C-9DA8-DF7A7B2AD562}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU
107 | {0C92C783-D4CA-405C-9DA8-DF7A7B2AD562}.AppStore|iPhone.ActiveCfg = Release|Any CPU
108 | {0C92C783-D4CA-405C-9DA8-DF7A7B2AD562}.AppStore|iPhone.Build.0 = Release|Any CPU
109 | EndGlobalSection
110 | GlobalSection(NestedProjects) = preSolution
111 | {3B2BD6CA-BFCA-4640-940E-624FABAEF8C7} = {E9773837-BF86-46C1-AA2E-C1ECE465C855}
112 | {ECB1239A-8DAD-4E69-A4D2-D879D46491F7} = {E9773837-BF86-46C1-AA2E-C1ECE465C855}
113 | {B54BEC10-F397-45B0-B080-E8E0292B2667} = {E9773837-BF86-46C1-AA2E-C1ECE465C855}
114 | EndGlobalSection
115 | EndGlobal
116 |
--------------------------------------------------------------------------------
/ReactiveExtensionExamples/App.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Refit;
3 | using Splat;
4 | using Xamarin.Forms;
5 |
6 | namespace ReactiveExtensionExamples
7 | {
8 | public class App : Application
9 | {
10 | public App ()
11 | {
12 | Values.Styles.Initialize ();
13 | MainPage = new UserInterface.Pages.NavigationContainerPage ();
14 | }
15 |
16 | protected override void OnStart ()
17 | {
18 | Locator
19 | .CurrentMutable
20 | .RegisterLazySingleton(
21 | () => RestService.For("https://api.duckduckgo.com"),
22 | typeof(Services.Api.IDuckDuckGoApi));
23 | }
24 |
25 | protected override void OnSleep ()
26 | {
27 | // Handle when your app sleeps
28 | }
29 |
30 | protected override void OnResume ()
31 | {
32 | // Handle when your app resumes
33 | }
34 | }
35 | }
36 |
37 |
--------------------------------------------------------------------------------
/ReactiveExtensionExamples/Extensions/IObservableExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Reactive.Linq;
3 | using System.Reactive.Disposables;
4 | using System.Threading;
5 | using Xamarin.Forms;
6 | using ReactiveUI;
7 | using System.Reactive;
8 |
9 | namespace ReactiveExtensionExamples
10 | {
11 | public static class IObservableExtensions
12 | {
13 | public static TDisposable DisposeWith (this TDisposable observable, CompositeDisposable disposables) where TDisposable : class, IDisposable
14 | {
15 | if (observable != null)
16 | disposables.Add (observable);
17 |
18 | return observable;
19 | }
20 |
21 | public static IObservable SelectUnit(this IObservable observable)
22 | {
23 | return observable.Select(x => Unit.Default);
24 | }
25 |
26 | public static IObservable IsNotNull (this IObservable observable)
27 | where TValue : class
28 | {
29 | return observable.Where(x => x != null);
30 | }
31 |
32 | public static IDisposable NavigateTo (this IObservable observable, VisualElement ve, Func createPage, bool animated = true)
33 | {
34 | return observable
35 | .ObserveOn(RxApp.TaskpoolScheduler)
36 | .Select(x => createPage(x))
37 | .ObserveOn(RxApp.MainThreadScheduler)
38 | .Do(async page => await ve.Navigation.PushAsync(page, animated))
39 | .Subscribe();
40 | }
41 | }
42 | }
43 |
44 |
--------------------------------------------------------------------------------
/ReactiveExtensionExamples/Extensions/StyleExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | using Xamarin.Forms;
4 |
5 | namespace ReactiveExtensionExamples.Extensions
6 | {
7 | public static class StyleExtensions
8 | {
9 | public static Style Extend (this Style style)
10 | {
11 | var newStyle = new Style (style.TargetType) {
12 | BasedOn = style
13 | };
14 | return newStyle;
15 | }
16 |
17 | public static Style Set (this Style style, BindableProperty property, T value)
18 | {
19 | style.Setters.Add (new Setter () { Property = property, Value = value });
20 | return style;
21 | }
22 | }
23 | }
24 |
25 |
26 |
--------------------------------------------------------------------------------
/ReactiveExtensionExamples/FodyWeavers.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/ReactiveExtensionExamples/FodyWeavers.xsd:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.
12 |
13 |
14 |
15 |
16 | A comma-separated list of error codes that can be safely ignored in assembly verification.
17 |
18 |
19 |
20 |
21 | 'false' to turn off automatic generation of the XML Schema file.
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/ReactiveExtensionExamples/Models/RssEntry.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | namespace ReactiveExtensionExamples.Models
3 | {
4 | public class RssEntry
5 | {
6 |
7 | public string Id
8 | {
9 | get;
10 | set;
11 | }
12 |
13 | public string Author
14 | {
15 | get;
16 | set;
17 | }
18 |
19 | public string Category
20 | {
21 | get;
22 | set;
23 | }
24 |
25 | public string Content
26 | {
27 | get;
28 | set;
29 | }
30 |
31 | public DateTimeOffset Updated
32 | {
33 | get;
34 | set;
35 | }
36 |
37 | public string Title
38 | {
39 | get;
40 | set;
41 | }
42 |
43 | public bool New
44 | {
45 | get;
46 | set;
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/ReactiveExtensionExamples/ReactiveExtensionExamples.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | netstandard2.1
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/ReactiveExtensionExamples/Services/Api/DuckDuckGoServiceModel.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Reactive;
3 |
4 | namespace ReactiveExtensionExamples.Services.Api
5 | {
6 | ///
7 | /// Icon for related topic(s) or external site(s)
8 | ///
9 | public class DuckDuckGoIcon
10 | {
11 | ///
12 | /// URL of icon
13 | ///
14 | public string Url { get; set; }
15 |
16 | ///
17 | /// Height of icon (px)
18 | ///
19 | public string Height { get; set; }
20 |
21 | ///
22 | /// Width of icon (px)
23 | ///
24 | public string Width { get; set; }
25 | }
26 |
27 | ///
28 | /// Individual result returned from the query
29 | ///
30 | public class DuckDuckGoQueryResult
31 | {
32 | ///
33 | /// HTML link(s) to related topic(s) or external site(s)
34 | ///
35 | public string Result { get; set; }
36 |
37 | ///
38 | /// Icon associated with related topic(s) or FirstUrl
39 | ///
40 | public DuckDuckGoIcon Icon { get; set; }
41 |
42 | ///
43 | /// First URL in Result
44 | ///
45 | public string FirstUrl { get; set; }
46 |
47 | ///
48 | /// Text from first URL
49 | ///
50 | public string Text { get; set; }
51 | }
52 |
53 | ///
54 | /// Overal results from query
55 | ///
56 | public class DuckDuckGoSearchResult
57 | {
58 | ///
59 | /// Topic summary containing HTML
60 | ///
61 | public string Abstract { get; set; }
62 |
63 | ///
64 | /// Topic summary containing no HTML
65 | ///
66 | public string AbstractText { get; set; }
67 |
68 | ///
69 | /// Type of Answer, e.g. calc, color, digest, info, ip, iploc, phone, pw, rand, regexp, unicode, upc, or zip (see goodies & tech pages for examples).
70 | ///
71 | public string AnswerType { get; set; }
72 |
73 | ///
74 | /// Name of Abstract Source
75 | ///
76 | public string AbstractSource { get; set; }
77 |
78 | ///
79 | /// Dictionary definition (may differ from Abstract)
80 | ///
81 | public string Definition { get; set; }
82 |
83 | ///
84 | /// Name of Definition source
85 | ///
86 | public string DefinitionSource { get; set; }
87 |
88 | ///
89 | /// Name of topic that goes with Abstract
90 | ///
91 | public string Heading { get; set; }
92 |
93 | ///
94 | /// Link to image that goes with Abstract
95 | ///
96 | public string Image { get; set; }
97 |
98 | ///
99 | /// Array of internal links to related topics associated with Abstract
100 | ///
101 | public List RelatedTopics { get; set; }
102 |
103 | ///
104 | /// Response category, i.e. A (article), D (disambiguation), C (category), N (name), E (exclusive), or nothing.
105 | ///
106 | public string Type { get; set; }
107 |
108 | ///
109 | /// !bang redirect URL
110 | ///
111 | public string Redirect { get; set; }
112 |
113 | ///
114 | /// Deep link to expanded definition page in DefinitionSource
115 | ///
116 | public string DefinitionUrl { get; set; }
117 |
118 | ///
119 | /// Instant answer
120 | ///
121 | public string Answer { get; set; }
122 |
123 | ///
124 | /// Array of external links associated with Abstract
125 | ///
126 | public List Results { get; set; }
127 |
128 | ///
129 | /// Deep link to the expanded topic page in AbstractSource
130 | ///
131 | public string AbstractUrl { get; set; }
132 | }
133 | }
--------------------------------------------------------------------------------
/ReactiveExtensionExamples/Services/Api/IDuckDuckGoApi.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using Refit;
7 | using System.Threading;
8 |
9 | namespace ReactiveExtensionExamples.Services.Api
10 | {
11 | public interface IDuckDuckGoApi
12 | {
13 | [Get("/?q={query}&format=json")]
14 | Task Search(string query);
15 |
16 | [Get("/?q={query}&format=json")]
17 | Task Search(string query, CancellationToken cancellationToken);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/ReactiveExtensionExamples/Services/Api/RssDownloader.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using Refit;
7 | using System.Threading;
8 | using ReactiveExtensionExamples.Models;
9 | using System.Net.Http;
10 | using System.Xml.Linq;
11 |
12 | namespace ReactiveExtensionExamples.Services.Api
13 | {
14 | public static class RssDownloader
15 | {
16 | private static readonly HttpClient client = new HttpClient();
17 |
18 | public static async Task> DownloadRss (string url, CancellationToken ct)
19 | {
20 | var rssStreamResponse = await client.GetAsync(url, ct).ConfigureAwait(false);
21 |
22 | if (!ct.IsCancellationRequested && rssStreamResponse.IsSuccessStatusCode)
23 | {
24 | var rssStream = await rssStreamResponse.Content.ReadAsStringAsync().ConfigureAwait(false);
25 |
26 | if (!ct.IsCancellationRequested)
27 | {
28 | return
29 | await Task.Run(() =>
30 | {
31 | XNamespace ns = "http://www.w3.org/2005/Atom";
32 |
33 | var entries =
34 | XDocument
35 | .Parse(rssStream)
36 | .Root
37 | .Descendants(ns + "entry");
38 |
39 | return entries
40 | .Select(entry =>
41 | new RssEntry
42 | {
43 | Id = entry?.Element(ns + "id")?.Value ?? string.Empty,
44 | Author = entry?.Element(ns + "author")?.Element(ns + "name")?.Value ?? string.Empty,
45 | Category = entry?.Element(ns + "category")?.Attribute("label")?.Value ?? string.Empty,
46 | Content = entry?.Element(ns + "content")?.Value ?? string.Empty,
47 | Updated = DateTimeOffset.Parse(entry?.Element(ns + "updated")?.Value),
48 | Title = entry?.Element(ns + "title")?.Value ?? string.Empty
49 | })
50 | .ToList();
51 | }, ct)
52 | .ConfigureAwait(false);
53 | }
54 | }
55 |
56 | return Enumerable.Empty();
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/ReactiveExtensionExamples/UserInterface/Cells/RssEntryCell.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Reactive.Linq;
3 | using ReactiveExtensionExamples.Models;
4 | using ReactiveExtensionExamples.ViewModels;
5 | using ReactiveUI;
6 | using ReactiveUI.XamForms;
7 | using Xamarin.Forms;
8 |
9 | namespace ReactiveExtensionExamples.UserInterface.Cells
10 | {
11 | class RssEntryCell : ReactiveContentView
12 | {
13 | Label newFlag;
14 |
15 | public RssEntryCell ()
16 | {
17 | var stackLayout = new StackLayout
18 | {
19 | Padding = new Thickness(8d, 0d),
20 | Spacing = 4d
21 | };
22 |
23 | newFlag = new Label
24 | {
25 | Text = "New",
26 | FontSize = Device.GetNamedSize(NamedSize.Micro, typeof(Label)),
27 | TextColor = Color.Accent,
28 | FontAttributes = FontAttributes.Italic,
29 | };
30 | stackLayout.Children.Add(newFlag);
31 |
32 | var title = new Label
33 | {
34 | FontSize = Device.GetNamedSize(NamedSize.Medium, typeof(Label)),
35 | LineBreakMode = LineBreakMode.TailTruncation,
36 | MaxLines = 3,
37 | HeightRequest = 70,
38 | };
39 | stackLayout.Children.Add(title);
40 |
41 | var category = new Label
42 | {
43 | FontSize = Device.GetNamedSize(NamedSize.Small, typeof(Label)),
44 | TextColor = Color.Gray,
45 | HeightRequest = 20,
46 | };
47 | stackLayout.Children.Add(category);
48 |
49 | var updated = new Label
50 | {
51 | FontSize = Device.GetNamedSize(NamedSize.Micro, typeof(Label)),
52 | FontAttributes = FontAttributes.Italic,
53 | TextColor = Color.Gray,
54 | HeightRequest = 20,
55 | };
56 | stackLayout.Children.Add(updated);
57 |
58 | var padding =
59 | new ContentView
60 | {
61 | BackgroundColor = Color.Silver,
62 | HeightRequest = 2,
63 | Margin = new Thickness(-8, 0),
64 | };
65 | stackLayout.Children.Add(padding);
66 |
67 | Content = stackLayout;
68 |
69 | this.WhenAnyValue(x => x.ViewModel)
70 | .IsNotNull()
71 | .ObserveOn(RxApp.MainThreadScheduler)
72 | .Do(
73 | vm =>
74 | {
75 | title.Text = vm.Title;
76 | category.Text = vm.Category;
77 | updated.Text = vm.Updated.ToLocalTime().ToString("dd MMM yyyy hh:mm tt");
78 | })
79 | .Subscribe();
80 |
81 | this.WhenAnyValue(x => x.ViewModel.New)
82 | .ObserveOn(RxApp.MainThreadScheduler)
83 | .BindTo(this, x => x.newFlag.IsVisible);
84 | }
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/ReactiveExtensionExamples/UserInterface/Pages/Async.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Reactive;
3 | using System.Reactive.Disposables;
4 | using System.Reactive.Linq;
5 | using Xamarin.Forms;
6 |
7 | namespace ReactiveExtensionExamples.UserInterface.Pages
8 | {
9 | public class Async : PageBase
10 | {
11 | Label outputLabel, calculationProgress;
12 | Button download;
13 |
14 | protected override void SetupUserInterface ()
15 | {
16 | Title = "Rx - Async";
17 |
18 | download = new Button{ Text = "Calculate" };
19 |
20 | Content = new StackLayout {
21 | Padding = new Thickness(8d),
22 | Spacing = 16d,
23 | Children = {
24 | download,
25 | (calculationProgress =
26 | new Label {
27 | HorizontalTextAlignment = TextAlignment.Center,
28 | FontAttributes = FontAttributes.Italic,
29 | Text = "Next Value: "
30 | }
31 | ),
32 | (outputLabel = new Label { HorizontalTextAlignment = TextAlignment.Center, Text = "Calculation Result" }),
33 |
34 | }
35 | };
36 | }
37 |
38 | protected override void SetupReactiveExtensions ()
39 | {
40 | var random = new Random(DateTime.Now.Millisecond);
41 |
42 | var calculationObservable =
43 | Observable
44 | .Interval(TimeSpan.FromMilliseconds(random.Next(100, 300)))
45 | .Zip(
46 | Observable.Range(random.Next(1, 5), random.Next(2, 7)),
47 | (t, r) => (long)r
48 | )
49 | .Scan((previous, current) => previous * current * (long)(random.Next(1, 35)))
50 | .Do(val => Device.BeginInvokeOnMainThread(() => calculationProgress.Text = string.Format("Next Value: {0}", val)));
51 |
52 | Observable
53 | .FromEventPattern (x => download.Clicked += x, x => download.Clicked -= x)
54 | .Subscribe (async args => {
55 | try {
56 | Device.BeginInvokeOnMainThread(() => {
57 | download.IsEnabled = false;
58 | outputLabel.Text = "Starting Calculation";
59 | });
60 |
61 | var result = await calculationObservable;
62 |
63 | Device.BeginInvokeOnMainThread(() =>
64 | outputLabel.Text = string.Format("Calculation Complete: {0}", result)
65 | );
66 |
67 | } finally {
68 | Device.BeginInvokeOnMainThread(() =>
69 | download.IsEnabled = true
70 | );
71 | }
72 | })
73 | .DisposeWith(SubscriptionDisposables);
74 | }
75 | }
76 | }
77 |
78 |
79 |
--------------------------------------------------------------------------------
/ReactiveExtensionExamples/UserInterface/Pages/AsyncEvent.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Reactive;
3 | using System.Reactive.Disposables;
4 | using System.Reactive.Linq;
5 | using ReactiveUI;
6 | using Xamarin.Forms;
7 |
8 | namespace ReactiveExtensionExamples.UserInterface.Pages
9 | {
10 | public class AsyncEvent : PageBase
11 | {
12 | Label outputLabel;
13 | Button calculate, stop;
14 |
15 | IDisposable calculationSubscription;
16 |
17 | protected override void SetupUserInterface ()
18 | {
19 | Title = "Rx - Async Events";
20 |
21 | calculate = new Button{ Text = "Calculate" };
22 |
23 | stop = new Button{ Text = "STOP" };
24 |
25 | Content = new StackLayout {
26 | Padding = new Thickness(8d),
27 | Spacing = 16d,
28 | Children = {
29 | calculate,
30 | stop,
31 | (outputLabel = new Label {
32 | HorizontalTextAlignment = TextAlignment.Center,
33 | Text = "Let's calcuNOW, not calcuLATEr"
34 | })
35 | }
36 | };
37 | }
38 |
39 | protected override void SetupReactiveExtensions ()
40 | {
41 | var stopClickedObservable =
42 | Observable
43 | .FromEventPattern (x => stop.Clicked += x, x => stop.Clicked -= x)
44 | .Do(args => System.Diagnostics.Debug.WriteLine("Button 2 Clicked"))
45 | .FirstAsync ();
46 |
47 | var calculationObservable =
48 | Observable
49 | .Interval (TimeSpan.FromMilliseconds (250))
50 | .Do (val => System.Diagnostics.Debug.WriteLine ("Next Value: {0}", val))
51 | .Scan ((previous, current) => previous + current);
52 |
53 | Observable
54 | .FromEventPattern (x => calculate.Clicked += x, x => calculate.Clicked -= x)
55 | .Subscribe (async args => {
56 | try {
57 | Device.BeginInvokeOnMainThread(() => calculate.IsEnabled = false);
58 |
59 | //Start Calculating
60 | calculationSubscription =
61 | calculationObservable
62 | .Subscribe(val =>
63 | Device.BeginInvokeOnMainThread(() =>
64 | outputLabel.Text = string.Format("Calculation Value: {0}", val)
65 | )
66 | );
67 |
68 | //This will only get the first click of the button after we start listening
69 | await stopClickedObservable;
70 |
71 | calculationSubscription?.Dispose();
72 |
73 | Device.BeginInvokeOnMainThread(() =>
74 | outputLabel.Text = string.Format("Clicked Stop at " + DateTime.Now)
75 | );
76 | } finally {
77 | Device.BeginInvokeOnMainThread(() => calculate.IsEnabled = true);
78 | }
79 | })
80 | .DisposeWith(SubscriptionDisposables);
81 | }
82 |
83 | protected override void OnDisappearing ()
84 | {
85 | base.OnDisappearing ();
86 |
87 | calculationSubscription?.Dispose();
88 | }
89 | }
90 | }
91 |
92 |
93 |
--------------------------------------------------------------------------------
/ReactiveExtensionExamples/UserInterface/Pages/AsyncToObservable.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Reactive;
3 | using System.Reactive.Disposables;
4 | using System.Reactive.Linq;
5 | using System.Threading.Tasks;
6 | using Xamarin.Forms;
7 |
8 | namespace ReactiveExtensionExamples.UserInterface.Pages
9 | {
10 | public class AsyncToObservable : PageBase
11 | {
12 | Label outputLabel;
13 | Button button1;
14 | ActivityIndicator loading;
15 |
16 | protected override void SetupUserInterface ()
17 | {
18 | Title = "Rx - Async to Observable";
19 |
20 | button1 = new Button{ Text = "Call Service" };
21 |
22 | loading = new ActivityIndicator { };
23 |
24 | Content = new StackLayout {
25 | Padding = new Thickness(8d),
26 | Spacing = 16d,
27 | Children = {
28 | button1,
29 | (outputLabel = new Label { HorizontalTextAlignment = TextAlignment.Center, Text = "" }),
30 | loading
31 | }
32 | };
33 | }
34 |
35 | protected override void SetupReactiveExtensions ()
36 | {
37 | Observable
38 | .FromEventPattern (x => button1.Clicked += x, x => button1.Clicked -= x)
39 | .Subscribe (async args => {
40 |
41 | Device.BeginInvokeOnMainThread(() => {
42 | outputLabel.TextColor = Color.Black;
43 | outputLabel.Text = "Starting Calculation";
44 | loading.IsRunning = true;
45 | });
46 |
47 | try {
48 | var result =
49 | await Observable
50 | .FromAsync(() => PerformCalculationAsync())
51 | .Timeout(TimeSpan.FromMilliseconds(300))
52 | .Retry(5)
53 | .Catch(tex => Observable.Return(-1))
54 | .Catch(ex => Observable.Return(-100));
55 |
56 | Device.BeginInvokeOnMainThread(() => {
57 | outputLabel.Text =
58 | result >= 0
59 | ? string.Format("Calculation Complete: {0}", result)
60 | : result == -100
61 | ? "Listen, things went really bad." + Environment.NewLine + "Reconsider your life choices"
62 | : "Bummer, it looks like your calculation failed";
63 |
64 | if(result < 0)
65 | outputLabel.TextColor = Color.Red;
66 | });
67 | } finally {
68 | Device.BeginInvokeOnMainThread(() => loading.IsRunning = false);
69 | }
70 | })
71 | .DisposeWith(SubscriptionDisposables);
72 | }
73 |
74 | //Imagine this is a faux web service or similar
75 | async Task PerformCalculationAsync (){
76 | var random = new Random (DateTime.Now.Millisecond);
77 |
78 | var delayTime = random.Next (150, 500);
79 |
80 | System.Diagnostics.Debug.WriteLine ("Delaying {0}", delayTime);
81 |
82 | await Task.Delay (delayTime);
83 |
84 | var calcedValue = random.Next (1, 10);
85 |
86 | System.Diagnostics.Debug.WriteLine ("Calced Value {0}", calcedValue);
87 |
88 | if (calcedValue % 2 == 0)
89 | throw new Exception ("Even number are not allowed");
90 |
91 | return calcedValue;
92 | }
93 | }
94 | }
95 |
96 |
97 |
--------------------------------------------------------------------------------
/ReactiveExtensionExamples/UserInterface/Pages/Buffer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using System.Reactive.Concurrency;
4 | using System.Reactive.Disposables;
5 | using System.Reactive.Linq;
6 | using Xamarin.Forms;
7 |
8 | namespace ReactiveExtensionExamples.UserInterface.Pages
9 | {
10 | public class Buffer : PageBase
11 | {
12 | Entry textEntry;
13 | StackLayout lastEntries;
14 |
15 | protected override void SetupUserInterface ()
16 | {
17 | Title = "Rx - Buffer";
18 |
19 | Content = new StackLayout {
20 | Padding = new Thickness(8d),
21 | Spacing = 16d,
22 | Children = {
23 | (textEntry = new Entry{ Placeholder = "Enter Some Text" }),
24 | new ScrollView {
25 | VerticalOptions = LayoutOptions.FillAndExpand, HorizontalOptions = LayoutOptions.FillAndExpand,
26 | Content = (lastEntries = new StackLayout{
27 | VerticalOptions = LayoutOptions.FillAndExpand, HorizontalOptions = LayoutOptions.FillAndExpand
28 | })
29 | }
30 | }
31 | };
32 | }
33 |
34 | protected override void SetupReactiveExtensions ()
35 | {
36 | Observable
37 | .FromEventPattern, TextChangedEventArgs> (
38 | x => textEntry.TextChanged += x,
39 | x => textEntry.TextChanged -= x)
40 | .Buffer (TimeSpan.FromSeconds (3), TaskPoolScheduler.Default)
41 | .Select(argsList =>
42 | string.Join(
43 | Environment.NewLine,
44 | argsList.Select(args => args.EventArgs.NewTextValue).Reverse().ToList()
45 | )
46 | )
47 | .Subscribe (text => {
48 | Device.BeginInvokeOnMainThread(() => {
49 | lastEntries.Children.Insert(
50 | 0,
51 | new Label { Text = text }
52 | );
53 |
54 | lastEntries.Children
55 | .Insert(
56 | 1,
57 | new Label {
58 | Text = string.Format("Received at {0:H:mm:ss}", DateTime.Now),
59 | FontAttributes = FontAttributes.Italic,
60 | FontSize = Device.GetNamedSize(NamedSize.Micro, typeof(Label)),
61 | TextColor = Color.Gray
62 | });
63 |
64 | lastEntries.Children
65 | .Insert(
66 | 2,
67 | new BoxView { BackgroundColor = Color.Gray, HeightRequest = 2d });
68 | });
69 | })
70 | .DisposeWith(SubscriptionDisposables);
71 | }
72 | }
73 | }
74 |
75 |
76 |
--------------------------------------------------------------------------------
/ReactiveExtensionExamples/UserInterface/Pages/BufferWithWhere.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using System.Reactive.Concurrency;
4 | using System.Reactive.Disposables;
5 | using System.Reactive.Linq;
6 | using Xamarin.Forms;
7 |
8 | namespace ReactiveExtensionExamples.UserInterface.Pages
9 | {
10 | public class BufferWithWhere : PageBase
11 | {
12 | Entry textEntry;
13 | StackLayout lastEntries;
14 |
15 | protected override void SetupUserInterface ()
16 | {
17 | Title = "Rx - Buffer with Filtering Where";
18 |
19 | Content = new StackLayout {
20 | Padding = new Thickness(8d),
21 | Spacing = 16d,
22 | Children = {
23 | (textEntry = new Entry{ Placeholder = "Enter Some Text" }),
24 | new ScrollView {
25 | VerticalOptions = LayoutOptions.FillAndExpand, HorizontalOptions = LayoutOptions.FillAndExpand,
26 | Content = (lastEntries = new StackLayout{
27 | VerticalOptions = LayoutOptions.FillAndExpand, HorizontalOptions = LayoutOptions.FillAndExpand
28 | })
29 | }
30 | }
31 | };
32 | }
33 |
34 | protected override void SetupReactiveExtensions ()
35 | {
36 | Observable
37 | .FromEventPattern, TextChangedEventArgs> (
38 | x => textEntry.TextChanged += x,
39 | x => textEntry.TextChanged -= x
40 | )
41 | .Buffer (TimeSpan.FromSeconds (3), TaskPoolScheduler.Default)
42 | .Where (argsList => argsList?.Any () ?? false)
43 | .Select(argsList =>
44 | string.Join(
45 | Environment.NewLine,
46 | argsList.Select(args => args.EventArgs.NewTextValue).Reverse().ToList()
47 | ))
48 | .Subscribe (text => {
49 | Device.BeginInvokeOnMainThread(() => {
50 | lastEntries.Children.Insert(
51 | 0,
52 | new Label { Text = text }
53 | );
54 |
55 | lastEntries.Children
56 | .Insert(
57 | 1,
58 | new Label {
59 | Text = string.Format("Received at {0:H:mm:ss}", DateTime.Now),
60 | FontAttributes = FontAttributes.Italic,
61 | FontSize = Device.GetNamedSize(NamedSize.Micro, typeof(Label)),
62 | TextColor = Color.Gray
63 | });
64 |
65 | lastEntries.Children
66 | .Insert(
67 | 2,
68 | new BoxView { BackgroundColor = Color.Gray, HeightRequest = 2d });
69 | });
70 | })
71 | .DisposeWith(SubscriptionDisposables);
72 | }
73 | }
74 | }
75 |
76 |
77 |
--------------------------------------------------------------------------------
/ReactiveExtensionExamples/UserInterface/Pages/CombineLatest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using System.Reactive.Disposables;
4 | using System.Reactive.Linq;
5 | using Xamarin.Forms;
6 |
7 | namespace ReactiveExtensionExamples.UserInterface.Pages
8 | {
9 | public class CombineLatest: PageBase
10 | {
11 | BoxView colorDisplay;
12 |
13 | Slider red, green, blue;
14 |
15 | public CombineLatest ()
16 | {
17 | Title = "Combine Latest";
18 |
19 | Content = new StackLayout {
20 | Padding = new Thickness(40d),
21 | Children = {
22 | (colorDisplay = new BoxView{ HeightRequest = 250 }),
23 |
24 | new Label{ Text = "Red"},
25 | (red = new Slider(0, 255, 0)),
26 |
27 | new Label{ Text = "Green"},
28 | (green = new Slider(0, 255, 0)),
29 |
30 | new Label{ Text = "Blue"},
31 | (blue = new Slider(0, 255, 0))
32 | }
33 | };
34 | }
35 |
36 |
37 | protected override void SetupReactiveExtensions ()
38 | {
39 | base.SetupReactiveExtensions ();
40 |
41 | Observable
42 | .CombineLatest (
43 | red
44 | .Events()
45 | .ValueChanged
46 | .Select(result => (int)result.NewValue),
47 | green
48 | .Events()
49 | .ValueChanged
50 | .Select(result => (int)result.NewValue)
51 | .StartWith(0),
52 | blue
53 | .Events()
54 | .ValueChanged
55 | .Select(result => (int)result.NewValue)
56 | .StartWith(0),
57 | (r, g, b) => Color.FromRgb (r, g, b)
58 | )
59 | .Do(x => System.Diagnostics.Debug.WriteLine($"current color: {x}"))
60 | .Subscribe (color => {
61 | Device.BeginInvokeOnMainThread (() => colorDisplay.BackgroundColor = color);
62 | })
63 | .DisposeWith (SubscriptionDisposables);
64 | }
65 | }
66 | }
67 |
68 |
69 |
--------------------------------------------------------------------------------
/ReactiveExtensionExamples/UserInterface/Pages/Delay.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Reactive.Disposables;
3 | using System.Reactive.Linq;
4 | using Xamarin.Forms;
5 |
6 | namespace ReactiveExtensionExamples.UserInterface.Pages
7 | {
8 | public class Delay : PageBase
9 | {
10 | Entry textEntry;
11 | StackLayout lastEntries;
12 |
13 | protected override void SetupUserInterface ()
14 | {
15 | Title = "Rx - Delay";
16 |
17 | Content = new StackLayout {
18 | Padding = new Thickness(8d),
19 | Spacing = 16d,
20 | Children = {
21 | (textEntry = new Entry{ Placeholder = "Enter Some Text" }),
22 | new ScrollView {
23 | VerticalOptions = LayoutOptions.FillAndExpand, HorizontalOptions = LayoutOptions.FillAndExpand,
24 | Content = (lastEntries = new StackLayout{
25 | VerticalOptions = LayoutOptions.FillAndExpand, HorizontalOptions = LayoutOptions.FillAndExpand
26 | })
27 | }
28 | }
29 | };
30 | }
31 |
32 | protected override void SetupReactiveExtensions ()
33 | {
34 | Observable
35 | .FromEventPattern, TextChangedEventArgs> (
36 | x => textEntry.TextChanged += x,
37 | x => textEntry.TextChanged -= x)
38 | //MTS : The PM said 3 seconds was a great delay
39 | .Delay (TimeSpan.FromSeconds (3))
40 |
41 | .Select(args => args.EventArgs.NewTextValue)
42 | .StartWith(string.Empty)
43 | .Subscribe(text => {
44 | Device.BeginInvokeOnMainThread(() => {
45 | lastEntries.Children
46 | .Insert(
47 | 0,
48 | new Label { Text = text });
49 | lastEntries.Children
50 | .Insert(
51 | 1,
52 | new Label
53 | {
54 | Text = string.Format("Received at {0:H:mm:ss}", DateTime.Now),
55 | FontAttributes = FontAttributes.Italic,
56 | FontSize = Device.GetNamedSize(NamedSize.Micro, typeof(Label)),
57 | TextColor = Color.Gray
58 | });
59 | lastEntries.Children
60 | .Insert(
61 | 2,
62 | new BoxView { BackgroundColor = Color.Gray, HeightRequest = 2d });
63 | });
64 | })
65 | .DisposeWith(SubscriptionDisposables);
66 | }
67 | }
68 | }
69 |
70 |
71 |
--------------------------------------------------------------------------------
/ReactiveExtensionExamples/UserInterface/Pages/DynamicData/DynamicDataDashboard.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Xamarin.Forms;
3 |
4 | namespace ReactiveExtensionExamples.UserInterface.Pages.DynamicData
5 | {
6 | public class DynamicDataDashboard : PageBase
7 | {
8 | ScrollView _mainScroll;
9 |
10 | StackLayout _mainLayout;
11 |
12 | Button _simpleDynamicData, _filterDynamicData, _sortDynamicData, _sourceCacheDynamicData;
13 |
14 | public DynamicDataDashboard ()
15 | {
16 | }
17 |
18 | protected override void SetupUserInterface ()
19 | {
20 | _simpleDynamicData =
21 | new Button
22 | {
23 | Text = "Simple"
24 | };
25 |
26 | _filterDynamicData =
27 | new Button
28 | {
29 | Text = "Filter"
30 | };
31 |
32 | _sortDynamicData =
33 | new Button
34 | {
35 | Text = "Sort"
36 | };
37 |
38 | _sourceCacheDynamicData =
39 | new Button
40 | {
41 | Text = "Source Cache"
42 | };
43 |
44 | _mainLayout =
45 | new StackLayout
46 | {
47 | Children =
48 | {
49 | _simpleDynamicData,
50 | _filterDynamicData,
51 | _sortDynamicData,
52 | _sourceCacheDynamicData,
53 | }
54 | };
55 |
56 | _mainScroll =
57 | new ScrollView
58 | {
59 | Content = _mainLayout,
60 | };
61 |
62 | Content = _mainScroll;
63 | }
64 |
65 | protected override void SetupReactiveExtensions ()
66 | {
67 | _simpleDynamicData
68 | .Events()
69 | .Clicked
70 | .NavigateTo(
71 | this,
72 | _ => new SimpleDynamicData())
73 | .DisposeWith(SubscriptionDisposables);
74 |
75 | _filterDynamicData
76 | .Events()
77 | .Clicked
78 | .NavigateTo(
79 | this,
80 | _ => new FilterDynamicData())
81 | .DisposeWith(SubscriptionDisposables);
82 |
83 | _sortDynamicData
84 | .Events()
85 | .Clicked
86 | .NavigateTo(
87 | this,
88 | _ => new SortDynamicData())
89 | .DisposeWith(SubscriptionDisposables);
90 |
91 | _sourceCacheDynamicData
92 | .Events()
93 | .Clicked
94 | .NavigateTo(
95 | this,
96 | _ => new SourceCacheDynamicData())
97 | .DisposeWith(SubscriptionDisposables);
98 | }
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/ReactiveExtensionExamples/UserInterface/Pages/DynamicData/FilterDynamicData.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Reactive;
3 | using System.Reactive.Disposables;
4 | using System.Reactive.Linq;
5 | using ReactiveUI;
6 | using ReactiveUI.XamForms;
7 | using Xamarin.Forms;
8 | using System.Runtime.InteropServices;
9 |
10 | namespace ReactiveExtensionExamples.UserInterface.Pages.DynamicData
11 | {
12 | public class FilterDynamicData : ReactiveContentPage
13 | {
14 | Entry textEntry;
15 | CollectionView searchResults;
16 | Button search;
17 | ActivityIndicator _loading;
18 | SerialDisposable _searchErrorDisposable;
19 |
20 | public FilterDynamicData () {
21 | Title = "Rx - Filter Dynamic Data";
22 |
23 | this.ViewModel = new ViewModels.DynamicData.FilterDynamicDataViewModel();
24 |
25 | Content = new StackLayout
26 | {
27 | Padding = new Thickness(8d),
28 | Children = {
29 | (textEntry = new Entry{ Placeholder = "Enter Search Terms" }),
30 | (search = new Button{ Text = "Search" }),
31 | (_loading = new ActivityIndicator{}),
32 | (searchResults = new CollectionView() {
33 | VerticalOptions = LayoutOptions.FillAndExpand,
34 | HorizontalOptions = LayoutOptions.FillAndExpand,
35 | ItemTemplate = new DataTemplate(typeof(Cells.RssEntryCell)),
36 | ItemSizingStrategy = ItemSizingStrategy.MeasureFirstItem,
37 | })
38 | }
39 | };
40 |
41 | this.WhenActivated((CompositeDisposable disposables) =>
42 | {
43 | this.Bind(ViewModel, x => x.SearchQuery, c => c.textEntry.Text)
44 | .DisposeWith(disposables);
45 |
46 | this.BindCommand(ViewModel, x => x.Search, c => c.search, this.WhenAnyValue(x => x.ViewModel.SearchQuery))
47 | .DisposeWith(disposables);
48 |
49 | Observable
50 | .CombineLatest(
51 | this.WhenAnyValue(x => x.ViewModel.SearchQuery)
52 | .Select(x => !string.IsNullOrEmpty(x))
53 | .DistinctUntilChanged(),
54 | this.WhenAnyValue(x => x.ViewModel.ResultCount),
55 | (hasSearch, resultCount) => hasSearch ? $"{resultCount} Matches" : "Search")
56 | .ObserveOn(RxApp.MainThreadScheduler)
57 | .BindTo(this, x => x.search.Text)
58 | .DisposeWith(disposables);
59 |
60 | //This is a one-way bind
61 | this.OneWayBind(ViewModel, x => x.SearchResults, c => c.searchResults.ItemsSource)
62 | .DisposeWith(disposables);
63 |
64 | this.WhenAnyValue(x => x.ViewModel)
65 | .Where(x => x != null)
66 | .SelectUnit()
67 | .InvokeCommand(this, x => x.ViewModel.Search)
68 | .DisposeWith(disposables);
69 | });
70 | }
71 | }
72 | }
73 |
74 |
75 |
--------------------------------------------------------------------------------
/ReactiveExtensionExamples/UserInterface/Pages/DynamicData/SimpleDynamicData.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Reactive;
3 | using System.Reactive.Disposables;
4 | using System.Reactive.Linq;
5 | using ReactiveUI;
6 | using ReactiveUI.XamForms;
7 | using Xamarin.Forms;
8 | using System.Runtime.InteropServices;
9 |
10 | namespace ReactiveExtensionExamples.UserInterface.Pages.DynamicData
11 | {
12 | public class SimpleDynamicData : ReactiveContentPage
13 | {
14 | CollectionView searchResults;
15 | Button search;
16 | ActivityIndicator _loading;
17 | SerialDisposable _searchErrorDisposable;
18 |
19 | public SimpleDynamicData () {
20 | Title = "Rx - Simple Dynamic Data";
21 |
22 | this.ViewModel = new ViewModels.DynamicData.SimpleDynamicDataViewModel();
23 |
24 | Content = new StackLayout
25 | {
26 | Padding = new Thickness(8d),
27 | Children = {
28 | (search = new Button{ Text = "Search" }),
29 | (_loading = new ActivityIndicator{}),
30 | (searchResults = new CollectionView() {
31 | VerticalOptions = LayoutOptions.FillAndExpand,
32 | HorizontalOptions = LayoutOptions.FillAndExpand,
33 | ItemTemplate = new DataTemplate(typeof(Cells.RssEntryCell)),
34 | ItemSizingStrategy = ItemSizingStrategy.MeasureFirstItem,
35 | })
36 | }
37 | };
38 |
39 | this.WhenActivated((CompositeDisposable disposables) =>
40 | {
41 |
42 | //This is a one-way bind
43 | this.OneWayBind(ViewModel, x => x.SearchResults, c => c.searchResults.ItemsSource)
44 | .DisposeWith(disposables);
45 |
46 | this.BindCommand(ViewModel, x => x.Search, c => c.search)//this.WhenAnyValue(x => x.ViewModel.SearchQuery)
47 | .DisposeWith(disposables);
48 |
49 | this.WhenAnyValue(x => x.ViewModel)
50 | .Where(x => x != null)
51 | .SelectUnit()
52 | .InvokeCommand(this, x => x.ViewModel.Search)
53 | .DisposeWith(disposables);
54 | });
55 | }
56 | }
57 | }
58 |
59 |
60 |
--------------------------------------------------------------------------------
/ReactiveExtensionExamples/UserInterface/Pages/DynamicData/SortDynamicData.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Reactive;
3 | using System.Reactive.Disposables;
4 | using System.Reactive.Linq;
5 | using ReactiveUI;
6 | using ReactiveUI.XamForms;
7 | using Xamarin.Forms;
8 | using System.Runtime.InteropServices;
9 | using ReactiveExtensionExamples.ViewModels.DynamicData;
10 |
11 | namespace ReactiveExtensionExamples.UserInterface.Pages.DynamicData
12 | {
13 | public class SortDynamicData : ReactiveContentPage
14 | {
15 | Button sortUpdatedAscending, sortUpdatedDescending, sortTitleAscending, sortTitleDescending;
16 | CollectionView searchResults;
17 | ActivityIndicator _loading;
18 | SerialDisposable _searchErrorDisposable;
19 |
20 | public SortDynamicData () {
21 | Title = "Rx - Sort Dynamic Data";
22 |
23 | this.ViewModel = new ViewModels.DynamicData.SortDynamicDataViewModel();
24 |
25 | Content = new StackLayout
26 | {
27 | Padding = new Thickness(8d),
28 | Children = {
29 | (sortTitleAscending = new Button{ Text = "Title - Ascending" }),
30 | (sortTitleDescending = new Button{ Text = "Title - Descending" }),
31 | (sortUpdatedAscending = new Button{ Text = "Updated - Ascending" }),
32 | (sortUpdatedDescending = new Button{ Text = "Updated - Descending" }),
33 | (_loading = new ActivityIndicator{}),
34 | (searchResults = new CollectionView() {
35 | VerticalOptions = LayoutOptions.FillAndExpand,
36 | HorizontalOptions = LayoutOptions.FillAndExpand,
37 | ItemTemplate = new DataTemplate(typeof(Cells.RssEntryCell)),
38 | ItemSizingStrategy = ItemSizingStrategy.MeasureFirstItem,
39 | })
40 | }
41 | };
42 |
43 | this.WhenActivated((CompositeDisposable disposables) =>
44 | {
45 | Observable
46 | .Merge(
47 | sortTitleAscending.Events().Clicked.Select(_ => SortType.TitleAscending),
48 | sortTitleDescending.Events().Clicked.Select(_ => SortType.TitleDescending),
49 | sortUpdatedAscending.Events().Clicked.Select(_ => SortType.DateTimeAscending),
50 | sortUpdatedDescending.Events().Clicked.Select(_ => SortType.DateTimeDescending))
51 | .Do(x => this.ViewModel.SelectedSortType = x)
52 | .Subscribe()
53 | .DisposeWith(disposables);
54 |
55 | //This is a one-way bind
56 | this.OneWayBind(ViewModel, x => x.SearchResults, c => c.searchResults.ItemsSource)
57 | .DisposeWith(disposables);
58 |
59 | this.WhenAnyValue(x => x.ViewModel)
60 | .Where(x => x != null)
61 | .SelectUnit()
62 | .InvokeCommand(this, x => x.ViewModel.Search)
63 | .DisposeWith(disposables);
64 | });
65 | }
66 | }
67 | }
68 |
69 |
70 |
--------------------------------------------------------------------------------
/ReactiveExtensionExamples/UserInterface/Pages/DynamicData/SourceCacheDynamicData.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Reactive;
3 | using System.Reactive.Disposables;
4 | using System.Reactive.Linq;
5 | using ReactiveUI;
6 | using ReactiveUI.XamForms;
7 | using Xamarin.Forms;
8 | using System.Runtime.InteropServices;
9 |
10 | namespace ReactiveExtensionExamples.UserInterface.Pages.DynamicData
11 | {
12 | public class SourceCacheDynamicData : ReactiveContentPage
13 | {
14 | CollectionView searchResults;
15 | ActivityIndicator _loading;
16 | SerialDisposable _searchErrorDisposable;
17 |
18 | public SourceCacheDynamicData () {
19 | Title = "Rx - Source Cache Dynamic Data";
20 |
21 | this.ViewModel = new ViewModels.DynamicData.SourceCacheDynamicDataViewModel();
22 |
23 | Content = new StackLayout
24 | {
25 | Padding = new Thickness(8d),
26 | Children = {
27 | (_loading = new ActivityIndicator{}),
28 | (searchResults = new CollectionView() {
29 | VerticalOptions = LayoutOptions.FillAndExpand,
30 | HorizontalOptions = LayoutOptions.FillAndExpand,
31 | ItemTemplate = new DataTemplate(typeof(Cells.RssEntryCell)),
32 | ItemSizingStrategy = ItemSizingStrategy.MeasureFirstItem,
33 | ItemsUpdatingScrollMode = ItemsUpdatingScrollMode.KeepScrollOffset,
34 | })
35 | }
36 | };
37 |
38 | this.WhenActivated((CompositeDisposable disposables) =>
39 | {
40 | //This is a one-way bind
41 | this.OneWayBind(ViewModel, x => x.SearchResults, c => c.searchResults.ItemsSource)
42 | .DisposeWith(disposables);
43 |
44 | this.WhenAnyValue(x => x.ViewModel)
45 | .Where(x => x != null)
46 | .SelectUnit()
47 | .InvokeCommand(this, x => x.ViewModel.Search)
48 | .DisposeWith(disposables);
49 | });
50 | }
51 | }
52 | }
53 |
54 |
55 |
--------------------------------------------------------------------------------
/ReactiveExtensionExamples/UserInterface/Pages/Merge.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Xamarin.Forms;
3 | using System.Reactive.Linq;
4 | using System.Reactive;
5 | using System.Reactive.Disposables;
6 |
7 | namespace ReactiveExtensionExamples.UserInterface.Pages
8 | {
9 | public class Merge : PageBase
10 | {
11 | Label outputLabel;
12 | Button button1, button2, button3, button4;
13 |
14 | protected override void SetupUserInterface ()
15 | {
16 | Title = "Rx - Merge";
17 |
18 | button1 = new Button{ Text = "Peter Venkman" };
19 |
20 | button2 = new Button{ Text = "Ray Stantz" };
21 |
22 | button3 = new Button{ Text = "Egon Spengler" };
23 |
24 | button4 = new Button{ Text = "Winston Zeddemore" };
25 |
26 | Content = new StackLayout {
27 | Padding = new Thickness(8d),
28 | Spacing = 16d,
29 | Children = {
30 | button1,
31 | button2,
32 | button3,
33 | button4,
34 | (outputLabel =
35 | new Label {
36 | HorizontalTextAlignment = TextAlignment.Center,
37 | Text = "Whoa, It's Slimer! Blast 'em!"
38 | }
39 | ),
40 |
41 | }
42 | };
43 |
44 |
45 | }
46 |
47 | protected override void SetupReactiveExtensions ()
48 | {
49 | Observable
50 | .Merge(
51 | Observable
52 | .FromEventPattern (
53 | x => button1.Clicked += x,
54 | x => button1.Clicked -= x),
55 | Observable
56 | .FromEventPattern (
57 | x => button2.Clicked += x,
58 | x => button2.Clicked -= x),
59 | Observable
60 | .FromEventPattern (
61 | x => button3.Clicked += x,
62 | x => button3.Clicked -= x),
63 | Observable
64 | .FromEventPattern (
65 | x => button4.Clicked += x,
66 | x => button4.Clicked -= x))
67 | .Select (args =>
68 | string.Format ("Who merged the Streams?{0}{1}",
69 | Environment.NewLine, ((Button)args.Sender).Text))
70 | .Subscribe (text => Device.BeginInvokeOnMainThread(() => outputLabel.Text = text))
71 | .DisposeWith(SubscriptionDisposables);
72 |
73 | }
74 | }
75 | }
76 |
77 |
78 |
--------------------------------------------------------------------------------
/ReactiveExtensionExamples/UserInterface/Pages/NavigationContainerPage.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Xamarin.Forms;
3 | using System.Reactive.Subjects;
4 | using System.Reactive.Linq;
5 | using ReactiveUI;
6 |
7 | namespace ReactiveExtensionExamples.UserInterface.Pages
8 | {
9 | public class NavigationContainerPage : MasterDetailPage
10 | {
11 | readonly NavListPage navListPage = new NavListPage();
12 |
13 | public NavigationContainerPage ()
14 | {
15 | var primaryNavPage = new NavigationPage(navListPage){
16 | Title = "Reactive Examples",
17 | };
18 |
19 | Master = primaryNavPage;
20 |
21 | this.MasterBehavior = MasterBehavior.Popover;
22 | this.IsPresented = true;
23 |
24 | navListPage
25 | .SelectedNavigation
26 | .ObserveOn (RxApp.MainThreadScheduler)
27 | .StartWith(NavigationPages.Delay)
28 | .Subscribe (navPage => {
29 |
30 | Page selectedPage = null;
31 | switch (navPage) {
32 | case NavigationPages.Async:
33 | selectedPage = new Pages.Async();
34 | break;
35 | case NavigationPages.AsyncEvents:
36 | selectedPage = new Pages.AsyncEvent();
37 | break;
38 | case NavigationPages.Delay:
39 | selectedPage = new Pages.Delay();
40 | break;
41 | case NavigationPages.Throttle:
42 | selectedPage = new Pages.Throttle();
43 | break;
44 | case NavigationPages.Buffer:
45 | selectedPage = new Pages.Buffer();
46 | break;
47 | case NavigationPages.BufferWithWhere:
48 | selectedPage = new Pages.BufferWithWhere();
49 | break;
50 | case NavigationPages.Merge:
51 | selectedPage = new Pages.Merge();
52 | break;
53 | case NavigationPages.Sample:
54 | selectedPage = new Pages.Sample();
55 | break;
56 | case NavigationPages.Scan:
57 | selectedPage = new Pages.Scan();
58 | break;
59 | case NavigationPages.AsyncToObservable:
60 | selectedPage = new Pages.AsyncToObservable ();
61 | break;
62 | case NavigationPages.CombineLatest:
63 | selectedPage = new Pages.CombineLatest ();
64 | break;
65 | case NavigationPages.StandardSearch:
66 | selectedPage = new Pages.StandardSearch();
67 | break;
68 | case NavigationPages.DynamicDataDashboard:
69 | selectedPage = new Pages.DynamicData.DynamicDataDashboard();
70 | break;
71 | case NavigationPages.SearchWithReactiveExtensions:
72 | selectedPage = new Pages.SearchWithReactiveExtensions();
73 | break;
74 | case NavigationPages.RxUiSearch:
75 | selectedPage = new Pages.ReactiveUiSearch();
76 | break;
77 | case NavigationPages.RxUiColorSlider:
78 | selectedPage = new Pages.ReactiveUiColorSlider();
79 | break;
80 | case NavigationPages.RxUiLogin:
81 | selectedPage = new Pages.ReactiveUiLogin();
82 | break;
83 | case NavigationPages.RxUiXamarinEssentials:
84 | selectedPage = new Pages.ReactiveUiEssentials();
85 | break;
86 | default:
87 | break;
88 | }
89 |
90 | if(selectedPage != null) {
91 | var detailNavPage = new NavigationPage(selectedPage) {};
92 |
93 | Detail = detailNavPage;
94 | }
95 |
96 | this.IsPresented = false;
97 |
98 | });
99 | }
100 |
101 | enum NavigationPages {
102 | Async,
103 | AsyncEvents,
104 | Delay,
105 | Throttle,
106 | Buffer,
107 | BufferWithWhere,
108 | Merge,
109 | Sample,
110 | Scan,
111 | AsyncToObservable,
112 | CombineLatest,
113 | RxUiColorSlider,
114 | RxUiLogin,
115 | StandardSearch,
116 | SearchWithReactiveExtensions,
117 | RxUiSearch,
118 | RxUiXamarinEssentials,
119 | DynamicDataDashboard,
120 | }
121 |
122 | class NavListPage : ContentPage {
123 | readonly Subject selectedNavigation = new Subject();
124 | public IObservable SelectedNavigation { get { return selectedNavigation.AsObservable(); } }
125 |
126 | public NavListPage () {
127 | Title = "Reactive Examples";
128 |
129 | var scrollContainer = new ScrollView {
130 | BackgroundColor = Color.FromHex("#dddddd"),
131 | };
132 |
133 | var navigationContainer = new StackLayout {
134 | Spacing = 8d
135 | };
136 |
137 | scrollContainer.Content = navigationContainer;
138 |
139 | var delay = new Button {
140 | Text = "Delay",
141 | Command = new Command(() => selectedNavigation.OnNext(NavigationPages.Delay)),
142 | };
143 | navigationContainer.Children.Add(delay);
144 |
145 | var throttle = new Button {
146 | Text = "Throttle",
147 | Command = new Command(() => selectedNavigation.OnNext(NavigationPages.Throttle)),
148 | };
149 | navigationContainer.Children.Add(throttle);
150 |
151 | var buffer = new Button {
152 | Text = "Buffer",
153 | Command = new Command(() => selectedNavigation.OnNext(NavigationPages.Buffer)),
154 | };
155 | navigationContainer.Children.Add(buffer);
156 |
157 | var bufferWithWhere = new Button {
158 | Text = "Buffer with Where",
159 | Command = new Command(() => selectedNavigation.OnNext(NavigationPages.BufferWithWhere)),
160 | };
161 | navigationContainer.Children.Add(bufferWithWhere);
162 |
163 | var sample = new Button {
164 | Text = "Sample",
165 | Command = new Command(() => selectedNavigation.OnNext(NavigationPages.Sample)),
166 | };
167 | navigationContainer.Children.Add(sample);
168 |
169 | var scan = new Button {
170 | Text = "Scan",
171 | Command = new Command(() => selectedNavigation.OnNext(NavigationPages.Scan)),
172 | };
173 | navigationContainer.Children.Add(scan);
174 |
175 | var merge = new Button {
176 | Text = "Merge",
177 | Command = new Command(() => selectedNavigation.OnNext(NavigationPages.Merge)),
178 | };
179 | navigationContainer.Children.Add(merge);
180 |
181 | var combineLatest = new Button {
182 | Text = "Combine Latest",
183 | Command = new Command(() => selectedNavigation.OnNext(NavigationPages.CombineLatest)),
184 | };
185 | navigationContainer.Children.Add(combineLatest);
186 |
187 | var asyncToObservable = new Button {
188 | Text = "Mix Async With Observables",
189 | Command = new Command(() => selectedNavigation.OnNext(NavigationPages.AsyncToObservable)),
190 | };
191 | navigationContainer.Children.Add(asyncToObservable);
192 |
193 | var reactiveAsync = new Button {
194 | Text = "Async",
195 | Command = new Command(() => selectedNavigation.OnNext(NavigationPages.Async)),
196 | };
197 | navigationContainer.Children.Add(reactiveAsync);
198 |
199 | var asyncEvents = new Button {
200 | Text = "Async Events",
201 | Command = new Command(() => selectedNavigation.OnNext(NavigationPages.AsyncEvents)),
202 | };
203 | navigationContainer.Children.Add(asyncEvents);
204 |
205 | var standardSearch = new Button
206 | {
207 | Text = "Standard Search",
208 | Command = new Command(() => selectedNavigation.OnNext(NavigationPages.StandardSearch)),
209 | };
210 | navigationContainer.Children.Add(standardSearch);
211 |
212 | var dynamicDataDashboard = new Button
213 | {
214 | Text = "Dynamic Data",
215 | Command = new Command(() => selectedNavigation.OnNext(NavigationPages.DynamicDataDashboard)),
216 | };
217 | navigationContainer.Children.Add(dynamicDataDashboard);
218 |
219 | var searchWithReactiveExtensions = new Button
220 | {
221 | Text = "Search with Reactive Extensions",
222 | Command = new Command(() => selectedNavigation.OnNext(NavigationPages.SearchWithReactiveExtensions)),
223 | };
224 | navigationContainer.Children.Add(searchWithReactiveExtensions);
225 |
226 | var rxuiSearch = new Button
227 | {
228 | Text = "RxUI - Search",
229 | Command = new Command(() => selectedNavigation.OnNext(NavigationPages.RxUiSearch)),
230 | };
231 | navigationContainer.Children.Add(rxuiSearch);
232 |
233 | var rxuiColorSlider = new Button {
234 | Text = "RxUI - Color Slider",
235 | Command = new Command(() => selectedNavigation.OnNext(NavigationPages.RxUiColorSlider)),
236 | };
237 | navigationContainer.Children.Add(rxuiColorSlider);
238 |
239 | var rxuiLogin = new Button {
240 | Text = "RxUI - Login",
241 | Command = new Command(() => selectedNavigation.OnNext(NavigationPages.RxUiLogin)),
242 | };
243 | navigationContainer.Children.Add(rxuiLogin);
244 |
245 | var rxuiEssentials = new Button {
246 | Text = "RxUI - Xamarin Essentials",
247 | Command = new Command(() => selectedNavigation.OnNext(NavigationPages.RxUiXamarinEssentials)),
248 | };
249 | navigationContainer.Children.Add(rxuiEssentials);
250 |
251 | var reactiveLogo = new Image {
252 | Source = "reactive_logo.png",
253 | Aspect = Aspect.AspectFit,
254 | Margin = new Thickness(8d)
255 | };
256 | navigationContainer.Children.Add(reactiveLogo);
257 |
258 | Content = scrollContainer;
259 | }
260 | }
261 | }
262 | }
263 |
264 |
--------------------------------------------------------------------------------
/ReactiveExtensionExamples/UserInterface/Pages/NonReactivePage.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Xamarin.Forms;
3 |
4 | namespace ReactiveExtensionExamples.UserInterface.Pages
5 | {
6 | public class NonReactivePage : ContentPage
7 | {
8 | int clickCount = 0;
9 |
10 | Label buttonClickedInformation;
11 | Button myButton;
12 |
13 | public NonReactivePage ()
14 | {
15 |
16 | myButton = new Button {
17 | Text = "This is a button"
18 | };
19 |
20 | myButton.Clicked += MyButton_Clicked;
21 | buttonClickedInformation = new Label { };
22 |
23 | Content = new StackLayout {
24 | Children = {
25 | myButton,
26 | buttonClickedInformation
27 | }
28 | };
29 | }
30 |
31 | void MyButton_Clicked (object sender, EventArgs e)
32 | {
33 | clickCount++;
34 | buttonClickedInformation.Text = string.Format ("Clicked {0} times", clickCount);
35 | }
36 | }
37 | }
38 |
39 |
40 |
--------------------------------------------------------------------------------
/ReactiveExtensionExamples/UserInterface/Pages/PageBase.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Xamarin.Forms;
3 | using System.Reactive.Disposables;
4 |
5 | namespace ReactiveExtensionExamples.UserInterface.Pages
6 | {
7 | public class PageBase : ContentPage
8 | {
9 | protected readonly CompositeDisposable SubscriptionDisposables = new CompositeDisposable ();
10 |
11 | public PageBase ()
12 | {
13 | SetupUserInterface ();
14 | }
15 |
16 | protected virtual void SetupUserInterface () {}
17 |
18 | protected virtual void SetupReactiveExtensions () {}
19 |
20 | protected override void OnAppearing ()
21 | {
22 | SetupReactiveExtensions ();
23 |
24 | base.OnAppearing ();
25 | }
26 |
27 | protected override void OnDisappearing ()
28 | {
29 | SubscriptionDisposables.Clear ();
30 |
31 | base.OnDisappearing ();
32 | }
33 | }
34 | }
35 |
36 |
--------------------------------------------------------------------------------
/ReactiveExtensionExamples/UserInterface/Pages/ReactiveUiColorSlider.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Reactive.Disposables;
3 | using System.Reactive.Linq;
4 | using ReactiveUI;
5 | using Xamarin.Forms;
6 | using System.Threading.Tasks;
7 | using ReactiveUI.XamForms;
8 |
9 | namespace ReactiveExtensionExamples.UserInterface.Pages
10 | {
11 | public class ReactiveUiColorSlider: ReactiveContentPage
12 | {
13 | readonly CompositeDisposable subscriptionDisposables = new CompositeDisposable ();
14 |
15 | BoxView colorDisplay;
16 |
17 | Slider red, green, blue;
18 |
19 | public ReactiveUiColorSlider ()
20 | {
21 | ViewModel = new ReactiveExtensionExamples.ViewModels.ColorSlider ();
22 |
23 | Title = "RxUI - Color Slider";
24 |
25 | Content = new StackLayout {
26 | Padding = new Thickness(40d),
27 | Children = {
28 | (colorDisplay = new BoxView{ HeightRequest = 250 }),
29 |
30 | new Label{ Text = "Red"},
31 | (red = new Slider(0, 255, 0)),
32 |
33 | new Label{ Text = "Green"},
34 | (green = new Slider(0, 255, 0)),
35 |
36 | new Label{ Text = "Blue"},
37 | (blue = new Slider(0, 255, 0))
38 | }
39 | };
40 |
41 | this.WhenActivated((CompositeDisposable disposables) =>
42 | {
43 | this.Bind (ViewModel, vm => vm.Red, c => c.red.Value)
44 | .DisposeWith(disposables);
45 |
46 | this.Bind (ViewModel, vm => vm.Green, c => c.green.Value)
47 | .DisposeWith(disposables);
48 |
49 | this.Bind (ViewModel, vm => vm.Blue, c => c.blue.Value)
50 | .DisposeWith(disposables);
51 |
52 | this.WhenAnyValue (x => x.ViewModel.Color)
53 | .Select (color => Color.FromRgba (color.R, color.G, color.B, color.A))
54 | .ObserveOn (RxApp.MainThreadScheduler)
55 | .BindTo (this, x => x.colorDisplay.BackgroundColor)
56 | .DisposeWith(disposables);
57 | });
58 | }
59 | }
60 | }
61 |
62 |
63 |
--------------------------------------------------------------------------------
/ReactiveExtensionExamples/UserInterface/Pages/ReactiveUiEssentials.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Reactive.Disposables;
3 | using System.Reactive.Linq;
4 | using ReactiveUI;
5 | using Xamarin.Forms;
6 | using System.Threading.Tasks;
7 | using ReactiveUI.XamForms;
8 |
9 | namespace ReactiveExtensionExamples.UserInterface.Pages
10 | {
11 | public class ReactiveUiEssentials : ReactiveContentPage
12 | {
13 | BoxView colorDisplay;
14 |
15 | Slider x, y, z;
16 |
17 | public ReactiveUiEssentials ()
18 | {
19 | ViewModel = new ReactiveExtensionExamples.ViewModels.XamarinEssentials ();
20 |
21 | Title = "RxUI - Xamarin Essentials";
22 |
23 | Content = new StackLayout {
24 | Padding = new Thickness(40d),
25 | Children = {
26 | (colorDisplay = new BoxView{ HeightRequest = 250 }),
27 |
28 | new Label{ Text = "X"},
29 | (x = new Slider(-1, 1, 0){ InputTransparent = true }),
30 |
31 | new Label{ Text = "Y"},
32 | (y = new Slider(-1, 1, 0){ InputTransparent = true }),
33 |
34 | new Label{ Text = "Z"},
35 | (z = new Slider(-1, 1, 0){ InputTransparent = true })
36 | }
37 | };
38 |
39 | this.WhenActivated((CompositeDisposable disposables) =>
40 | {
41 | this.OneWayBind (ViewModel, vm => vm.X, c => c.x.Value)
42 | .DisposeWith(disposables);
43 |
44 | this.OneWayBind (ViewModel, vm => vm.Y, c => c.y.Value)
45 | .DisposeWith(disposables);
46 |
47 | this.OneWayBind (ViewModel, vm => vm.Z, c => c.z.Value)
48 | .DisposeWith(disposables);
49 |
50 | this.WhenAnyValue (x => x.ViewModel.Color)
51 | .ObserveOn (RxApp.MainThreadScheduler)
52 | .Select (color => Color.FromRgba (color.R, color.G, color.B, color.A))
53 | .BindTo (this, x => x.colorDisplay.BackgroundColor)
54 | .DisposeWith(disposables);
55 | });
56 | }
57 | }
58 | }
59 |
60 |
61 |
--------------------------------------------------------------------------------
/ReactiveExtensionExamples/UserInterface/Pages/ReactiveUiLogin.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Reactive;
3 | using System.Reactive.Disposables;
4 | using System.Reactive.Linq;
5 | using ReactiveUI;
6 | using ReactiveUI.XamForms;
7 | using Xamarin.Forms;
8 |
9 | namespace ReactiveExtensionExamples.UserInterface.Pages
10 | {
11 | public class ReactiveUiLogin : ReactiveContentPage
12 | {
13 | Entry emailEntry, passwordEntry;
14 |
15 | Button login;
16 |
17 | ActivityIndicator loading;
18 |
19 | public ReactiveUiLogin ()
20 | {
21 | ViewModel = new ReactiveExtensionExamples.ViewModels.Login ();
22 |
23 | Title = "RxUI - Login";
24 |
25 | Content = new StackLayout {
26 | Padding = new Thickness(40d),
27 | Children = {
28 | (emailEntry = new Entry{ Placeholder = "Email" }),
29 | (passwordEntry = new Entry { Placeholder = "Password", IsPassword = true }),
30 | (login = new Button{ Text = "Login" }),
31 | (loading = new ActivityIndicator{ HorizontalOptions = LayoutOptions.Center }),
32 | }
33 | };
34 |
35 | this.WhenActivated((CompositeDisposable disposables) =>
36 | {
37 | this.Bind (ViewModel, vm => vm.EmailAddress, c => c.emailEntry.Text)
38 | .DisposeWith(disposables);
39 |
40 | this.Bind (ViewModel, vm => vm.Password, c => c.passwordEntry.Text)
41 | .DisposeWith(disposables);
42 |
43 | this.OneWayBind (ViewModel, vm => vm.IsLoading, c => c.loading.IsRunning)
44 | .DisposeWith(disposables);
45 |
46 | this.OneWayBind (ViewModel, vm => vm.IsLoading, c => c.loading.IsVisible)
47 | .DisposeWith(disposables);
48 |
49 | this.BindCommand (ViewModel, vm => vm.PerformLogin, c => c.login)
50 | .DisposeWith(disposables);
51 |
52 | this.WhenAnyObservable (x => x.ViewModel.PerformLogin)
53 | .ObserveOn (RxApp.MainThreadScheduler)
54 | .SelectMany (async _ =>
55 | {
56 | await DisplayAlert("Log In", "It's Log, It's Log", "It's Big, It's Heavy, It's Wood");
57 | return Unit.Default;
58 | })
59 | .Subscribe()
60 | .DisposeWith(disposables);
61 | });
62 | }
63 | }
64 | }
65 |
66 |
67 |
--------------------------------------------------------------------------------
/ReactiveExtensionExamples/UserInterface/Pages/ReactiveUiSearch.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Reactive;
3 | using System.Reactive.Disposables;
4 | using System.Reactive.Linq;
5 | using System.Reactive.Threading.Tasks;
6 | using ReactiveUI;
7 | using ReactiveUI.XamForms;
8 | using Xamarin.Forms;
9 |
10 | namespace ReactiveExtensionExamples.UserInterface.Pages
11 | {
12 | public class ReactiveUiSearch : ReactiveContentPage
13 | {
14 | private Entry _textEntry;
15 | private ListView _searchResults;
16 | private RefreshView _pullToRefresh;
17 | private Button _search;
18 | private ActivityIndicator _loading;
19 | private SerialDisposable _searchErrorDisposable;
20 |
21 | public ReactiveUiSearch() {
22 | Title = "Rx - Search";
23 |
24 | this.ViewModel = new ViewModels.SearchViewModel();
25 |
26 | Content = new StackLayout
27 | {
28 | Padding = new Thickness(8d),
29 | Children =
30 | {
31 | (_textEntry = new Entry{ Placeholder = "Enter Search Terms" }),
32 | (_search = new Button{ Text = "Search" }),
33 | (_loading = new ActivityIndicator{}),
34 | (_pullToRefresh =
35 | new RefreshView
36 | {
37 | Content =
38 | _searchResults =
39 | new ListView(ListViewCachingStrategy.RecycleElement)
40 | {
41 | ItemTemplate = new DataTemplate(typeof(DuckDuckGoResultCell))
42 | },
43 | HorizontalOptions = LayoutOptions.FillAndExpand,
44 | VerticalOptions = LayoutOptions.FillAndExpand,
45 | }),
46 | }
47 | };
48 |
49 | this.WhenActivated(
50 | (CompositeDisposable disposables) =>
51 | {
52 | _searchErrorDisposable?.Dispose();
53 | _searchErrorDisposable = new SerialDisposable();
54 |
55 | //TODO: RxSUI - Item 1 - Here we are just setting up bindings to our UI Elements
56 | //This is a two-way bind
57 | this.Bind(ViewModel, x => x.SearchQuery, c => c._textEntry.Text)
58 | .DisposeWith(disposables);
59 |
60 | this.BindCommand(ViewModel, x => x.Search, c => c._search, this.WhenAnyValue(x => x.ViewModel.SearchQuery))
61 | .DisposeWith(disposables);
62 |
63 | //Once this event is fired off, it will start the refresh
64 | Observable
65 | .FromEventPattern(
66 | x => _pullToRefresh.Refreshing += x,
67 | x => _pullToRefresh.Refreshing -= x)
68 | .Select(_ => this.WhenAnyValue(x => x.ViewModel.SearchQuery).Take(1))
69 | .Switch()
70 | .InvokeCommand(this, x => x.ViewModel.Search)
71 | .DisposeWith(disposables);
72 |
73 | //This will only trigger when the search command completes
74 | this.WhenAnyObservable(x => x.ViewModel.Search)
75 | .Select(_ => false)
76 | .ObserveOn(RxApp.MainThreadScheduler)
77 | .BindTo(this, ui => ui._pullToRefresh.IsRefreshing)
78 | .DisposeWith(disposables);
79 |
80 | //This is a one-way bind
81 | this.OneWayBind(ViewModel, x => x.SearchResults, c => c._searchResults.ItemsSource)
82 | .DisposeWith(disposables);
83 |
84 | //TODO: RxSUI - Item 2 - User error allows us to interact with our users and get feedback on how to handle an exception
85 | this.WhenAnyValue(x => x.ViewModel.SearchError)
86 | .Where(x => x != null)
87 | .Subscribe(searchError =>
88 | {
89 | _searchErrorDisposable.Disposable =
90 | searchError
91 | .RegisterHandler(async interaction =>
92 | {
93 | var result = await this.DisplayAlert("Error", $"{interaction.Input}{Environment.NewLine}Would you like to retry?", "Retry", "Cancel");
94 | interaction.SetOutput(result);
95 | });
96 | })
97 | .DisposeWith(disposables);
98 |
99 | _searchErrorDisposable
100 | .DisposeWith(disposables);
101 | });
102 | }
103 |
104 | class DuckDuckGoResultCell : ReactiveViewCell
105 | {
106 | Image icon;
107 |
108 | Label displayText;
109 |
110 | public DuckDuckGoResultCell()
111 | {
112 | var grid = new Grid
113 | {
114 | Padding = new Thickness(8d),
115 | ColumnDefinitions = {
116 | new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) },
117 | new ColumnDefinition { Width = new GridLength(3, GridUnitType.Star) }
118 | }
119 | };
120 |
121 | icon = new Image
122 | {
123 | VerticalOptions = LayoutOptions.FillAndExpand,
124 | Aspect = Aspect.AspectFit
125 | };
126 | grid.Children.Add(icon, 0, 0);
127 |
128 | displayText = new Label
129 | {
130 | HorizontalOptions = LayoutOptions.FillAndExpand,
131 | VerticalOptions = LayoutOptions.FillAndExpand
132 | };
133 | grid.Children.Add(displayText, 1, 0);
134 |
135 | View = grid;
136 |
137 | this.WhenActivated((CompositeDisposable disposables) =>
138 | {
139 | this.WhenAnyValue(x => x.ViewModel.ImageUrl)
140 | .ObserveOn(RxApp.MainThreadScheduler)
141 | .Subscribe(x => icon.Source = x)
142 | .DisposeWith(disposables);
143 |
144 | this.OneWayBind(ViewModel, x => x.DisplayText, x => x.displayText.Text)
145 | .DisposeWith(disposables);
146 | });
147 | }
148 | }
149 | }
150 | }
151 |
152 |
153 |
--------------------------------------------------------------------------------
/ReactiveExtensionExamples/UserInterface/Pages/Sample.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using System.Reactive.Concurrency;
4 | using System.Reactive.Disposables;
5 | using System.Reactive.Linq;
6 | using Xamarin.Forms;
7 |
8 | namespace ReactiveExtensionExamples.UserInterface.Pages
9 | {
10 | public class Sample : PageBase
11 | {
12 | Entry textEntry;
13 | WebView webView;
14 |
15 | protected override void SetupUserInterface ()
16 | {
17 | Title = "Rx - Sample";
18 |
19 | Content = new StackLayout {
20 | Padding = new Thickness(8d),
21 | Children = {
22 | (textEntry = new Entry{ Placeholder = "Enter Search Terms" }),
23 | (webView = new WebView {
24 | VerticalOptions = LayoutOptions.FillAndExpand,
25 | HorizontalOptions = LayoutOptions.FillAndExpand,
26 | })
27 | }
28 | };
29 | }
30 |
31 | protected override void SetupReactiveExtensions ()
32 | {
33 | Observable
34 | .FromEventPattern, TextChangedEventArgs> (
35 | x => textEntry.TextChanged += x,
36 | x => textEntry.TextChanged -= x)
37 | .Select(args =>
38 | string.Format("https://frinkiac.com/?q={0}", args.EventArgs.NewTextValue.Replace(" ", "+")))
39 | .Subscribe (
40 | searchUrl => Device.BeginInvokeOnMainThread(() => webView.Source = searchUrl),
41 | ex => Device.BeginInvokeOnMainThread(() => webView.Source = "https://frinkiac.com/caption/S04E05/1135500"))
42 | .DisposeWith(SubscriptionDisposables);
43 | }
44 | }
45 | }
46 |
47 |
48 |
--------------------------------------------------------------------------------
/ReactiveExtensionExamples/UserInterface/Pages/Scan.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Net.Http;
5 | using System.Reactive.Disposables;
6 | using System.Reactive.Linq;
7 | using System.Threading.Tasks;
8 | using System.Xml.Linq;
9 | using Xamarin.Forms;
10 | using System.Threading;
11 | using ReactiveExtensionExamples.Models;
12 | using ReactiveExtensionExamples.Services.Api;
13 |
14 | namespace ReactiveExtensionExamples.UserInterface.Pages
15 | {
16 | public class Scan : PageBase
17 | {
18 | CollectionView rssFeed;
19 |
20 | readonly HttpClient client = new HttpClient();
21 |
22 | protected override void SetupUserInterface ()
23 | {
24 | Title = "Rx - Scan";
25 |
26 |
27 | Content = new StackLayout {
28 | Children = {
29 | (rssFeed = new CollectionView {
30 | ItemTemplate = new DataTemplate (typeof(Cells.RssEntryCell)),
31 | ItemSizingStrategy = ItemSizingStrategy.MeasureAllItems,
32 | })
33 | }
34 | };
35 | }
36 |
37 | protected override void SetupReactiveExtensions ()
38 | {
39 | Observable
40 | .Interval(TimeSpan.FromMilliseconds(300))
41 | .StartWith(0L)
42 | .Select(_ => Observable.FromAsync(cancellationToken => DownloadMultipleRss(cancellationToken)))
43 | .Switch()
44 | .Scan(new List(),
45 | (accumulatedItems, newItems) =>
46 | {
47 | var itemsToAdd =
48 | newItems
49 | .Where(x => !accumulatedItems.Any(ai => ai.Id.Equals(x.Id)))
50 | .Select(x => { x.New = true; return x;})
51 | .ToList();
52 |
53 | foreach (var item in accumulatedItems)
54 | item.New = false;
55 |
56 | accumulatedItems.InsertRange(0, itemsToAdd);
57 |
58 | return
59 | accumulatedItems
60 | .OrderByDescending(x => x.New)
61 | .ThenByDescending(x => x.Updated)
62 | .Take(250)
63 | .ToList();
64 | })
65 | .Where(x => x?.Any(rss => rss.New) ?? false)
66 | .Subscribe(result => Device.BeginInvokeOnMainThread(() => rssFeed.ItemsSource = result))
67 | .DisposeWith(SubscriptionDisposables);
68 | }
69 |
70 | Task> DownloadMultipleRss(CancellationToken ct){
71 | return Task.Run>(async () =>
72 | {
73 |
74 | System.Diagnostics.Debug.WriteLine($"Starting download at {DateTimeOffset.Now}");
75 |
76 | var askReddit = RssDownloader.DownloadRss("https://www.reddit.com/r/AskReddit/new/.rss", ct);
77 | var todayILearned = RssDownloader.DownloadRss("https://www.reddit.com/r/todayilearned/new/.rss", ct);
78 | var news = RssDownloader.DownloadRss("https://www.reddit.com/r/news/new/.rss", ct);
79 | var worldNews = RssDownloader.DownloadRss("https://www.reddit.com/r/worldnews/new/.rss", ct);
80 |
81 | var tcs = new TaskCompletionSource