0

I have a Custom Control:

I don't want to get into specifics so for simplicity's sake i have 3 Dependency Properties :

MyCustomControl (CS) :

public class MyCustomControl : Control
{
    DP Value1
    DP InternalValue
    DP SelectedValue 


   OnValue1Changed()
   {
       InternalValue = CalculateBasedOn1();
   }

   static bool _isSetInternally;  
   OnInternalValueChanged()
   {
       if(Condition())
       {
           _isSetInternally = true;
           SelectedValue = e.NewValue;
       }
       else
       {
           Value1 = FixValue1();                        
       } 
   }

   OnSelectedValueChanged()
   {
       if(_isSetInternally)
       {
          _isSetInternally = false;
          return; 
       }

       Value1 = ExtractValue1FromInput(e.NewValue);
   }

   public List<string> Values
   {
       get{ return new List<string>() { "1","2",......,"200"};}
   }

 }

My ControlTemplate (Again Simplified) :

    <ControlTemplate>
        <ComboBox x:Name="cb" 
           ItemsSource="{Binding RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay, Path=Values}"
           SelectedItem={Binding RelativeSource={RelativeSource TemplatedParent}, Path=Value1, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}
    </ControlTemplate>

The Problem : cb is showing the last value chosen , even after it was fixed as explained below .

Flow :

1) Input :

1.1) SelectedValue is bound to a property in my DataContext , it receives a value.

1.2) OnSelectedValueChanged() sets Value1.

1.3) Value1 Sets "cb" SelectedItem via binding.

1.4) OnValue1Changed sets InternalValue.

1.5) OnInternalValueChanged() flags _isSetInternally = true and Updates SelectedValue.

1.6) OnSelectedValueChanged() zeros _isSetInternally = false and stops flow (return).

2) Output :

2.1) cb's SelectedItem is changed.

2.2) Value1 is set via Binding.

2.3) OnValue1Changed() sets InternalValue.

2.4) If Condition is met propagate Output.

2.4.1) go to (1.4)

2.4.2) THE PROBLEM , Condition was not met, Set Value1 again with a valid value.

2.5) go to (1.4)

The Problem in 2.4.2 is that The ComboBox is still showing the non - valid value chosen in (2.1)

Observing with snoop i can see that the SelectedItem is correct and have been changed , but the SelectedValue and SelectedIndex are still the one's chosen before the Fix.

ComboBox in template , snooped

*Further more iv'e attempted to Coerce Value1 on a Coercion Callback , it had the same effect.

Any idea's why the ComboBox doesn't update it's Value via Binding in this scenario ?

eran otzap
  • 12,293
  • 20
  • 84
  • 139
  • FYI , iv'e also tried Binding To SelectedValue instead of SelectedItem and Binding to both of them with SelectedValue using a OneWay binding. – eran otzap Mar 10 '14 at 13:14

3 Answers3

0

I don't know if I did understand well the problem, but what you should have would be a Binding to the SelectedItem in Combobox.

When that value changes, you should raise a PropertyChanged event with the INotifyPropertyChanged.

In your combo, you then have the properties DisplayMemberPath and SelectedValuePath.

Can you work with it?

Regards,

sexta13
  • 1,558
  • 1
  • 11
  • 19
  • What would you give SelectedValuePath ? , I don't need to use INotifyPropertyChanged here , it's a Binding between 2 DP's they raise their own kind of notification. – eran otzap Mar 10 '14 at 13:15
  • http://stackoverflow.com/questions/3797034/confused-with-wpf-combobox-displaymemberpath-selectedvalue-and-selectedvaluepath something like this example no? – sexta13 Mar 10 '14 at 13:22
  • are you saying that i need SelectedValuePath ? notice that my ItemsSource is just a List how do i give a SelectedValuePath to be the actual item. – eran otzap Mar 10 '14 at 13:27
0

O'k so the problem as it seems was some synchronization issue in the ComboBox according to this article :

http://ikriv.com/dev/wpf/ConfusedComboBox/index.shtml

special thanks to Uriel Jacobson

it is recommended to make the update on the ComboBox asynchronously using Dispatcher.BeginInvoke(...)

 OnInternalValueChanged()
 {
     if(Condition())
     {
         _isSetInternally = true;
         SelectedValue = e.NewValue;
     }
     else
     {
         Application.Current.Dispatcher.BeginInvoke(new System.Action(() =>
         {
             Value1 = FixValue1();
         }));                      
     } 
 }

Edit : 19.7.2014

Though this did patch and fix this issue , i later re-wrote the entire control in a much simpler fashion , one of the things i did was remove the ValidationRule i placed on the Binding to the Combobox's SelectedItem.

When doing so , the Binding between my DependencyProperty and the Combox's SelectedItem updated normally. FYI , I removed the use of WPF Validation system all together in my experience they just don't work well (I of course usually use other words to describe WPF Validations , but not here :) ).

eran otzap
  • 12,293
  • 20
  • 84
  • 139
-1

I didn't quite follow the process here, but I think Value1 is never set if your code follows the happy path. It looks like after Value1 is set with the "non-corrected" value, you set InternalValue, and then set SelectedValue like this:

if(Condition())
{
    _isSetInternally = true;
    SelectedValue = e.NewValue;
}

And after that OnSelectedValueChanged just resets the flag and exists:

if(_isSetInternally)
{
   _isSetInternally = false;
   return; 
}

You never seem to correct Value1.

But I may be way off here.

CKII
  • 1,439
  • 14
  • 26
  • OnInternalValueChanged corrects Value1 , all the Dependency Properties are set to their correct values , it's The ComboBox's SelectedValue that does not get correct. – eran otzap Mar 10 '14 at 13:13
  • @eranotzap - As you posted it, `OnInternalValueChanged` **doesn't** change `Value1`. If the condition is met, it only sets the flag, `SelectedValue`, and then exits. – CKII Mar 10 '14 at 13:20
  • else { Value1 = FixValue1(); } – eran otzap Mar 10 '14 at 13:25
  • @eranotzap - You said the problem occurs in your happy path, when the condition is true - Then the else never runs. – CKII Mar 10 '14 at 13:26
  • your right i wrote 2.4.1 instead of 2.4.2 , i fixed it not. – eran otzap Mar 10 '14 at 13:30
  • @eranotzap - In that case, you should check if the whole cycle doesn't happen start again with `OnValue1Changed`. It's possible that when you set the fixed value, `Value1` fires `PropertyChanged`. This would cause `OnValue1Changed` to run, and then `OnInternalValueChanged` and `OnSelectedValueChanged`. The problem with the last two methods is that you're using `e.NewValue`, which is incorrect in that scenario. Is it possible your'e setting `Value1` twice? Once with the "fixed" value, and then with `e.NewValue`? – CKII Mar 10 '14 at 13:40
  • No , iv'e tested it over and over , Value1Changed() is called again but the flow stops in the return when _isSetInternally was flagged. – eran otzap Mar 10 '14 at 13:42