如何在 WPF 中显示窗口而不丢失控件?
Posted
技术标签:
【中文标题】如何在 WPF 中显示窗口而不丢失控件?【英文标题】:How can I show the window in WPF without lost controls? 【发布时间】:2021-12-30 17:08:05 【问题描述】:我登录时有一个程序,我必须等待从服务器加载数据库。所以我创建了一个“正在加载...”窗口。加载数据库后,这将自动关闭“加载”窗口、“登录”窗口并打开 MainWindow.xaml。 但是我的程序有一个问题:当我在 Login.xaml.cs 中使用 waitForm.show() 时,它运行良好,但是“等待”窗口上的控件,如进度条、文本块,它不显示。如果我使用 waitForm.showdialog (),它将显示进度条和文本块控件。但它不会自动关闭。所以我的 Mainwindow.xaml 没有打开。 是否可以使用 show() 但显示控件? 对不起我的英语不好。谢谢。
Waiting.xaml
<Window x:Class="TMO.Waiting"
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:TMO"
mc:Ignorable="d"
Title="Waiting" Height="100" Width="300" WindowStartupLocation="CenterScreen" WindowStyle="None">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<ProgressBar Margin="10,20,10,5" Minimum="0" Maximum="100" Name="pbStatus" IsIndeterminate="True" Grid.Row="0"/>
<TextBlock Grid.Row="1" Margin="0,5,0,5" TextAlignment="Center" VerticalAlignment="Center" FontSize="20" FontWeight="Bold" >Loading...</TextBlock>
</Grid>
</Window>
Waiting.xaml.cs
using System.Windows;
namespace TMO
/// <summary>
/// Interaction logic for Waiting.xaml
/// </summary>
public partial class Waiting : Window
public static MessageBoxResult result;
public Waiting()
InitializeComponent();
public Waiting(Window parent1)
InitializeComponent();
public void CloseWaiting()
DialogResult = true;
this.Close();
WaitFunc.cs
using System.Threading;
using System.Windows;
namespace TMO
public class WaitFunc
Waiting wait;
Thread loadthread;
public void show()
loadthread = new Thread(new ThreadStart(LoadingProcess));
loadthread.Start();
public void show(Window parent)
loadthread=new Thread(new ParameterizedThreadStart(LoadingProcess));
loadthread.Start(parent);
public void Close()
if(wait!=null)
wait.Dispatcher.BeginInvoke(new System.Threading.ThreadStart(wait.CloseWaiting));
wait=null;
loadthread=null;
public void LoadingProcess()
wait=new Waiting();
wait.ShowDialog();
public void LoadingProcess(object parent)
Window parent1 = parent as Window;
wait=new Waiting(parent1);
wait.ShowDialog();
Login.xaml.cs
private void btnLogin_Click(object sender, RoutedEventArgs e)
this.Hide();
Waiting waitForm = new Waiting();
waitForm.Show(); //If I use waitForm.showdialog() it will display all control
Thread.Sleep(500);
MainWindow main = new MainWindow(txtUserName.Text);
this.Hide();
waitForm.Close();
main.Show();
this.Close();
【问题讨论】:
线程和窗口确实不能很好地混合。我不是 WPF 人(所以我不能告诉你这样做的正确方法,但是那个额外的线程可能是问题所在。你需要弄清楚如何打开“等待”窗口,以及它何时关闭发送向另一个窗口显示消息。另外,了解await Task.Delay
;在用户界面中使用Thread.Sleep
是个坏主意。
谢谢。该程序是用 WinForms 编写的,但现在我将其更改为 WPF。我是 WPF 新手,所以我对 WPF 有很多问题。
对于 Winforms,看看我对这个问题的回答。您也许可以将其适应 WPF:***.com/questions/62936319/…
@FlyDog57 关于线程和 Windows 是正确的。在幕后,您无意中在一个线程上创建了一个表单,但在另一个线程上初始化了组件。 C# 中的 Windows 始终在初始应用线程上创建,无论它们在何处初始化。
这里的线程实例太多。也从未见过从 Dispatcher 调用创建线程。总是有新意。不要这样做。
【参考方案1】:
您应该从应用程序的入口点管理应用程序启动顺序的所有窗口。因此,我建议将启动 MainWindow
的逻辑从 Login.xaml.cs 移动到 App.xaml.cs。
此外,要创建一个可以显示控件并允许输入处理的 UI 线程,您必须将该线程显式配置为 STA 线程并另外启动 Dispatcher
消息循环。
请注意,如果您在必要时使用异步 API 和后台线程(使用 Task.Run
而不是 Thread
),那么您不需要额外的 UI 线程来显示初始屏幕。
您应该将LoginSuccessful
和LoginFailed
事件添加到LoginWindow
。
您的流程可以这样实现:
App.xaml
<Application Startup="App_OnStartup" />
App.xaml.cs
public partial class App : Application
private Window SplashScreen get; set;
private void App_OnStartup(object sender, StartupEventArgs e)
ShutdownMode = ShutdownMode.OnExplicitShutdown;
var loginScreen = new LoginWindow();
loginScreen.LoginSuccessful += RunApplication_OnLoginSuccessful;
loginScreen.LoginFailed += ShutdownApplication_OnLoginFailed;
loginScreen.Show();
private void RunApplication_OnLoginSuccessful(object sender, EventArgs e)
var newUiThread = new Thread(new ThreadStart(ShowSplashScreen))
ApartmentState = ApartmentState.STA,
IsBackground = false,
;
newUiThread.Start();
var mainWindow = new MainWindow();
mainWindow.Loaded += CloseSplashScreen_OnMainWindowLoaded;
mainWindow.Closed += ShutdownApplication_OnMainWindowClosed;
// TODO::Initialize application
mainWindow.Show();
private void ShowSplashScreen()
this.SplashScreen = new Window() Content = "SplashScreen" ;
this.SplashScreen.Show();
// Start event queue
Dispatcher.Run();
private void CloseSplashScreen_OnMainWindowLoaded(object sender, RoutedEventArgs e)
this.SplashScreen.Dispatcher.InvokeAsync(() =>
this.SplashScreen.Close();
this.SplashScreen.Dispatcher.InvokeShutdown();
);
private void ShutdownApplication_OnMainWindowClosed(object sender, EventArgs e) => Shutdown();
private void ShutdownApplication_OnLoginFailed(object sender, EventArgs e) => Shutdown();
【讨论】:
以上是关于如何在 WPF 中显示窗口而不丢失控件?的主要内容,如果未能解决你的问题,请参考以下文章
wpf combobox 关闭窗口后,再次调用不显示已经选择的值?
如何在 webbrowser WPF 顶部打开用户控件作为弹出窗口