我可以用什么WPF方法在控件的第一个显示器上设置一些属性?

时间:2011-06-15 09:14:08

标签: c# wpf xaml

我需要在用户关闭应用程序时存储我的应用程序窗口的大小/位置和状态,并在用户此后打开应用程序时将其设置回来。

我使用注册表键很容易做到这一点(这是最好的方法吗?),但我想知道我应该把代码放在哪里设置这些属性。

我认为,当窗口首次“出现”时,我需要设置它们。但是我有几种方法可以用于这种情况,即:

  • Window.Show()
  • Window.Activate()
  • Window.ApplyTemplate()
  • Window.Arrange()
  • Window.ArrangeCore()
  • Window.ArrangeOverride()
  • Window.BeginInit()
  • Window.EndInit()
  • Window.Measure()
  • Window.MeasureCore()
  • Window.MeasureOverride()
  • Window.OnApplyTemplate()
  • Window.OnInitialized()
  • Window.OnRender()
  • Window.UpdateLayout()

我知道他们中的大多数只是个坏主意(例如,UpdateLayout()会经常被称为waaaaaaay)。 Idealy我正在寻找一种只在窗口生命中调用一次的方法,这样我就不必添加一个标志来检查这是否是方法的第一次调用。

那么哪一个在这种情况下最好?为什么?

方面的问题:我把代码保存在Window.Close()中(我在我的MyWindow类中覆盖了这个方法),但我也可以把它放在Window.OnClosing中( )或Window.OnClosed()。这对我的情况有什么不同吗?

方面问题(之二):我还必须保存数据网格的列顺序,在这种情况下我应该在哪里放置“保存”和“加载”代码?

3 个答案:

答案 0 :(得分:3)

好吧,在我看来,你像对待旧式的WinForms应用程序一样对待WPF。您不再需要监视表单事件以从表单属性中检索信息。大多数WPF控件属性都称为Dependency Property

Amonst依赖属性引入的一些聪明的东西是Data Binding

如果您考虑使用MVVM Architecture编写应用程序,您将很快能够自己处理以下内容... =)

在View * 1 中,您可以创建依赖项属性或标准属性,并实现INotifyPropertyChanged,其中包含大小/布局/位置/等。然后将表单的属性(在xaml或代码中)绑定到View的属性。然后,您可以实现任何您喜欢的功能来存储/检索默认值,并在更改表单时通过简单地调整视图中属性的Get / Set来自动更新。

作为Windows标题的快速示例:

<Window x:Class="MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="{Binding Path=DisplayName}"
        WindowStartupLocation="CenterScreen" >
    <Grid>...</Grid>
</Window>

视图的示例实现:

public class SampleView : System.ComponentModel.INotifyPropertyChanged
{

    public event PropertyChangedEventHandler System.ComponentModel.INotifyPropertyChanged.PropertyChanged;
    public delegate void PropertyChangedEventHandler(object sender, System.ComponentModel.PropertyChangedEventArgs e);

    private string _Title;
    public string Title {
        get {
            if (_Title == null) {
                _Title = My.Settings.MainWindowTitle;
            }
            return _Title;
        }
        set {
            _Title = value;
            if (!(_Title == My.Settings.MainWindowTitle)) {
                if (PropertyChanged != null) {
                    PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs("Title"));
                }
                My.Settings.MainWindowTitle = Title;
                My.Settings.Save();
            }
        }
    }
}

编辑:关于如何最好地存储用户偏好,我不会推荐注册表,但这远非闻所未闻。现在的注册表中包含了很多设置,在我看来,注册表并非真正设计用于。考虑将application settings设置为用户范围。这将处理存储/检索数据的位置和方式的大部分问题,并为您提供良好的类型安全接口。

* 1 我个人更喜欢尝试将所有内容绑定到ViewModel并且几乎完全是愚蠢的视图;虽然我知道有很多有效的案例用于具有代码的视图。我不会说尺寸/布局/等真的是商业逻辑关注,并不是我到目前为止所关注的事情,所以这应该可以在View本身中处理。

编辑2 - 用户/应用程序范围设置的快速示例:

以下是我添加到项目中的设置的快速图片:

Settings screenshot

以下代码尝试同时使用应用程序和用户范围设置。 注意:应用程序范围设置在运行时是ReadOnly

public class SettingsExample
{
    private Form1 frmMain = new Form1();

    public void Main()
    {
        frmMain.BackColor = My.Settings.DefaultBackColour;
    }

    public void UserLoggedIn()
    {
        frmMain.BackColor = My.Settings.UserBackcolour;
    }

    public void UpdateUserBackcolour(System.Drawing.Color newColour)
    {
        My.Settings.UserBackcolour = newColour;
        My.Settings.Save();
    }

    public void UpdateDefaultBackcolour(System.Drawing.Color newColour)
    {
        My.Settings.DefaultBackColour = newColour;
        // Compiler Error
        // This property is read only because it is an application setting
        // Only user settings can be changed at runtime
    }

}

答案 1 :(得分:0)

首先,你忘记了

  

加载事件 - 在元素发生时   布局,渲染和准备   相互作用。 (继承自   FrameworkElement。)受

没有一个简单的答案。场景可能会有所不同,无论它是一个孩子“对话式”窗口(然后我只是在Show()之前设置一个大小的行,一个同一窗口的新实例或应用程序的新实例。

我认为UpdateLayout()是一个坏主意。实际上,这是一个非常好的主意。例如:

private bool m_onStart = true;

public MainWindow()
{
        InitializeComponent();
        this.LayoutUpdated += new EventHandler(MainWindow_LayoutUpdated);
}

void MainWindow_LayoutUpdated(object sender, EventArgs e)
{
     if (m_onStart)
     {
         m_onStart = false;
         Dispatcher.BeginInvoke(() =>
         {
              //Start App
         }
         );
     }
 }

即使它被称为每秒一千次(这是非常不可能的),你甚至不会注意到它并且不会损害性能。

总而言之,您可以创建一个保存用户首选项的辅助方法,然后再读取它。由于任务是视图相关的并且使用MVVM并且绑定对此来说是一种过度杀伤,因此在Loaded事件中设置大小(在完成所有ctors,初始化和可视化树时执行此操作)。

答案 2 :(得分:0)

我的选择:我最终将代码加载到window.Show()中的注册表中加载值。

我之所以这样做是因为有两件事:

  1. 我存储窗口的状态,(最小化/最大化)和WPF的方式,我需要先设置宽度/高度,然后设置最大化状态(如果需要),否则会弄乱布局。如果我没有先设置宽度/高度,那么当我在此后取消最大化窗口时,我会松开它们。所以我必须精确地按顺序做事:宽度+高度和它们的状态。 (此外,在使用多个屏幕时,这是必要的,否则您将松开正在处理的屏幕)。这意味着上面的一些方法是不切实际的(例如“措施”)

  2. 除此之外,如果我将代码放在上面提到的大部分方法中,我会在第一次显示时看到一个看起来很糟糕的效果:窗口首先会显示其高度和宽度设置,在屏幕中间,然后经过一小段延迟后,窗口最大化。

  3. 将代码放在window.Show()中设法解决这两个问题。我可能与一个或多个其他方法有相同的结果,但我只是厌倦了尝试不同的配置,最后使用第一个让我完全满意的。