窗口关闭后出现wpf绑定事件

时间:2014-04-22 15:22:54

标签: .net wpf multithreading events mvvm

我有一个愚蠢的问题,我相信很多人都会遇到这个问题。然而,我无法找到一个令人满意的解决方案。

考虑简单项目(参见下面的代码)。当我在字段中键入文本而不是单击[x]关闭窗口时,VeryImportantProperty的更改将在窗口的关闭事件之后发生。结果窗口关闭而不要求保存更改。

是否有已知的解决方法或更好的编程技术?我发现的唯一建议是通过启动具有较低优先级的子同步线程(无所事事)来延迟关闭处理程序。然而,这并没有太大作用,因为绑定和关闭事件处理程序在同一个线程中运行。

C#:

    namespace CloseRequestTestProject {

    public class MyViewModel : INotifyPropertyChanged {
        public MyViewModel() { _isDirty = false; _veryImportantProperty = "Change me!"; }
        public event PropertyChangedEventHandler PropertyChanged;
        protected virtual void OnPropertyChanged(string propertyName) {
            PropertyChangedEventHandler handler = this.PropertyChanged;
            if (handler != null) {
                var e = new PropertyChangedEventArgs(propertyName);
                handler(this, e);
            }
        }

        private bool _isDirty;
        private string _veryImportantProperty;
        public string VeryImportantProperty {
            get { return _veryImportantProperty; }
            set {
                if (value != _veryImportantProperty) {
                    Trace.TraceWarning("Binding event!");
                    Trace.TraceWarning("ThreadId is " + System.Threading.Thread.CurrentThread.ManagedThreadId.ToString());
                    _isDirty = true;
                    _veryImportantProperty = value;
                    OnPropertyChanged("VeryImportantProperty");
                }
            }
        }

        public void viewIsClosing(object sender, CancelEventArgs e) {
            Trace.TraceWarning("View is closing");
            Trace.TraceWarning("ThreadId is " + System.Threading.Thread.CurrentThread.ManagedThreadId.ToString());
            if (_isDirty) {
                switch (MessageBox.Show("VeryImportantProperty has changed. Save changes?", "Question", MessageBoxButton.YesNoCancel, MessageBoxImage.Warning)) {
                    case MessageBoxResult.Yes: ; break;
                    case MessageBoxResult.No: ; break;
                    default: e.Cancel = true; break;
                }
            }
        }
    }

    public partial class MainWindow : Window {
        public MainWindow() {
            InitializeComponent();
            MyViewModel vm = new MyViewModel();
            DataContext = vm;
            Closing += vm.viewIsClosing;
        }

    }
}

XAML:

    <Window x:Class="CloseRequestTestProject.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <WrapPanel>
            <TextBlock Text="Very Important Property" VerticalAlignment="Top" Margin="10"/>
            <TextBox Text="{Binding VeryImportantProperty}" VerticalAlignment="Top" Margin="10" MinWidth="200"/>
        </WrapPanel>

    </Grid>
</Window>

3 个答案:

答案 0 :(得分:2)

确定。线程与此无关。似乎单击[x]按钮不会触发LostFocus,文本框LostFocus和绑定在窗口实际关闭后发生,或者至少在要关闭的路径上已经过了Closing事件。因此,如果您不想使用PropertyChanged UpdateSourceTrigger,则可能需要使用UpdateSource方法强制绑定事件。以下MainWindow成员函数可以解决这个问题:

private void TriggerLostFocusBinding() {
        // Gets the element with keyboard focus.
        UIElement elementWithFocus = Keyboard.FocusedElement as UIElement;
        if (elementWithFocus!=null && elementWithFocus is FrameworkElement) {
            FrameworkElement bindingElement = (elementWithFocus as FrameworkElement);
            //update all binding expressions found for this element
            foreach (PropertyDescriptor pd in
                TypeDescriptor.GetProperties(bindingElement, new Attribute[] { 
                    new PropertyFilterAttribute(PropertyFilterOptions.SetValues) 
                })) {
                DependencyPropertyDescriptor dpd = DependencyPropertyDescriptor.FromProperty(pd);
                if (dpd != null && dpd.DependencyProperty != null) {
                    BindingExpression be = bindingElement.GetBindingExpression(dpd.DependencyProperty);
                    if (be != null) be.UpdateSource();
                }
            }
        }
    }

答案 1 :(得分:1)

在绑定中添加UpdateSourceTrigger=PropertyChanged可能是一个好的开始!

  • 这样,您的VM将始终与UI保持同步
  • ,当IsDirty被调用时,您的true属性将为ViewIsClosing

答案 2 :(得分:0)

如果您的UI包含许多文本框,并且您不愿意为所有这些文本框更改UpdateSourceTrigger,只需在viewIsClosing方法中添加此小技巧即可。

var w = (Application.Current.Windows.OfType<MainWindow>().FirstOrDefault());
w.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));