我在一个 XAML 窗口上放置了几个下拉列表框。当我展开它们中的任意一个时,下拉部分出现在屏幕左上角。
我使用的是 Visual Studio 2008 C# Express。我不记得在使用 Visual Studio 2008(试用版)时有这种现象,尽管我使用的是相同的框架 (3.5)。
private delegate void DelegateOpenWindow();
private DelegateOpenWindow m_DelegateOpenWindow;
private Timer loginTimer = new Timer(200);
private void MainWindow1_Loaded(object sender, RoutedEventArgs e)
{
// create delegate used for asynchronous call
m_DelegateOpenWindow= new DelegateOpenWindow(this.OpenWindow);
// start a timer to fire off the open window.
loginTimer.Elapsed += loginTimer_Elapsed;
loginTimer.Enabled = true;
}
void loginTimer_Elapsed(object sender, ElapsedEventArgs e)
{
loginTimer.Enabled = false;
this.Dispatcher.BeginInvoke(m_DelegateOpenWindow);
}
void OpenWindow()
{
MyWindow w = new MyWindow();
w.Owner = this;
w.ShowDialog();
}
这似乎是一个bug。
解决方法:
使用Window.Show()
代替,并使用自定义逻辑模拟ShowDialog()
的行为。
昨天我开始观察到这个问题(以及其他奇怪的行为习惯),当我尝试从Window.Loaded事件处理程序中“微调”窗口大小、形状、颜色并调用登录对话框时。在每个MVVM模式应用程序的代码后台中,我一直都做得很好。昨天,我决定将其从每个应用程序的代码后台移动到一个合并的代码后台基类中,因为预处理已经在所有这些应用程序中变得普遍。当我这样做时,登录对话框中两个ComboBox中的下拉菜单突然出现在屏幕的左上角。我似乎通过使用以下技术来“解决”它(您的结果可能会有所不同):
protected void WindowBaseLoadedHandler(object sender, RoutedEventArgs e)
{
...non-essential lines of code removed...
if (DataContext != null)
{
Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
{
/*----------------------------------------------------------------------
* Do we have a View Model? If so, perform standard VM Initialization...
*---------------------------------------------------------------------*/
this.IsEnabled = false;
LoginDlg loginDlg = new LoginDlg();
loginDlg.ShowDialog();
if (!loginDlg.Success)
{
/*-----------------------------------
* Log on failed -- terminate app...
*----------------------------------*/
...termination logic removed...
}
this.IsEnabled = true;
}));
}
WindowBaseLoadedHandler 是 Loaded 事件处理程序。LoginDlg 是一个 WPF 应用程序,其中包含两个 ComboBox 的对话框。
回顾一下:在我将代码合并到基类的 Loaded 事件处理程序中后,ComboBox 的下拉列表出现在屏幕的左上角。一旦我将逻辑封装到 Dispatcher.BeginInvoke 调用中,适当的 ComboBox 行为就会返回,并显示当前项目下方的列表。
我怀疑 WPF 需要应用程序从 Loaded 事件返回以完成布局系统的初始化。这并不能完全解释为什么之前可以工作,但我将把追寻“为什么”的愿望留到未来的某个雨天,并庆祝今天克服了最新的障碍。
无论如何,我希望有人能从中受益。
我正在使用最新的 .Net 4.5 和 WPF 框架,但仍然遇到这个问题。我注意到的一件事是,只有在附加了调试器时才会出现这个问题。当调试器未附加时,一切正常。
我在Visual Studio 2019上遇到了同样的问题。 使用window.Show()可以解决问题,但可能会破坏您的设计。 解决方法是异步打开窗口。
var yourDialog= new YourDialog();
yourDialog.Owner = this;
TaskCompletionSource<bool?> completion = new TaskCompletionSource<bool?>();
this.Dispatcher.BeginInvoke(new Action(() =>
completion.SetResult(yourDialog.ShowDialog())));
bool? result = await completion.Task;
你也可以通过创建扩展方法来实现更优雅的解决方案:
public static class AsyncWindowExtension
{
public static Task<bool?> ShowDialogAsync(this Window self)
{
if (self == null) throw new ArgumentNullException("self");
TaskCompletionSource<bool?> completion = new TaskCompletionSource<bool?>();
self.Dispatcher.BeginInvoke(new Action(() => completion.SetResult(self.ShowDialog())));
return completion.Task;
}
}
你可以像这样使用它:
await dlgReview.ShowDialogAsync();
这是WPF中的一个bug(恐怕不是唯一的一个)。当我在Loaded事件中打开另一个窗口时,就会出现这个问题,类似于:
private void Window_Loaded(object sender, RoutedEventArgs e)
{
Window selectionWindow = new SelectionWindow();
bool? result = selectionWindow.ShowDialog();
if (result == true)
RecordChanged();
}
我已经找到了一个解决方法。
Loaded
处理程序在Dispatcher
上排队另一条消息,以打开Window
。 - Kent Boogaart