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

WinUI3中实现事件回调:Toolbar按钮触发Content框架切换

问题描述

我有一个主XAML页面,包含Toolbar和Content两个Frame,并且为这两个Frame准备了多个不同页面。Toolbar框架里用了CommandBar,请问怎么通过Toolbar框架(比如TaskerToolbar)内的AppBarButton点击事件,在MainPage中切换Content框架?或者说怎么把TaskerToolbar的点击事件传递到MainPage.xaml?感谢帮助。

现有代码

MainPage.xaml

<Grid>
    <Grid.RowDefinitions>
        <!-- toolbar-->
        <RowDefinition Height="110"/>
        <RowDefinition/>
    </Grid.RowDefinitions>

    <Border CornerRadius="0,0,0,10" BorderThickness="0,0,0,5">
        <Border.BorderBrush>
            <LinearGradientBrush StartPoint=".5,0" EndPoint=".5,1" Opacity="0.02">
                <GradientStop Color="Gray" Offset="0"/>
                <GradientStop Color="Black" Offset=".03"/>
                <GradientStop Color="Black" Offset=".98"/>
                <GradientStop Color="Gray" Offset="1"/>
            </LinearGradientBrush>
        </Border.BorderBrush>
        <Border CornerRadius="0,0,0,10" BorderBrush="#D4D4D4" BorderThickness="1,0,0,1">
            <!-- toolbar frame -->
            <Grid Grid.Row="0" Background="#F3F3F3">
                <Frame x:Name="toolbarFrame"/>
            </Grid>
        </Border>
    </Border>
    <!-- content frame -->
    <Grid Grid.Row="1">
        <Frame x:Name="contentFrame"/>
    </Grid>
</Grid>

MainPage.xaml.cs

public MainChromeWindowView()
{
    this.InitializeComponent();

    toolbarFrame.Navigate(typeof(TaskerToolbar), null);
    contentFrame.Navigate(typeof(SingleAView), null);
}

TaskerToolbar.xaml

<Grid>
    <CommandBar Style="{StaticResource OpenDownCommandBar}" HorizontalAlignment="Left" IsOpen="False" DefaultLabelPosition="Collapsed" Margin="0,15,0,0">
        <AppBarButton Style="{StaticResource ImageAppBarButtonStyle}" LabelPosition="Default" Label="Recorder">
            <AppBarButton.Content>
                <Image>
                    <Image.Source>
                        <SvgImageSource UriSource="/Assets/icons/icon1.svg"/>
                    </Image.Source>
                </Image>
            </AppBarButton.Content>
        </AppBarButton>
        <AppBarButton Style="{StaticResource ImageAppBarButtonStyle}" LabelPosition="Default" Label="Configuration">
            <AppBarButton.Content>
                <Image>
                    <Image.Source>
                        <SvgImageSource UriSource="/Assets/icons/icon2.svg"/>
                    </Image.Source>
                </Image>
            </AppBarButton.Content>
        </AppBarButton>
    </CommandBar>
</Grid>

解决方案

方法1:自定义事件传递(直接高效)

这种方式适合简单场景,通过在Toolbar页面定义自定义事件,让MainPage订阅并处理导航逻辑。

步骤1:修改TaskerToolbar的代码后台

添加自定义事件和按钮点击处理逻辑:

public partial class TaskerToolbar : Page
{
    // 定义导航请求事件,传递目标页面类型
    public event EventHandler<Type>? NavigateRequested;

    public TaskerToolbar()
    {
        InitializeComponent();
    }

    private void RecorderButton_Click(object sender, RoutedEventArgs e)
    {
        // 触发事件,传递Recorder页面类型
        NavigateRequested?.Invoke(this, typeof(RecorderView));
    }

    private void ConfigurationButton_Click(object sender, RoutedEventArgs e)
    {
        // 触发事件,传递Configuration页面类型
        NavigateRequested?.Invoke(this, typeof(ConfigurationView));
    }
}

步骤2:给TaskerToolbar的按钮绑定点击事件

在XAML中添加Click属性:

<Grid>
    <CommandBar Style="{StaticResource OpenDownCommandBar}" HorizontalAlignment="Left" IsOpen="False" DefaultLabelPosition="Collapsed" Margin="0,15,0,0">
        <AppBarButton Style="{StaticResource ImageAppBarButtonStyle}" LabelPosition="Default" Label="Recorder" Click="RecorderButton_Click">
            <AppBarButton.Content>
                <Image>
                    <Image.Source>
                        <SvgImageSource UriSource="/Assets/icons/icon1.svg"/>
                    </Image.Source>
                </Image>
            </AppBarButton.Content>
        </AppBarButton>
        <AppBarButton Style="{StaticResource ImageAppBarButtonStyle}" LabelPosition="Default" Label="Configuration" Click="ConfigurationButton_Click">
            <AppBarButton.Content>
                <Image>
                    <Image.Source>
                        <SvgImageSource UriSource="/Assets/icons/icon2.svg"/>
                    </Image.Source>
                </Image>
            </AppBarButton.Content>
        </AppBarButton>
    </CommandBar>
