Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Strange behaviour (or bug?) with ComboBox in WPF when changing DataContext and having bound ItemsSource and SelectedItem

I'm trying to debug a strange error in a combobox bound to an itemssource and selecteditem. It's driving me crazy.

The problem arise when changing selected tabitem in which the combobox exists. (Actually it's only when changing the DataContext of the ComboBox). The SelectedItem-binding has a custom validationrule to give error if value is null.

The problem is that wpf calls my custom rule when switching tabitems (DataContext) and tries to validate a value of null, even though the selecteditem-source never is null. This is a problem.

This is a simplified case I made that shows the same error:

Set a breakpoint in NotNullValidationRule.Validate and see how WPF tries to validate SelectedItem as null even though it is not present in any of the view model instances.

UPDATE

After some more experimenting I've discovered that the TabControl actually is irrelevant. Even with a simple ComboBox and a button to toggle it's DataContext I get the exact same problem. I'm replacing the code example with a new version.

using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;

namespace ComboBoxValidationBugTest
{
  public partial class MainWindow : Window
  {
    private Test t1, t2;

    public MainWindow()
    {
      InitializeComponent();

      t1 = new Test();
      t1.Items.Add("A");
      t1.Items.Add("B");
      t1.Items.Add("C");
      t1.SelectedItem = "A";

      t2 = new Test();
      t2.Items.Add("B");
      t2.Items.Add("C");
      t2.Items.Add("D");
      t2.SelectedItem = "B";

      ComboBox1.DataContext = t1;
    }

    private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
    {
      ComboBox1.DataContext = ComboBox1.DataContext == t1 ? t2 : t1;
    }
  }

  public class Test : INotifyPropertyChanged
  {
    private string _selectedItem;

    private ObservableCollection<string> _items = new ObservableCollection<string>();

    public ObservableCollection<string> Items
    {
      get
      {
        return _items;
      }
    }

    public string SelectedItem
    {
      get
      {
        return _selectedItem;
      }
      set
      {
        _selectedItem = value;
        OnPropertyChanged("SelectedItem");
      }
    }

    public override string ToString()
    {
      return _selectedItem;
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged(string propertyName)
    {
      PropertyChangedEventHandler handler = PropertyChanged;
      if (handler != null)
      {
        handler(this, new PropertyChangedEventArgs(propertyName));
      }
    }
  }

  public class NotNullValidationRule : ValidationRule
  {
    public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
    {
      if (value == null)
      {
        return new ValidationResult(false, "Value was null");
      }

      return new ValidationResult(true, null);
    }
  }
}

And the XAML:

<Window x:Class="ComboBoxValidationBugTest.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:comboBoxValidationBugTest="clr-namespace:ComboBoxValidationBugTest"
            Title="MainWindow" Height="350" Width="525">
      <Grid>
        <DockPanel>
          <Button Content="Toggle DataContext" DockPanel.Dock="Top" Click="ButtonBase_OnClick" />
          <ComboBox ItemsSource="{Binding Items}" VerticalAlignment="Top" x:Name="ComboBox1">
            <ComboBox.SelectedItem>
              <Binding Path="SelectedItem">
                <Binding.ValidationRules>
                  <comboBoxValidationBugTest:NotNullValidationRule />
                </Binding.ValidationRules>
              </Binding>
            </ComboBox.SelectedItem>
          </ComboBox>
        </DockPanel>
      </Grid>
    </Window>
like image 432
Andreas Zita Avatar asked Sep 18 '13 08:09

Andreas Zita


1 Answers

The issue you are getting is due the fact TabControl in wpf is virtualized. Only the visible tab actually exists and is rendered with the selected item. So on switching tabitem, control invalidates the previous tab and renders the tab with newly selected item and hence triggers dependency property change and in turn your ValidationRule.

So basically you will have to turn off the Tab virtualization to fix this. There are some workarounds to solve this. But a good solution is provided in the article below:

http://www.codeproject.com/Articles/460989/WPF-TabControl-Turning-Off-Tab-Virtualization

like image 84
Nitin Avatar answered Sep 19 '22 19:09

Nitin