2

I have a MVVM project with a View and a ViewModel in its DataContext.

In this project I have a class ComboBoxCustom which inherits from ComboBox. I define some additional functionality in my ComboBoxCustom class.

To this ComboBoxCustom class I assign a control template to define its appearance.

The (simplified) style defining the (simplified) control template looks like:

<Style TargetType="{x:Type lib:ComboBoxCustom}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type lib:ComboBoxCustom}">
                <StackPanel>
                    <TextBlock Text="{TemplateBinding TextPropertyInComboBoxCustom}"/>

                    <ComboBox   DataContext="{TemplateBinding DataContext}" 
                                ItemsSource="{TemplateBinding ItemsSource}"
                                DisplayMemberPath="{TemplateBinding DisplayMemberPath}"
                                SelectedValuePath="{TemplateBinding SelectedValuePath}"
                                SelectedValue="{TemplateBinding SelectedValue}" 
                                SelectedItem="{TemplateBinding SelectedItem}"/>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

Which resides in a ResourceDictionary. The real control template has some additional features which are left out since they are not relevant for the question.

I use this ComboBoxCustom control in my View using:

<lib:ComboBoxCustom ItemsSource="{Binding MyObservableCollectionOfMyObjects}"
                     TextPropertyInComboBoxCustom="MyText"
                     DisplayMemberPath="MyDescription"
                     SelectedValuePath="MyValue"
                     SelectedItem="{Binding SelectedMyObject, Mode=TwoWay}"/>

The view is ok, and all items get loaded in the ComboBox which I can select.

The problem is that when I select a different item in the ComboBox, the property SelectedMyObject in my ViewModel does not get updated and consequently its setter is not called. Therefore, the (correct) information about the selected object is not available in my ViewModel.

When I use <ComboBox .../> (without the TextPropertyInComboBoxCustom property) instead of <lib:ComboBoxCustom .../> everything works just fine but then I don't have the additional functionality defined in ComboBoxMessage which I need.

Can anyone tell me what is wrong and how to fix this issue so I can use ComboBoxMessage in my view? Preferably without breaking the MVVM pattern.

Thank you!

Stefan
  • 919
  • 2
  • 13
  • 24
  • 1
    `{TemplateBinding SelectedItem}` is a OneWay binding (see [this post](http://stackoverflow.com/questions/5913176/in-wpf-why-doesnt-templatebinding-work-where-binding-does)). Change it to a binding with RelativeSource=TemplatedParent as suggested in the accepted answer – ASh Mar 16 '17 at 13:29
  • Thank you ASh, this was indeed the problem. – Stefan Mar 24 '17 at 08:34
  • `:)`. What about `SelectedValue="{TemplateBinding SelectedValue}"` ? It is the same thing – ASh Mar 24 '17 at 08:37
  • I was wondering about that, can the binding to `SelectedValue` indeed be removed when `SelectedItem` is already bound? – Stefan Mar 24 '17 at 08:39
  • `lib:ComboBoxCustom` is a general purpose control. Even if current use-case doesn't utilize SelectedValue property, in other situations it may come handy, so it is a reason to fix. On the other hand if "YAGNI" condition is true, remove SelectedValue for good. In any case it is better to do smth and not leave broken code around – ASh Mar 25 '17 at 13:53

2 Answers2

3

Thanx to ASh's comment and information in this post.

The problem is that the TemplateBinding is one way. Therefore, all information from the ViewModel can get into the controls in the template. But not the other way around.

The solution is to specify a normal binding as:

SelectedItem ="{Binding SelectedItem, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}"

Whichs does about the same as a TemplateBinding but is two way.

The control template has become:

<Style TargetType="{x:Type lib:ComboBoxCustom}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type lib:ComboBoxCustom}">
                <StackPanel>
                    <TextBlock Text="{TemplateBinding TextPropertyInComboBoxCustom}"/>

                    <ComboBox   DataContext="{TemplateBinding DataContext}" 
                                ItemsSource="{TemplateBinding ItemsSource}"
                                DisplayMemberPath="{TemplateBinding DisplayMemberPath}"
                                SelectedValuePath="{TemplateBinding SelectedValuePath}"
                                SelectedValue="{TemplateBinding SelectedValue}" 
                                SelectedItem ="{Binding SelectedItem, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}"/>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

I am not sure about the SelectedValue property though. With the template like this, it works both when I use the SelectedValue property or the SelectedItem property.

The Mode=TwoWay option can be omitted in the view since the default binding mode for SelectedItem is already two way. The view line becomes:

<lib:ComboBoxCustom ItemsSource="{Binding MyObservableCollectionOfMyObjects}"
                    TextPropertyInComboBoxCustom="MyText"
                    DisplayMemberPath="MyDescription"
                    SelectedValuePath="MyValue"
                    SelectedItem="{Binding SelectedMyObject}"/>
Community
  • 1
  • 1
Stefan
  • 919
  • 2
  • 13
  • 24
1

Bind SelectedValue to the property in ViewModel

SelectedValue="{Binding SelectedMyObject, Mode=TwoWay}"

in your lib:ComboBoxCustom

Rekshino
  • 6,954
  • 2
  • 19
  • 44
  • Thanks, the problem persists. The `SelectedMyObject` is still not updated. This is because the template binding linking `ComboBox` in the ControlTemplate with `ComboBoxCustom` is OneWay. So the binding in the view in `ComboBoxCustom` will never get the change to relay any new information from the controls in the template. – Stefan Mar 16 '17 at 14:45