DeCaf
DeCaf

Reputation: 6116

Expander inside ListBox not showing content when expanded the first time from binding

I have a dialog window that contains a ListBox whose ItemTemplate contains an expander. Its IsExpanded is bound to a property in the item view model. The ListBoxItem's IsSelected property is also bound to the IsExpanded property in the item view model object. And finally the SelectedItem property of the ListBox is bound to a property with the same name in the view model.

The problem here is that when setting up the view model before showing the dialog and setting it to the DataContext of the dialog, the item in the listbox gets selected as it should, the expander arrow shows that it is in the expanded state, but the content of the expander is not displayed.

If I set up the view model after showing the dialog, eg. in the Loaded handler of the dialog things work as expected. What is going on here, and what would be the best way to fix it?

The dialog window is defined as:

<Window x:Class="WpfApplication1.Dialog"
       xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
       xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
       xmlns:l="clr-namespace:WpfApplication1"
       Title="Dialog" Height="300" Width="300">
  <Grid>
     <ListBox ItemsSource="{Binding Items}" SelectedItem="{Binding SelectedItem, Mode=TwoWay}">
        <ListBox.ItemTemplate>
           <DataTemplate>
              <Expander Header="Expander" x:Name="MyExpander" IsExpanded="{Binding IsExpanded, Mode=TwoWay}">
                 <Rectangle Width="100" Height="20" Fill="Red" />
              </Expander>
           </DataTemplate>
        </ListBox.ItemTemplate>
        <ListBox.ItemContainerStyle>
           <Style TargetType="ListBoxItem">
              <Setter Property="IsSelected" Value="{Binding IsExpanded, Mode=TwoWay}" />
           </Style>
        </ListBox.ItemContainerStyle>
     </ListBox>
  </Grid>

And the ViewModel (implementation not included for the sake of brevity):

public interface IMyViewModel : INotifyPropertyChanged
{
   object SelectedItem { get; set; }
   ObservableCollection<IMyItemViewModel> Items { get; }
}

public interface IMyItemViewModel : INotifyPropertyChanged
{
   bool IsExpanded { get; set; }
}

Then I have a simple main window with a button, and its Click handler is defined as:

private void Button_Click(object sender, RoutedEventArgs e)
{
   MyViewModel vm = new MyViewModel();
   MyItemViewModel item = new MyItemViewModel();
   vm.Items.Add(item);
   vm.SelectedItem = item;

   Dialog dialog = new Dialog();
   dialog.DataContext = vm;
   dialog.ShowDialog();
}

When I run the application and click the button, the dialog shows up, the expander arrow indicates that it is in the expanded state, but its content is not displayed. Clicking on the expander collapses it, and clicking it again expands it, this time showing the content. Putting the same code directly in the Main Window instead of a dialog however works as it is supposed to.

If I just do a Dispatcher.BeginInvoke(new Action(() => vm.SelectedItem = item); instead of setting it directly things also seem to work, but this feels a bit shaky.

What can be done to fix this problem?

Upvotes: 2

Views: 1268

Answers (1)

Mike Fuchs
Mike Fuchs

Reputation: 12319

Sounds like the content of the expander is not measured again after it is loaded, if the IsExpanded property is already set to true. Or to put it another way, the content is measured when it has still no actual size.

I suppose the easiest solution would be to just set the SelectedItem once the dialog has been loaded:

dialog.Loaded += (s, x) => vm.SelectedItem = item;

Upvotes: 2

Related Questions