为什么WindowStartupLocation = CenterScreen将我的窗口放置在屏幕中心以外的位置?

15

这里是窗口声明:

<Window
    x:Class="Pse.ExperimentBase.SplashWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Style="{StaticResource _windowStyle}"
    WindowStartupLocation="CenterScreen"
    WindowStyle="None">

以下是样式声明:

<Style
    x:Key="_windowStyle"
    TargetType="Window">
    <Setter
        Property="Width"
        Value="{Binding Path=InitialWindowWidth}" />
    <Setter
        Property="Height"
        Value="{Binding Path=InitialWindowHeight}" />
    <Setter
        Property="Icon"
        Value="Resources/MyIcon.ico" />
    <Setter
        Property="Background"
        Value="{StaticResource _fadedOrangeBrush}" />
    <Setter
        Property="FontSize"
        Value="11" />
</Style>

讨论:

屏幕大小为1280 X 1024。窗口大小(由InitialWindowWidth,InitialWindowHeight绑定确定)为800 X 600。

当窗口打开时,它出现在188, 141(左侧,顶部)。这基本上是它应该出现的“西北方向”。如果我计算真正居中的值,它应该是240, 212(左侧,顶部)。

一个线索?

问题总是出现在第一个窗口。如果我打开同样窗口的第二个实例,它将会出现在正确位置。

另一个线索?

只有在打开第一个实例之前创建两个实例,第二个窗口实例才能出现在正确位置。

所以......

Window win1 = windowFactory.CreateSplashWindow();
win1.Show();
Window win2 = windowFactory.CreateSplashWindow();
win2.Show();
win1.Hide();

...影响到win1和win2的偏移量。

但是...

Window win1 = windowFactory.CreateSplashWindow();
Window win2 = windowFactory.CreateSplashWindow();
win1.Show();
win2.Show();
win1.Hide();

...offsets win1 but shows win2 dead center.

所以我的问题是:

这里到底发生了什么?


编辑:

又一个线索。如果我添加...

Width="800"
Height="600"

通过将偏移量绑定到我的样式中,可以消除偏移问题。但这对我来说不是一个解决方案,因为我需要窗口大小成为存储在设置文件中的首选项。这就是为什么我在我的样式中使用数据绑定。(请注意,宽度和高度的数据绑定工作正常--所有窗口都显示为正确的大小)。


编辑#2:

我在调试模式下检查了宽度和高度,并得出了这个有趣的结论:

如果我创建多个窗口实例但不打开任何窗口,则所有窗口实例的宽度和高度属性均为“NaN”。

如果我仅打开一个实例,则所有窗口实例的宽度和高度属性突然变为有效数字,而不仅仅是我打开的窗口的宽度和高度。

这告诉我WPF将数据绑定推迟到第一个窗口上调用Show()。

-->现在的问题是:我能否强制WPF在显示第一个窗口之前进行数据绑定?


编辑#3:

我刚刚向Microsoft提交了关于此问题的错误报告。以下是链接:

https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=477598

编辑#4:解决方法

目前还没有真正的解决办法,但我刚刚确认您可以使用一个虚拟窗口来解决这个问题。按照以下步骤操作:

  1. 创建一个名为“BlankWindow”的新XPF窗口(参见下面的XAML)。
  2. 创建BlankWindow的实例以及您想在屏幕上居中的窗口(FirstWindow)。
  3. 执行以下代码序列:

...

BlankWindow.Show()
FirstWindow.Show()
BlankWindow.Hide()

我的虚拟窗口的XAML代码只是这样的:

<Window
    x:Class="Pse.ExperimentBase.Windows.BlankWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="BlankWindow"
    Height="0"
    Width="0"
    WindowStyle="None">
    <Grid></Grid>
</Window>

在“FirstWindow”打开之前,我没有看到明显的闪烁,所以我认为这是一个足够好的解决方法,直到微软解决这个bug。

4个回答

4

另一个解决方法是...给绑定添加一个备用值。

Width="{Binding Width, FallbackValue=500}" Height="{Binding Height, FallbackValue=500}"

4
你的WidthHeight绑定是否造成了阻碍?如果你根本没有设置WidthHeight,那么窗口是否出现在正确的位置?如果是的话,那么这个位置是否与上述绑定设置时相同?
如果所有这些都是真的,那么似乎WPF在获取从你的绑定中得到WidthHeight值之前计算了窗口的位置。我不确定有什么好方法可以解决这个问题 - 或许可以先手动设置WidthHeight(无论是在XAML中还是构造函数中),然后在它被显示后再设置绑定?

1
@Andy,我认为你是对的。没有绑定时,窗口默认大小为907 X 747,并且窗口被放置在坐标(188,141)处。有绑定时,大小为800 X 600,但坐标仍然是(188,141)。然而,这并不适用于同一窗口的其他实例。不过,你给了我另一个测试的想法(请参见上面的编辑)。 - devuxer

0

在您的示例中,我没有看到与边距相关的任何参考。然而,当我的项目(在网格中)在设计器中拖动一个对象的边缘时,经常会因为Visual Studio更改其边距而感到沮丧(我认为它应该更改其宽度,而不是边距)。而且,奇怪的边距可能会导致意想不到的位置问题。


外边距/宽高变化的行为取决于父容器的对齐方式。如果在你要改变宽度的维度上被拉伸以填充,则设计师会更改边距,概念是你想相对于父级更改子元素的大小,仍然允许子元素在父级大小更改时调整大小。我同意这很烦人。 - Gusdor

0

这是一个相当老的帖子,但由于我找不到任何合适的解决方案,我有另外一种解决方法: 您可以手动计算您对话框的位置 (http://social.msdn.microsoft.com/Forums/eu/wpf/thread/054800c3-56dc-4700-a2fb-d2b79fc9ef9a)。

虽然不是非常优雅,但它是有效的:

public class Controller
{
     private Double CalcCoordinate(Double parentCoordinate, Double parentSize, Double thisSize)
     {
          return parentCoordinate + (parentSize - thisSize) / 2;
     }
     public Controller(Double width, Double height, Window owner)
     {
          Width = width;
          Height = height;
          Left = CalcCoordinate(owner.Left, owner.Width, Width);
          Top = CalcCoordinate(owner.Top, owner.Height, Height);
     }

public Double Width { get; set; }
public Double Height { get; set; }
public Double Left { get; set; }
public Double Top { get; set; }

}

然后,只需将窗口的位置和大小绑定到这些属性上,并不要忘记将WindowStartupLocation更改为Manual。

由于某种原因,我无法理解,Top/Left必须以TwoWay模式绑定,否则我无法使其正常工作。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接