如何在Visual Basic WPF中实现多窗口共用导航栏可复用组件
解决方案
完全可以通过WPF的用户控件(UserControl) 实现导航栏的复用,将导航栏的UI和业务逻辑封装为独立组件后,所有窗口均可直接引用,无需重复复制代码,具体实现步骤如下:
步骤1:创建导航栏用户控件
右键你的WPF项目 -> 选择【添加】->【新建项】-> 选中【用户控件(WPF)】,命名为NavbarControl.xaml,点击确定后会自动生成对应的XAML布局文件和VB后台类文件。
步骤2:迁移导航栏XAML布局
将你现有代码中navGrid的完整内容复制到NavbarControl.xaml中,最终NavbarControl.xaml代码如下:
<UserControl x:Class="RestaurantManager.NavbarControl" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:fa5="http://schemas.fontawesome.com/icons/" mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="70"> <Grid x:Name="navGrid" Background="#212121"> <Grid.RowDefinitions> <RowDefinition Height="70"/> <RowDefinition Height="70"/> <RowDefinition Height="70"/> <RowDefinition Height="70"/> </Grid.RowDefinitions> <!-- 菜单项定义 --> <Border Grid.Row="0" BorderBrush="White" BorderThickness="0 0 0 .1"/> <Button x:Name="logoBtn" Grid.Row="0" Panel.ZIndex="10" TabIndex="10" Opacity="0"/> <TextBlock Grid.Row="0" VerticalAlignment="Center" HorizontalAlignment="Center" FontSize="40" Foreground="#03DAC5"> E </TextBlock> <Border Grid.Row="1" BorderBrush="White" BorderThickness="0 0 0 .1"/> <Button x:Name="floorBtn" Grid.Row="1" Panel.ZIndex="10" TabIndex="10" Opacity="0"/> <StackPanel x:Name="floorStack" Grid.Row="1" VerticalAlignment="Center" HorizontalAlignment="Center"> <fa5:SvgAwesome Icon="Solid_BorderAll" Width="20" Foreground="White"/> <TextBlock Foreground="White" HorizontalAlignment="Center" Margin="0,5,0,0">Floor</TextBlock> </StackPanel> <Border Grid.Row="2" BorderBrush="White" BorderThickness="0 0 0 .1"/> <Button x:Name="clientsBtn" Grid.Row="2" Panel.ZIndex="10" TabIndex="10" Opacity="0"/> <StackPanel x:Name="clientsStack" Grid.Row="2" VerticalAlignment="Center" HorizontalAlignment="Center" Opacity=".1"> <fa5:SvgAwesome Icon="Solid_UserFriends" Width="20" Foreground="White"/> <TextBlock Foreground="White" HorizontalAlignment="Center" Margin="0,5,0,0">Clients</TextBlock> </StackPanel> <Border Grid.Row="3" BorderBrush="White" BorderThickness="0 0 0 .1"/> <Button x:Name="settingsBtn" Grid.Row="3" Panel.ZIndex="10" TabIndex="10" Opacity="0"/> <StackPanel x:Name="settingsStack" Grid.Row="3" VerticalAlignment="Center" HorizontalAlignment="Center" Opacity=".1"> <fa5:SvgAwesome Icon="Solid_Cogs" Width="20" Foreground="White"/> <TextBlock Foreground="White" HorizontalAlignment="Center" Margin="0,5,0,0">Settings</TextBlock> </StackPanel> </Grid> </UserControl>
步骤3:迁移导航栏逻辑到用户控件后台
将原窗口中的导航交互逻辑复制到NavbarControl.xaml.vb中,为了适配多窗口复用,建议新增路由事件通知父窗口导航项被点击:
Imports System.Windows.RoutedEventArgs Partial Public Class NavbarControl ' 自定义导航点击路由事件,父窗口可以监听该事件处理跳转逻辑 Public Shared ReadOnly NavItemClickedEvent As RoutedEvent = EventManager.RegisterRoutedEvent( "NavItemClicked", RoutingStrategy.Bubble, GetType(RoutedEventHandler), GetType(NavbarControl)) Public Event NavItemClicked As RoutedEventHandler Private Sub SetAllInactive() For Each child In navGrid.Children If TypeOf child Is StackPanel Then child.Opacity = 0.1 End If Next End Sub Private Sub LogoBtnClick(sender As Object, e As RoutedEventArgs) Handles logoBtn.Click SetAllInactive() floorStack.Opacity = 1 RaiseEvent NavItemClicked(Me, New RoutedEventArgs With {.Source = "Logo"}) End Sub Private Sub FloorBtnClick(sender As Object, e As RoutedEventArgs) Handles floorBtn.Click SetAllInactive() floorStack.Opacity = 1 RaiseEvent NavItemClicked(Me, New RoutedEventArgs With {.Source = "Floor"}) End Sub Private Sub ClientsBtnClick(sender As Object, e As RoutedEventArgs) Handles clientsBtn.Click SetAllInactive() clientsStack.Opacity = 1 RaiseEvent NavItemClicked(Me, New RoutedEventArgs With {.Source = "Clients"}) End Sub Private Sub SettingsBtnClick(sender As Object, e As RoutedEventArgs) Handles settingsBtn.Click SetAllInactive() settingsStack.Opacity = 1 RaiseEvent NavItemClicked(Me, New RoutedEventArgs With {.Source = "Settings"}) End Sub End Class
步骤4:在窗口中引用导航控件
修改你原来的窗口XAML代码,移除原有导航栏代码,引入封装好的NavbarControl即可,以你原有的FloorWindow为例:
<Window x:Class="FloorWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:RestaurantManager" mc:Ignorable="d" Title="Restaurant Floor" Height="450" Width="800"> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="70"/> <ColumnDefinition Width="1*"/> </Grid.ColumnDefinitions> <!-- 引用封装好的导航控件 --> <local:NavbarControl x:Name="MainNavbar" NavItemClicked="MainNavbar_NavItemClicked"/> <!-- 第二列放当前窗口的独有内容 --> <Grid Grid.Column="1"> <!-- 此处写当前窗口的业务UI --> </Grid> </Grid> </Window>
在窗口后台监听导航点击事件,处理对应跳转逻辑即可:
Private Sub MainNavbar_NavItemClicked(sender As Object, e As RoutedEventArgs) Dim navKey As String = e.Source.ToString() Select Case navKey Case "Floor" ' 处理Floor页跳转逻辑 Case "Clients" ' 处理Clients页跳转逻辑 Case "Settings" ' 处理Settings页跳转逻辑 End Select End Sub
优化建议
- 原逻辑中通过字符串对比控件类型的写法可以替换为
If TypeOf child Is StackPanel Then,运行更稳定,不会因为类名变动出现异常 - 后续如果需要扩展导航项,只需要修改
NavbarControl的代码,所有引用该控件的窗口都会自动同步更新
内容的提问来源于stack exchange,提问作者DeadSec
相关产品推荐
相关产品推荐

