Skip to content

Commit 40b03e1

Browse files
authored
feat(notification): replace MessageBox with toast notifications (#25)
Add Windows toast notifications as an alternative to MessageBox for breakpoint hit notifications. Users can now choose their preferred notification style via Tools > Options > Breakpoint Notifier > General. - Add Microsoft.Toolkit.Uwp.Notifications package - Create Options page with NotificationStyle setting (MessageBox/Toast/Both) - Default to Toast for less intrusive notifications Closes #4
1 parent 67d99f8 commit 40b03e1

4 files changed

Lines changed: 70 additions & 5 deletions

File tree

src/CodingWithCalvin.BreakpointNotifier/BreakpointNotifierPackage.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.Runtime.InteropServices;
33
using System.Threading;
4+
using CodingWithCalvin.BreakpointNotifier.Options;
45
using CodingWithCalvin.Otel4Vsix;
56
using Microsoft.VisualStudio.Shell;
67
using Microsoft.VisualStudio.Shell.Interop;
@@ -12,6 +13,7 @@ namespace CodingWithCalvin.BreakpointNotifier
1213
[InstalledProductRegistration(Vsix.Name, Vsix.Description, Vsix.Version)]
1314
[Guid("136f3004-4048-4dd9-bd6d-7ff910b2c900")]
1415
[ProvideAutoLoad(UIContextGuids80.SolutionExists, PackageAutoLoadFlags.BackgroundLoad)]
16+
[ProvideOptionPage(typeof(GeneralOptions), "Breakpoint Notifier", "General", 0, 0, true)]
1517
public sealed class BreakpointNotifierPackage : AsyncPackage
1618
{
1719
protected override async Task InitializeAsync(
@@ -35,7 +37,7 @@ IProgress<ServiceProgressData> progress
3537

3638
builder.Initialize();
3739

38-
DebuggerEvents.Initialize();
40+
DebuggerEvents.Initialize(this);
3941
}
4042

4143
protected override void Dispose(bool disposing)

src/CodingWithCalvin.BreakpointNotifier/CodingWithCalvin.BreakpointNotifier.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
<ItemGroup>
1212
<PackageReference Include="CodingWithCalvin.Otel4Vsix" Version="0.2.2" />
13+
<PackageReference Include="Microsoft.Toolkit.Uwp.Notifications" Version="7.*" />
1314
<PackageReference Include="Microsoft.VisualStudio.SDK" Version="17.14.40265" />
1415
<PackageReference Include="Microsoft.VSSDK.BuildTools" Version="17.*" PrivateAssets="all" />
1516
</ItemGroup>

src/CodingWithCalvin.BreakpointNotifier/DebuggerEvents.cs

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
using System;
22
using System.Collections.Generic;
33
using System.Windows.Forms;
4+
using CodingWithCalvin.BreakpointNotifier.Options;
45
using CodingWithCalvin.Otel4Vsix;
56
using Microsoft;
7+
using Microsoft.Toolkit.Uwp.Notifications;
68
using Microsoft.VisualStudio;
79
using Microsoft.VisualStudio.Debugger.Interop;
810
using Microsoft.VisualStudio.Shell;
@@ -12,20 +14,24 @@ namespace CodingWithCalvin.BreakpointNotifier
1214
{
1315
public sealed class DebuggerEvents : IVsDebuggerEvents, IDebugEventCallback2
1416
{
15-
private DebuggerEvents()
17+
private readonly BreakpointNotifierPackage _package;
18+
19+
private DebuggerEvents(BreakpointNotifierPackage package)
1620
{
1721
ThreadHelper.ThrowIfNotOnUIThread();
1822

23+
_package = package;
24+
1925
var debugger = (IVsDebugger)
2026
ServiceProvider.GlobalProvider.GetService(typeof(IVsDebugger));
2127
Assumes.Present(debugger);
2228
debugger.AdviseDebuggerEvents(this, out _);
2329
debugger.AdviseDebugEventCallback(this);
2430
}
2531

26-
public static DebuggerEvents Initialize()
32+
public static DebuggerEvents Initialize(BreakpointNotifierPackage package)
2733
{
28-
return new DebuggerEvents();
34+
return new DebuggerEvents(package);
2935
}
3036

3137
public int OnModeChange(DBGMODE dbgmodeNew)
@@ -43,14 +49,16 @@ public int Event(
4349
ref Guid riidEvent,
4450
uint dwAttrib)
4551
{
52+
ThreadHelper.ThrowIfNotOnUIThread();
53+
4654
if (pEvent is IDebugBreakpointEvent2)
4755
{
4856
using var activity = VsixTelemetry.StartCommandActivity("BreakpointNotifier.BreakpointHit");
4957

5058
try
5159
{
5260
VsixTelemetry.LogInformation("Breakpoint hit detected");
53-
MessageBox.Show("Breakpoint Hit!");
61+
ShowNotification();
5462
}
5563
catch (Exception ex)
5664
{
@@ -64,5 +72,32 @@ public int Event(
6472

6573
return VSConstants.S_OK;
6674
}
75+
76+
private void ShowNotification()
77+
{
78+
ThreadHelper.ThrowIfNotOnUIThread();
79+
80+
var style = GetNotificationStyle();
81+
82+
if (style == NotificationStyle.MessageBox || style == NotificationStyle.Both)
83+
{
84+
MessageBox.Show("Breakpoint Hit!");
85+
}
86+
87+
if (style == NotificationStyle.Toast || style == NotificationStyle.Both)
88+
{
89+
new ToastContentBuilder()
90+
.AddText("Breakpoint Hit!")
91+
.Show();
92+
}
93+
}
94+
95+
private NotificationStyle GetNotificationStyle()
96+
{
97+
ThreadHelper.ThrowIfNotOnUIThread();
98+
99+
var options = _package.GetDialogPage(typeof(GeneralOptions)) as GeneralOptions;
100+
return options?.Style ?? NotificationStyle.Toast;
101+
}
67102
}
68103
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
using System.ComponentModel;
2+
using System.Runtime.InteropServices;
3+
using Microsoft.VisualStudio.Shell;
4+
5+
namespace CodingWithCalvin.BreakpointNotifier.Options
6+
{
7+
public enum NotificationStyle
8+
{
9+
[Description("Message Box")]
10+
MessageBox,
11+
12+
[Description("Toast Notification")]
13+
Toast,
14+
15+
[Description("Both")]
16+
Both
17+
}
18+
19+
[ComVisible(true)]
20+
public class GeneralOptions : DialogPage
21+
{
22+
[Category("Notification")]
23+
[DisplayName("Notification Style")]
24+
[Description("Choose how breakpoint notifications are displayed. Toast notifications are less intrusive and don't steal focus.")]
25+
public NotificationStyle Style { get; set; } = NotificationStyle.Toast;
26+
}
27+
}

0 commit comments

Comments
 (0)