</Grid>

步骤3:在MainPage中订阅事件并处理导航

修改MainPage.xaml.cs,监听Toolbar的导航请求事件:

public MainChromeWindowView()
{
    InitializeComponent();

    toolbarFrame.Navigate(typeof(TaskerToolbar), null);
    // 订阅Frame导航完成事件,确保能获取到Toolbar实例
    toolbarFrame.Navigated += ToolbarFrame_Navigated;
    contentFrame.Navigate(typeof(SingleAView), null);
}

private void ToolbarFrame_Navigated(object sender, NavigationEventArgs e)
{
    // 获取当前Toolbar页面实例,订阅导航请求事件
    if (e.Content is TaskerToolbar toolbar)
    {
        toolbar.NavigateRequested += Toolbar_NavigateRequested;
    }
}

private void Toolbar_NavigateRequested(object? sender, Type targetPageType)
{
    // 切换ContentFrame的页面
    contentFrame.Navigate(targetPageType, null);
}

方法2:MVVM命令绑定(解耦架构)

如果项目采用MVVM模式,通过共享ViewModel实现命令绑定,更符合架构设计原则。

步骤1:定义导航ViewModel

using System.ComponentModel;
using System.Windows.Input;

public class NavigationViewModel : INotifyPropertyChanged
{
    public ICommand NavigateCommand { get; }
    private Type? _targetPageType;

    public Type? TargetPageType
    {
        get => _targetPageType;
        set
        {
            _targetPageType = value;
            OnPropertyChanged();
        }
    }

    public NavigationViewModel()
    {
        NavigateCommand = new RelayCommand<Type>(pageType => TargetPageType = pageType);
    }

    public event PropertyChangedEventHandler? PropertyChanged;

    protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

// 实现基础RelayCommand
public class RelayCommand<T> : ICommand
{
    private readonly Action<T> _execute;
    private readonly Predicate<T>? _canExecute;

    public RelayCommand(Action<T> execute, Predicate<T>? canExecute = null)
    {
        _execute = execute ?? throw new ArgumentNullException(nameof(execute));
        _canExecute = canExecute;
    }

    public bool CanExecute(object? parameter) => _canExecute?.Invoke((T)parameter!) ?? true;

    public void Execute(object? parameter) => _execute((T)parameter!);

    public event EventHandler? CanExecuteChanged
    {
        add => CommandManager.RequerySuggested += value;
        remove => CommandManager.RequerySuggested -= value;
    }
}

步骤2:注册全局ViewModel

在App.xaml中将ViewModel注册为全局资源:

<Application.Resources>
    <local:NavigationViewModel x:Key="NavigationVM"/>
</Application.Resources>

步骤3:TaskerToolbar绑定命令

修改XAML,将按钮的Command绑定到全局ViewModel的命令,并传递目标页面类型:

<Grid DataContext="{StaticResource NavigationVM}">
    <CommandBar Style="{StaticResource OpenDownCommandBar}" HorizontalAlignment="Left" IsOpen="False" DefaultLabelPosition="Collapsed" Margin="0,15,0,0">
        <AppBarButton Style="{StaticResource ImageAppBarButtonStyle}" LabelPosition="Default" Label="Recorder"
                      Command="{Binding NavigateCommand}" CommandParameter="{x:Type local:RecorderView}">
            <AppBarButton.Content>
                <Image>
                    <Image.Source>
                        <SvgImageSource UriSource="/Assets/icons/icon1.svg"/>
                    </Image.Source>
                </Image>
            </AppBarButton.Content>
        </AppBarButton>
        <AppBarButton Style="{StaticResource ImageAppBarButtonStyle}" LabelPosition="Default" Label="Configuration"
                      Command="{Binding NavigateCommand}" CommandParameter="{x:Type local:ConfigurationView}">
            <AppBarButton.Content>
                <Image>
                    <Image.Source>
                        <SvgImageSource UriSource="/Assets/icons/icon2.svg"/>
                    </Image.Source>
                </Image>
            </AppBarButton.Content>
        </AppBarButton>
    </CommandBar>
</Grid>

步骤4:MainPage监听ViewModel变化

修改MainPage.xaml.cs,监听ViewModel的页面类型变化并导航:

public MainChromeWindowView()
{
    InitializeComponent();

    toolbarFrame.Navigate(typeof(TaskerToolbar), null);
    contentFrame.Navigate(typeof(SingleAView), null);

    // 获取全局ViewModel并订阅属性变化事件
    var navigationVM = (NavigationViewModel)Application.Current.Resources["NavigationVM"];
    navigationVM.PropertyChanged += NavigationVM_PropertyChanged;
}

private void NavigationVM_PropertyChanged(object? sender, PropertyChangedEventArgs e)
{
    if (e.PropertyName == nameof(NavigationViewModel.TargetPageType) && sender is NavigationViewModel vm)
    {
        if (vm.TargetPageType != null)
        {
            contentFrame.Navigate(vm.TargetPageType, null);
        }
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 13:07:02