Skip to content

Commit 5265970

Browse files
authored
Wpf example (#666)
* Initial checkin of code from stream * Cleaning up example code * Updating to leverage PocketView DSL
1 parent b2079c8 commit 5265970

File tree

11 files changed

+385
-0
lines changed

11 files changed

+385
-0
lines changed

samples/connect-wpf/App.xaml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<Application x:Class="WpfConnect.App"
2+
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3+
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4+
xmlns:local="clr-namespace:WpfConnect"
5+
StartupUri="MainWindow.xaml">
6+
<Application.Resources>
7+
8+
</Application.Resources>
9+
</Application>

samples/connect-wpf/App.xaml.cs

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
using Microsoft.DotNet.Interactive;
2+
using Microsoft.DotNet.Interactive.Commands;
3+
using Microsoft.DotNet.Interactive.CSharp;
4+
using Microsoft.DotNet.Interactive.Server;
5+
using System.CommandLine;
6+
using System.CommandLine.Invocation;
7+
using System.IO;
8+
using System.Threading.Tasks;
9+
using System.Windows;
10+
11+
namespace WpfConnect
12+
{
13+
/// <summary>
14+
/// Interaction logic for App.xaml
15+
/// </summary>
16+
public partial class App : Application
17+
{
18+
private CompositeKernel _Kernel;
19+
20+
private const string NamedPipeName = "InteractiveWpf";
21+
22+
private bool RunOnDispatcher { get; set; }
23+
24+
protected override void OnStartup(StartupEventArgs e)
25+
{
26+
base.OnStartup(e);
27+
_Kernel = new CompositeKernel();
28+
_Kernel.UseLog();
29+
30+
AddDispatcherCommand(_Kernel);
31+
32+
CSharpKernel csharpKernel = RegisterCSharpKernel();
33+
34+
_ = Task.Run(async () =>
35+
{
36+
//Load WPF app assembly
37+
await csharpKernel.SendAsync(new SubmitCode(@$"#r ""{typeof(App).Assembly.Location}""
38+
using {nameof(WpfConnect)};"));
39+
//Add the WPF app as a variable that can be accessed
40+
await csharpKernel.SetVariableAsync("App", this);
41+
42+
//Start named pipe
43+
_Kernel.UseNamedPipeKernelServer(NamedPipeName, new DirectoryInfo("."));
44+
});
45+
}
46+
47+
protected override void OnExit(ExitEventArgs e)
48+
{
49+
_Kernel?.Dispose();
50+
base.OnExit(e);
51+
}
52+
53+
private void AddDispatcherCommand(Kernel kernel)
54+
{
55+
var dispatcherCommand = new Command("#!dispatcher", "Enable or disable running code on the Dispatcher")
56+
{
57+
new Option<bool>("--enabled", getDefaultValue:() => true)
58+
};
59+
dispatcherCommand.Handler = CommandHandler.Create<bool>(enabled =>
60+
{
61+
RunOnDispatcher = enabled;
62+
});
63+
kernel.AddDirective(dispatcherCommand);
64+
}
65+
66+
private CSharpKernel RegisterCSharpKernel()
67+
{
68+
var csharpKernel = new CSharpKernel()
69+
.UseDefaultFormatting()
70+
.UseNugetDirective()
71+
.UseKernelHelpers()
72+
.UseWho()
73+
.UseDotNetVariableSharing()
74+
//This is added locally
75+
.UseWpf();
76+
77+
_Kernel.Add(csharpKernel);
78+
79+
csharpKernel.AddMiddleware(async (KernelCommand command, KernelInvocationContext context, KernelPipelineContinuation next) =>
80+
{
81+
if (RunOnDispatcher)
82+
{
83+
await Dispatcher.InvokeAsync(async () => await next(command, context));
84+
}
85+
else
86+
{
87+
await next(command, context);
88+
}
89+
});
90+
91+
return csharpKernel;
92+
}
93+
}
94+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
using System.Windows;
2+
3+
[assembly: ThemeInfo(
4+
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
5+
//(used if a resource is not found in the page,
6+
// or application resource dictionaries)
7+
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
8+
//(used if a resource is not found in the page,
9+
// app, or any theme specific resource dictionaries)
10+
)]
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<Window x:Class="WpfConnect.MainWindow"
2+
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3+
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4+
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
5+
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
6+
mc:Ignorable="d"
7+
Title="MainWindow" Height="450" Width="800">
8+
<Grid>
9+
<TextBlock Text="{Binding Text}" />
10+
</Grid>
11+
</Window>
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
using Microsoft.DotNet.Interactive;
2+
using System.Windows;
3+
4+
namespace WpfConnect
5+
{
6+
/// <summary>
7+
/// Interaction logic for MainWindow.xaml
8+
/// </summary>
9+
public partial class MainWindow : Window
10+
{
11+
public MainWindow()
12+
{
13+
InitializeComponent();
14+
DataContext = new MainWindowViewModel();
15+
}
16+
}
17+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
using System.ComponentModel;
2+
3+
namespace WpfConnect
4+
{
5+
public class MainWindowViewModel : INotifyPropertyChanged
6+
{
7+
public event PropertyChangedEventHandler PropertyChanged;
8+
9+
private string _text = "Init Value";
10+
public string Text
11+
{
12+
get => _text;
13+
set
14+
{
15+
if (_text != value)
16+
{
17+
_text = value;
18+
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(nameof(Text)));
19+
}
20+
}
21+
}
22+
}
23+
}

samples/connect-wpf/NuGet.config

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<configuration>
3+
<solution>
4+
<add key="disableSourceControlIntegration" value="true" />
5+
</solution>
6+
<packageSources>
7+
<clear />
8+
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
9+
<add key="dotnet5" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet5/nuget/v3/index.json" />
10+
<add key="dotnet-tools" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-tools/nuget/v3/index.json" />
11+
<add key="dotnet-try" value="https://dotnet.myget.org/F/dotnet-try/api/v3/index.json" />
12+
</packageSources>
13+
</configuration>

samples/connect-wpf/README.dib

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
#!markdown
2+
3+
## Connect to the WPF app
4+
Before running cell, ensure the WPF app is running. This will connect the notebook to the WPF app. This must be run before any of the other cells.
5+
6+
#!csharp
7+
8+
#!connect named-pipe --kernel-name wpf --pipe-name InteractiveWpf
9+
10+
#!markdown
11+
12+
## Formatter (Rendering)
13+
14+
#!csharp
15+
16+
#!wpf
17+
#!dispatcher
18+
using System.Windows.Media;
19+
20+
App.MainWindow.Background = new SolidColorBrush(Colors.Fuchsia);
21+
App.MainWindow.Background
22+
23+
#!csharp
24+
25+
#!wpf
26+
#!dispatcher
27+
using System.Windows.Media;
28+
using System.Windows.Controls;
29+
using System.Windows;
30+
31+
32+
var grid = (Grid)App.MainWindow.Content;
33+
grid.Background = new SolidColorBrush(Colors.Blue);
34+
grid
35+
36+
#!markdown
37+
38+
## View Model Stuff
39+
40+
Create and apply a new view model to the main window.
41+
42+
#!csharp
43+
44+
#!wpf
45+
using System.ComponentModel;
46+
public class TestViewModel : INotifyPropertyChanged
47+
{
48+
public event PropertyChangedEventHandler PropertyChanged;
49+
50+
private string _text = "Notebook Initial Value";
51+
public string Text
52+
{
53+
get => _text;
54+
set
55+
{
56+
if (_text != value)
57+
{
58+
_text = value;
59+
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(nameof(Text)));
60+
}
61+
}
62+
}
63+
}
64+
65+
var vm = new TestViewModel();
66+
#!dispatcher
67+
App.MainWindow.DataContext = vm;
68+
69+
#!markdown
70+
71+
Update the value on the data bound property.
72+
73+
#!csharp
74+
75+
#!wpf
76+
vm.Text = "Value changed!"
77+
78+
#!markdown
79+
80+
## Dispatcher stuff
81+
82+
Demonstate enabling and disabling running code on the dispatcher.
83+
84+
#!csharp
85+
86+
#!wpf
87+
88+
#!dispatcher --enabled true
89+
//This should work
90+
App.MainWindow.Title
91+
92+
#!dispatcher --enabled false
93+
//This is expected to fail
94+
App.MainWindow.Title
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
2+
3+
<PropertyGroup>
4+
<TargetFramework>netcoreapp3.1</TargetFramework>
5+
<OutputType>WinExe</OutputType>
6+
<UseWPF>true</UseWPF>
7+
<DisableArcade>1</DisableArcade>
8+
</PropertyGroup>
9+
10+
<ItemGroup>
11+
<PackageReference Include="Microsoft.DotNet.Interactive.CSharp" Version="1.0.0-beta.20374.1" />
12+
</ItemGroup>
13+
14+
</Project>

samples/connect-wpf/WpfConnect.sln

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 12.00
3+
# Visual Studio Version 16
4+
VisualStudioVersion = 16.0.30310.162
5+
MinimumVisualStudioVersion = 10.0.40219.1
6+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WpfConnect", "WpfConnect.csproj", "{3DE53DCF-00AD-4614-9CFD-A342317E7347}"
7+
EndProject
8+
Global
9+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
10+
Debug|Any CPU = Debug|Any CPU
11+
Release|Any CPU = Release|Any CPU
12+
EndGlobalSection
13+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
14+
{3DE53DCF-00AD-4614-9CFD-A342317E7347}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15+
{3DE53DCF-00AD-4614-9CFD-A342317E7347}.Debug|Any CPU.Build.0 = Debug|Any CPU
16+
{3DE53DCF-00AD-4614-9CFD-A342317E7347}.Release|Any CPU.ActiveCfg = Release|Any CPU
17+
{3DE53DCF-00AD-4614-9CFD-A342317E7347}.Release|Any CPU.Build.0 = Release|Any CPU
18+
EndGlobalSection
19+
GlobalSection(SolutionProperties) = preSolution
20+
HideSolutionNode = FALSE
21+
EndGlobalSection
22+
GlobalSection(ExtensibilityGlobals) = postSolution
23+
SolutionGuid = {18629B67-9CB9-4089-A138-6C2BE0D43500}
24+
EndGlobalSection
25+
EndGlobal

0 commit comments

Comments
 (0)