1

I have a ListBox in WPB bound to an ObservableCollection

public static readonly DependencyProperty ProgramsProperty =
       DependencyProperty.Register("Programs",
       typeof(ObservableCollection<ProgramData>), typeof(ProgramView),
                                                                                                                    new PropertyMetadata(default(ObservableCollection<ProgramData>)));
    public ObservableCollection<ProgramData> Programs
    {
        get { return (ObservableCollection<ProgramData>)GetValue(ProgramsProperty); }
        set { SetValue(ProgramsProperty, value); }
    }

and the selected element of the ListBox bound to one element

public static readonly DependencyProperty SelectedProgramProperty =
     DependencyProperty.Register("SelectedProgram", 
     typeof(ProgramData), typeof(ProgramView), 
                                                                                                                    new PropertyMetadata(default(ProgramData)));
    public DispenserInfo SelectedProgram
    {
        get { return (ProgramData)GetValue(SelectedProgramProperty); }
        set { SetValue(SelectedProgramProperty, value); }
    }

When the user changes the selected element in the ListBox I would like to check the status of the "old" selected element - maybe we need to save the old element - and react in some way.

So I would like to something like this

public static bool UpdateCallback(ProgramData oldVal, ProgramData newVal)
    {
        if (oldVal.DataChanged == false)
            return true;
        var res = MessageBox.Show("Save or discard changes", "Question",
                    MessageBoxButton.YesNoCancel, MessageBoxImage.Question);
        switch (res)
        {
            case MessageBoxResult.Yes: 
                oldVal.Save();
                return true;
            case MessageBoxResult.No:
                oldVal.Discard();
                return true;
            case MessageBoxResult.Cancel:
                return false;
        }
        return true;
    }

I there a way to do this with validation/coercing callbacks?

Update:

As Oliver and Sheridan suggested I tried the changed as well as the coerce callback and this is working quite well to do some tasks in between clicking and refreshing the UI. But when I try to cancel the update in the coerceCallback like this

private static object CoerceValueCallback(DependencyObject dependencyObject, object baseValue)
    {
        var @this = (ProgramView)dependencyObject;
        var res = MessageBox.Show("Save or discard changes", "Question", MessageBoxButton.YesNoCancel, MessageBoxImage.Question);
        switch (res)
        {
            case MessageBoxResult.Cancel:
                return @this.SelectedProgram;
        }
        return baseValue;
    }

the UI stays at the old values as expected but the ListBox highlights the wrong line - the one the user has clicked and not the one from the binding when canceled in the coerceCallback. Do I need to update the binding manually? Any ideas

Maick
  • 45
  • 8
  • How about listening to PropertyChanged event? In its args you have the old val and new val. – dev hedgehog Nov 06 '13 at 09:04
  • @dev hedgehog: DependencyObjects are not necessarily implementing INotifyPropertyChanged... this concept was built (partly) to get rid of this event :) – Olivier Nov 06 '13 at 09:17
  • Moreover, if despite of being a DependencyObject, your class implements INotifyPropertychanged, and if the event PropertyChanged is not explicitly raised in the dependency property's changed callback, it wont be raised at all. Thus, it makes to sense to listen dependency properties on PropertyChanged ! – Olivier Nov 06 '13 at 09:19
  • Thanks Oliver for your precise explaination haha but that is what I ment. I am talking about dependency property changed event which args contain old val and new val. :) – dev hedgehog Nov 06 '13 at 09:22

2 Answers2

2

You can add a PropertyChanged handler to your DependencyProperty:

public static readonly DependencyProperty ProgramsProperty =
       DependencyProperty.Register("Programs",
       typeof(ObservableCollection<ProgramData>), typeof(ProgramView),
       new UIPropertyMetadata(default(ObservableCollection<ProgramData>, 
       (d, e) => ((ProgramView)d).OnProgramsChanged(d, e))));

private void OnProgramsChanged(DependencyObject dependencyObject, 
    DependencyPropertyChangedEventArgs e)
{
    // Do something with e.OldValue and e.NewValue here
}
Sheridan
  • 68,826
  • 24
  • 143
  • 183
0

Sure there is. Look at the last parameter of DependencyProperty.Register !

    public static readonly DependencyProperty MyPropertyProperty =
        System.Windows.DependencyProperty.Register("MyProperty", typeof(string), typeof(MyClass), new PropertyMetadata(default(string), (
            sender, args) =>
        {
            //changed callback
                var @this = (MyClass)sender;
                var newval = (string)args.NewValue;
                var oldval = (string)args.NewValue;
                // do whatever you like
            },
            (s, e) =>
                {
                    // coerce callback
                    if (e == null)
                        return " - empty string -";
                    return e;
                }));

For most WPF dependency properties, there is even more: Properties metadata are an instance of FrameworkPropertyMetadata instead of simple good old PropertyMetadata. It has more "hidden" options. You may use it as well instead of PropertyMetadata.

Olivier
  • 5,578
  • 2
  • 31
  • 46
  • Hi Oliver the callbacks are working quite well for me but when I try to cancel the ListBox change from the coercingCallback, its not fully updating the ui (see my Update). Do you have any ideas? – Maick Nov 07 '13 at 12:08
  • Is your binding to SelectedItem two ways ? i.e. SelectedItem="{Binding SelectedProgram, Mode=TwoWays}" in xaml. – Olivier Nov 07 '13 at 13:46
  • Yes the Mode is set to TwoWay – Maick Nov 07 '13 at 13:56
  • http://stackoverflow.com/questions/516210/how-do-i-make-binding-respect-dependencyproperty-value-coercion Use property changed callback to coerce your value – Olivier Nov 07 '13 at 15:16