Search code examples
c#iconswinui-3appbarcontentdialog

Get exception: 'Value does not fall within the expected range.' when setting the Icon property of the AppBarButton in WinUI3


I am designing an interface using WinUI3. And I want to use a list of AppBarButton to show the connection status via the Icon of the AppBarButton.

Because the status would change, I use two FontIcon named disconnectedIcon and connectedIcon, and set the Icon property to one of them to show the current connection status.

A ContentDialog named connectDialog will pop up after clicking tester1ConnectionButton or tester2ConnectionButton to have the user fill in the name of the machine(optional) and the IP address of the machine.

After clicking Connect button in the connectDialog, the process will start connecting. Once the connection done, the Icon of the button will set to connectedIcon. Vice versa.

However, this error pops up sometimes when running the code which sets the Icon property (Not only the ones pop up the connectDialog):

System.ArgumentException: 'Value does not fall within the expected range.'


Here are the codes:

MainWindow.xaml

<Window
    x:Class="TesterWinUI3_240606.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:TesterWinUI3_240606"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Grid RowDefinitions="auto,*,auto,auto,auto,*,auto,auto" ColumnDefinitions="auto, auto, auto, *">
        
        <!--Upper part-->
        <Grid Grid.Column="0" Grid.Row="0" Grid.ColumnSpan="3">
            <Border BorderBrush="{ThemeResource ControlStrongStrokeColorDefaultBrush}" BorderThickness="2.5" CornerRadius="3" Margin="5">
                <StackPanel Orientation="Horizontal" HorizontalAlignment="Left" VerticalAlignment="Center">
                     <!--Title at the top--> 
                    <TextBlock Text="Connection" FontSize="16" FontWeight="Bold" Margin="5"/>

                     <!--Connection settings-->
                    <AppBarElementContainer VerticalAlignment="Center">
                        <StackPanel Orientation="Vertical">
                            <TextBlock Text="Handler" Margin="5"/>
                            <AppBarButton x:Name="handlerConnectionButton" Label="Disconnected" Width="80" Click="handlerConnectionButton_Click">
                                <FontIcon Glyph="&#xF78A;"/>
                            </AppBarButton>
                        </StackPanel>
                    </AppBarElementContainer>
                    <AppBarSeparator/>
                    <AppBarElementContainer VerticalAlignment="Center">
                        <StackPanel Orientation="Vertical">
                            <TextBlock x:Name="tester1NameTextBlock" Text="TESTER 1" Margin="5"/>
                            <AppBarButton x:Name="tester1ConnectionButton" Label="Disconnected" Width="80" Click="tester1ConnectionButton_Click">
                                <FontIcon Glyph="&#xF78A;"/>
                                <!--<FontIcon Glyph="&#xEB90;"/>-->
                            </AppBarButton>
                        </StackPanel>
                    </AppBarElementContainer>
                    <AppBarElementContainer VerticalAlignment="Center">
                        <StackPanel Orientation="Vertical">
                            <TextBlock x:Name="tester2NameTextBlock" Text="TESTER 2" Margin="5"/>
                            <AppBarButton x:Name="tester2ConnectionButton" Label="Disconnected" Width="80" Click="tester2ConnectionButton_Click">
                                <FontIcon Glyph="&#xF78A;"/>
                            </AppBarButton>
                        </StackPanel>
                    </AppBarElementContainer>
                </StackPanel>
            </Border>
        </Grid>
        
        <!-- Other UI -->
        
    </Grid>
</Window>

MainWindow.xaml.cs

using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Controls.Primitives;
using Microsoft.UI.Xaml.Data;
using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Navigation;
using System.Drawing.Text;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.ApplicationModel.DataTransfer;

// To learn more about WinUI, the WinUI project structure,
// and more about our project templates, see: http://aka.ms/winui-project-info.

namespace TesterWinUI3_240606
{
    /// <summary>
    /// An empty window that can be used on its own or navigated to within a Frame.
    /// </summary>
    public sealed partial class MainWindow : Window
    {
        // ...

        // Flags for connection status
        private bool isHandlerConnected = false;
        private bool isTester1Connected = false;
        private bool isTester2Connected = false;
        FontIcon disconnectedIcon;
        FontIcon connectedIcon;
        private string tester1Name = "TESTER 1";
        private string tester2Name = "TESTER 2";

        public MainWindow()
        {
            this.InitializeComponent();

            // ...

            // Icon
            // Reference:https://stackoverflow.com/questions/34724792/how-to-programmatically-create-textblock-using-segoe-mdl2-assets-font-in-wpf

            disconnectedIcon = new FontIcon();
            disconnectedIcon.Glyph = "\uF78A";    // -> CancelMedium, unicode character code
            disconnectedIcon.FontFamily = new FontFamily("Segoe MDL2 Assets");
            //{
            //    Glyph = "\uF78A",    // -> CancelMedium, unicode character code
            //    //Glyph = "\xF78A",    // -> CancelMedium, hex character code
            //    //Glyph = "\xEB90",  // -> StatusErrorFull, hex character code
            //    FontFamily = new FontFamily("Segoe MDL2 Assets"),                
            //};

            connectedIcon = new FontIcon();
            connectedIcon.Glyph = "\uF78C";    // -> AcceptMedium, unicode character code
            connectedIcon.FontFamily = new FontFamily("Segoe MDL2 Assets");

            //{
            //    Glyph = "\uF78C",
            //    //Glyph = "\xF78C",    // -> AcceptMedium, hex character code
            //    //Glyph = "\xEC61",  // -> CompletedSolid, hex character code
            //    FontFamily = new FontFamily("Segoe MDL2 Assets"),
            //};

        }

