你是否还在为WPF应用中的窗口跳转而头疼?多个窗口之间如何优雅地传递数据?模态和非模态窗口有什么区别?其实这里与Winform基本一回事,这里讲的不是导航。作为一名C#开发者,掌握多窗口开发技巧是构建专业级桌面应用的必备技能。
今天,我将通过一个完整的实战项目,带你从零开始掌握WPF多窗口开发的所有核心技术。这不仅仅是理论讲解,而是一套可以直接应用到项目中的完整解决方案!
在实际项目中,我们经常遇到这些场景:
单窗口应用虽然简单,但用户体验往往不够友好,多窗口设计能带来更好的交互体验和功能分离。
模态窗口(Modal)特点:
非模态窗口(Modeless)特点:
C#// 模态窗口显示
SecondWindow modal = new SecondWindow();
bool? result = modal.ShowDialog(); // 阻塞执行
// 非模态窗口显示
ThirdWindow modeless = new ThirdWindow();
modeless.Show(); // 立即返回,不阻塞
C#public SecondWindow(string userName)
{
InitializeComponent();
this.userName = userName;
txtWelcome.Text = $"欢迎,{userName}!";
}
C#public class SecondWindow : Window
{
public string ReturnData { get; private set; } = "无数据";
private void btnConfirm_Click(object sender, RoutedEventArgs e)
{
ReturnData = "用户提交的数据";
this.DialogResult = true; // 关键:设置返回值
}
}
// 主窗口接收
bool? result = secondWindow.ShowDialog();
if (result == true)
{
string data = secondWindow.ReturnData;
}
C#public class ThirdWindow : Window
{
public event EventHandler<string> DataReceived;
private void SendMessage()
{
DataReceived?.Invoke(this, "来自子窗口的消息");
}
}
// 主窗口订阅事件
thirdWindow.DataReceived += (sender, data) => {
AddMessage($"接收到数据:{data}");
};
C#public partial class MainWindow : Window
{
private List<Window> openWindows = new List<Window>();
// 打开模态窗口
private void btnOpenSecond_Click(object sender, RoutedEventArgs e)
{
string userName = txtUserName.Text.Trim();
if (string.IsNullOrEmpty(userName))
{
MessageBox.Show("请先输入您的姓名!", "提示");
return;
}
SecondWindow secondWindow = new SecondWindow(userName);
secondWindow.Owner = this; // ⭐ 关键:设置父窗口
bool? result = secondWindow.ShowDialog();
if (result == true)
{
AddMessage($"接收到数据:{secondWindow.ReturnData}");
}
}
// 打开非模态窗口
private void btnOpenThird_Click(object sender, RoutedEventArgs e)
{
ThirdWindow thirdWindow = new ThirdWindow(txtUserName.Text);
thirdWindow.Owner = this;
thirdWindow.DataReceived += (sender, data) => {
AddMessage($"实时消息:{data}");
};
openWindows.Add(thirdWindow);
thirdWindow.Show(); // 非阻塞显示
}
}
C#public partial class SecondWindow : Window
{
public string ReturnData { get; private set; }
private void btnConfirm_Click(object sender, RoutedEventArgs e)
{
// ⭐ 数据验证
if (cmbProfession.SelectedItem == null)
{
MessageBox.Show("请选择职业!");
return;
}
// ⭐ 收集并格式化数据
ReturnData = BuildUserData();
this.DialogResult = true; // 设置返回结果
}
private string BuildUserData()
{
var profession = ((ComboBoxItem)cmbProfession.SelectedItem).Content;
var experience = (int)sliderExperience.Value;
return $"职业: {profession}, 经验: {experience}年";
}
}
C#public partial class ThirdWindow : Window
{
public event EventHandler<string> DataReceived;
private DispatcherTimer timer;
public ThirdWindow(string userName)
{
InitializeComponent();
InitializeTimer(); // ⭐ 初始化定时器
}
private void InitializeTimer()
{
timer = new DispatcherTimer
{
Interval = TimeSpan.FromSeconds(1)
};
timer.Tick += (s, e) => {
txtCurrentTime.Text = DateTime.Now.ToString("HH:mm:ss");
};
timer.Start();
}
private void btnSendMessage_Click(object sender, RoutedEventArgs e)
{
string message = txtMessage.Text.Trim();
if (!string.IsNullOrEmpty(message))
{
DataReceived?.Invoke(this, message); // ⭐ 触发事件
txtMessage.Clear();
}
}
}
C#<Window x:Class="AppWpfWindows.MainWindow"
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:AppWpfWindows"
mc:Ignorable="d"
Title="MainWindow" Height="620
" Width="800">
<Grid Margin="20">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="多窗口应用程序演示"
FontSize="24" FontWeight="Bold"
HorizontalAlignment="Center"
Foreground="#2E8B57" Margin="0,0,0,20"/>
<StackPanel Grid.Row="1" Orientation="Horizontal" Margin="0,10">
<TextBlock Text="输入您的姓名:" VerticalAlignment="Center"
FontSize="14" Width="100"/>
<TextBox x:Name="txtUserName" Width="200" Height="30"
FontSize="14" Padding="5"/>
</StackPanel>
<GroupBox Grid.Row="2" Header="窗口操作" Margin="0,20,0,10"
FontWeight="Bold" Foreground="#2E8B57">
<StackPanel Margin="10">
<Button x:Name="btnOpenSecond" Content="打开第二个窗口 (模态)"
Height="35" Margin="5" FontSize="14"
Background="#87CEEB" BorderBrush="#4682B4"
Click="btnOpenSecond_Click"/>
<Button x:Name="btnOpenThird" Content="打开第三个窗口 (非模态)"
Height="35" Margin="5" FontSize="14"
Background="#98FB98" BorderBrush="#32CD32"
Click="btnOpenThird_Click"/>
<Button x:Name="btnShowAllWindows" Content="显示所有打开的窗口"
Height="35" Margin="5" FontSize="14"
Background="#F0E68C" BorderBrush="#DAA520"
Click="btnShowAllWindows_Click"/>
</StackPanel>
</GroupBox>
<GroupBox Grid.Row="3" Header="消息日志" Margin="0,10"
FontWeight="Bold" Foreground="#2E8B57">
<ScrollViewer Height="100" Margin="5">
<TextBlock x:Name="txtMessages" FontSize="12"
TextWrapping="Wrap" Foreground="#333"/>
</ScrollViewer>
</GroupBox>
<TextBlock Grid.Row="4" x:Name="txtWindowInfo"
FontSize="12" Foreground="#666"
VerticalAlignment="Bottom" Margin="0,10"/>
<Button Grid.Row="5" x:Name="btnExit" Content="退出应用程序"
Height="30" Width="120"
Background="#FFB6C1" BorderBrush="#DC143C"
HorizontalAlignment="Right"
Click="btnExit_Click"/>
</Grid>
</Window>
C#using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace AppWpfWindows
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private List<Window> openWindows = new List<Window>();
public MainWindow()
{
InitializeComponent();
this.Loaded += MainWindow_Loaded;
this.Closing += MainWindow_Closing;
}
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
AddMessage("主窗口已加载");
UpdateWindowInfo();
}
private void MainWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
// 关闭所有子窗口
foreach (var window in openWindows.ToList())
{
if (window.IsLoaded)
{
window.Close();
}
}
}
private void btnOpenSecond_Click(object sender, RoutedEventArgs e)
{
try
{
string userName = txtUserName.Text.Trim();
if (string.IsNullOrEmpty(userName))
{
MessageBox.Show("请先输入您的姓名!", "提示",
MessageBoxButton.OK, MessageBoxImage.Warning);
return;
}
SecondWindow secondWindow = new SecondWindow(userName);
secondWindow.Owner = this; // 设置父窗口
secondWindow.WindowClosed += SecondWindow_WindowClosed;
openWindows.Add(secondWindow);
AddMessage($"正在以模态方式打开第二个窗口,用户:{userName}");
// 模态显示
bool? result = secondWindow.ShowDialog();
if (result == true)
{
AddMessage("第二个窗口返回了确定结果");
}
else
{
AddMessage("第二个窗口被取消或关闭");
}
}
catch (Exception ex)
{
MessageBox.Show($"打开窗口时发生错误:{ex.Message}", "错误",
MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private void btnOpenThird_Click(object sender, RoutedEventArgs e)
{
try
{
string userName = txtUserName.Text.Trim();
if (string.IsNullOrEmpty(userName))
{
userName = "匿名用户";
}
ThirdWindow thirdWindow = new ThirdWindow(userName);
thirdWindow.Owner = this; // 设置父窗口
thirdWindow.WindowClosed += ThirdWindow_WindowClosed;
thirdWindow.DataReceived += ThirdWindow_DataReceived;
openWindows.Add(thirdWindow);
AddMessage($"正在以非模态方式打开第三个窗口,用户:{userName}");
// 非模态显示
thirdWindow.Show();
UpdateWindowInfo();
}
catch (Exception ex)
{
MessageBox.Show($"打开窗口时发生错误:{ex.Message}", "错误",
MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private void btnShowAllWindows_Click(object sender, RoutedEventArgs e)
{
var openWindowsList = openWindows.Where(w => w.IsLoaded).ToList();
if (openWindowsList.Count == 0)
{
AddMessage("当前没有打开的子窗口");
return;
}
string windowsList = string.Join("\n",
openWindowsList.Select(w => $"- {w.Title} ({w.GetType().Name})"));
MessageBox.Show($"当前打开的窗口:\n{windowsList}",
"窗口列表", MessageBoxButton.OK, MessageBoxImage.Information);
AddMessage($"显示了 {openWindowsList.Count} 个打开的窗口信息");
}
private void btnExit_Click(object sender, RoutedEventArgs e)
{
if (MessageBox.Show("确定要退出应用程序吗?", "确认退出",
MessageBoxButton.YesNo, MessageBoxImage.Question) == MessageBoxResult.Yes)
{
Application.Current.Shutdown();
}
}
private void SecondWindow_WindowClosed(object sender, EventArgs e)
{
if (sender is SecondWindow window)
{
openWindows.Remove(window);
AddMessage($"第二个窗口已关闭,返回数据:{window.ReturnData}");
UpdateWindowInfo();
}
}
private void ThirdWindow_WindowClosed(object sender, EventArgs e)
{
if (sender is ThirdWindow window)
{
openWindows.Remove(window);
AddMessage("第三个窗口已关闭");
UpdateWindowInfo();
}
}
private void ThirdWindow_DataReceived(object sender, string data)
{
AddMessage($"从第三个窗口接收到数据:{data}");
}
private void AddMessage(string message)
{
string timeStamp = DateTime.Now.ToString("HH:mm:ss");
txtMessages.Text += $"[{timeStamp}] {message}\n";
// 自动滚动到底部
if (txtMessages.Parent is ScrollViewer scrollViewer)
{
scrollViewer.ScrollToBottom();
}
}
private void UpdateWindowInfo()
{
var openCount = openWindows.Count(w => w.IsLoaded);
txtWindowInfo.Text = $"窗口信息:主窗口 | 打开的子窗口数量:{openCount}";
}
}
}
C#<Window x:Class="AppWpfWindows.SecondWindow"
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:AppWpfWindows"
mc:Ignorable="d"
Title="SecondWindow" Height="500" Width="800">
<Grid Margin="20">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" x:Name="txtWelcome"
FontSize="18" FontWeight="Bold"
HorizontalAlignment="Center"
Foreground="#8B4513" Margin="0,0,0,15"/>
<GroupBox Grid.Row="1" Header="数据输入" Margin="0,10"
FontWeight="Bold" Foreground="#8B4513">
<StackPanel Margin="10">
<StackPanel Orientation="Horizontal" Margin="0,5">
<TextBlock Text="选择您的职业:" Width="100" VerticalAlignment="Center"/>
<ComboBox x:Name="cmbProfession" Width="200" Height="25">
<ComboBoxItem Content="软件工程师"/>
<ComboBoxItem Content="产品经理"/>
<ComboBoxItem Content="UI设计师"/>
<ComboBoxItem Content="测试工程师"/>
<ComboBoxItem Content="其他"/>
</ComboBox>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,5">
<TextBlock Text="工作经验:" Width="100" VerticalAlignment="Center"/>
<Slider x:Name="sliderExperience" Width="150" Minimum="0" Maximum="20"
Value="1" TickFrequency="1" IsSnapToTickEnabled="True"/>
<TextBlock x:Name="txtExperienceValue" Text="1 年"
VerticalAlignment="Center" Margin="10,0"/>
</StackPanel>
</StackPanel>
</GroupBox>
<GroupBox Grid.Row="2" Header="个人偏好" Margin="0,10"
FontWeight="Bold" Foreground="#8B4513">
<StackPanel Margin="10">
<CheckBox x:Name="chkNewsletter" Content="订阅技术通讯" Margin="0,3"/>
<CheckBox x:Name="chkUpdates" Content="接收产品更新" Margin="0,3"/>
<CheckBox x:Name="chkPromotions" Content="接收优惠信息" Margin="0,3"/>
</StackPanel>
</GroupBox>
<GroupBox Grid.Row="3" Header="备注" Margin="0,10"
FontWeight="Bold" Foreground="#8B4513">
<TextBox x:Name="txtComments" TextWrapping="Wrap"
AcceptsReturn="True" Margin="5"
VerticalScrollBarVisibility="Auto"/>
</GroupBox>
<StackPanel Grid.Row="4" Orientation="Horizontal"
HorizontalAlignment="Right" Margin="0,15,0,0">
<Button x:Name="btnConfirm" Content="确认" Width="80" Height="30"
Margin="5" Background="#90EE90" BorderBrush="#32CD32"
Click="btnConfirm_Click"/>
<Button x:Name="btnCancel" Content="取消" Width="80" Height="30"
Margin="5" Background="#FFB6C1" BorderBrush="#DC143C"
Click="btnCancel_Click"/>
</StackPanel>
</Grid>
</Window>
C#using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
namespace AppWpfWindows
{
/// <summary>
/// Interaction logic for SecondWindow.xaml
/// </summary>
public partial class SecondWindow : Window
{
public event EventHandler WindowClosed;
public string ReturnData { get; private set; } = "无数据";
private string userName;
public SecondWindow(string userName)
{
InitializeComponent();
this.userName = userName;
this.Loaded += SecondWindow_Loaded;
this.Closing += SecondWindow_Closing;
// 绑定滑块值变化事件
sliderExperience.ValueChanged += SliderExperience_ValueChanged;
}
private void SecondWindow_Loaded(object sender, RoutedEventArgs e)
{
txtWelcome.Text = $"欢迎,{userName}!";
// 设置默认值
cmbProfession.SelectedIndex = 0;
UpdateExperienceDisplay();
}
private void SecondWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
WindowClosed?.Invoke(this, EventArgs.Empty);
}
private void SliderExperience_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
UpdateExperienceDisplay();
}
private void UpdateExperienceDisplay()
{
if (txtExperienceValue != null)
{
int years = (int)sliderExperience.Value;
txtExperienceValue.Text = $"{years} 年";
}
}
private void btnConfirm_Click(object sender, RoutedEventArgs e)
{
try
{
// 验证必要输入
if (cmbProfession.SelectedItem == null)
{
MessageBox.Show("请选择您的职业!", "提示",
MessageBoxButton.OK, MessageBoxImage.Warning);
return;
}
// 收集数据
var selectedProfession = ((ComboBoxItem)cmbProfession.SelectedItem).Content.ToString();
var experience = (int)sliderExperience.Value;
var preferences = new List<string>();
if (chkNewsletter.IsChecked == true) preferences.Add("技术通讯");
if (chkUpdates.IsChecked == true) preferences.Add("产品更新");
if (chkPromotions.IsChecked == true) preferences.Add("优惠信息");
var comments = txtComments.Text.Trim();
// 构建返回数据
ReturnData = $"职业: {selectedProfession}, " +
$"经验: {experience}年, " +
$"偏好: {string.Join(",", preferences)}" +
(string.IsNullOrEmpty(comments) ? "" : $", 备注: {comments}");
// 显示确认信息
var result = MessageBox.Show(
$"确认提交以下信息?\n\n{ReturnData}",
"确认信息",
MessageBoxButton.YesNo,
MessageBoxImage.Question);
if (result == MessageBoxResult.Yes)
{
this.DialogResult = true;
this.Close();
}
}
catch (Exception ex)
{
MessageBox.Show($"处理数据时发生错误:{ex.Message}", "错误",
MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private void btnCancel_Click(object sender, RoutedEventArgs e)
{
ReturnData = "用户取消操作";
this.DialogResult = false;
this.Close();
}
}
}
C#<Window x:Class="AppWpfWindows.ThirdWindow"
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:AppWpfWindows"
mc:Ignorable="d"
Title="ThirdWindow" Height="500" Width="800">
<Grid Margin="15">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" x:Name="txtTitle"
FontSize="20" FontWeight="Bold"
HorizontalAlignment="Center"
Foreground="#4B0082" Margin="0,0,0,15"/>
<GroupBox Grid.Row="1" Header="实时信息" Margin="0,5"
FontWeight="Bold" Foreground="#4B0082">
<StackPanel Margin="10">
<StackPanel Orientation="Horizontal" Margin="0,5">
<TextBlock Text="当前时间:" Width="80" VerticalAlignment="Center"/>
<TextBlock x:Name="txtCurrentTime" FontSize="14"
FontWeight="Bold" Foreground="#8B008B"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,5">
<TextBlock Text="窗口状态:" Width="80" VerticalAlignment="Center"/>
<TextBlock x:Name="txtWindowStatus" FontSize="12"
Foreground="#8B008B"/>
</StackPanel>
</StackPanel>
</GroupBox>
<GroupBox Grid.Row="2" Header="消息中心" Margin="0,10"
FontWeight="Bold" Foreground="#4B0082">
<Grid Margin="10">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="发送消息到主窗口:" Margin="0,0,0,5"/>
<TextBox Grid.Row="1" x:Name="txtMessage"
TextWrapping="Wrap" AcceptsReturn="True"
VerticalScrollBarVisibility="Auto"
FontSize="12" Margin="0,0,0,10"/>
<StackPanel Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Right">
<Button x:Name="btnSendMessage" Content="发送消息"
Width="80" Height="25" Margin="5,0"
Background="#DDA0DD" BorderBrush="#9370DB"
Click="btnSendMessage_Click"/>
<Button x:Name="btnClearMessage" Content="清除"
Width="60" Height="25" Margin="5,0"
Background="#F0E68C" BorderBrush="#DAA520"
Click="btnClearMessage_Click"/>
</StackPanel>
</Grid>
</GroupBox>
<GroupBox Grid.Row="3" Header="窗口操作" Margin="0,10"
FontWeight="Bold" Foreground="#4B0082">
<StackPanel Orientation="Horizontal" Margin="10" HorizontalAlignment="Center">
<Button x:Name="btnMinimize" Content="最小化"
Width="70" Height="25" Margin="5"
Background="#87CEEB" BorderBrush="#4682B4"
Click="btnMinimize_Click"/>
<Button x:Name="btnToggleTopmost" Content="置顶切换"
Width="70" Height="25" Margin="5"
Background="#98FB98" BorderBrush="#32CD32"
Click="btnToggleTopmost_Click"/>
<Button x:Name="btnChangePosition" Content="改变位置"
Width="70" Height="25" Margin="5"
Background="#F0E68C" BorderBrush="#DAA520"
Click="btnChangePosition_Click"/>
</StackPanel>
</GroupBox>
<Button Grid.Row="4" x:Name="btnClose" Content="关闭窗口"
Width="100" Height="30" Margin="0,15,0,0"
Background="#FFB6C1" BorderBrush="#DC143C"
HorizontalAlignment="Right"
Click="btnClose_Click"/>
</Grid>
</Window>
C#using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.Windows.Threading;
namespace AppWpfWindows
{
/// <summary>
/// Interaction logic for ThirdWindow.xaml
/// </summary>
public partial class ThirdWindow : Window
{
public event EventHandler WindowClosed;
public event EventHandler<string> DataReceived;
private DispatcherTimer timer;
private string userName;
private Random random = new Random();
public ThirdWindow(string userName)
{
InitializeComponent();
this.userName = userName;
this.Loaded += ThirdWindow_Loaded;
this.Closing += ThirdWindow_Closing;
this.StateChanged += ThirdWindow_StateChanged;
InitializeTimer();
}
private void ThirdWindow_Loaded(object sender, RoutedEventArgs e)
{
txtTitle.Text = $"{userName} 的工作空间";
UpdateWindowStatus();
// 设置窗口初始位置(相对于父窗口偏移)
if (this.Owner != null)
{
this.Left = this.Owner.Left + 50;
this.Top = this.Owner.Top + 50;
}
timer.Start();
}
private void ThirdWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
timer?.Stop();
WindowClosed?.Invoke(this, EventArgs.Empty);
}
private void ThirdWindow_StateChanged(object sender, EventArgs e)
{
UpdateWindowStatus();
}
private void InitializeTimer()
{
timer = new DispatcherTimer
{
Interval = TimeSpan.FromSeconds(1)
};
timer.Tick += Timer_Tick;
}
private void Timer_Tick(object sender, EventArgs e)
{
txtCurrentTime.Text = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
}
private void UpdateWindowStatus()
{
if (txtWindowStatus != null)
{
string status = this.WindowState switch
{
WindowState.Normal => "正常",
WindowState.Minimized => "最小化",
WindowState.Maximized => "最大化",
_ => "未知"
};
string topmost = this.Topmost ? " | 置顶" : "";
txtWindowStatus.Text = $"{status}{topmost}";
}
}
private void btnSendMessage_Click(object sender, RoutedEventArgs e)
{
string message = txtMessage.Text.Trim();
if (string.IsNullOrEmpty(message))
{
MessageBox.Show("请输入要发送的消息!", "提示",
MessageBoxButton.OK, MessageBoxImage.Warning);
return;
}
string fullMessage = $"[来自 {userName}]: {message}";
DataReceived?.Invoke(this, fullMessage);
MessageBox.Show("消息已发送到主窗口!", "成功",
MessageBoxButton.OK, MessageBoxImage.Information);
txtMessage.Clear();
}
private void btnClearMessage_Click(object sender, RoutedEventArgs e)
{
txtMessage.Clear();
txtMessage.Focus();
}
private void btnMinimize_Click(object sender, RoutedEventArgs e)
{
this.WindowState = WindowState.Minimized;
}
private void btnToggleTopmost_Click(object sender, RoutedEventArgs e)
{
this.Topmost = !this.Topmost;
UpdateWindowStatus();
string status = this.Topmost ? "已启用" : "已禁用";
MessageBox.Show($"窗口置顶功能{status}", "窗口置顶",
MessageBoxButton.OK, MessageBoxImage.Information);
}
private void btnChangePosition_Click(object sender, RoutedEventArgs e)
{
// 随机改变窗口位置
double screenWidth = SystemParameters.PrimaryScreenWidth;
double screenHeight = SystemParameters.PrimaryScreenHeight;
double newLeft = random.Next(0, (int)(screenWidth - this.Width));
double newTop = random.Next(0, (int)(screenHeight - this.Height));
this.Left = newLeft;
this.Top = newTop;
MessageBox.Show($"窗口已移动到新位置\n坐标: ({newLeft:F0}, {newTop:F0})",
"位置变更", MessageBoxButton.OK, MessageBoxImage.Information);
}
private void btnClose_Click(object sender, RoutedEventArgs e)
{
if (MessageBox.Show("确定要关闭此窗口吗?", "确认关闭",
MessageBoxButton.YesNo, MessageBoxImage.Question) == MessageBoxResult.Yes)
{
this.Close();
}
}
}
}
C#// ❌ 错误做法:忘记移除事件订阅
thirdWindow.DataReceived += SomeHandler;
// ✅ 正确做法:窗口关闭时清理资源
private void ThirdWindow_Closing(object sender, CancelEventArgs e)
{
timer?.Stop(); // 停止定时器
DataReceived = null; // 清理事件订阅
}
C#// ⭐ 设置父窗口,确保层级关系
secondWindow.Owner = this;
// ⭐ 应用关闭时清理所有子窗口
private void MainWindow_Closing(object sender, CancelEventArgs e)
{
foreach (var window in openWindows.ToList())
{
if (window.IsLoaded) window.Close();
}
}
C#// ⭐ UI更新必须在主线程执行
Dispatcher.Invoke(() => {
txtMessages.Text += $"[{DateTime.Now:HH:mm:ss}] {message}\n";
});
在实际项目中,多窗口开发常用于:
C#// 窗口状态持久化
public class WindowStateManager
{
public static void SaveWindowState(Window window, string key)
{
Properties.Settings.Default[$"{key}_Left"] = window.Left;
Properties.Settings.Default[$"{key}_Top"] = window.Top;
Properties.Settings.Default[$"{key}_Width"] = window.Width;
Properties.Settings.Default[$"{key}_Height"] = window.Height;
Properties.Settings.Default.Save();
}
public static void RestoreWindowState(Window window, string key)
{
var left = Properties.Settings.Default[$"{key}_Left"];
if (left is double leftValue) window.Left = leftValue;
// ... 其他属性恢复
}
}
C#public abstract class BaseWindow : Window
{
protected virtual void OnWindowLoaded()
{
this.Owner = Application.Current.MainWindow;
this.WindowStartupLocation = WindowStartupLocation.CenterOwner;
}
protected virtual void OnWindowClosing()
{
// 子类重写实现清理逻辑
}
}
C#public static class WindowManager
{
private static readonly Dictionary<Type, Window> _windows = new();
public static T ShowSingle<T>() where T : Window, new()
{
if (_windows.TryGetValue(typeof(T), out var existing))
{
existing.Activate();
return (T)existing;
}
var window = new T();
_windows[typeof(T)] = window;
window.Closed += (s, e) => _windows.Remove(typeof(T));
window.Show();
return window;
}
}
通过本文的完整实战演示,我们掌握了WPF多窗口开发的三个核心要点:
掌握这些技术,你就能构建出用户体验优秀、架构清晰的桌面应用程序。在现代软件开发中,良好的多窗口设计不仅提升了用户体验,更体现了开发者的技术水准。
💡 思考题:
📚 延伸学习建议:
觉得这篇文章对你有帮助吗?请分享给更多需要的C#开发同行,让我们一起提升技术水平!🚀
🚀 WPF多窗口开发实战:从入门到精通的完整指南
相关信息
通过网盘分享的文件:AppWpfWindows.zip 链接: https://pan.baidu.com/s/1c7tqDJu9-yEaidHo6TV20g?pwd=wzn3 提取码: wzn3 --来自百度网盘超级会员v9的分享
本文作者:技术老小子
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!