0

In my Avalonia app using the MVVM Community Toolkit, I need to dynamically create a ComboBox from code-behind, having it data-bound to a collection of a subclass of a class that encapsulates properties that describe the ComboBox's selection. The displayed and ToolTip text change as new languages are selected (language selection code is omitted here and replaced with something simpler), and the selected value is of an enum type instead of a string. I cannot get the change handler to call when a new value is selected.

I created a simple app to isolate the problem; code follows.

DropDownOption.cs:

public partial class DropDownOption : ObservableObject {
  public FruitType Fruit { get; set; } = FruitType.None;
  [ObservableProperty]
  protected string _fruitName = string.Empty;
  [ObservableProperty]
  protected string _toolTipText = string.Empty;
}
public enum FruitType {
  Apple,
  Orange,
  Banana,
  None
}

DynaCBXVM.cs

public partial class DynaCBXVM : ObservableObject {
  [ObservableProperty]
  private FruitType _selectedFruit;
  public ObservableCollection<DropDownOption> FruitOptions { get; set; } = [];

  public DynaCBXVM() {
    FruitOptions.Add(new DropDownOption { Fruit = FruitType.Banana, FruitName = "Cavendish", ToolTipText = "Most common" });
    FruitOptions.Add(new DropDownOption { Fruit = FruitType.Apple, FruitName = "Macintosh", ToolTipText = "Also a computer" });
    FruitOptions.Add(new DropDownOption { Fruit = FruitType.Orange, FruitName = "Navel", ToolTipText = "Also a body part" });
    Debug.WriteLine("And off we go...");
  }

  partial void OnSelectedFruitChanged(FruitType oldValue, FruitType newValue) {
    // Never gets here
    Debug.WriteLine($"Unselecting {oldValue} and selecting {newValue}");
  }
}

DynaCBX.axaml

<UserControl xmlns="https://github.com/avaloniaui"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
             x:Class="ManualCollectionBindingTest.Views.DynaCBX">
  <StackPanel x:Name="stpDynamicCBXes" />
</UserControl>

DynaCBX.axaml.cs

public partial class DynaCBX : UserControl {
  public DynaCBX() {
    InitializeComponent();
    var comBox = new ComboBox {
      [!ComboBox.ItemsSourceProperty] = new Binding("FruitOptions"),
      [!ComboBox.SelectedValueProperty] = new Binding("SelectedFruit", BindingMode.TwoWay),
      [!ComboBox.SelectedValueBindingProperty] = new Binding("Fruit"),
      [!ComboBox.DisplayMemberBindingProperty] = new Binding("FruitName"),
      Margin = new Thickness(100)
    };

    comBox.ItemTemplate = new FuncDataTemplate<DropDownOption>((item, _) => {
      var optionTextBox = new TextBlock {
        [!TextBlock.TextProperty] = new Binding("FruitName")
      };

      if (item != null && !string.IsNullOrEmpty(item.ToolTipText)) {
        ToolTip.SetTip(optionTextBox, item.ToolTipText);
      }

      return optionTextBox;
    });

    stpDynamicCBXes.Children.Add(comBox);
    DataContext = new DynaCBXVM { SelectedFruit = FruitType.Apple };     // I know, I know…
  }
}

Before anyone asks, I can't use XAML files that contain the ComboBoxes. If I could, and I omitted the ToolTip for the options, their XAML would look like this:

<ComboBox ItemsSource="{Binding FruitOptions}"
    SelectedValue="{Binding SelectedFruit}"
    SelectedValueBinding="{Binding Fruit}"
    DisplayMemberBinding="{Binding FruitName}" />

Rendered ComboBox

It works pretty well, as pictured above, it just doesn't indicate when the selection changes. Also, it does not start up with "Macintosh" selected; it's unselected instead. I noticed these error messages in the "Debug" window:

[Binding]An error occurred binding 'SelectedValueBinding' to 'Fruit' at 'Fruit': 'Could not find a matching property accessor for 'Fruit' on 'ManualCollectionBindingTest.ViewModels.DynaCBXVM'.' (ComboBox #16468652)

[Binding]An error occurred binding 'DisplayMemberBinding' to 'FruitName' at 'FruitName': 'Could not find a matching property accessor for 'FruitName' on 'ManualCollectionBindingTest.ViewModels.DynaCBXVM'.' (ComboBox #16468652)

Why is it looking for Fruit and FruitName to be properties of DynaCBXVM, when they're properties of DropDownOption?

Thanks in advance for how to fix this.

1 Answer 1

0

Figured it out! The correct way to declare the ComboBox is:

    var comBox = new ComboBox {
      [!ComboBox.ItemsSourceProperty] = new Binding("FruitOptions"),
      [!ComboBox.SelectedValueProperty] = new Binding("SelectedFruit", BindingMode.TwoWay),
      SelectedValueBinding = new Binding("Fruit"),
      Margin = new Thickness(100)
    };

And, vital to the aforementioned language-switching mechanism, you can reassign the dynamically bound properties of the DropDownOption objects (which unfortunately doesn't include tooltips in this example):

    partial void OnSelectedFruitChanged(FruitType oldValue, FruitType newValue) {
      // Gets here now!
      Debug.WriteLine($"Unselecting {oldValue} and selecting {newValue}");

      if (oldValue == FruitType.Banana && newValue == FruitType.Orange) {
        FruitOptions[1].FruitName = "Cortland";
        FruitOptions[1].ToolTipText = "My favorite";
      }
    }

Updated option text, but same ToolTip

Hope this helps someone.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.