{"id":13327253,"url":"https://github.com/richk1/HelloWorldRUI","last_synced_at":"2025-03-11T02:32:36.935Z","repository":{"id":211501589,"uuid":"169426712","full_name":"richk1/HelloWorldRUI","owner":"richk1","description":"Hello World WPF example using ReactiveUI","archived":false,"fork":false,"pushed_at":"2023-12-08T23:52:42.000Z","size":68,"stargazers_count":16,"open_issues_count":0,"forks_count":5,"subscribers_count":1,"default_branch":"master","last_synced_at":"2024-10-10T22:21:05.298Z","etag":null,"topics":["csharp","dotnet-framework","hello-world","reactiveui","windows-desktop","wpf"],"latest_commit_sha":null,"homepage":null,"language":"C#","has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/richk1.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-02-06T15:21:59.000Z","updated_at":"2024-04-03T09:02:27.000Z","dependencies_parsed_at":null,"dependency_job_id":"42313a13-3c06-4e46-9672-8330165f7712","html_url":"https://github.com/richk1/HelloWorldRUI","commit_stats":{"total_commits":9,"total_committers":2,"mean_commits":4.5,"dds":0.4444444444444444,"last_synced_commit":"8d4aa42f9b6977e763117de5a7cce26ea6115460"},"previous_names":["richk1/helloworldrui"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/richk1%2FHelloWorldRUI","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/richk1%2FHelloWorldRUI/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/richk1%2FHelloWorldRUI/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/richk1%2FHelloWorldRUI/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/richk1","download_url":"https://codeload.github.com/richk1/HelloWorldRUI/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":221205585,"owners_count":16776218,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["csharp","dotnet-framework","hello-world","reactiveui","windows-desktop","wpf"],"created_at":"2024-07-29T18:53:51.778Z","updated_at":"2024-10-23T14:31:51.984Z","avatar_url":"https://github.com/richk1.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"﻿# HelloWorldRUI\nAn example WPF HelloWorld program written using the ReactiveUI framework.\n\n1. Original: 3 February 2019\u003cbr\u003e\n2. Latest Update: Dec 2023\n   * Now using DotNet 8 / C# 12\n   * Mostly minor tweaks to syntax\n   * My level of proficiency with ReactiveUI hasn't increased much since I originally posted this (I've been doing other things), so I've left most of my original commentary unchanged. My observations may well be outdated.\n   * Added a copy of the same project written without using ReactiveUI for comparison.\n\n## Motivation\nI'm just a hobbiest programmer these days, and thought I'd give ReactiveUI a spin.  I had an unexpectedly difficult time figuring out how to get a simple starter program working using ReactiveUI. I found some documentation sources useful, and others confusing, or lacking SIMPLE examples for the current software versions.  So, once I figured out some of the basics, I thought I'd post my working example here on github. It's just a slightly fancy Hello World that makes use of a few ReactiveUI components.\n\n(My biggest problem was getting the bindings to work. I think that was because I was using a version of the Splat service locater code in my app.xaml.cs file that was incompatible with the rest of my code. I had tried using different versions of that line that I copied from various documentation sources/examples, but not evidently ones that were in sync with the rest of my code. The sample that finally solved my problem was one of the two buried in the ReactvieUI source code itself - ReactiveDemo. This is the code the ReactiveUI 'Getting Started' page currently walks you through.)\n\n----\n## This Example\nWhat it does: \u003cbr\u003e\nOpens a Window that shows the greeting \"Hello World\", cycling between a few \ndifferent languages.\n\n\u003ccenter\u003e\n\n![](https://cdn.jsdelivr.net/gh/richk1/HelloWorldRUI@master/Animation.gif)\n\n\u003c/center\u003e\n\n----\n### Development Environment\nI'm currently using Visual Studio 2022 Community Edition preview. \n\n### Dependencies\nYou'll need to install the following NuGet Packages into your environment. They'll\nin-turn load others that they require.\n\n* NuGet Packges\n    * ReactiveUI.WPF\n    * ReactiveUI.Fody\n\n(Note: I perhaps complicated the example a bit by using Fody, but its pretty \nstraitforward to use, has given me very little trouble, and makes the code look \ncleaner.)\n\n----\n### Source Files\n#### File: App.xaml\nNothing really notable here. Standard WPF layout created by the Visual Studio \nproject creater.\n\n#### File: App.xaml.cs\nAdditions to the base template are:\n##### Namespaces\nThese namespaces are used by the Locator statement below.\n````csharp\nusing ReactiveUI;\nusing Splat;\nusing System.Reflection;\n````\n##### App Class Constructor\nAdd the following constructor code to the boilerplate produced by VS:\n````csharp\npublic App()\n{\n    Locator.CurrentMutable.RegisterViewsForViewModels(Assembly.GetCallingAssembly());\n}\n````\nNote \n* The Splat \"Locater...\" line magically connects the Views and ViewModels. I've seen various other incarnations of this line, but this is the one that worked for me.\n* Splat is ReactiveUI's built-in dependency inversion container. When I first tried ReactiveUI I was unfamiliar with the concepts of Dependency Injection/Inversion of Control and the various frameworks that help implement them. Apparently ReactiveUI supports overriding Splat with other popular DI implementations, but I myself haven't tried that yet.\n\n#### File: MainWindow.xaml\n\n##### Window properties\n* The top level XAML object is ReactiveWindow rather than a vanilla WPF Window.\n* x:TypeArguments ties the specific ViewModel class to this ReactiveWindow.\n* Namespaces aliases are also defined for the project and for reactiveui\n\n````xml\n\u003creactiveui:ReactiveWindow \n    x:Class=\"HelloWorldRUI.MainWindow\"\n    x:TypeArguments=\"helloworldrui:AppViewModel\"\n    xmlns:helloworldrui=\"clr-namespace:HelloWorldRUI\"\n    xmlns:reactiveui=\"http://reactiveui.net\"\n    ...\n    Title=\"HelloWorld RUI\"\u003e\n    ...\n\u003c/reactiveui:ReactiveWindow\u003e\n````\n\n##### Window Layout\n\nIn this example we are only going to display two strings, each in its own TextBlock. \nIn a normal WPF project you'd bind the UI elements to ViewModel properties explicitly \nwithin the XAML.  Here, we simply ensure that each TextBlock has a unique name. \nThe binding is done in the MainWindow's constructor code, which references \nthe x:Name values.\n\n````xml\n\u003cStackPanel\u003e\n    \u003cTextBlock x:Name=\"GreetingTextBlock\" /\u003e\n    \u003cTextBlock x:Name=\"LangTextBlock\" /\u003e\n\u003c/StackPanel\u003e\n````\n\n\n#### File: MainWindow.xaml.cs\nIn the HelloWorldRUI project, I've left both the View and ViewModel code in the \nsame file.  This is not normally done, but it doesn't hurt anything, and I think \nmakes it a little easier to read as an example.\n\nIn a WPF MVVM program, the View class constructor is normally responsible for calling \nInitializeComponent() and creating an instance of the ViewModel. In this ReactiveUI \ncode the constructor also binds the View and ViewModel properties, as shown below.\n\n##### View Code\n````csharp\nusing ReactiveUI;\nusing System.Reactive.Disposables;\n\nnamespace HelloWorldRUI\n{\n    public partial class MainWindow : ReactiveWindow\u003cAppViewModel\u003e\n    {\n        public MainWindow()\n        {\n            InitializeComponent();\n            ViewModel = new AppViewModel();\n\n            this.WhenActivated(d =\u003e {\n                this.OneWayBind(ViewModel, viewModel =\u003e viewModel.Lang,\n                    view =\u003e view.LangTextBlock.Text).DisposeWith(d);\n                this.OneWayBind(ViewModel, viewModel =\u003e viewModel.Greeting,\n                    view =\u003e view.GreetingTextBlock.Text).DisposeWith(d);\n            });\n        }\n    }\n}\n````\n\n##### ViewModel Code\nAssuming you understand Reactive Observable pipelines, this code is \nreasonably straightforward. \n\nUsing Fody slightly changes the declarations of the Reactive properties and \nObservableAsPropertyHelpers compared to how you've probably seen it described in \nReactiveUI documentation. Setting ObservableAsPropertyHelper values using ToProperty()\nchanges slightly as well. \nSee the Fody readme (referenced farther below) for a more detailed explanation.\n\nThe Observable.Interval pipeline creates an observable that fires every 2 seconds, \neventually resulting in changes to the Language displayed on the UI. The take(100) \nfunction terminates the pipeline after 100 iterations. The Select() function\nspecified will increment the Lang to the next one in the Greetings dictionary. \nObserveOn(RxApp.MainThreadScheduler) needs to be used when changing any property \nbound to the View. ToPropertyEx() will set the value of the ObservableAsProperty \nLang to the language output in the Select() function. Since Lang is bound to the \nLangTextBlock in the View, the UI will automatically reflect this change.  The \ninitial value of the Observable Lang is passed as an argument to ToPropertyEx(), \nas it cant be set otherwise. \n\nThe final observable pipeline will take care of updating the Greeting. This pipeline\nis set to 'tick' whenever the ViewModel's Lang property changes (as specified by\nthe WhenAnyValue function). The Where() clause ignores any values that are not \ncontained in the Keys array. The Select() function looks up the Greeting associated \nwith the Lang. Again, ObserveOn makes sure our change is visible to the UI, and \nthe Subscribe() function simply sets the Reactive property Greeting with the \nresult created by Select. Since Greeting is a 'simple' Reactive property (not an \nObservable) you can just set its value. Its initial value is specified on its \ndeclaration line.\n\n(Experiment: try removing one of the ObserveOn lines and see what happens.\nIt's useful for future reference to see for yourself how that error manifests.)\n\n````csharp\nusing ReactiveUI;\nusing ReactiveUI.Fody.Helpers;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Reactive.Linq;\n\nnamespace HelloWorldRUI\n{\n    public class AppViewModel : ReactiveObject\n    {\n        [Reactive] public string Greeting { get; set; } = \"Greeting\";\n        [ObservableAsProperty] public string Lang { get; }\n        private readonly int maxCount = 100;\n        private readonly int dwellDuration = 2;\n\n        public AppViewModel()\n        {\n            Dictionary\u003cstring, string\u003e Greetings = new Dictionary\u003cstring, string\u003e() {\n                { \"English\", \"Hello World!\" },\n                { \"French\", \"Bonjour le monde!\" },\n                { \"German\", \"Hallo Welt!\" },\n                { \"Japanese\", \"Kon'nichiwa sekai!\" },\n                { \"Spanish\", \"¡Hola Mundo!\" },\n            };\n            string[] keys = Greetings.Keys.ToArray();\n\n            // select next language every 2 seconds (100 times)\n            Observable.Interval(TimeSpan.FromSeconds(dwellDuration))\n                .Take(maxCount)\n                .Select(_ =\u003e keys[(Array.IndexOf(keys, Lang) + 1) % keys.Length])\n                .ObserveOn(RxApp.MainThreadScheduler)\n                .ToPropertyEx(this, x =\u003e x.Lang, \"Language\");\n\n            // update Greeting when language changes\n            this.WhenAnyValue(x =\u003e x.Lang)\n                .Where(lang =\u003e keys.Contains(lang))\n                .Select(x =\u003e Greetings[x])\n                .ObserveOn(RxApp.MainThreadScheduler)\n                .Subscribe(x =\u003e Greeting = x);\n        }\n    }\n}\n````\n----\n\n## Misc Notes\n### Fody\nIf you create these source files by hand, you may get an error when building the \nproject the first time indicating that the FodyWeavers.xml file couldn't be found \nand had to be generated for you. In that case simply rebuild the project and the \nerror should resolve itself. (Rebuilding after the FodyWeavers file was created\nalso resolved an Intellisense error I had in the MainWindow.xaml file which claimed \nit couldn't find the AppViewModel.)\n\n### Intellisense\nIntegration of ReactiveUI and Fody with Intellisense seems to be incomplete or \nslow at times. Occasionally I think I found Intellisense reported errors which \nresolved themselves by rebuilding the project.\n\n## Useful References\n\u003ch3\u003e\n\u003cimg src=\"https://d33wubrfki0l68.cloudfront.net/1b88ade41838e8036700f92c2fb945455fb45fb3/97288/assets/img/logo.png\"\nwidth=\"25\"/\u003e\nReactiveUI Project\n\u003c/h3\u003e\n\nLinks:\n* Main Website: https://reactiveui.net/\n* API: https://reactiveui.net/api/\n* GitHub: https://github.com/reactiveui/ReactiveUI\n* Getting Started example - ReactiveDemo.sln:\n   * master: https://github.com/reactiveui/ReactiveUI.Samples/tree/main/wpf/getting-started \n   * snapshot of the version I looked at (in case it changes): [0a8d8fb4afa90fc839026a66d1193fccdfb44938](https://github.com/reactiveui/ReactiveUI/tree/0a8d8fb4afa90fc839026a66d1193fccdfb44938/samples/getting-started)\n\n### ReactiveUI.Fody:\nThis was originally an independant project, but has since been absorbed into the ReactiveUI GitHub repo.\n\nLinks:\n* ReactiveUI Page discussing Fody: https://www.reactiveui.net/docs/handbook/view-models/boilerplate-code\n* Current Source in ReactiveUI GitHub: \n    https://github.com/reactiveui/ReactiveUI/tree/master/src/ReactiveUI.Fody\n* Old GitHub project: https://github.com/kswoll/ReactiveUI.Fody\n\n### ReactiveX (C#)\nReactive Extensions (ReactiveX) is a project which provides Reactive libraries for a number of programming languages and platforms. One such supported combination is C#/DotNet (aka the System.Reactive package). The base form of Reactive programming has no direct support for Graphical User Interfaces (that's where ReactiveUI comes in.) Its been around for quite a while, and there's some pretty good documentation for it, though not necessarily all up to date for C#/DotNet. However the older stuff is still VERY useful.\n(You may notice that over time there has been some significant overlap of the maintainers of dotnet/reactive and ReactiveUI)\n\nLinks:\n* ReactiveX Website: http://reactivex.io/\n* Rx.NET aka dotnet/Reactive aka System.Reactive GitHub repo: \n    https://github.com/dotnet/reactive\n* Introduction to Rx online Book\n    http://introtorx.com/Content/v1.0.10621.0/00_Foreword.html\n\tThe above link now redirects to https://introrx.com, which is most probably an updated version, or at least an updated presentation of the material originally written in 2012. (I haven't had time to look through the new info.)\n\n\n### You, I and ReactiveUI (Book by Kent Boogaart, April 2018)\nAt the time I'm writing this section (~2019), this book was (and perhaps still is) the only organized, well written, reasonably complete reference/tutorial for ReactiveUI that I could find. I'm still working my way through it, and I'm finding it very useful. (Thanks to the author for making the effort to write and publish it!) \nThe biggest shortcoming of the book, for me as a newcomer, was the way the sample code was organized. I found that it made it very difficult for me to observe and emulate each example as a standalone application.  \nI offer the following observations for my fellow noobs:\u003cbr\u003e\n* The sample code is organized as one big project. I found this a bit awkward when trying to browse the source code, as the files for each example (app files, MainWindow and View files, and ViewModel files), are spread out far apart from one another, making for a bit of work when trying to view all the files that belong to a single example.\n* The project makes use of an external WPF UI toolkit (MahApps.metro) that I haven't found discussed in the text (as far as I've read). \n* The service locator code used in the project's app.xaml.cs doesn't match what I'm using in my HelloWorld (which I copied from ReactiveUI's 'getting started' sample). In fact, I dont see a description of RegisterViewsForModels() in the book. (Note: the book doesn't claim to describe all the APIs.) Probably the way the book does it would work for me if I understood the service locator API better.\n* (I haven't finished reading it all yet. I'm skipping around reading the parts I think I need to get my code working.) \n\nLinks\n* Book GitHub repo: https://github.com/kentcb/YouIandReactiveUI\n\n[ReactiveUILogo]:https://d33wubrfki0l68.cloudfront.net/1b88ade41838e8036700f92c2fb945455fb45fb3/97288/assets/img/logo.png\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frichk1%2FHelloWorldRUI","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frichk1%2FHelloWorldRUI","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frichk1%2FHelloWorldRUI/lists"}