Skip to main content
January 28, 2022
Uncategorized

Adding event handlers: Sounds, Dark Mode & Windowing (Part 2)

Adding event handlers: Sounds, Dark Mode & Windowing (Part 2)

Note that in part 1 of the WinAppSDK Blog Series you implemented the visual layer of the DrumPad using WinUI 3 controls. However, if you click on any buttons, nothing happens, because we have not defined any actions, which we will do now. You will be guided to accomplish the following in this post: 

  • Play a sound when a drum pad button is clicked
  • Switch into dark mode when the toggle switch is turned on
  • Go into a specific windowing mode when a presenter mode is selected

Play a sound when a drum pad button is clicked

  1. Download sound clips to your project’s Assets folder

NOTE: You may download any .wav sound clips for this part. Optionally, you could use my sound clips here as well. Store the sound clips under your project’s Assets folder because that is the folder path we will provide to specify which sound to play.

    2. Install the System.Windows.Extensions NuGet Package

NOTE: Playing a sound in WinUI apps requires using a library called System.Media, accessible through a NuGet Package. Following these two steps to install the NuGet Package in Visual Studio:

  • Navigate to Solution Explorer, under Dependencies, right-click on Packages, select Manage NuGet Packages
  • Select the Browse tab, search System.Windows.Extensions and install it

    3. Add the following namespace to MainAppWindow.xaml.cs:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
using System.Media;
using System.Media;
using System.Media;

4. Create a call-back function that plays a sound in class MainWindow : Window :

