This notebook and the C# project in this folder demonstrates how you can use .NET Interactive to embed a kernel within an app, connect to it from another kernel, and use the notebook to change the app's runtime state.
First, let's start the WPF app and connect to it.
Start-Process -NoNewWindow dotnet run
Once the cell above has finished running, you should see the app's window open. Next, we'll connect to it using a named pipe. The code that sets this up within the WPF app can be seen in App.xaml.cs
.
#!connect named-pipe --kernel-name wpf --pipe-name InteractiveWpf
Kernel added: #!wpf
The topology of connected kernels now looks like this:
flowchart LR
subgraph WPF app
embedded["Embedded C# kernel"]
end
subgraph notebook
CompositeKernel-->n1["Local C# kernel"]
CompositeKernel-->n2
n2["#!wpf kernel added using #!connect"]--named pipe-->embedded
end
The notebook outputs here are displayed using custom formatters defined within the WPF app itself. Take a look at the file WpfFormatterMixins.cs
.
You'll also notice that you can get completions for the App
object which is exposed to the notebook's kernel by the embedded kernel.
#!dispatcher
using System.Windows.Media;
App.MainWindow.Background = new SolidColorBrush(Colors.Fuchsia);
App.MainWindow.Background
#!dispatcher
using System.Windows.Media;
using System.Windows.Controls;
using System.Windows;
var content = (Grid)App.MainWindow.Content;
content.Background = new SolidColorBrush(Colors.RoyalBlue);
content.UpdateLayout();
content
Create and apply a new view model to the main window.
using System.ComponentModel;
using System.Collections.ObjectModel;
public class TestViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _text = "Initial Value from notebook view model";
public string Text
{
get => _text;
set
{
if (_text != value)
{
_text = value;
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(nameof(Text)));
}
}
}
}
var vm = new TestViewModel();
#!wpf
#!dispatcher
App.MainWindow.DataContext = vm;
Update the value on the data bound property.
vm.Text = "Value changed!"
Value changed!
Demonstate enabling and disabling running code on the dispatcher.
#!dispatcher --enabled
//This should work
App.MainWindow.Title = "Title change executed on dispatcher thread";
#!dispatcher --enabled false
//This is expected to fail
App.MainWindow.Title = "Not so much";
System.InvalidOperationException: The calling thread cannot access this object because a different thread owns it. at System.Windows.Threading.Dispatcher.<VerifyAccess>g__ThrowVerifyAccess|7_0() at System.Windows.Application.get_MainWindow() at Submission#15.<<Initialize>>d__0.MoveNext() --- End of stack trace from previous location --- at Microsoft.CodeAnalysis.Scripting.ScriptExecutionState.RunSubmissionsAsync[TResult](ImmutableArray`1 precedingExecutors, Func`2 currentExecutor, StrongBox`1 exceptionHolderOpt, Func`2 catchExceptionOpt, CancellationToken cancellationToken)