自动位置窗口 - WPF

时间:2017-09-18 09:23:40

标签: c# wpf

我创建了一些窗口,禁用了边框并设置了SizeToContent =" Height"。

XAML:

<Window Title="info" Width="350" ResizeMode="NoResize" WindowStyle="None" Opacity="1" Background="{x:Null}" AllowsTransparency="True" SizeToContent="Height">
   <Grid>
     <StackPanel Name="maingrid" Background="AliceBlue">
     </StackPanel>
   </Grid>
</Window>

当我打开此窗口时,我将其置于右下方屏幕:

private void setposition()
{
   var primaryMonitorArea = SystemParameters.WorkArea;
   this.Left = primaryMonitorArea.Right - this.Width;
   this.Top = primaryMonitorArea.Bottom - this.Height;
}

有时我会尝试将子项添加到 maingrid ,然后重新定位,因为Height已更改:

Border brd = new Border();
DockPanel.SetDock(brd, Dock.Top);
brd.Margin =new System.Windows.Thickness(0,5,0,0);
DockPanel dpanel = new DockPanel();
System.Windows.Controls.Label header = new System.Windows.Controls.Label();
header.Content = "test";
DockPanel.SetDock(header, Dock.Top);
dpanel.Children.Add(header);
brd.Child = dpanel;
maingrid.Children.Add(brd);
setposition();
System.Windows.Forms.Timer tmer = new System.Windows.Forms.Timer();
tmer.Tick += (sender, e) => timertick(brd, tmer);
tmer.Interval = 5 * 1000;
tmer.Enabled = true;

之后,窗口位置在垂直平面中随机定位。 5秒后,我摧毁了它的边界并试图重新定位窗口

private void timertick(Border brd, System.Windows.Forms.Timer timer)
{
  maingrid.Children.Remove(brd);
  timer.Enabled = false;
  setposition();
  timer.Dispose();         
}

再次垂直位置有一些奇怪的价值。

屏幕上的位置:

Position on screen

3 个答案:

答案 0 :(得分:0)

你能更准确地指出你的期望并尝试用语言描述你的问题吗?

如果您尝试定位窗户,为什么不使用 WindowStartupLocation

  

将WindowStartupLocation设置为Manual会导致窗口根据其Left和Top属性值进行定位。如果未指定Left或Top属性,则它们的值由Windows确定。   设置CenterScreen会使窗口位于包含鼠标光标的屏幕中央。

     

将WindowStartupLocation设置为CenterOwner会导致窗口出现   位于其所有者窗口的中心(请参阅所有者),如果   指定。所有者窗口可以是另一个WPF窗口或   非WPF窗口。

MSDN source

答案 1 :(得分:0)

在你调用setposition();的地方,窗口大小尚未实现,获得实际大小的最简单方法是通过将动作放入调度程序队列来等待渲染。

考虑这个例子:

public MainWindow()
{
    InitializeComponent();
    Loaded += (s, e) => Title = $"{ActualHeight}"; // 39
    var timer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(1), IsEnabled = true };
    timer.Tick += (s, e) =>
    {
        stackPanel.Children.Add(new TextBlock { Text = "123", Width = 200, Height = 20 });
        Title = $"{ActualHeight}"; // 39, 59, 79 ... wrong
        // below is correct way, displays 59, 79 ...
        //Dispatcher.Invoke(() => Title = $"{ActualHeight}", DispatcherPriority.Render);
    };
}

这里我有一个窗口,其大小与内容相同并且有stackPanel

<Window SizeToContent="Height" Width="200" ...>
    <StackPanel x:Name="panel" />
</Window>

计时器每秒都会向其添加新的TextBlock

现在,如果我尝试像这样更新Title,那么ActualHeight仍然是旧版本,首次更新后第一个值为39这是错误的(它应该是{ {1}})。发生这种情况是因为添加子项并不会导致立即重新计算所有内容并进行渲染。不是。相反,所有此类操作(测量,排列和渲染)都通过调度程序排队,而会在稍后发生。这就是WPF的工作原理。

要解决问题,我可以简单地告诉&#34;请在渲染后执行此操作&#34;

20 + 39 = 59

答案 2 :(得分:0)

我发现,maingrid.ActualHeight在渲染窗口之前有实际高度,并且这样做:

this.Top = primaryMonitorArea.Bottom - maingrid.ActualHeight;

工作正常。