Think about what happens if you select UpdateSource on Expander 2 when Expander 1 is selected:
ConvertBack is called for Expander 2 with the current value IsExpanded ( false ) and returns null .SelectedExpander updated to null .Convert is called for all other expanders because the SelectedExpander changed, as a result of which all other IsExpanded values ββwill be set to false .
Of course, this is the wrong behavior. Thus, the decision depends on the fact that the source is never updated, unless the user actually switches the expander.
So, I suspect the problem is that initializing the controls somehow causes the source code to be updated. Even if Expander 1 was correctly initialized as extended, it would reset when the bindings were updated on any of the other extenders.
To make ConvertBack correct, it must know about other expanders: it should return only null if all of them are reset. However, I do not see a clean way to handle this from the converter. Perhaps the best solution would be to use one-way binding (no ConvertBack ) and handle Expanded and Collapsed events or similar (where _expanders is a list of all expander controls):
private void OnExpanderIsExpandedChanged(object sender, RoutedEventArgs e) { var selectedExpander = _expanders.FirstOrDefault(e => e.IsExpanded); if (selectedExpander == null) { viewmodel.SelectedExpander = null; } else { viewmodel.SelectedExpander = selectedExpander.Tag; } }
In this case, I use Tag for the identifier used in the viewmodel.
EDIT:
To solve the problem of more than "MVVM", you can have a collection of viewmodels for each expander with an individual property to bind IsExpanded to:
public class ExpanderViewModel { public bool IsSelected { get; set; }
Save the collection to ExpanderListViewModel and add PropertyChanged handlers for each of them during initialization:
// in ExpanderListViewModel foreach (var expanderViewModel in Expanders) { expanderViewModel.PropertyChanged += Expander_PropertyChanged; } ... private void Expander_PropertyChanged(object sender, PropertyChangedEventArgs e) { var thisExpander = (ExpanderViewModel)sender; if (e.PropertyName == "IsSelected") { if (thisExpander.IsSelected) { foreach (var otherExpander in Expanders.Except(new[] {thisExpander})) { otherExpander.IsSelected = false; } } } }
Then bind each expander to another item in the Expanders collection:
<Expander Header="Expander 1" IsExpanded="{Binding Expanders[0].IsSelected}"> <TextBlock>Expander 1</TextBlock> </Expander> <Expander Header="Expander 2" IsExpanded="{Binding Expanders[1].IsSelected}"> <TextBlock>Expander 2</TextBlock> </Expander>
(You can also examine the definition of ItemsControl to dynamically create extenders from a collection.)
In this case, the SelectedExpander property is no longer needed, but it can be implemented as follows:
private ExpanderViewModel _selectedExpander; public ExpanderViewModel SelectedExpander { get { return _selectedExpander; } set { if (_selectedExpander == value) { return; } // deselect old expander if (_selectedExpander != null) { _selectedExpander.IsSelected = false; } _selectedExpander = value; // select new expander if (_selectedExpander != null) { _selectedExpander.IsSelected = true; } OnPropertyChanged("SelectedExpander"); } }
And update the above PropertyChanged handler as:
if (thisExpander.IsSelected) { ... SelectedExpander = thisExpander; } else { SelectedExpander = null; }
So, now these two lines will be equivalent ways to initialize the first expander:
viewModel.SelectedExpander = viewModel.Expanders[0]; viewModel.Expanders[0].IsSelected = true;