0
votes

I want to create a ComboBox + TextBox and for this I tried to set "IsEditable" true in ComboBox. I'm using a collection of objects in as ItemSource for ComboBox which is working fine. But when I enter any text it shows me this error.

Error: BindingExpression path error: 'ComboBoxOption' property not found on 'Windows.Foundation.IReference`1'. Exception thrown:'System.NullReferenceException' in CBExample2.exe

I think this error is because when I enter text in combobox it takes it as a string which is not converted back to object.

Here is my full code.

MainPage.xaml

<Page.Resources>
    <local:ComboBoxItemConvert x:Key="ComboBoxItemConvert" />
</Page.Resources>

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <StackPanel>
        <ComboBox Name="ComboBox"
                  DisplayMemberPath="ComboBoxHumanReadableOption"
                  Header="ComboBox"
                  IsEditable="True"
                  IsTextSearchEnabled="True"
                  ItemsSource="{x:Bind ComboBoxOptions}"
                  SelectedItem="{x:Bind SelectedComboBoxOption, Mode=TwoWay, Converter={StaticResource ComboBoxItemConvert}}"
                  SelectedValuePath="ComboBoxOption" />
        <TextBlock Name="BoundTextblock" Text="{x:Bind SelectedComboBoxOption.ComboBoxHumanReadableOption, Mode=OneWay}" />
    </StackPanel>
</Grid>

The code behind

public sealed partial class MainPage : Page, INotifyPropertyChanged
{

    private ObservableCollection<ComboBoxItem> ComboBoxOptions;
    public event PropertyChangedEventHandler PropertyChanged;
    public MainPage()
    {
        this.InitializeComponent();
        ComboBoxOptions = new ObservableCollection<ComboBoxItem>();
        ComboBoxOptionsManager.GetComboBoxList(ComboBoxOptions);
        SelectedComboBoxOption = ComboBoxOptions[0];
    }

    public class ComboBoxItem
    {
        public string ComboBoxOption { get; set; }
        public string ComboBoxHumanReadableOption { get; set; }
    }

    public class ComboBoxOptionsManager
    {
        public static void GetComboBoxList(ObservableCollection<ComboBoxItem> ComboBoxItems)
        {
            var allItems = getComboBoxItems();
            ComboBoxItems.Clear();
            allItems.ForEach(p => ComboBoxItems.Add(p));
        }

        private static List<ComboBoxItem> getComboBoxItems()
        {
            var items = new List<ComboBoxItem>();

            items.Add(new ComboBoxItem() { ComboBoxOption = "Option1", ComboBoxHumanReadableOption = "Option 1" });
            items.Add(new ComboBoxItem() { ComboBoxOption = "Option2", ComboBoxHumanReadableOption = "Option 2" });
            items.Add(new ComboBoxItem() { ComboBoxOption = "Option3", ComboBoxHumanReadableOption = "Option 3" });

            return items;
        }
    }

    ComboBoxItem _SelectedComboBoxOption ;
    public ComboBoxItem SelectedComboBoxOption
    {
        get
        {
            return _SelectedComboBoxOption;
        }
        set
        {
            if (_SelectedComboBoxOption != value)
            {
                _SelectedComboBoxOption = value;
                RaisePropertyChanged("SelectedComboBoxOption");
            }
        }
    }

    void RaisePropertyChanged(string prop)
    {
        if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(prop)); }
    }
}

And the converter class

public class ComboBoxItemConvert : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, string language)
    {
        if (value.GetType().Equals(typeof(ComboBoxItem)))
        {
            return value;
        }
        return new ComboBoxItem() { ComboBoxOption = "new", ComboBoxHumanReadableOption = "new" }; 

    }

    public object ConvertBack(object value, Type targetType, object parameter, string language)
    {
        return value as MainPage.ComboBoxItem;
    }
}

What I want is to add the entered text as the new option in combobox which should be treated as selected when entered. Please help. I'm stuck at this point for past 4 days. Thanks in advance.

1

1 Answers

1
votes

The cause of the error is that the converter does not consider the case of null.

Please follow the steps below:

  1. Removed the definitions of the ComboBoxItem and ComboBoxOptionsManager classes from the MainPage and defined them separately.

  2. Override ComboBoxItem's Equal method to handle subsequent item additions

public class ComboBoxItem
{
    public string ComboBoxOption { get; set; }
    public string ComboBoxHumanReadableOption { get; set; }

    public override bool Equals(object obj)
    {
        return obj is ComboBoxItem item &&
               ComboBoxOption == item.ComboBoxOption;
    }
}
  1. Modify the structure of ComboBoxItemConvert and add a dataset
public class ComboBoxItemConvert : IValueConverter
{
    public ObservableCollection<ComboBoxItem> Options { get; set; }
    public object Convert(object value, Type targetType, object parameter, string language)
    {
        if (value !=null && value.GetType().Equals(typeof(ComboBoxItem)))
        {
            return value;
        }
        var newItem = new ComboBoxItem() { ComboBoxOption = "new", ComboBoxHumanReadableOption = "new" };
        if(Options!=null && !Options.Contains(newItem))
        {
            Options.Add(newItem);
        }
        return newItem;
    }

    public object ConvertBack(object value, Type targetType, object parameter, string language)
    {
        return value as ComboBoxItem;
    }
}

Usage

<local:ComboBoxItemConvert x:Key="ComboBoxItemConvert" Options="{x:Bind ComboBoxOptions}" />

When you enter an item that is not in the default data set (ComboBoxOptions) and press Enter, a new ComboBoxItem is added to the data set as the selected item.


If you need to enter custom text and generate new entries accordingly, you need to add something new.

MainPage.xaml.cs

private string _editText;
public string EditText
{
    get => _editText;
    set { _editText = value; RaisePropertyChanged("EditText"); }
}

Converter

...
public string ComboBoxEditText { get; set; }
...
public object Convert(object value, Type targetType, object parameter, string language)
{
    ...
    var newItem = new ComboBoxItem() { ComboBoxOption = ComboBoxEditText ?? "new", ComboBoxHumanReadableOption = ComboBoxEditText ?? "new" };
    if (Options != null && !Options.Contains(newItem))
    {
        Options.Add(newItem);
    }
    return newItem;
}

MainPage.xaml

<Page.Resources>
    <local:ComboBoxItemConvert x:Key="ComboBoxItemConvert" Options="{x:Bind ComboBoxOptions}" ComboBoxEditText="{x:Bind EditText,Mode=OneWay}"/>
</Page.Resources>

...
<ComboBox Name="ComboBox"
      ...
      IsEditable="True"
      IsTextSearchEnabled="True"
      Text="{x:Bind EditText,Mode=TwoWay}"
      ...
      />
...

This will fulfill your requirements.

Best regards.