WPF 多窗口分文件實現方案
項目文件結構
WindowSwitcher/
├── App.xaml
├── App.xaml.cs
├── MainWindow.xaml
├── MainWindow.xaml.cs
├── Views/
│ ├── SettingsWindow.xaml
│ ├── SettingsWindow.xaml.cs
│ ├── DataWindow.xaml
│ ├── DataWindow.xaml.cs
│ ├── HelpWindow.xaml
│ └── HelpWindow.xaml.cs
└── Services/└── WindowManager.cs
1. 主窗口 (MainWindow.xaml)
<Window x:Class="WindowSwitcher.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:local="clr-namespace:WindowSwitcher"Title="窗口切換管理器" Height="450" Width="800"><Grid><Grid.RowDefinitions><RowDefinition Height="Auto"/><RowDefinition Height="*"/></Grid.RowDefinitions><!-- 導航按鈕區域 --><StackPanel Grid.Row="0" Orientation="Horizontal" Background="#f0f0f0" Padding="10"><Button Content="主窗口" Margin="5" Padding="10,5" Click="ShowMainWindow"/><Button Content="設置窗口" Margin="5" Padding="10,5" Click="ShowSettingsWindow"/><Button Content="數據窗口" Margin="5" Padding="10,5" Click="ShowDataWindow"/><Button Content="幫助窗口" Margin="5" Padding="10,5" Click="ShowHelpWindow"/><Button Content="退出應用" Margin="5" Padding="10,5" Click="ExitApplication" Background="#ffcccc" Foreground="DarkRed"/></StackPanel><!-- 主窗口內容 --><StackPanel Grid.Row="1" VerticalAlignment="Center" HorizontalAlignment="Center"><TextBlock Text="歡迎使用窗口切換管理器" FontSize="24" Margin="0,0,0,20"/><TextBlock Text="{Binding CurrentStatus}" FontSize="18" Foreground="#666"/><Button Content="刷新窗口狀態" Margin="0,20,0,0" Padding="10,5" Click="RefreshStatus"/></StackPanel></Grid>
</Window>
2. 主窗口代碼 (MainWindow.xaml.cs)
using WindowSwitcher.Services;
using System.Windows;namespace WindowSwitcher
{public partial class MainWindow : Window{private readonly WindowManager _windowManager = WindowManager.Instance;public string CurrentStatus => $"當前活動窗口: {_windowManager.ActiveWindow?.Title}";public MainWindow(){InitializeComponent();DataContext = this;// 初始化所有窗口_windowManager.InitializeWindows();// 顯示主窗口_windowManager.ShowWindow<MainWindow>();}// 按鈕事件處理private void ShowMainWindow(object sender, RoutedEventArgs e) => _windowManager.ShowWindow<MainWindow>();private void ShowSettingsWindow(object sender, RoutedEventArgs e) => _windowManager.ShowWindow<SettingsWindow>();private void ShowDataWindow(object sender, RoutedEventArgs e) => _windowManager.ShowWindow<DataWindow>();private void ShowHelpWindow(object sender, RoutedEventArgs e) => _windowManager.ShowWindow<HelpWindow>();private void ExitApplication(object sender, RoutedEventArgs e) => Application.Current.Shutdown();private void RefreshStatus(object sender, RoutedEventArgs e){// 刷新數據綁定OnPropertyChanged(nameof(CurrentStatus));}// 實現簡單的屬性變更通知public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;protected virtual void OnPropertyChanged(string propertyName){PropertyChanged?.Invoke(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));}}
}
3. 設置窗口 (Views/SettingsWindow.xaml)
<Window x:Class="WindowSwitcher.Views.SettingsWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"Title="系統設置" Height="400" Width="600"><Grid Margin="20"><Grid.RowDefinitions><RowDefinition Height="Auto"/><RowDefinition Height="Auto"/><RowDefinition Height="Auto"/><RowDefinition Height="Auto"/><RowDefinition Height="*"/></Grid.RowDefinitions><TextBlock Grid.Row="0" Text="系統設置" FontSize="20" FontWeight="Bold" Margin="0,0,0,20"/><StackPanel Grid.Row="1" Orientation="Horizontal" Margin="0,0,0,10"><TextBlock Text="主題:" Width="100" VerticalAlignment="Center"/><ComboBox Width="200"><ComboBoxItem Content="淺色主題"/><ComboBoxItem Content="深色主題" IsSelected="True"/></ComboBox></StackPanel><StackPanel Grid.Row="2" Orientation="Horizontal" Margin="0,0,0,10"><TextBlock Text="語言:" Width="100" VerticalAlignment="Center"/><ComboBox Width="200"><ComboBoxItem Content="中文"/><ComboBoxItem Content="English"/></ComboBox></StackPanel><StackPanel Grid.Row="3" Orientation="Horizontal" Margin="0,0,0,20"><TextBlock Text="自動保存:" Width="100" VerticalAlignment="Center"/><CheckBox IsChecked="True" VerticalAlignment="Center"/></StackPanel><Button Grid.Row="4" Content="返回主窗口" HorizontalAlignment="Center" Padding="10,5" Click="ReturnToMain" VerticalAlignment="Bottom"/></Grid>
</Window>
4. 設置窗口代碼 (Views/SettingsWindow.xaml.cs)
using System.Windows;
using WindowSwitcher.Services;namespace WindowSwitcher.Views
{public partial class SettingsWindow : Window{private readonly WindowManager _windowManager = WindowManager.Instance;public SettingsWindow(){InitializeComponent();}private void ReturnToMain(object sender, RoutedEventArgs e){_windowManager.ShowWindow<MainWindow>();}}
}
5. 數據窗口 (Views/DataWindow.xaml)
<Window x:Class="WindowSwitcher.Views.DataWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"Title="數據分析" Height="500" Width="700"><Grid Margin="15"><Grid.RowDefinitions><RowDefinition Height="Auto"/><RowDefinition Height="*"/><RowDefinition Height="Auto"/></Grid.RowDefinitions><TextBlock Grid.Row="0" Text="數據分析面板" FontSize="20" FontWeight="Bold" Margin="0,0,0,15"/><!-- 數據表格 --><DataGrid Grid.Row="1" AutoGenerateColumns="True" ItemsSource="{Binding DataItems}"><DataGrid.Columns><DataGridTextColumn Header="ID" Binding="{Binding Id}" Width="Auto"/><DataGridTextColumn Header="名稱" Binding="{Binding Name}" Width="*"/><DataGridTextColumn Header="數值" Binding="{Binding Value}" Width="Auto"/><DataGridTextColumn Header="日期" Binding="{Binding Date, StringFormat=yyyy-MM-dd}" Width="Auto"/></DataGrid.Columns></DataGrid><StackPanel Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,15,0,0"><Button Content="加載數據" Padding="10,5" Margin="0,0,10,0" Click="LoadData"/><Button Content="返回主窗口" Padding="10,5" Click="ReturnToMain"/></StackPanel></Grid>
</Window>
6. 數據窗口代碼 (Views/DataWindow.xaml.cs)
using System.Windows;
using System.Collections.ObjectModel;
using WindowSwitcher.Services;namespace WindowSwitcher.Views
{public partial class DataWindow : Window{private readonly WindowManager _windowManager = WindowManager.Instance;// 數據模型public class DataItem{public int Id { get; set; }public string Name { get; set; }public double Value { get; set; }public DateTime Date { get; set; }}// 數據集合public ObservableCollection<DataItem> DataItems { get; } = new ObservableCollection<DataItem>();public DataWindow(){InitializeComponent();DataContext = this;// 初始加載一些示例數據LoadSampleData();}private void LoadSampleData(){DataItems.Clear();// 添加示例數據DataItems.Add(new DataItem { Id = 1, Name = "項目A", Value = 42.5, Date = DateTime.Now.AddDays(-2) });DataItems.Add(new DataItem { Id = 2, Name = "項目B", Value = 78.3, Date = DateTime.Now.AddDays(-1) });DataItems.Add(new DataItem { Id = 3, Name = "項目C", Value = 15.2, Date = DateTime.Now });}private void LoadData(object sender, RoutedEventArgs e){// 模擬加載更多數據int startId = DataItems.Count + 1;for (int i = 0; i < 5; i++){DataItems.Add(new DataItem {Id = startId + i,Name = $"新增項目{startId + i}",Value = new Random().NextDouble() * 100,Date = DateTime.Now.AddDays(-i)});}}private void ReturnToMain(object sender, RoutedEventArgs e){_windowManager.ShowWindow<MainWindow>();}}
}
7. 幫助窗口 (Views/HelpWindow.xaml)
<Window x:Class="WindowSwitcher.Views.HelpWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"Title="幫助文檔" Height="350" Width="500"><Grid Margin="15"><Grid.RowDefinitions><RowDefinition Height="Auto"/><RowDefinition Height="*"/><RowDefinition Height="Auto"/></Grid.RowDefinitions><TextBlock Grid.Row="0" Text="幫助與支持" FontSize="20" FontWeight="Bold" Margin="0,0,0,10"/><ScrollViewer Grid.Row="1" VerticalScrollBarVisibility="Auto"><TextBlock TextWrapping="Wrap"><Run FontWeight="Bold">如何使用窗口切換功能:</Run><LineBreak/>1. 點擊頂部導航欄中的按鈕可在不同窗口間切換<LineBreak/>2. 每個窗口都提供"返回主窗口"按鈕<LineBreak/>3. 窗口狀態會被保留,再次打開時保持上次的狀態<LineBreak/><LineBreak/><Run FontWeight="Bold">常見問題:</Run><LineBreak/>Q: 為什么關閉按鈕只是隱藏窗口?<LineBreak/>A: 這是設計選擇,為了保持窗口狀態并快速切換。要完全退出應用,請使用"退出應用"按鈕。<LineBreak/><LineBreak/><Run FontWeight="Bold">技術支持:</Run><LineBreak/>如有任何問題,請聯系 support@windowswitcher.com</TextBlock></ScrollViewer><Button Grid.Row="2" Content="返回主窗口" Padding="10,5" HorizontalAlignment="Center" Margin="0,15,0,0" Click="ReturnToMain"/></Grid>
</Window>
8. 幫助窗口代碼 (Views/HelpWindow.xaml.cs)
using System.Windows;
using WindowSwitcher.Services;namespace WindowSwitcher.Views
{public partial class HelpWindow : Window{private readonly WindowManager _windowManager = WindowManager.Instance;public HelpWindow(){InitializeComponent();}private void ReturnToMain(object sender, RoutedEventArgs e){_windowManager.ShowWindow<MainWindow>();}}
}
9. 窗口管理器 (Services/WindowManager.cs)
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Windows;
using WindowSwitcher.Views;namespace WindowSwitcher.Services
{public sealed class WindowManager{// 單例實例private static readonly Lazy<WindowManager> _instance = new Lazy<WindowManager>(() => new WindowManager());public static WindowManager Instance => _instance.Value;// 存儲所有窗口實例private readonly Dictionary<Type, Window> _windows = new Dictionary<Type, Window>();// 當前活動窗口public Window ActiveWindow { get; private set; }private WindowManager() { }// 初始化所有窗口public void InitializeWindows(){// 創建主窗口CreateWindow<MainWindow>();// 創建其他窗口CreateWindow<SettingsWindow>();CreateWindow<DataWindow>();CreateWindow<HelpWindow>();}// 創建窗口實例private void CreateWindow<T>() where T : Window, new(){var windowType = typeof(T);if (!_windows.ContainsKey(windowType)){var window = new T();window.Closing += OnWindowClosing;_windows[windowType] = window;}}// 顯示指定類型的窗口public void ShowWindow<T>() where T : Window{var windowType = typeof(T);if (_windows.TryGetValue(windowType, out Window window)){// 隱藏當前活動窗口if (ActiveWindow != null && ActiveWindow != window){ActiveWindow.Hide();}// 顯示新窗口window.Show();window.Activate();window.Focus();ActiveWindow = window;}else{// 如果窗口尚未創建,則創建并顯示CreateWindow<T>();ShowWindow<T>();}}// 窗口關閉事件處理private void OnWindowClosing(object sender, CancelEventArgs e){if (sender is Window window){// 阻止實際關閉,改為隱藏e.Cancel = true;window.Hide();// 如果關閉的是當前活動窗口,則激活主窗口if (ActiveWindow == window){ShowWindow<MainWindow>();}}}// 獲取特定類型的窗口public T GetWindow<T>() where T : Window{return _windows.TryGetValue(typeof(T), out Window window) ? (T)window : null;}// 關閉所有窗口并退出public void ShutdownApplication(){// 關閉所有窗口foreach (var window in _windows.Values){window.Closing -= OnWindowClosing;window.Close();}_windows.Clear();Application.Current.Shutdown();}}
}
10. 應用程序入口 (App.xaml)
<Application x:Class="WindowSwitcher.App"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"StartupUri="MainWindow.xaml"><Application.Resources><!-- 全局樣式可以放在這里 --></Application.Resources>
</Application>
11. 應用程序入口代碼 (App.xaml.cs)
using System.Windows;
using WindowSwitcher.Services;namespace WindowSwitcher
{public partial class App : Application{protected override void OnStartup(StartupEventArgs e){base.OnStartup(e);// 初始化窗口管理器var windowManager = WindowManager.Instance;windowManager.InitializeWindows();}protected override void OnExit(ExitEventArgs e){// 清理資源WindowManager.Instance.ShutdownApplication();base.OnExit(e);}}
}
關鍵實現說明
1. 分文件組織
- MainWindow:主窗口,作為應用入口
- Views文件夾:包含所有其他窗口(設置、數據、幫助)
- Services文件夾:包含窗口管理器服務
2. 窗口管理器核心功能
// 創建窗口
private void CreateWindow<T>() where T : Window, new()
{var windowType = typeof(T);if (!_windows.ContainsKey(windowType)){var window = new T();window.Closing += OnWindowClosing;_windows[windowType] = window;}
}// 切換窗口
public void ShowWindow<T>() where T : Window
{// ...window.Show();window.Activate();window.Focus();ActiveWindow = window;
}// 處理關閉事件
private void OnWindowClosing(object sender, CancelEventArgs e)
{e.Cancel = true; // 阻止實際關閉window.Hide(); // 改為隱藏
}
3. 數據綁定示例
在數據窗口中,展示了如何使用數據綁定:
// 數據模型
public class DataItem
{public int Id { get; set; }public string Name { get; set; }public double Value { get; set; }public DateTime Date { get; set; }
}// 數據集合
public ObservableCollection<DataItem> DataItems { get; } = new ObservableCollection<DataItem>();// XAML綁定
<DataGrid ItemsSource="{Binding DataItems}">
4. 屬性變更通知
在主窗口中實現了簡單的屬性變更通知:
public string CurrentStatus => $"當前活動窗口: {_windowManager.ActiveWindow?.Title}";// 刷新方法
private void RefreshStatus(object sender, RoutedEventArgs e)
{OnPropertyChanged(nameof(CurrentStatus));
}// 實現INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
使用說明
-
項目結構:
- 創建一個新的WPF應用程序項目
- 按上述文件結構組織代碼
-
啟動應用:
- 應用程序啟動時會初始化所有窗口
- 主窗口首先顯示
-
窗口切換:
- 使用頂部導航欄按鈕在不同窗口間切換
- 每個窗口都有返回主窗口的按鈕
-
關閉窗口:
- 點擊關閉按鈕實際上是隱藏窗口
- 使用"退出應用"按鈕完全關閉程序
-
數據保持:
- 每個窗口的狀態在切換時保持不變
- 數據窗口中的數據在重新打開時保持
這種分文件實現方式使代碼結構清晰,便于維護和擴展,同時保持了窗口切換的高性能和狀態保持特性。