silviubogan
silviubogan

Reputation: 3461

How to change the editable ComboBox's text to something else when a specific ComboBoxItem is selected?

The new text will never be present in the items inside ComboBox. Below is the full XAML + code-behind that does not work as I wish. What I am trying to achieve is to have the user select an actual group of items from the combo box, or a No Group (italic, grayed out) item that should be applied as an empty string in the text box inside the ComboBox.

I also tried:

  1. with StaysOpenOnEdit="True" (with the same result) and
  2. by handling the Selected event of the "click me" ComboBoxItem (the event handler is called before the Text or SelectedItem properties of the ComboBox change).

XAML

<Window x:Class="cs_wpf_test_12.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        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"
        xmlns:local="clr-namespace:cs_wpf_test_12"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <StackPanel Orientation="Vertical">
        <ComboBox SelectionChanged="ComboBox_SelectionChanged"
                  IsEditable="True">
            <ComboBoxItem>test</ComboBoxItem>
            <ComboBoxItem Foreground="Gray">click me</ComboBoxItem>
        </ComboBox>
    </StackPanel>
</Window>

Code-behind

internal bool HandlingSelectionChange = false;
private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    if (HandlingSelectionChange)
    {
        return;
    }
    HandlingSelectionChange = true;
    var cb = sender as ComboBox;
    if (cb.Text == "click me")
    {
        cb.Text = "";
        e.Handled = true;
    }
    HandlingSelectionChange = false;
}

Expected: when the user clicks on the "click me" item in the drop-down the text of the ComboBox becomes an empty string. The rest of the items, when clicked, should copy their text normally into the text box of the ComboBox.

Actual:

  1. Start the program.
  2. Select the "click me" item.
  3. The Text changes to "click me" (not grayed out) instead of "".
  4. Click on the "test" item.
  5. The Text changes to "" (empty string) instead of "test".
  6. Click again on the "test" item.
  7. The Text changes to "test".

Update

I wish to use MVVM but I am still a beginner. I have several ComboBoxes like shown above inside a DataGridTemplateColumn, and for each of the ComboBoxes (which should have the same drop-down contents) I think I should have a ViewModel for each of its ComboBoxItems. If possible, I would like to learn how I could I use MVVM correctly in this situation.

The big XAML

<DataGridTemplateColumn Header="Group Name">
    <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
            <Label Content="{Binding GroupName, Mode=OneWay}">
            </Label>
        </DataTemplate>
    </DataGridTemplateColumn.CellTemplate>
    <DataGridTemplateColumn.CellEditingTemplate>
        <DataTemplate>
            <ComboBox IsEditable="True" StaysOpenOnEdit="True"
                        ItemsSource="{Binding Path=Clocks.GroupsVM,
                RelativeSource={RelativeSource AncestorType=local:ClockDataGrid}}"
                        PreviewKeyDown="ComboBox_PreviewKeyDown"
                        SelectionChanged="ComboBox_SelectionChanged"
                        Text="{Binding GroupName}">
                <ComboBox.Resources>
                    <Style TargetType="ComboBoxItem">
                        <Setter Property="FontStyle" Value="{Binding FontStyle}"/>
                        <Setter Property="Foreground" Value="{Binding Foreground}"/>
                    </Style>
                </ComboBox.Resources>
            </ComboBox>
        </DataTemplate>
    </DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>

The big code-behind

private void ComboBox_PreviewKeyDown(object sender, KeyEventArgs e)
{
    var cb = sender as ComboBox;

    if ((e.Key == Key.Return ||
        e.Key == Key.Enter) &&
        cb.Text != "")
    {
        bool duplicate = false;
        foreach (ClockGroupVM vm in Clocks.GroupsVM)
        {
            if (vm.Name == cb.Text)
            {
                cb.SelectedItem = vm;
                duplicate = true;
                break;
            }
        }

        if (duplicate)
        {
            return;
        }

        // create a ClockGroupM and corresponding ClockGroupVM
        // (ClockGroupVM inherits from ClockGroupM)
        var cvm = new ClockGroupVM()
        {
            Name = cb.Text
        };
        Clocks.Groups.Insert(0, cvm);
        cb.SelectedItem = cvm;
    }
}

internal bool HandlingSelectionChange = false;
private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    if (HandlingSelectionChange)
    {
        return;
    }
    HandlingSelectionChange = true;

    var cb = sender as ComboBox;

    //if (cb.SelectedItem is the VM with Style != Normal)

    ClockGroupVM foundVM = null;
    foreach (ClockGroupVM vm in Clocks.GroupsVM)
    {
        if (vm.FontStyle != FontStyles.Normal &&
            ReferenceEquals(cb.SelectedItem, vm))
        {
            foundVM = vm;
            break;
        }
    }

    if (foundVM != null)
    {
        cb.Text = "";
        e.Handled = true;
    }

    HandlingSelectionChange = false;
}

Upvotes: 0

Views: 1490

Answers (1)

Mathivanan KP
Mathivanan KP

Reputation: 2044

ComboBox.Text wont update immediately in the SelectionChanged event. You can use SelectedValue's content instead. And set SelectedValue to null. Change your if condition like this.

if ((cb.SelectedValue as ComboBoxItem).Content.ToString() == "click me")
{
    cb.Text = "";
    cb.SelectedValue = null;
    e.Handled = true;
}

Upvotes: 1

Related Questions