Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>Well, the good news is, I know why <code>SelectedCode</code> isn't being set when I'm not using a value converter. The bad news is, I still have something of a mystery, but the problem's been pushed up the food chain a bit, and I have a workaround.</p> <p>This control is essentially a strongly-typed combo box with a bunch of additional features that are made possible by the fact that it knows what kind of items are in it. The <code>SelectedCode</code> and <code>CodeLookupTable</code> properties are strongly typed, and they hide the underlying <code>SelectedItem</code> and <code>ItemsSource</code> properties, which aren't. (This, by the way, is why this is a user control and not a subclass of <code>ComboBox</code>; I don't want those properties to be visible because a lot of things can happen if they get set improperly, none of them good.)</p> <p>Here's what's happening. This is my debugging output when the value converter is attached (the number is the hash code of the control, because I've got a bunch of them that all get drawn simultaneously when the program's initialized):</p> <pre><code>14626603: OnCodeLookupTablePropertyChanged CodeLookupTable property set to Proceedings.Model.CodeLookupTable box.MainComboBox.ItemsSource = MS.Internal.Data.EnumerableCollectionView 14626603: OnSelectedCodePropertyChanged: SelectedCode property set to Unlicensed Driver [VC12500(A)] box.MainComboBox.ItemsSource = MS.Internal.Data.EnumerableCollectionView </code></pre> <p>This is the expected behavior. The <code>CodeLookupTable</code> property is set, so setting <code>SelectedCode</code> to one of the items in that collection correctly sets <code>SelectedItem</code> on the underlying <code>ComboBox</code>.</p> <p>But without the value converter, we get this:</p> <pre><code>16143157: OnSelectedCodePropertyChanged: SelectedCode property set to Unlicensed Driver [VC12500(A)] box.MainComboBox.ItemsSource = 16143157: OnCodeLookupTablePropertyChanged CodeLookupTable property set to Proceedings.Model.CodeLookupTable box.MainComboBox.ItemsSource = MS.Internal.Data.EnumerableCollectionView </code></pre> <p>Here, the <code>SelectedCode</code> property is being set <em>before</em> the <code>CodeLookupTable</code> property is. So when the method tries to set <code>SelectedItem</code> on the underlying <code>ComboBox</code>, nothing happens, because the <code>ItemsSource</code> is null. </p> <p>And here is the root of the problem. I've foolishly assumed that the order that bindings update their target in is the same as the order they're declared in the XAML. (One of the reasons I've expressed the bindings as elements instead of attributes is because the order of elements in an XML document is deterministic and the order of attributes isn't. It's not like I didn't think about this.) This is apparently not the case. </p> <p>I've also assumed, maybe a little less foolishly, that the order in which bindings update their target isn't dependent on whether or not they have attached value converters. Well, it is. I wonder what else it depends on.</p> <p>Mercifully, I have a way to work around this. Since my <code>CodeLookup</code> object contains a reference to the <code>CodeLookupTable</code>, I can make the <code>SelectedCode</code> setter set the <code>CodeLookupTable</code> (and thus the <code>ItemsSource</code>) property first, if it hasn't already been set. That'll make this problem go away without having to stick a fake value converter on the binding and hope that the way bindings behave never changes.</p> <p><strong>Edit</strong></p> <p>Here's what the property declarations look like:</p> <pre><code>#region SelectedCode public static readonly DependencyProperty SelectedCodeProperty = DependencyProperty.Register( "SelectedCode", typeof(CodeLookup), typeof(CodeLookupBox), new FrameworkPropertyMetadata(OnSelectedCodePropertyChanged)); private static void OnSelectedCodePropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e) { CodeLookupBox box = (CodeLookupBox)source; CodeLookup code = e.NewValue as CodeLookup; // this right here is the fix to the original problem: if (box.CodeLookupTable == null &amp;&amp; code != null) { box.CodeLookupTable = code.Table; } box.MainComboBox.SelectedItem = e.NewValue; } public CodeLookup SelectedCode { get { return GetValue(SelectedCodeProperty) as CodeLookup; } set { SetValue(SelectedCodeProperty, value); } } #endregion #region CodeLookupTable public static readonly DependencyProperty CodeLookupTableProperty = DependencyProperty.Register( "CodeLookupTable", typeof(CodeLookupTable), typeof(CodeLookupBox), new FrameworkPropertyMetadata(OnCodeLookupTablePropertyChanged)); private static void OnCodeLookupTablePropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e) { CodeLookupBox box = (CodeLookupBox)source; CodeLookupTable table = (CodeLookupTable)e.NewValue; box.ViewSource = new CollectionViewSource { Source = table.Codes }; box.View = box.ViewSource.View; box.MainComboBox.ItemsSource = box.View; } public CodeLookupTable CodeLookupTable { get { return GetValue(CodeLookupTableProperty) as CodeLookupTable; } set { SetValue(CodeLookupTableProperty, value); } } #endregion </code></pre>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload