Update property bindings problem in a wpf dialog box with a default button (IsDefault=true)

Update property bindings problem in a wpf dialog box with a default button (IsDefault=true)

Old forum URL: forums.lhotka.net/forums/t/10035.aspx


Andreas posted on Sunday, February 06, 2011

Hi

We want to implement a web forms like validation scenario in a wpf application. The model is like this: The user fills out the fields on the form and presses the enter key when finished. At that point the application logic validates the properties and if it finds a broken rule it shows up a property error status and prevents the dialog from closing (setting the Cancel-Property of the CancelEventArgs of the windows Closing event handler to true). The problem is, that this scenario doesn’t work when using a button with IsDefault="True", because the property bindings seems to get updated too late and so can’t be validate in the windows Closing event handler.

Scenario: We have a UserControl with a Text-Box and an Ok-Default-Button (IsDefault="True"). The Value-Property of the Textbox is bound to a property on a ViewModel. We set the UserControl to the content property of a window and are showing the window within a modal dialog (ShowDialog()).
If you enter text into the Text-Box and then press the Return/Enter-Key without leaving the text box, the dialog box will be closed because the Default-Button handles the enter key. The odd thing here is that the binding of the Textbox-Value gets updated only after the Closing-Event gets fired. This happens because the textbox gets an OnFocusChanged event after the dialog box closed. But at that point in time it’s too late to prevent the dialog from closing window. The window is already closing. The ViewModel’s property that is bound to the Textbox Value property still has its old value while the window fires the Closing event. Unfortunately, we can’t use UpdatSourceTrigger=OnProperyChanged and have to stick with the default OnFocusChanged for the property bindings, because OnProperyChanged will hit the rules for each key stroke while editing, what we don’t want.

Any ideas how to get around this problem?

Andreas

Here's the code:

<Window x:Class="WpfApplication1.Window1"

    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    Title="Window1" Height="300" Width="300"

    WindowStartupLocation="CenterScreen">

    <Grid>

        <Grid.ColumnDefinitions>

            <ColumnDefinition/>

            <ColumnDefinition/>

            <ColumnDefinition/>

        </Grid.ColumnDefinitions>

        <Grid.RowDefinitions>

            <RowDefinition/>

            <RowDefinition/>

            <RowDefinition/>

        </Grid.RowDefinitions>

        <Button Grid.Column="1" Margin="10,10,10,10"  Grid.Row="2" Content="Test" Command="{Binding Path=OpenDialogCommand}"></Button>

    </Grid>

</Window>

 

 <UserControl x:Class="WpfApplication1.DialogDefinition"

    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    Height="300" Width="300">

    <Grid>

        <Grid.ColumnDefinitions>

            <ColumnDefinition/>

            <ColumnDefinition/>

            <ColumnDefinition/>

        </Grid.ColumnDefinitions>

        <Grid.RowDefinitions>

            <RowDefinition/>

            <RowDefinition/>

            <RowDefinition/>

        </Grid.RowDefinitions>

        <StackPanel Grid.Column="1" Grid.Row="1" HorizontalAlignment="Stretch" VerticalAlignment="Top" >

            <TextBox Text="{Binding Path=Text}"></TextBox>

        </StackPanel>

        <Button Name="Hallo" Grid.Column="0" Grid.Row="2" Margin="10,10,10,10" Content="Cancel" Command="{Binding Path=CancelDialogCommand}"></Button>

        <Button Grid.Column="2" Grid.Row="2" Content="Ok" Margin="10,10,10,10" IsDefault="True" Command="{Binding Path=OkDialogCommand}"></Button>

    </Grid>

</UserControl>

 

using System.Windows;

 

namespace WpfApplication1
{

    /// <summary>

    /// Interaction logic for Window1.xaml

    /// </summary>

    public partial class Window1 : Window

    {

        public Window1()

        {

            InitializeComponent();

            this.DataContext = new MainWindowViewModel();

        }

    }

}

 

using System.ComponentModel;

using System.Windows;

using System.Windows.Input;

using Microsoft.Practices.Composite.Presentation.Commands;


namespace
WpfApplication1

{

    public class MainWindowViewModel : PopupServiceViewModelBase

    {

        #region INotifyPropertyChanged Members

 

        public event PropertyChangedEventHandler PropertyChanged;

 

        protected void OnPropertyChanged(string name)

        {

            PropertyChangedEventHandler propertyChangedEventHandler = this.PropertyChanged;

            if (propertyChangedEventHandler != null)

                propertyChangedEventHandler(this, new PropertyChangedEventArgs(name));

        }

 

        public ICommand OpenDialogCommand { get; set; }

        public ICommand OkDialogCommand { get; set; }

        public ICommand CancelDialogCommand { get; set; }

 

        private string _text = "Hi";

        public string Text

        {

            get

            {

                return this._text;

            }

            set

            {

                System.Diagnostics.Debug.WriteLine("Setter Text...");

                if (this._text == value)

                    return;

 

                this._text = value;

 

                this.OnPropertyChanged("Text");

            }

        }

 

        public MainWindowViewModel()

        {

            this.OpenDialogCommand = new DelegateCommand<object>(OpenDialog);

            this.OkDialogCommand = new DelegateCommand<object>(CloseOkDialog);

            this.CancelDialogCommand = new DelegateCommand<object>(CloseCancelDialog);

        }

 

        private void CloseCancelDialog(object obj)

        {

            System.Diagnostics.Debug.WriteLine("CloseCancelDialog()...");

            this._window.DialogResult = false;

        }

 

        private void CloseOkDialog(object obj)

        {

            System.Diagnostics.Debug.WriteLine("CloseOkDialog()...");

            this._window.DialogResult = true;

        }

 

 

        private Window _window;

        private void OpenDialog(object obj)

        {

            _window = new Window();

            _window.Closing += Window_Closing;

            _window.SizeToContent = SizeToContent.WidthAndHeight;

            _window.ResizeMode = ResizeMode.NoResize;

            _window.WindowStartupLocation = WindowStartupLocation.CenterScreen;

            _window.Content = new DialogDefinition();

            _window.DataContext = this;

            _window.ShowDialog();

        }

 

        private void Window_Closing(object sender, CancelEventArgs e)

        {

            System.Diagnostics.Debug.WriteLine("Window_Closing-Event...");

            e.Cancel = false;

        }

 

        #endregion

    }

}

 

Copyright (c) Marimer LLC