MainAppWindow.xaml.cs:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
private void pad_clicked(object sender, RoutedEventArgs e)
{
// get the full path to your app’s folder where it is installed
var installedPath = Windows.ApplicationModel.Package.Current.InstalledLocation.Path;
// join path above with the sub-paths in your Assets folder and the specific sound file
var soundFile = Path.Join(installedPath, "Assets", "rim.wav");
SoundPlayer player = new System.Media.SoundPlayer(soundFile);
player.Play();
}
private void pad_clicked(object sender, RoutedEventArgs e) { // get the full path to your app’s folder where it is installed var installedPath = Windows.ApplicationModel.Package.Current.InstalledLocation.Path; // join path above with the sub-paths in your Assets folder and the specific sound file var soundFile = Path.Join(installedPath, "Assets", "rim.wav"); SoundPlayer player = new System.Media.SoundPlayer(soundFile); player.Play(); }
private void pad_clicked(object sender, RoutedEventArgs e)
{
    // get the full path to your app’s folder where it is installed
    var installedPath = Windows.ApplicationModel.Package.Current.InstalledLocation.Path;
    // join path above with the sub-paths in your Assets folder and the specific sound file
    var soundFile = Path.Join(installedPath, "Assets", "rim.wav");

    SoundPlayer player = new System.Media.SoundPlayer(soundFile);
    player.Play();
}

5. link the call-back function above to a button via the Click parameter:

MainWindow.xaml:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<Button Grid.Row="0" Grid.Column="0" … … Click="pad_clicked" />
<Button Grid.Row="0" Grid.Column="0" … … Click="pad_clicked" />
<Button Grid.Row="0" Grid.Column="0" … … Click="pad_clicked" />

Repeat step 4 and 5 to attach another sound clip to a different button until all the buttons have a click action! For your reference, check out my MainWindow.xaml.cs and MainWindow.xaml on GitHub.

Implement dark mode

The dark mode is an app appearance where the content is laid out on a dark background. It gives the app a different aesthetic look and can improve readability, especially in a low-light environment.

  1. Define SolidColorBrush under the light and dark themes below the comment :

App.xaml:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Light">
<SolidColorBrush x:Key="HomePageBackgroundBrush" Color="Transparent"/>
</ResourceDictionary>
<ResourceDictionary x:Key="Dark">
<SolidColorBrush x:Key="HomePageBackgroundBrush" Color="Black"/>
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
<ResourceDictionary.ThemeDictionaries> <ResourceDictionary x:Key="Light"> <SolidColorBrush x:Key="HomePageBackgroundBrush" Color="Transparent"/> </ResourceDictionary> <ResourceDictionary x:Key="Dark"> <SolidColorBrush x:Key="HomePageBackgroundBrush" Color="Black"/> </ResourceDictionary> </ResourceDictionary.ThemeDictionaries>
<ResourceDictionary.ThemeDictionaries>
    <ResourceDictionary x:Key="Light">
        <SolidColorBrush x:Key="HomePageBackgroundBrush" Color="Transparent"/>
    </ResourceDictionary>
    <ResourceDictionary x:Key="Dark">
        <SolidColorBrush x:Key="HomePageBackgroundBrush" Color="Black"/>
    </ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>

    2. Indicate which theme to use via the Background parameter (here we specified it on the outermost Grid in MainWindow.xaml):

MainWindow.xaml:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<Grid Background="{ThemeResource HomePageBackgroundBrush}">
<Grid Background="{ThemeResource HomePageBackgroundBrush}">
<Grid Background="{ThemeResource HomePageBackgroundBrush}">

    3. Define the action for ToggleSwitch in class MainWindow : Window :

MainWinow.xaml.cs:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
private void ToggleSwitch_Toggled(object sender, RoutedEventArgs e)
{
ToggleSwitch toggleSwitch = sender as ToggleSwitch;
if (toggleSwitch.IsOn) {
((FrameworkElement)this.Content).RequestedTheme = ElementTheme.Dark;
}
else {
((FrameworkElement)this.Content).RequestedTheme = ElementTheme.Light;
}
}
private void ToggleSwitch_Toggled(object sender, RoutedEventArgs e) { ToggleSwitch toggleSwitch = sender as ToggleSwitch; if (toggleSwitch.IsOn) { ((FrameworkElement)this.Content).RequestedTheme = ElementTheme.Dark; } else { ((FrameworkElement)this.Content).RequestedTheme = ElementTheme.Light; } }
private void ToggleSwitch_Toggled(object sender, RoutedEventArgs e)
{
    ToggleSwitch toggleSwitch = sender as ToggleSwitch;
    if (toggleSwitch.IsOn) {
        ((FrameworkElement)this.Content).RequestedTheme = ElementTheme.Dark;
    }
    else {
        ((FrameworkElement)this.Content).RequestedTheme = ElementTheme.Light;
    }
}

    4. Link the call-back function above to the ToggleSwitch control:

MainWindow.xaml:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<ToggleSwitch … … Toggled="ToggleSwitch_Toggled" />
<ToggleSwitch … … Toggled="ToggleSwitch_Toggled" />
<ToggleSwitch … … Toggled="ToggleSwitch_Toggled" />

NOTE: You can save the state of the theme resource and persist the option the user chose at the next time of app startup with four easy steps. Follow this blog post to implement this functionality.

Managing window display

Using the Windowing features in WinAppSDK provides a lot of flexibility and customization to your app experience. One of these customization features is the ability to change the Presenter mode of your window. There are 3 presenters you can use: Compact Overlay, Fullscreen, and Overlapped mode, with overlapped being the default presenter.

  1. Add the following namespaces:

MainWindow.xaml.cs:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
using WinRT;
// Needed for WindowId
using Microsoft.UI;
// Needed for AppWindow
using Microsoft.UI.Windowing;
// Needed for XAML hwnd interop
using WinRT.Interop;
using WinRT; // Needed for WindowId using Microsoft.UI; // Needed for AppWindow using Microsoft.UI.Windowing; // Needed for XAML hwnd interop using WinRT.Interop;
using WinRT;
// Needed for WindowId
using Microsoft.UI;
// Needed for AppWindow
using Microsoft.UI.Windowing;
// Needed for XAML hwnd interop
using WinRT.Interop;

    2. Declare the following variable in class MainWindow : Window :

MainWindow.xaml.cs:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
AppWindow m_appWindow;
AppWindow m_appWindow;
AppWindow m_appWindow;

    3. Define a helper method GetAppWindowForCurrentWindow() :

MainWindow.xaml.cs:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
private AppWindow GetAppWindowForCurrentWindow()
{
IntPtr hWnd = WinRT.Interop.WindowNative.GetWindowHandle(this);
WindowId myWndId = Microsoft.UI.Win32Interop.GetWindowIdFromWindow(hWnd);
return AppWindow.GetFromWindowId(myWndId);
}
private AppWindow GetAppWindowForCurrentWindow() { IntPtr hWnd = WinRT.Interop.WindowNative.GetWindowHandle(this); WindowId myWndId = Microsoft.UI.Win32Interop.GetWindowIdFromWindow(hWnd); return AppWindow.GetFromWindowId(myWndId); }
private AppWindow GetAppWindowForCurrentWindow()
{
    IntPtr hWnd = WinRT.Interop.WindowNative.GetWindowHandle(this);
    WindowId myWndId = Microsoft.UI.Win32Interop.GetWindowIdFromWindow(hWnd);
    return AppWindow.GetFromWindowId(myWndId);
}

NOTE: The preceding code gets the window Id for the hwnd and then uses that window Id to get the current AppWindow. This will create the WinAppSDK window layer that gives you access to all the window customization features of the app window.

    4. Call GetAppWindowForCurrentWindow in the MainWindow() method:

MainWindow.xaml.cs:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
m_appWindow = GetAppWindowForCurrentWindow();
m_appWindow = GetAppWindowForCurrentWindow();
m_appWindow = GetAppWindowForCurrentWindow();

    5. Set the current app window to the Compact Overlay presenter via the SetPresenter method in class MainWindow : Window :

MainWindow.xaml.cs:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
private void SwtichPresenter_CompOverlay(object sender, RoutedEventArgs e)
{
m_appWindow.SetPresenter(AppWindowPresenterKind.CompactOverlay);
}
private void SwtichPresenter_CompOverlay(object sender, RoutedEventArgs e) { m_appWindow.SetPresenter(AppWindowPresenterKind.CompactOverlay); }
private void SwtichPresenter_CompOverlay(object sender, RoutedEventArgs e)
{
    m_appWindow.SetPresenter(AppWindowPresenterKind.CompactOverlay);
}

    6. Modify the MenuFlyouItem control to add the SwtichPresenter_CompOverlay function using the Click parameter:

MainWindow.xaml:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<MenuFlyoutItem Text="Compact Overlay" Click="SwtichPresenter_CompOverlay"/>
<MenuFlyoutItem Text="Compact Overlay" Click="SwtichPresenter_CompOverlay"/>
<MenuFlyoutItem Text="Compact Overlay" Click="SwtichPresenter_CompOverlay"/>

NOTE: Repeat the steps 5 and 6 to implement the FullScreen and Overlapped (the default) presenters. The only change needed is to modify step 5 to use AppWindowPresenterKind.Overlapped or AppWindowPresenterKind.FullScreen.

Result

Just like that, you can see how we can leverage the amazing Windowing features to customize windowing options for your app. If you run your app again and click the drop-down button and choose Compact Overlay, the app window will not allow other app windows to overlap on top of it. To learn more about Windowing, see Manage app windows for additional information on what you can do with this feature.

Here is the completed code covered in this post.

Moving forward

Next, we will keep evolving the app with single-instancing.