You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WinUI3设置AppBarButton的Icon属性时偶现参数异常的解决方法

解决WinUI3中切换AppBarButton Icon时的ArgumentException异常

我在WinUI3界面开发中,通过切换AppBarButton的Icon属性来展示设备连接状态:创建了两个全局FontIcon实例disconnectedIcon和connectedIcon,连接成功时切换为connectedIcon,断开时切回disconnectedIcon。点击tester1ConnectionButton或tester2ConnectionButton会弹出ContentDialog让用户输入设备信息,确认连接后执行Icon切换操作。但偶发以下异常,主要出现在ContentDialog确认后的代码块中:

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

相关代码

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;

namespace TesterWinUI3_240606
{
    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
            disconnectedIcon = new FontIcon();
            disconnectedIcon.Glyph = "\uF78A";    // -> CancelMedium, unicode character code
            disconnectedIcon.FontFamily = new FontFamily("Segoe MDL2 Assets");

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

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

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

        private async void tester1ConnectionButton_Click(object sender, RoutedEventArgs e)
        {
            if (isTester1Connected == true)
            {
                // Disconnect from TESTER 1
                isTester1Connected = false;
                tester1ConnectionButton.Label = "Disconnected";
                tester1ConnectionButton.Icon = disconnectedIcon;
                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;

                    if (string.IsNullOrEmpty(tester1Name) == 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;
                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;

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

问题原因与解决方案

原因

WinUI3中,单个UI元素(如FontIcon实例)不能同时被多个父控件引用。当你把同一个connectedIcon或disconnectedIcon赋值给多个AppBarButton.Icon时,控件树会出现冲突,导致偶发的ArgumentException——尤其是快速操作多个按钮切换状态时,同一个FontIcon实例可能被尝试附加到多个父节点,触发异常。

解决方法

方法1:为每个按钮维护独立的状态Icon

不再复用全局FontIcon实例,而是为每个按钮分别创建专属的connected和disconnected图标:

// 替换原有全局FontIcon声明,改为每个按钮单独的实例
private FontIcon handlerDisconnectedIcon, handlerConnectedIcon;
private FontIcon tester1DisconnectedIcon, tester1ConnectedIcon;
private FontIcon tester2DisconnectedIcon, tester2ConnectedIcon;

public MainWindow()
{
    this.InitializeComponent();

    // 初始化每个按钮的Icon实例
    handlerDisconnectedIcon = CreateFontIcon("\uF78A");
    handlerConnectedIcon = CreateFontIcon("\uF78C");
    
    tester1DisconnectedIcon = CreateFontIcon("\uF78A");
    tester1ConnectedIcon = CreateFontIcon("\uF78C");
    
    tester2DisconnectedIcon = CreateFontIcon("\uF78A");
    tester2ConnectedIcon = CreateFontIcon("\uF78C");
}

// 封装创建FontIcon的辅助方法,减少重复代码
private FontIcon CreateFontIcon(string glyph)
{
    return new FontIcon
    {
        Glyph = glyph,
        FontFamily = new FontFamily("Segoe MDL2 Assets")
    };
}

然后在按钮点击事件中使用对应按钮的专属Icon:

// handler按钮切换逻辑
handlerConnectionButton.Icon = isHandlerConnected ? handlerConnectedIcon : handlerDisconnectedIcon;

// tester1按钮切换逻辑
tester1ConnectionButton.Icon = isTester1Connected ? tester1ConnectedIcon : tester1DisconnectedIcon;

// tester2按钮切换逻辑
tester2ConnectionButton.Icon = isTester2Connected ? tester2ConnectedIcon : tester2DisconnectedIcon;

方法2:切换时动态生成新的FontIcon

如果不想维护多个实例,也可以在每次切换状态时创建新的FontIcon对象:

// 断开连接时
tester1ConnectionButton.Icon = new FontIcon { Glyph = "\uF78A", FontFamily = new FontFamily("Segoe MDL2 Assets") };

// 连接成功时
tester1ConnectionButton.Icon = new FontIcon { Glyph = "\uF78C", FontFamily = new FontFamily("Segoe MDL2 Assets") };

额外修正

原代码中tester1ConnectionButton_Click方法里有拼写错误:if (string.IsNullOrEmpty(stest1Name) == false),应改为tester1Name,避免逻辑异常。

内容的提问来源于stack exchange,提问作者FUSE0604

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 10:30:56