开发者

WPF ComboBox DropDown part appears in the wrong place

开发者 https://www.devze.com 2022-12-15 14:33 出处:网络
I put several ComboB开发者_StackOverflow中文版oxes on a XAML window. When I expand any of them, the DropDown part appears on the upper left corner of the screen.

I put several ComboB开发者_StackOverflow中文版oxes on a XAML window. When I expand any of them, the DropDown part appears on the upper left corner of the screen.

I use Visual Studio 2008 C# Express. I don't remember this phenomenon when I used Visual Studio 2008 (Trial Version), though I use the same FrameWork (3.5).


It seems to be a bug.

Workaround:

Use Window.Show() instead with a custom logic to simulate the ShowDialog() behavior.


This appears to be a bug in WPF. In my case, I was trying to open a window in the Loaded event of another window. To get around this, I set a timer up to fire, then used a delegate to open the window (cannot open the window in a timer event because the calling thread that opens a window must be STA).

Edit - timer isn't necessary - didn't see the answer above just queue it on the dispatcher...

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();
}


I started observing this (and other strange behavioral quirks) yesterday when I tried to "tweak" window sizes, shapes, colors, and invoke a log-on dialog from the Window.Loaded event handler. I had been doing this just fine in each of a dozen+ individual "MVVM" pattern apps. Yesterday, I decided to move this from each app's code behind into a consolidated code-behind base class, since the pre-processing had become common in all those apps. When I did, the drop-downs in two ComboBoxes in the log-in dialog suddenly appeared in the upper left corner of my screen. I seem to have "solved" it by using the following technique (your mileage may vary):

    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 is the Loaded event handler. LoginDlg is a WPF app with a dialog containing two ComboBoxes.

Recap: After I consolidated the code into the Loaded event handler of the base class the ComboBox's drop down lists appeared in the upper left corner of my screen. Once I wrapped the logic into the Dispatcher.BeginInvoke call, the appropriate ComboBox behavior returned with lists below the current item.

I suspect WPF needs the application to return from the Loaded event to complete the layout system's initialization. That doesn't fully explain why it worked before, but I'll have to queue up my desire to hunt that "why" down for some rainy day in the future and celebrate overcoming the latest obstacle for today.

In any event, I hope someone finds this of use.


I'm using the latest .Net 4.5 and WPF framework and I still have this problem. One thing I noticed is that it only happen when there's an attached debugger. When the debugger is not attached, everything works fine.


I had the same problem on Visual Studio 2019. Using window.Show() can help but it can ruin your design. The solution is to open the window asynchronously.

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;

You can also create a more elegant solution by making the extension method:

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;
    }
}

And you can use it like this:

await dlgReview.ShowDialogAsync();


It’s a bug in WPF (not the only one, I'm afraid). It happened when I opened another window in the Loaded Event, something like:

private void Window_Loaded(object sender, RoutedEventArgs e)
{
    Window selectionWindow = new SelectionWindow();
    bool? result = selectionWindow.ShowDialog();
    if (result == true)
        RecordChanged();
}

I already found a workabout.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号