郑攀思家 發表於 2026-3-8 17:31:00

笔记:在 WPF 中通过侧边导航栏实现内容切换

<p>本篇笔记主要记录,如何在 WPF 中利用 ListBox+ContentControl+UserControl 三个控件,以侧边导航栏的形式实现内容切换。</p>
<p>效果图如下:</p>
<pre><code class="language-txt">┌─────────┬────────────────────────────┐
│Title│                            │
├─────────┤                            │
│Title│                            │
├─────────┤                            │
│Title│                            │
├─────────┤         Content          │
│         │                            │
│         │                            │
│         │                            │
│         │                            │
│         │                            │
└─────────┴────────────────────────────┘
</code></pre>
<p>所使用的开发环境如下:</p>
<ul>
<li>语言版本:.Net 10</li>
<li>MVVM 框架:CommunityToolkit.Mvvm 8.4.0</li>
</ul>
<h2 id="以事件驱动的实现方案">以事件驱动的实现方案</h2>
<p>具体实现思路为,利用 ListBox 控件搭建导航栏,然后创建选中事件,如果事件被触发,则将对应的 UserControl 控件赋值给 ContentControl 控件的 Content 属性,至此实现内容切换。</p>
<ul>
<li>XAML 代码</li>
</ul>
<pre><code class="language-xml">&lt;DockPanel&gt;
    &lt;!-- 导航栏列表框,用于页面导航切换,包含首页、设置、关于三个选项 --&gt;
    &lt;ListBox x:Name="NavBar" SelectionChanged="NavBar_OnSelectionChanged"&gt;
      &lt;ListBoxItem&gt;首页&lt;/ListBoxItem&gt;
      &lt;ListBoxItem&gt;设置&lt;/ListBoxItem&gt;
      &lt;ListBoxItem&gt;关于&lt;/ListBoxItem&gt;
    &lt;/ListBox&gt;
    &lt;!-- 内容控件,用于显示与导航选项对应的页面内容 --&gt;
    &lt;ContentControl x:Name="ContCtrl"&gt;&lt;/ContentControl&gt;
&lt;/DockPanel&gt;
</code></pre>
<ul>
<li>后置代码</li>
</ul>
<pre><code class="language-csharp">public partial class MainWindow : Window
{
    public MainWindow()
    {
      InitializeComponent();

      // 初始化导航栏,默认选中第一项,并设置内容控件显示首页视图
      NavBar.SelectedIndex = 0;
      ContCtrl.Content = new HomeView();
    }

    // 当导航栏选择项发生改变时,触发的事件处理函数
    private void NavBar_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
    {
      // 获取当前选中的列表项
      var item = NavBar.SelectedItem as ListBoxItem;

      // 根据选中项的内容切换到对应的视图页面
      ContCtrl.Content = item?.Content switch
      {
            "首页" =&gt; new HomeView(),
            "设置" =&gt; new SettingView(),
            "关于" =&gt; new AboutView(),
            _ =&gt; ContCtrl
      };
    }
}
</code></pre>
<ul>
<li>自定义 UserControl 控件内容。由于现阶段实现的很简单,三个用户控件只有文本不同,所以在这里只给出了其中一个 UserControl 控件的内容</li>
</ul>
<pre><code class="language-xml">&lt;UserControl x:Class="TryDemo.HomeView"
             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:local="clr-namespace:TryDemo"
             mc:Ignorable="d"
             d:DesignHeight="300" d:DesignWidth="300"&gt;
   
    &lt;Grid Background="LightBlue"&gt;
      &lt;TextBlock Text="首页"
                   HorizontalAlignment="Center"
                   VerticalAlignment="Center"
                   FontSize="20"/&gt;
    &lt;/Grid&gt;
