WPF 中心子窗口无法使用 sizetocontent

发布于 2024-08-11 09:32:01 字数 218 浏览 3 评论 0原文

如果我将 SizeToContent 设置为 WidthAndHeight,则 WindowStartupLocation="CenterOwner" 无法正常工作。新窗口的中心不是位于其父窗口的中心,而是看起来更像是子窗口的左上角位于父窗口的中心。 如果我删除 SizeToContent 那么一切都可以。 怎么了?

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

梦回梦里 2024-08-18 09:32:01

好吧,雷把这一点说得非常出色。简单来说,他想说的是,您在 Loaded 事件中设置控件的内容,该事件会重置 Height 和高度。窗口定位完成后的Width(以及ActualHeightActualWidth)。

要解决此问题,您有两种选择:

  1. 将内容值设置代码移至构造函数,或者
  2. 添加一个简单的方法来根据 Owner 重新计算 Window 的位置并在 Loaded 事件结束时调用此方法,如下所示:

...

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

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;
    }
}
陌伤ぢ 2024-08-18 09:32:01

当显示窗口时,对其进行测量,然后使用测量过程计算出的窗口的 ActualWidthActualHeight 来处理 WindowStartupLocation

您描述的行为告诉我 ActualWidthActualHeight 在 Show() 或 ShowDialog() 调用时被测量为零或相对较小,然后才设置为非零值。

例如,如果窗口的内容是使用仅在 Loaded 事件上设置的 DataContext 构建的,则可能会发生这种情况。当调用 Show() 时,窗口尚未Loaded,因此它没有数据。稍后,当 Loaded 事件触发时,它会设置 DataContext 并且窗口会更新其内容,但定位已经发生。

还有许多其他场景,例如使用 Dispatcher.BeginInvoke 调用填充的内容,或者来自单独线程的内容,或者延迟或异步的绑定。

基本上,您需要查找任何可能导致调用 Show() 时窗口内容小于正常内容的因素,并修复它。

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.

待"谢繁草 2024-08-18 09:32:01

绑定的动态内容大多直接通过 GUI 呈现,但有时通过 GUI 调度。计时器和其他线程可以发起(MVVM)属性更改事件。可以肯定的是,渲染会在很短的时间内完成,但不能保证,因为位置是 WPF 调度程序队列的优先级。
因此,您无法说出渲染何时完成,并且 WPF 无法说出有关处理顺序的信息 - 因此 WPF 现在无法计算 StartPosition 的理想时间。

一个技巧是等待 WPF 队列为空。那么您就可以确定 WPF 有时间处理您的代码。这意味着,您延迟了 Window 的 ShowDialog 调用。

因此,请为 GUI 主线程提供所需的所有时间,以执行 MVVM 的动态内容更改或其他动态更改。
不要尝试手动计算位置,它非常复杂,要支持多显示器。
尝试使用此代码打开窗口,当WPF完成所有操作时,它仅打开窗口。

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

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);
夏尔 2024-08-18 09:32:01

你的问题有点模棱两可。您在哪个窗口(“父”或“子”)上设置 SizeToContent 和 WindowStartupLocation?

如果我在项目中创建第二个窗口并按照您描述的方式设置其 SizeToContent 和 WindowStartupLocation,我会得到所需的结果。

我能想到的唯一你可能忘记的事情就是实际告诉子窗口它的所有者是谁:

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

或者,更简洁地说:

new Window2 { Owner = this }.ShowDialog();

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();
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文