【问题标题】:Wrong startup position of window, when SizeToContent is Manual, and Window.Top and Window.Left are databound当 SizeToContent 为 Manual 且 Window.Top 和 Window.Left 是数据绑定时,窗口的启动位置错误
【发布时间】:2017-02-24 13:11:40
【问题描述】:

我遇到了一个相当奇怪的问题。似乎只要窗口的 SizeToContent 设置为 Manual 并且尺寸和顶部/左侧属性是数据绑定的,Presentation Framework 就不会在显示窗口时更新窗口的位置。

我有一个对话框窗口控件,用于在其中显示其他用户控件,其行为应由 ViewModel 控制。我希望用SizeToContent.WidthAndHeight 和ResizeMode.NoResize 显示一些对话框,并将它们放在所有者窗口的中心。我希望在可以调整大小但以特定大小打开的对话框中显示的其他控件。

我做了一个测试项目只是为了隔离问题。这是我的对话框:

<Window x:Class="DialogTests.Dialog"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:DialogTests"
    mc:Ignorable="d"

    ResizeMode="CanResize"
    SizeToContent="WidthAndHeight"
    WindowStartupLocation="CenterOwner"
    Width="{Binding Path=FormWidth, Mode=TwoWay}"
    Height="{Binding Path=FormHeight, Mode=TwoWay}"
    Top="{Binding Path=FormTop, Mode=TwoWay}"
    Left="{Binding Path=FormLeft, Mode=TwoWay}"
    Title="Dialog">

    <StackPanel>
        <Button Height="100">Button 1</Button>
        <Button Width="100">Button 2</Button>
    </StackPanel>
</Window>



public partial class Dialog : Window
{
    public Dialog()
    {
        InitializeComponent();
        this.DataContext = new DialogVm();
    }
}

public class DialogVm : INotifyPropertyChanged
{
    private double _formWidth;
    private double _formHeight;
    private double _formTop;
    private double _formLeft;

    public DialogVm()
    {
        FormWidth = 400;
        FormHeight = 400;
    }

    public double FormWidth
    {
        get { return _formWidth; }
        set
        {
            if (value.Equals(_formWidth)) return;
            _formWidth = value;
            OnPropertyChanged();
        }
    }

    public double FormHeight
    {
        get { return _formHeight; }
        set
        {
            if (value.Equals(_formHeight)) return;
            _formHeight = value;
            OnPropertyChanged();
        }
    }

    public double FormTop
    {
        get { return _formTop; }
        set
        {
            if (value.Equals(_formTop)) return;
            _formTop = value;
            OnPropertyChanged();
        }
    }

    public double FormLeft
    {
        get { return _formLeft; }
        set
        {
            if (value.Equals(_formLeft)) return;
            _formLeft = value;
            OnPropertyChanged();
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    [NotifyPropertyChangedInvocator]
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

将 Dialog 数据绑定到这些属性的原因是为了保存状态(尺寸和位置),以便在应使用这些先前值打开某些对话框的情况下使用它。

在我的主应用程序中,我只需按一个按钮即可打开对话框:

private void Button_Click(object sender, RoutedEventArgs e)
{
    var dialog = new Dialog();
    dialog.Owner = this;
    dialog.ShowDialog();
}

如果你运行它,那么一切都会按预期运行。对话框调整大小以适合内容,并相对于主应用程序放置在中心。 FormWidth、FormHeight、FormTop 和 FormLeft 的设置器被调用。对于维度,它首先从构造函数中调用,然后由 PresentationFramework 调用。

但是,如果您返回对话框 xaml,并将 SizeToContent="WidthAndHeight" 更改为 SizeToContent="Manual",对话框将显示在屏幕的左上角 (0,0)。对话框的尺寸在视图模型构造器 (400 x 400) 中设置,并且不会再次被调用(如预期的那样)。问题是,FormTop 和 FormLeft 的 Setter 方法永远不会被调用。预期行为是 400x400 窗口应显示在所有者窗口的中心。

如果我更改 Dialog.xaml 以删除数据绑定,并显式设置维度,它会正常工作:

<Window x:Class="DialogTests.Dialog"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:DialogTests"
    mc:Ignorable="d"

    ResizeMode="CanResize"
    SizeToContent="Manual"
    WindowStartupLocation="CenterOwner"
    Width="400"
    Height="400"
    Title="Dialog">
...
</Dialog>

这种行为是错误吗?还是我缺少什么?看起来很奇怪,只要将 SizeToContent 设置为 WidthAndHeight,或者我完全删除绑定并在 xaml 中设置属性,它就可以正常工作。

【问题讨论】:

    标签: c# wpf dialog


    【解决方案1】:

    这取决于绑定何时解决以及窗口位置何时设置。

    您可以在调用InitializeComponent() 方法之前设置Dialog 窗口的DataContext:

    public Dialog()
    {
        this.DataContext = new DialogVm();
        InitializeComponent();
    }
    

    或将WindowStartupLocation 设置为Manual 并计算Left 和Top 源属性的值:

    How do you center your main window in WPF?

    【讨论】:

    • 切换 InitializeComponent 的顺序和设置 DataContext 对定位有效,但遗憾的是,它搞砸了对话框中显示的其他内容。我想我必须自己做定位,但希望我可以让 WPF 处理。
    • 你指的是什么“其他东西”?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-04-21
    • 1970-01-01
    • 2011-07-19
    • 2010-10-15
    • 2022-12-12
    • 2014-07-10
    • 2010-12-05
    相关资源
    最近更新 更多