&lt;/UserControl&gt;
</code></pre>
<h2 id="mvvm-模式的实现方案">MVVM 模式的实现方案</h2>
<p>与事件驱动的实现思路,并没有多大的区别,依旧是检测 ListBox 选择项是否更改,如果发生变化,则修改 ContentControl 的 Content 属性,实现动态切换。</p>
<p>在这里,将事件触发的逻辑改为了数据绑定,当被绑定的属性值发生变化,会调用更改方法。</p>
<p>同时也进行了部分升级,比如:</p>
<ul>
<li>自定义 ListBox 模板,显示个性化内容,同时导航项也变为从后台添加,依此可以实现动态更新。</li>
<li>关于导航项的数据,则变为了自定义的记录结构,其中定义了要显示的内容,同时保存了对应要切换的视图类型,以便简化视图切换,直接使用反射获取对应的视图实例。</li>
<li>使用 CommunityToolkit.Mvvm 框架,简化 MVVM 模式的实现。</li>
</ul>
<p>下面就是代码实现了:</p>
<ul>
<li>XAML 代码</li>
</ul>
<pre><code class="language-xml">&lt;DockPanel&gt;
    &lt;!-- 导航栏列表框,绑定到 ViewModel 的 NavItems 集合和 SelectedItem 属性,实现导航项的数据驱动显示 --&gt;
    &lt;ListBox x:Name="NavBar" ItemsSource="{Binding NavItems}"
             SelectedItem="{Binding SelectedItem}"&gt;
      &lt;ListBox.ItemTemplate&gt;
            &lt;!-- 自定义列表项模板,定义每个导航项的显示布局 --&gt;
            &lt;DataTemplate&gt;
                &lt;DockPanel&gt;
                  &lt;!-- 显示导航项的图标 --&gt;
                  &lt;TextBlock Text="{Binding Icon}"/&gt;
                  &lt;!-- 显示导航项的标题文本 --&gt;
                  &lt;TextBlock Text="{Binding Title}"/&gt;
                &lt;/DockPanel&gt;
            &lt;/DataTemplate&gt;
      &lt;/ListBox.ItemTemplate&gt;
    &lt;/ListBox&gt;
    &lt;!-- 内容控制器,绑定到 ViewModel 的 CurrentView 属性,动态显示当前选中的视图 --&gt;
    &lt;ContentControl Content="{Binding CurrentView}"&gt;&lt;/ContentControl&gt;
&lt;/DockPanel&gt;
</code></pre>
<ul>
<li>自定义的视图模型</li>
</ul>
<pre><code class="language-csharp">public partial class MainWindowViewModel : ObservableObject
{
    // 被选中的导航项属性
   
    private NavItem _selectedItem;
   
    // 当前显示的视图对象属性
   
    private object _currentView;
   
    // 导航栏项目数组,包含所有可用的导航选项
    public NavItem[] NavItems { get; init; }
   
    public MainWindowViewModel()
    {
      // 初始化三个导航项:首页、设置、关于,分别关联对应的视图类型
      NavItems =
      [
            new NavItem("首页", "🏠️", typeof(HomeView)),
            new NavItem("设置", "⚙️", typeof(SettingView)),
            new NavItem("关于", "ℹ️", typeof(AboutView))
      ];
      // 默认选中第一个导航项(首页)
      SelectedItem = NavItems;
    }

    /// &lt;summary&gt;
    /// SelectedItem 属性改变时的方法实现,用于切换当前显示的视图
    /// &lt;/summary&gt;
    /// &lt;param name="value"&gt;新选中的导航项,包含目标视图的类型信息&lt;/param&gt;
    partial void OnSelectedItemChanged(NavItem value)
    {
      // 通过反射创建对应视图类型的实例
      var view = Activator.CreateInstance(value.View);
      // 如果创建成功则更新当前视图,否则保持原视图不变
      CurrentView = view ?? CurrentView;
    }
}
</code></pre>
<ul>
<li>导航项类型定义</li>
</ul>
<pre><code class="language-csharp">/// &lt;summary&gt;
/// 导航项记录类型,封装导航栏项目的显示信息和关联的视图类型
/// &lt;/summary&gt;
/// &lt;param name="Title"&gt;导航项显示的标题文本&lt;/param&gt;
/// &lt;param name="Icon"&gt;导航项显示的图标字符&lt;/param&gt;
/// &lt;param name="View"&gt;导航项关联的视图类型,用于创建对应的页面&lt;/param&gt;
public record NavItem(string Title, string Icon, Type View);
</code></pre>
<ul>
<li>UserControl 控件保持不变</li>
</ul>
<h2 id="小结">小结</h2>
<p>在这里选择 ListBox 作为导航栏的原因是,其具有单选功能,可以轻易地实现选项排他性,保证只有一个选项被选中。</p>
<p>一开始想的是用 Button + 布局控件实现,但发现排他性的实现比较麻烦,转而使用 ListBox 控件。</p><br><br>
来源:https://www.cnblogs.com/qisork/p/19686839
頁: [1]
查看完整版本: 笔记:在 WPF 中通过侧边导航栏实现内容切换