├── AkkaAndWpfExample.ThermostatUI
├── App.config
├── Properties
│ ├── Settings.settings
│ ├── Settings.Designer.cs
│ ├── AssemblyInfo.cs
│ ├── Resources.Designer.cs
│ └── Resources.resx
├── packages.config
├── App.xaml.cs
├── App.xaml
├── MainWindow.xaml.cs
├── ViewModel
│ ├── MainViewModel.cs
│ └── ViewModelLocator.cs
├── MainWindow.xaml
└── AkkaAndWpfExample.ThermostatUI.csproj
├── AkkaAndWpfExample.ThermostatSystem
├── packages.config
├── ThermostatSystem.cs
├── Actors
│ ├── PubSubActor.cs
│ ├── TemperatureSensorActor.cs
│ └── ThermostatActor.cs
├── Properties
│ └── AssemblyInfo.cs
└── AkkaAndWpfExample.ThermostatSystem.csproj
├── LICENSE
├── AkkaAndWpfExample.sln
├── .gitignore
└── README.md
/AkkaAndWpfExample.ThermostatUI/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/AkkaAndWpfExample.ThermostatUI/Properties/Settings.settings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/AkkaAndWpfExample.ThermostatSystem/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/AkkaAndWpfExample.ThermostatUI/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/AkkaAndWpfExample.ThermostatUI/App.xaml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Configuration;
4 | using System.Data;
5 | using System.Linq;
6 | using System.Threading.Tasks;
7 | using System.Windows;
8 |
9 | namespace AkkaAndWpfExample.ThermostatUI
10 | {
11 | ///
12 | /// Interaction logic for App.xaml
13 | ///
14 | public partial class App : Application
15 | {
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/AkkaAndWpfExample.ThermostatSystem/ThermostatSystem.cs:
--------------------------------------------------------------------------------
1 | using Akka.Actor;
2 |
3 | namespace WpfAkkaIntegration.ThermostatSystem
4 | {
5 | public class ThermostatSystem
6 | {
7 | private ActorSystem _system;
8 | private IActorRef _thermostatActor;
9 |
10 | public ThermostatSystem()
11 | {
12 | _system = ActorSystem.Create(nameof(ThermostatSystem));
13 | _thermostatActor = CreateThermostatActor();
14 | }
15 |
16 | private IActorRef CreateThermostatActor()
17 | {
18 | var props = Props.Create();
19 | return _system.ActorOf(props, "thermostat");
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/AkkaAndWpfExample.ThermostatUI/App.xaml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/AkkaAndWpfExample.ThermostatUI/MainWindow.xaml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using System.Windows;
7 | using System.Windows.Controls;
8 | using System.Windows.Data;
9 | using System.Windows.Documents;
10 | using System.Windows.Input;
11 | using System.Windows.Media;
12 | using System.Windows.Media.Imaging;
13 | using System.Windows.Navigation;
14 | using System.Windows.Shapes;
15 |
16 | namespace AkkaAndWpfExample.ThermostatUI
17 | {
18 | ///
19 | /// Interaction logic for MainWindow.xaml
20 | ///
21 | public partial class MainWindow : Window
22 | {
23 | public MainWindow()
24 | {
25 | InitializeComponent();
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Aaron Lenoir
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/AkkaAndWpfExample.ThermostatUI/Properties/Settings.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace AkkaAndWpfExample.ThermostatUI.Properties
12 | {
13 |
14 |
15 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
16 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
17 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
18 | {
19 |
20 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
21 |
22 | public static Settings Default
23 | {
24 | get
25 | {
26 | return defaultInstance;
27 | }
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/AkkaAndWpfExample.ThermostatUI/ViewModel/MainViewModel.cs:
--------------------------------------------------------------------------------
1 | using GalaSoft.MvvmLight;
2 | using GalaSoft.MvvmLight.CommandWpf;
3 |
4 | namespace AkkaAndWpfExample.ThermostatUI.ViewModel
5 | {
6 | public class MainViewModel : ViewModelBase
7 | {
8 | public MainViewModel()
9 | {
10 | }
11 |
12 | public RelayCommand IncreaseTargetTemperature { get; private set; }
13 |
14 | public RelayCommand DecreaseTargetTemperature { get; private set; }
15 |
16 | private double _temperature = 20;
17 | public double Temperature
18 | {
19 | get
20 | {
21 | return _temperature;
22 | }
23 | set
24 | {
25 | _temperature = value;
26 | RaisePropertyChanged(nameof(Temperature));
27 | }
28 | }
29 |
30 | private double _targetTemperature = 21;
31 | public double TargetTemperature
32 | {
33 | get
34 | {
35 | return _targetTemperature;
36 | }
37 | set
38 | {
39 | _targetTemperature = value;
40 | RaisePropertyChanged(nameof(TargetTemperature));
41 | }
42 | }
43 | }
44 | }
--------------------------------------------------------------------------------
/AkkaAndWpfExample.ThermostatSystem/Actors/PubSubActor.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading.Tasks;
3 | using Akka.Actor;
4 | using System.Collections.Generic;
5 |
6 | namespace WpfAkkaIntegration.ThermostatSystem.Actors
7 | {
8 | public class PubSubActor : ReceiveActor
9 | {
10 |
11 | #region Messages
12 |
13 | public class Subscribe
14 | {
15 | public IActorRef Subscriber { get; private set; }
16 | public Subscribe(IActorRef subscriber)
17 | {
18 | Subscriber = subscriber;
19 | }
20 | }
21 |
22 | #endregion
23 |
24 | #region States
25 |
26 | public void AcceptSubscribers()
27 | {
28 | Receive(message => handleSubscribe(message));
29 | }
30 |
31 | #endregion
32 |
33 | #region State
34 |
35 | private List _subscribers = new List();
36 |
37 | #endregion
38 |
39 | #region Handlers
40 |
41 | private void handleSubscribe(Subscribe message)
42 | {
43 | _subscribers.Add(message.Subscriber);
44 | }
45 |
46 | #endregion
47 |
48 | public void Publish(object message)
49 | {
50 | foreach(var subscriber in _subscribers)
51 | {
52 | subscriber.Tell(message);
53 | }
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/AkkaAndWpfExample.ThermostatSystem/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("AkkaAndWpfExample.ThermostatSystem")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("AkkaAndWpfExample.ThermostatSystem")]
13 | [assembly: AssemblyCopyright("Copyright © 2016")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("ba8d5cbb-ee18-42b7-b871-eb055f2f57af")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/AkkaAndWpfExample.ThermostatUI/MainWindow.xaml:
--------------------------------------------------------------------------------
1 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/AkkaAndWpfExample.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 14
4 | VisualStudioVersion = 14.0.24720.0
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AkkaAndWpfExample.ThermostatSystem", "AkkaAndWpfExample.ThermostatSystem\AkkaAndWpfExample.ThermostatSystem.csproj", "{BA8D5CBB-EE18-42B7-B871-EB055F2F57AF}"
7 | EndProject
8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AkkaAndWpfExample.ThermostatUI", "AkkaAndWpfExample.ThermostatUI\AkkaAndWpfExample.ThermostatUI.csproj", "{A089EB45-0F65-4BED-AD47-4D067C74A31E}"
9 | EndProject
10 | Global
11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
12 | Debug|Any CPU = Debug|Any CPU
13 | Release|Any CPU = Release|Any CPU
14 | EndGlobalSection
15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
16 | {BA8D5CBB-EE18-42B7-B871-EB055F2F57AF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
17 | {BA8D5CBB-EE18-42B7-B871-EB055F2F57AF}.Debug|Any CPU.Build.0 = Debug|Any CPU
18 | {BA8D5CBB-EE18-42B7-B871-EB055F2F57AF}.Release|Any CPU.ActiveCfg = Release|Any CPU
19 | {BA8D5CBB-EE18-42B7-B871-EB055F2F57AF}.Release|Any CPU.Build.0 = Release|Any CPU
20 | {A089EB45-0F65-4BED-AD47-4D067C74A31E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21 | {A089EB45-0F65-4BED-AD47-4D067C74A31E}.Debug|Any CPU.Build.0 = Debug|Any CPU
22 | {A089EB45-0F65-4BED-AD47-4D067C74A31E}.Release|Any CPU.ActiveCfg = Release|Any CPU
23 | {A089EB45-0F65-4BED-AD47-4D067C74A31E}.Release|Any CPU.Build.0 = Release|Any CPU
24 | EndGlobalSection
25 | GlobalSection(SolutionProperties) = preSolution
26 | HideSolutionNode = FALSE
27 | EndGlobalSection
28 | EndGlobal
29 |
--------------------------------------------------------------------------------
/AkkaAndWpfExample.ThermostatUI/ViewModel/ViewModelLocator.cs:
--------------------------------------------------------------------------------
1 | /*
2 | In App.xaml:
3 |
4 |
6 |
7 |
8 | In the View:
9 | DataContext="{Binding Source={StaticResource Locator}, Path=ViewModelName}"
10 |
11 | You can also use Blend to do all this with the tool's support.
12 | See http://www.galasoft.ch/mvvm
13 | */
14 |
15 | using GalaSoft.MvvmLight.Ioc;
16 | using Microsoft.Practices.ServiceLocation;
17 |
18 | namespace AkkaAndWpfExample.ThermostatUI.ViewModel
19 | {
20 | ///
21 | /// This class contains static references to all the view models in the
22 | /// application and provides an entry point for the bindings.
23 | ///
24 | public class ViewModelLocator
25 | {
26 | ///
27 | /// Initializes a new instance of the ViewModelLocator class.
28 | ///
29 | public ViewModelLocator()
30 | {
31 | ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
32 |
33 | ////if (ViewModelBase.IsInDesignModeStatic)
34 | ////{
35 | //// // Create design time view services and models
36 | //// SimpleIoc.Default.Register();
37 | ////}
38 | ////else
39 | ////{
40 | //// // Create run time view services and models
41 | //// SimpleIoc.Default.Register();
42 | ////}
43 |
44 | SimpleIoc.Default.Register();
45 | }
46 |
47 | public MainViewModel Main
48 | {
49 | get
50 | {
51 | return ServiceLocator.Current.GetInstance();
52 | }
53 | }
54 |
55 | public static void Cleanup()
56 | {
57 | // TODO Clear the ViewModels
58 | }
59 | }
60 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Build Folders (you can keep bin if you'd like, to store dlls and pdbs)
2 | [Bb]in/
3 | [Oo]bj/
4 |
5 | # mstest test results
6 | TestResults
7 |
8 | ## Ignore Visual Studio temporary files, build results, and
9 | ## files generated by popular Visual Studio add-ons.
10 |
11 | # User-specific files
12 | *.suo
13 | *.user
14 | *.sln.docstates
15 |
16 | # Build results
17 | [Dd]ebug/
18 | [Rr]elease/
19 | x64/
20 | *_i.c
21 | *_p.c
22 | *.ilk
23 | *.meta
24 | *.obj
25 | *.pch
26 | *.pdb
27 | *.pgc
28 | *.pgd
29 | *.rsp
30 | *.sbr
31 | *.tlb
32 | *.tli
33 | *.tlh
34 | *.tmp
35 | *.log
36 | *.vspscc
37 | *.vssscc
38 | .builds
39 |
40 | # Visual C++ cache files
41 | ipch/
42 | *.aps
43 | *.ncb
44 | *.opensdf
45 | *.sdf
46 |
47 | # Visual Studio profiler
48 | *.psess
49 | *.vsp
50 | *.vspx
51 |
52 | # Guidance Automation Toolkit
53 | *.gpState
54 |
55 | # ReSharper is a .NET coding add-in
56 | _ReSharper*
57 |
58 | # NCrunch
59 | *.ncrunch*
60 | .*crunch*.local.xml
61 |
62 | # Installshield output folder
63 | [Ee]xpress
64 |
65 | # DocProject is a documentation generator add-in
66 | DocProject/buildhelp/
67 | DocProject/Help/*.HxT
68 | DocProject/Help/*.HxC
69 | DocProject/Help/*.hhc
70 | DocProject/Help/*.hhk
71 | DocProject/Help/*.hhp
72 | DocProject/Help/Html2
73 | DocProject/Help/html
74 |
75 | # Click-Once directory
76 | publish
77 |
78 | # Publish Web Output
79 | *.Publish.xml
80 |
81 | # NuGet Packages Directory
82 | packages
83 |
84 | # Windows Azure Build Output
85 | csx
86 | *.build.csdef
87 |
88 | # Windows Store app package directory
89 | AppPackages/
90 |
91 | # Others
92 | [Bb]in
93 | [Oo]bj
94 | sql
95 | TestResults
96 | [Tt]est[Rr]esult*
97 | *.Cache
98 | ClientBin
99 | [Ss]tyle[Cc]op.*
100 | ~$*
101 | *.dbmdl
102 | Generated_Code #added for RIA/Silverlight projects
103 |
104 | # Backup & report files from converting an old project file to a newer
105 | # Visual Studio version. Backup files are not needed, because we have git ;-)
106 | _UpgradeReport_Files/
107 | Backup*/
108 | UpgradeLog*.XML
109 | FlacLibSharp.Sandbox/ProfilingSessions/
110 | FlacLibSharp/ProfilingSessions/
111 | FlacLibSharp/*.me5
--------------------------------------------------------------------------------
/AkkaAndWpfExample.ThermostatSystem/Actors/TemperatureSensorActor.cs:
--------------------------------------------------------------------------------
1 | using Akka.Actor;
2 | using System;
3 |
4 | namespace WpfAkkaIntegration.ThermostatSystem.Actors
5 | {
6 | public class TemperatureSensorActor : ReceiveActor
7 | {
8 | public TemperatureSensorActor()
9 | {
10 | Become(Idle);
11 | }
12 |
13 | #region Messages
14 |
15 | public class StartMeasuring { }
16 |
17 | public class StopMeasuring { }
18 |
19 | public class TakeMeasurement { }
20 |
21 | public class TemperatureMeasured
22 | {
23 | public double Temperature { get; private set; }
24 |
25 | public TemperatureMeasured(double temperature)
26 | {
27 | Temperature = temperature;
28 | }
29 | }
30 |
31 | #endregion
32 |
33 | #region State
34 |
35 | private ICancelable _schedule;
36 |
37 | #endregion
38 |
39 | #region States
40 |
41 | public void Idle()
42 | {
43 | Receive(message => handleStart());
44 | }
45 |
46 | public void Measuring()
47 | {
48 | Receive(message => HandleTakeMeasurement());
49 | Receive(message => HandleStop());
50 | }
51 |
52 | #endregion
53 |
54 | #region Handlers
55 |
56 | private void handleStart()
57 | {
58 | _schedule = Context.System.Scheduler.ScheduleTellRepeatedlyCancelable(0, 500, Self, new TakeMeasurement(), Self);
59 | Become(Measuring);
60 | }
61 |
62 | private void HandleStop()
63 | {
64 | _schedule.Cancel();
65 | Become(Idle);
66 | }
67 |
68 | private double _temperature = 0.01;
69 | private void HandleTakeMeasurement()
70 | {
71 | // TODO: Implement measurement
72 | _temperature += 0.01;
73 | var message = new TemperatureMeasured(Math.Round(_temperature,2));
74 | Context.Parent.Tell(message);
75 | }
76 |
77 | #endregion
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/AkkaAndWpfExample.ThermostatUI/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Resources;
3 | using System.Runtime.CompilerServices;
4 | using System.Runtime.InteropServices;
5 | using System.Windows;
6 |
7 | // General Information about an assembly is controlled through the following
8 | // set of attributes. Change these attribute values to modify the information
9 | // associated with an assembly.
10 | [assembly: AssemblyTitle("AkkaAndWpfExample.ThermostatUI")]
11 | [assembly: AssemblyDescription("")]
12 | [assembly: AssemblyConfiguration("")]
13 | [assembly: AssemblyCompany("")]
14 | [assembly: AssemblyProduct("AkkaAndWpfExample.ThermostatUI")]
15 | [assembly: AssemblyCopyright("Copyright © 2016")]
16 | [assembly: AssemblyTrademark("")]
17 | [assembly: AssemblyCulture("")]
18 |
19 | // Setting ComVisible to false makes the types in this assembly not visible
20 | // to COM components. If you need to access a type in this assembly from
21 | // COM, set the ComVisible attribute to true on that type.
22 | [assembly: ComVisible(false)]
23 |
24 | //In order to begin building localizable applications, set
25 | //CultureYouAreCodingWith in your .csproj file
26 | //inside a . For example, if you are using US english
27 | //in your source files, set the to en-US. Then uncomment
28 | //the NeutralResourceLanguage attribute below. Update the "en-US" in
29 | //the line below to match the UICulture setting in the project file.
30 |
31 | //[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
32 |
33 |
34 | [assembly: ThemeInfo(
35 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
36 | //(used if a resource is not found in the page,
37 | // or application resource dictionaries)
38 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
39 | //(used if a resource is not found in the page,
40 | // app, or any theme specific resource dictionaries)
41 | )]
42 |
43 |
44 | // Version information for an assembly consists of the following four values:
45 | //
46 | // Major Version
47 | // Minor Version
48 | // Build Number
49 | // Revision
50 | //
51 | // You can specify all the values or you can default the Build and Revision Numbers
52 | // by using the '*' as shown below:
53 | // [assembly: AssemblyVersion("1.0.*")]
54 | [assembly: AssemblyVersion("1.0.0.0")]
55 | [assembly: AssemblyFileVersion("1.0.0.0")]
56 |
--------------------------------------------------------------------------------
/AkkaAndWpfExample.ThermostatSystem/Actors/ThermostatActor.cs:
--------------------------------------------------------------------------------
1 | using Akka.Actor;
2 |
3 | namespace WpfAkkaIntegration.ThermostatSystem.Actors
4 | {
5 | public class ThermostatActor : PubSubActor
6 | {
7 | public ThermostatActor()
8 | {
9 | Become(Active);
10 | }
11 |
12 | #region Messages
13 |
14 | public class IncreaseTargetTemperature
15 | {
16 | public int Step { get; private set; }
17 | public IncreaseTargetTemperature(int step) { Step = step; }
18 | }
19 |
20 | public class DecreaseTargetTemperature
21 | {
22 | public double Step { get; private set; }
23 | public DecreaseTargetTemperature(double step) { Step = step; }
24 | }
25 |
26 | public class TargetTemperatureSet
27 | {
28 | public double TargetTemperature { get; private set; }
29 | public TargetTemperatureSet(double targetTemperature) { TargetTemperature = targetTemperature; }
30 | }
31 |
32 | #endregion
33 |
34 | #region State
35 |
36 | private double _targetTemperature;
37 | private IActorRef _temperatureSensor;
38 |
39 | #endregion
40 |
41 | #region States
42 |
43 | public void Active()
44 | {
45 | if (_temperatureSensor == null)
46 | {
47 | _temperatureSensor = CreateSensor();
48 | _temperatureSensor.Tell(new TemperatureSensorActor.StartMeasuring());
49 | }
50 |
51 | Receive(message => HandleTargetChange(message.Step));
52 | Receive(message => HandleTargetChange(message.Step * -1));
53 | Receive(message => HandleMeasurement(message));
54 |
55 | AcceptSubscribers();
56 | }
57 |
58 | private IActorRef CreateSensor()
59 | {
60 | var props = Props.Create();
61 | return Context.ActorOf();
62 | }
63 |
64 | #endregion
65 |
66 | #region Handlers
67 |
68 | private void HandleTargetChange(double step)
69 | {
70 | _targetTemperature += step;
71 | var message = new TargetTemperatureSet(_targetTemperature);
72 | Publish(message);
73 | }
74 |
75 | private void HandleMeasurement(TemperatureSensorActor.TemperatureMeasured message)
76 | {
77 | Publish(message);
78 | }
79 |
80 | #endregion
81 |
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/AkkaAndWpfExample.ThermostatUI/Properties/Resources.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace AkkaAndWpfExample.ThermostatUI.Properties
12 | {
13 |
14 |
15 | ///
16 | /// A strongly-typed resource class, for looking up localized strings, etc.
17 | ///
18 | // This class was auto-generated by the StronglyTypedResourceBuilder
19 | // class via a tool like ResGen or Visual Studio.
20 | // To add or remove a member, edit your .ResX file then rerun ResGen
21 | // with the /str option, or rebuild your VS project.
22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
25 | internal class Resources
26 | {
27 |
28 | private static global::System.Resources.ResourceManager resourceMan;
29 |
30 | private static global::System.Globalization.CultureInfo resourceCulture;
31 |
32 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
33 | internal Resources()
34 | {
35 | }
36 |
37 | ///
38 | /// Returns the cached ResourceManager instance used by this class.
39 | ///
40 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
41 | internal static global::System.Resources.ResourceManager ResourceManager
42 | {
43 | get
44 | {
45 | if ((resourceMan == null))
46 | {
47 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("AkkaAndWpfExample.ThermostatUI.Properties.Resources", typeof(Resources).Assembly);
48 | resourceMan = temp;
49 | }
50 | return resourceMan;
51 | }
52 | }
53 |
54 | ///
55 | /// Overrides the current thread's CurrentUICulture property for all
56 | /// resource lookups using this strongly typed resource class.
57 | ///
58 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
59 | internal static global::System.Globalization.CultureInfo Culture
60 | {
61 | get
62 | {
63 | return resourceCulture;
64 | }
65 | set
66 | {
67 | resourceCulture = value;
68 | }
69 | }
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/AkkaAndWpfExample.ThermostatSystem/AkkaAndWpfExample.ThermostatSystem.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {BA8D5CBB-EE18-42B7-B871-EB055F2F57AF}
8 | Library
9 | Properties
10 | AkkaAndWpfExample.ThermostatSystem
11 | AkkaAndWpfExample.ThermostatSystem
12 | v4.6.1
13 | 512
14 |
15 |
16 | true
17 | full
18 | false
19 | bin\Debug\
20 | DEBUG;TRACE
21 | prompt
22 | 4
23 |
24 |
25 | pdbonly
26 | true
27 | bin\Release\
28 | TRACE
29 | prompt
30 | 4
31 |
32 |
33 |
34 | ..\packages\Akka.1.0.6\lib\net45\Akka.dll
35 | True
36 |
37 |
38 | ..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll
39 | True
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
68 |
--------------------------------------------------------------------------------
/AkkaAndWpfExample.ThermostatUI/Properties/Resources.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
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 |
106 | text/microsoft-resx
107 |
108 |
109 | 2.0
110 |
111 |
112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
113 |
114 |
115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
--------------------------------------------------------------------------------
/AkkaAndWpfExample.ThermostatUI/AkkaAndWpfExample.ThermostatUI.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {A089EB45-0F65-4BED-AD47-4D067C74A31E}
8 | WinExe
9 | Properties
10 | AkkaAndWpfExample.ThermostatUI
11 | AkkaAndWpfExample.ThermostatUI
12 | v4.6.1
13 | 512
14 | {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
15 | 4
16 | true
17 |
18 |
19 | AnyCPU
20 | true
21 | full
22 | false
23 | bin\Debug\
24 | DEBUG;TRACE
25 | prompt
26 | 4
27 |
28 |
29 | AnyCPU
30 | pdbonly
31 | true
32 | bin\Release\
33 | TRACE
34 | prompt
35 | 4
36 |
37 |
38 |
39 | ..\packages\MvvmLightLibs.5.2.0.0\lib\net45\GalaSoft.MvvmLight.dll
40 | True
41 |
42 |
43 | ..\packages\MvvmLightLibs.5.2.0.0\lib\net45\GalaSoft.MvvmLight.Extras.dll
44 | True
45 |
46 |
47 | ..\packages\MvvmLightLibs.5.2.0.0\lib\net45\GalaSoft.MvvmLight.Platform.dll
48 | True
49 |
50 |
51 | ..\packages\CommonServiceLocator.1.3\lib\portable-net4+sl5+netcore45+wpa81+wp8\Microsoft.Practices.ServiceLocation.dll
52 | True
53 |
54 |
55 |
56 |
57 | ..\packages\MvvmLightLibs.5.2.0.0\lib\net45\System.Windows.Interactivity.dll
58 | True
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 | 4.0
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 | MSBuild:Compile
76 | Designer
77 |
78 |
79 |
80 |
81 | MSBuild:Compile
82 | Designer
83 |
84 |
85 | App.xaml
86 | Code
87 |
88 |
89 | MainWindow.xaml
90 | Code
91 |
92 |
93 |
94 |
95 | Code
96 |
97 |
98 | True
99 | True
100 | Resources.resx
101 |
102 |
103 | True
104 | Settings.settings
105 | True
106 |
107 |
108 | ResXFileCodeGenerator
109 | Resources.Designer.cs
110 |
111 |
112 |
113 | SettingsSingleFileGenerator
114 | Settings.Designer.cs
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
129 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # AkkaAndWpfExample
2 |
3 | Code backing up a blogpost on integrating Akka.NET and WPF. This branch is the starting point, the solution I came up with is in the "bridged" branch.
4 |
5 | The post: https://blog.aaronlenoir.com/2016/04/20/integrating-wpf-and-akka-net-2
6 |
7 | Full text:
8 |
9 | I'm wondering how to combine an Akka.NET actor system with a WPF front-end.
10 |
11 | The [Actor Model](http://doc.akka.io/docs/akka/snapshot/general/actors.html) provides a nice way to build concurrent systems. There are several implementations for .NET. [Orleans](https://github.com/dotnet/orleans) and [Akka.Net](http://getakka.net/) are the best known.
12 |
13 | As an excercise, let's build a "thermostat" system.
14 |
15 | I'll leave out some implementation details so we can focus on the integration.
16 |
17 | ## The Thermostat System
18 |
19 | The system will display both the current and the desired temperature to the user.
20 |
21 | It'll have two buttons to increase and decrease the desired temperature.
22 |
23 | In the background, the system should control the heating but we'll ignore that bit here.
24 |
25 | ### UI
26 |
27 | We'll create a basic WPF application with MVVM. The ViewModel will have a property for the current temperature and the target temperature.
28 |
29 | It also accepts two commands: ```IncreaseTargetTemperature``` and ```DecreaseTargetTemperature```.
30 |
31 | ### Back-end
32 |
33 | The back-end should have a thermometer that takes measurements on a regular basis.
34 |
35 | It should also keep track of the target temperature, set by the user.
36 |
37 | The target temperature can change by two messages sent to the system:
38 |
39 | * Increase the target temperature
40 | * Reduce the target temperature
41 |
42 | As output, the system should emit:
43 |
44 | * The current temperature, when a measurement takes place
45 | * The target temperature, when it's changed
46 |
47 | ## Thermostat Actor System
48 |
49 | ### Actors
50 |
51 | This simple actor system can consist of two Actors:
52 |
53 | * TemperatureSensorActor
54 | * ThermostatActor
55 |
56 | ### TemperatureSensor
57 |
58 | The TemperatureSensor sends a message ```TemperatureMeasured``` to its Parent actor. This happens as a response to a ```TakeMeasurement``` message.
59 |
60 | The TemperatureSensor sends the ```TakeMeasurement``` to itself on a fixed interval.
61 |
62 | ### Thermostat
63 |
64 | The Thermostat is in charge of creating a TemperatureSensor.
65 |
66 | It's a ["pub-sub"](https://github.com/petabridge/akka-bootcamp/blob/0ac15bdc4dbe54f9169e8e6e026bf0ec28e8f2a2/src/Unit-2/lesson3/README.md#how-do-i-do-pubsub-with-akkanet-actors) actor that should send to its subscribers the following messages:
67 |
68 | * ```TargetTemperatureSet```
69 | * ```TemperatureMeasured```
70 |
71 | ## Bridge
72 |
73 | With the UI and the actor system ready, we can start to make them talk with each other.
74 |
75 | > The code we start with is on [github](https://github.com/AaronLenoir/AkkaAndWpfExample/tree/master). If you want to try this yourself first, you could fork it. If not, just read on to see how I do it.
76 |
77 | To support this, WPF should react to messages sent by the Actors. At the same time, WPF should be able to send messages to the Actor System, in some way.
78 |
79 | To support this communication we could create a "Bridge". The Bridge will contain:
80 |
81 | * A Bridge Actor
82 | * One or more public functions
83 |
84 | The Bridge Actor will have a reference to the ViewModel. The ViewModel will have a reference to the Bridge (not the Bridge Actor!).
85 |
86 | The Bridge can expose methods the ViewModel can call which pass messages to the Actor System. For example, a method ```IncreaseTargetTemperature```.
87 |
88 | The Bridge Actor calls functions exposed by the ViewModel. These will update the ViewModel's properties. The actor does this only while handling messages.
89 |
90 | ### Bridge Interfaces
91 |
92 | To avoid exposing the entire ViewModel to the Bridge Actor, an interface seems like a good idea. And an interface to abstract away the Bridge passed to the ViewModel would also help.
93 |
94 | In short, we'll have two Interfaces. One implemented by the ViewModel, the other by the Bridge itself.
95 |
96 | We can define these two interfaces:
97 |
98 | * ```IThermostatView```
99 | * ```UpdateCurrentTemperature```
100 | * ```UpdateTargetTemperature```
101 | * ```IThermostatBridge```
102 | * ```IncreaseTargetTemperature```
103 | * ```DecreaseTargetTemperature```
104 |
105 | The Bridge Actor references ```IThermostatView``` while the ViewModel implements it.
106 |
107 | The ViewModel references ```IThermostatBridge``` while the Bridge implements it.
108 |
109 | ## Implementation
110 |
111 | ### Views
112 |
113 | The views, as discussed above, are simple:
114 |
115 | ```
116 | // Implemented by the Bridge
117 | public interface IThermostatBridge
118 | {
119 | void IncreaseTargetTemperature();
120 | void DecreaseTargetTemperature();
121 | }
122 | ```
123 |
124 | ```
125 | // Implemented by the ViewModel (in the WPF project)
126 | public interface IThermostatView
127 | {
128 | void UpdateCurrentTemperature(double currentTemperature);
129 | void UpdateTargetTemperature(double targetTemperature);
130 | }
131 | ```
132 |
133 | ### Bridge Actor
134 |
135 | The Bridge Actor is small, just passing messages around. It does have two dependencies.
136 |
137 | ```
138 | public class BridgeActor : ReceiveActor
139 | {
140 | private IThermostatView _thermostatView;
141 | private IActorRef _thermostatActor;
142 |
143 | public BridgeActor(IThermostatView thermostatView, IActorRef thermostatActor)
144 | {
145 | _thermostatView = thermostatView;
146 | _thermostatActor = thermostatActor;
147 | Become(Active);
148 | }
149 |
150 | public void Active()
151 | {
152 | Receive(message => _thermostatView.UpdateCurrentTemperature(message.Temperature));
153 | Receive(message => _thermostatView.UpdateTargetTemperature(message.TargetTemperature));
154 | Receive(message => _thermostatActor.Tell(message));
155 | Receive(message => _thermostatActor.Tell(message));
156 | }
157 | }
158 | ```
159 |
160 | ### Bridge
161 |
162 | The bridge will implement the ```IThermostatBridge``` interface:
163 |
164 | ```
165 | public class ThermostatBridge : IThermostatBridge
166 | {
167 | private IActorRef _bridgeActor;
168 |
169 | private readonly IncreaseTargetTemperature increaseMessage = new IncreaseTargetTemperature(1);
170 | private readonly DecreaseTargetTemperature decreaseMessage = new DecreaseTargetTemperature(1);
171 |
172 | public ThermostatBridge(IActorRef bridgeActor)
173 | {
174 | _bridgeActor = bridgeActor;
175 | }
176 |
177 | public void IncreaseTargetTemperature()
178 | {
179 | _bridgeActor.Tell(increaseMessage);
180 | }
181 |
182 | public void DecreaseTargetTemperature()
183 | {
184 | _bridgeActor.Tell(decreaseMessage);
185 | }
186 | }
187 | ```
188 | ### System Creation
189 |
190 | I already had a seperate class to create the Actor System. For the excercise, I'll create the system when the WPF app starts. But this might not be the ideal place.
191 |
192 | ```
193 | public partial class App : Application
194 | {
195 | private static WpfAkkaIntegration.ThermostatSystem.ThermostatSystem _system = new WpfAkkaIntegration.ThermostatSystem.ThermostatSystem();
196 |
197 | public static WpfAkkaIntegration.ThermostatSystem.ThermostatSystem ThermostatSystem => _system;
198 | }
199 | ```
200 |
201 | In that class, there's a function ```CreateThermostatBridge``` which can create our Bridge. Here's the full ThermostatSystem class:
202 |
203 | ```
204 | public class ThermostatSystem
205 | {
206 | private ActorSystem _system;
207 | private IActorRef _thermostatActor;
208 |
209 | public ThermostatSystem()
210 | {
211 | _system = ActorSystem.Create(nameof(ThermostatSystem));
212 | _thermostatActor = CreateThermostatActor();
213 | }
214 |
215 | private IActorRef CreateThermostatActor()
216 | {
217 | var props = Props.Create();
218 | return _system.ActorOf(props, "thermostat");
219 | }
220 |
221 | public IThermostatBridge CreateThermostatBridge(IThermostatView thermostatView)
222 | {
223 | var bridgeActor = CreateBridgeActor(thermostatView);
224 | _thermostatActor.Tell(new Subscribe(bridgeActor));
225 |
226 | return new ThermostatBridge(bridgeActor);
227 | }
228 |
229 | private IActorRef CreateBridgeActor(IThermostatView thermostatView)
230 | {
231 | var props = Props.Create(() => new BridgeActor(thermostatView, _thermostatActor))
232 | .WithDispatcher("akka.actor.synchronized-dispatcher");
233 | return _system.ActorOf(props, "bridge");
234 | }
235 | }
236 | ```
237 |
238 | #### Synchronized Dispatcher
239 |
240 | The code above creates the BridgeActor using the [synchronized dispatcher](http://getakka.net/docs/working-with-actors/Dispatchers#synchronizeddispatcher).
241 |
242 | This makes that actor run on the UI-thread, so calling methods on the ViewModel should be safe.
243 |
244 | From the [akka.net docs](http://getakka.net/docs/working-with-actors/Dispatchers#synchronizeddispatcher):
245 |
246 | > You may use this dispatcher to create actors that update UIs in a reactive manner. An application that displays real-time updates of stock prices may have a dedicated actor to update the UI controls directly for example.
247 |
248 | > **Note:** As a general rule, actors running in this dispatcher shouldn't do much work. Avoid doing any extra work that may be done by actors running in other pools.
249 |
250 | ### Bridge Creation
251 |
252 | Finally we have everything to hook it all up in the ViewModel.
253 |
254 | ```
255 | public MainViewModel()
256 | {
257 | _bridge = App.ThermostatSystem.CreateThermostatBridge(this);
258 |
259 | IncreaseTargetTemperature = new RelayCommand(() => _bridge.IncreaseTargetTemperature());
260 | DecreaseTargetTemperature = new RelayCommand(() => _bridge.DecreaseTargetTemperature());
261 | }
262 | ```
263 |
264 | You can also see the implementation of the two commands the view can receive.
265 |
266 | I do this in the constructor of the ViewModel for this excercise. In real life, I suppose dependency injection would be adviceable. But then the ViewModel would still need to assign itself to the Bridge afterwards.
267 |
268 | ## Result
269 |
270 | Running the app gives me this WPF app that updates its view based on messages that occur in the Actor System.
271 |
272 | Additionally, the view triggers changes to the system by sending it messages. It does this when it receives a command.
273 |
274 | When you click a button in WPF, all it does is ask the bridge to increase the target temperature. The Actor System will then process that message. When it's done, the Actor System forwards a new message back to WPF so it can update the view.
275 |
276 | This is all asynchronous, because of the actor system.
277 |
278 | In the example, the current temperature only goes up. This is because I did not simulate an actual room that heats. I decided to just let the temperature increase at every measurement taken. Just to show the temperature is changing.
279 |
280 | ### Code
281 |
282 | The example project is on github:
283 |
284 | * [Finished](https://github.com/AaronLenoir/AkkaAndWpfExample/tree/bridged)
285 | * [Before creating the bridge](https://github.com/AaronLenoir/AkkaAndWpfExample/tree/master)
286 |
287 | ## Conclusion
288 |
289 | It's possible to loosely couple a WPF app with an Akka.NET Actor System. I used something I called a Bridge, [but there must be a better name](https://web.archive.org/web/20160420223239/http://martinfowler.com/bliki/TwoHardThings.html).
290 |
291 | Something I don't like here is that the Bridge and Bridge Actor should be more generic. It mentions "Thermostat" everywhere.
292 |
293 | Do you think this is a reasonable way to put a WPF front-end on an Akka.NET Actor System? Let me know in the comments, or on [twitter](https://www.twitter.com/lenoir_aaron) or something.
294 |
--------------------------------------------------------------------------------