Question

If i set SizeToContent to WidthAndHeight, then WindowStartupLocation="CenterOwner" does not work properly. Instead of the center of the new window to be at the center of its parent owner, it looks more like the top left hand corner of the child window to be at the center of the parent. If i remove SizeToContent then all is ok. What is wrong?

Was it helpful?

Solution

When a window is shown, it is measured, then WindowStartupLocation is processed using the ActualWidth and ActualHeight of the window computed by the measure process.

The behavior you describe tells me ActualWidth and ActualHeight are measured to be either zero or relatively small at the time of the Show() or ShowDialog() call and only later set to nonzero values.

This can happen if, for example, the content of the window is built using a DataContext that is only set on a Loaded event. When the Show() is called, the window has not been Loaded yet so it has no data. Later when the Loaded event fires it sets DataContext and the window updates its content but the positioning has already occured.

There are many other scenarios, for example contents filled using a Dispatcher.BeginInvoke call, or from a separate thread, or bindings that are delayed or asynchronous.

Basically you need to look for anything that could cause the content of your window to be smaller than normal at the moment Show() is called, and fix it.

OTHER TIPS

Well, Ray has put this up brilliantly. In simple terms, what he wants to say is, that you are setting the content of your controls in your Loaded event, which resets the Height & Width (and also the ActualHeight & ActualWidth) after the positioning of the window is done.

To fix this, you have two alternatives:

  1. Move your content value setting code to the constructor, or,
  2. Add a simple method to recalculate the position of your Window according to the Owner and call this method at the end of your Loaded event, like this:

...

private void CenterOwner()
{
    if (Owner != null)
    {
        double top = Owner.Top + ((Owner.Height - this.ActualHeight) / 2);
        double left = Owner.Left + ((Owner.Width - this.ActualWidth) / 2);

        this.Top = top < 0 ? 0 : top;
        this.Left = left < 0 ? 0 : left;
    }
}

Binded dynamic content is mostly rendered Gui-directly but sometimes GUI-dispatched. Timer and other threads can initiate (MVVM) property change events. It is sure, that the rendering is done in near time, but not garanted, because position an priority of the WPF Dispacher queue. So, you can not say when rendering is finished, and WPF cannot say something about the order of processing- so WPF cannot now the ideal time to calculate the StartPosition.

A trick is, to wait, that the WPF-queue is emtpy. Then you are sure that, WPF has time to process your code. That means, you delay the ShowDialog call for the Window.

So give the GUI-Main Thread all the time it needs, to perform the dynamic content changes for MVVM or other dynamics changes. Do not try to calculate the position manually, it is very complex, to support multi displays. Try this code to open the window, it opens the Window only, when WPF finished all operations.

        win.Dispatcher.Invoke(new Action(() => win.ShowDialog()), DispatcherPriority.ApplicationIdle);

Your question is a bit ambiguous. On which window (the "parent" or the "child") are you setting SizeToContent and WindowStartupLocation?

If I create a second window in my project and set its SizeToContent and WindowStartupLocation the way you describe, I get the desired results.

The only thing I can think of that you may be forgetting is to actually tell the child window who its Owner is:

Window2 w = new Window2();
w.Owner = this; // "this" being the parent window
w.ShowDialog();

Or, more succinctly:

new Window2 { Owner = this }.ShowDialog();
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top