        // Other methods...

        private void handlerConnectionButton_Click(object sender, RoutedEventArgs e)
        {
            if (isHandlerConnected == true)
            {
                // Disconnect from handler
                isHandlerConnected = false;
                handlerConnectionButton.Label = "Disconnected";
                handlerConnectionButton.Icon = disconnectedIcon;
                //handlerConnectionButton.Content = "Disconnected";
                return;
            }

            // Connect to handler
            isHandlerConnected = true;
            handlerConnectionButton.Label = "Connected";
            handlerConnectionButton.Icon = connectedIcon;
            //handlerConnectionButton.Content = "Connected";
        }

        private async void tester1ConnectionButton_Click(object sender, RoutedEventArgs e)
        {
            if (isTester1Connected == true)
            {
                // Disconnect from TESTER 1
                isTester1Connected = false;
                tester1ConnectionButton.Label = "Disconnected";
                tester1ConnectionButton.Icon = disconnectedIcon;
                //tester1ConnectionButton.Content = "Disconnected";
                return;
            }

            // Connect to TESTER 1
            ContentDialog connectDialog = new ContentDialog
            {
                XamlRoot = Content.XamlRoot,
                Title = "Connect to TESTER 1",
                Content = new StackPanel 
                {
                    Orientation = Orientation.Vertical,
                    HorizontalAlignment = HorizontalAlignment.Left,
                    VerticalAlignment = VerticalAlignment.Center,
                    Children = 
                    {
                        new TextBlock { Text = "Name the TESTER:" },
                        new TextBox { Name = "tester1NameTextBox", PlaceholderText = tester1Name },
                        new TextBlock { Text = "Enter the IP address of TESTER:" },
                        new TextBox { Name = "tester1IP"}
                    },
                    Padding = new Thickness(5),
                },
                PrimaryButtonText = "Connect",
                CloseButtonText = "Cancel",
                DefaultButton = ContentDialogButton.Primary,
            };

            ContentDialogResult result = await connectDialog.ShowAsync();

            if (result == ContentDialogResult.Primary)
            {
                TextBox nameTextBox = (TextBox)((StackPanel)connectDialog.Content).Children[1];
                TextBox ipTextBox = (TextBox)((StackPanel)connectDialog.Content).Children[3];

                tester1Name = nameTextBox.Text.Trim();
                string ip = ipTextBox.Text.Trim();

                if (string.IsNullOrEmpty(ip) == false)
                {
                    // Connect to TESTER 1
                    isTester1Connected = true;
                    tester1ConnectionButton.Label = "Connected";
                    tester1ConnectionButton.Icon = connectedIcon;
                    //tester1ConnectionButton.Content = "Connected";

                    if (string.IsNullOrEmpty(stest1Name) == false)
                    {
                        tester1NameTextBlock.Text = tester1Name;
                    }
                }
            }
        }

        private async void tester2ConnectionButton_Click(object sender, RoutedEventArgs e)
        {
            if (isTester2Connected == true)
            {
                // Disconnect from TESTER 2
                isTester2Connected = false;
                tester2ConnectionButton.Label = "Disconnected";
                tester2ConnectionButton.Icon = disconnectedIcon;
                //tester2ConnectionButton.Content = "Disconnected";
                return;
            }

            // Connect to TESTER 2
            ContentDialog connectDialog = new ContentDialog
            {
                XamlRoot = Content.XamlRoot,
                Title = "Connect to TESTER 2",
                Content = new StackPanel
                {
                    Orientation = Orientation.Vertical,
                    HorizontalAlignment = HorizontalAlignment.Left,
                    VerticalAlignment = VerticalAlignment.Center,
                    Children =
                    {
                        new TextBlock { Text = "Name the TESTER:" },
                        new TextBox { Name = "tester2NameTextBox", PlaceholderText = tester2Name },
                        new TextBlock { Text = "Enter the IP address of TESTER:" },
                        new TextBox { Name = "tester2IP"}
                    },
                    Padding = new Thickness(5),
                },
                PrimaryButtonText = "Connect",
                CloseButtonText = "Cancel",
                DefaultButton = ContentDialogButton.Primary,
            };

            ContentDialogResult result = await connectDialog.ShowAsync();

            if (result == ContentDialogResult.Primary)
            {
                TextBox nameTextBox = (TextBox)((StackPanel)connectDialog.Content).Children[1];
                TextBox ipTextBox = (TextBox)((StackPanel)connectDialog.Content).Children[3];

                tester2Name = nameTextBox.Text.Trim();
                string ip = ipTextBox.Text.Trim();

                if (string.IsNullOrEmpty(ip) == false)
                {
                    // Connect to TESTER 2
                    isTester2Connected = true;
                    tester2ConnectionButton.Label = "Connected";
                    tester2ConnectionButton.Icon = connectedIcon;
                    //tester2ConnectionButton.Content = "Connected";

                    if (string.IsNullOrEmpty(tester2Name) == false)
                    {
                        tester2NameTextBlock.Text = tester2Name;
                    }
                }
            }
        }
    }
}

The only information I know is that it happens sometimes after it step in the if- block:

if (result == ContentDialogResult.Primary)

Is there a solution to fix this error?


Solution

  • It can be reproduced stably by making two AppBarButton.Icon properties refer to the same FontIcon resource. You can make every AppBarButton.Icon has its own FontIcon resource.