dotnet / wpf

WPF is a .NET Core UI framework for building Windows desktop applications.
MIT License
7.07k stars 1.17k forks source link

Can't disable Text input space in ComboBox? #7947

Closed CodingOctocat closed 1 year ago

CodingOctocat commented 1 year ago

Description

I have a ComboBox which is editable, the Text property is bound to the backend and does Trim work, but the ComboBox's text box can still enter spaces.

Tip: “空格键” is mean Space key

动画2

Reproduction Steps

View:

<Window x:Class="ComboBoxTextTrimWhitespace.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:ComboBoxTextTrimWhitespace"
        Title="MainWindow"
        Width="400"
        SizeToContent="Height"
        d:DataContext="{d:DesignInstance Type=local:MainWindow,
                                         IsDesignTimeCreatable=True}"
        DataContext="{Binding RelativeSource={RelativeSource Self}}"
        FontSize="24"
        mc:Ignorable="d">
    <StackPanel>
        <TextBlock Text="TextBox:" />

        <TextBox Text="{Binding MyText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />

        <TextBlock Text="ComboBox:" />

        <ComboBox Height="36"
                  IsEditable="True"
                  Text="{Binding MyText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />

        <TextBlock Text="Log:" />

        <TextBox x:Name="Log"
                 Height="500" />
    </StackPanel>
</Window>

Code-Behind:

using System.Windows;

using PropertyChanged;

namespace ComboBoxTextTrimWhitespace;

/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
[AddINotifyPropertyChangedInterface]
public partial class MainWindow : Window
{
    private string _myText = "";

    public string MyText
    {
        get => _myText;
        set
        {
            _myText = value.Trim();
            Log.Text += $"MyText = \"{_myText}\"\n";
            OnPropertyChanged();
        }
    }

    public MainWindow()
    {
        InitializeComponent();
    }
}

Sample:

ComboBoxTextTrimWhitespace.zip

Expected behavior

ComboBox's text box should not be able to enter spaces.

Actual behavior

Even after the Trim, ComboBox's text box can still enter spaces.

Regression?

No response

Known Workarounds

No response

Impact

No response

Configuration

.NET 7 x64

版本 Windows 11 专业版 版本 22H2 安装日期 ‎2022/‎10/‎5 ‎星期三 操作系统版本 22621.1848 体验 Windows Feature Experience Pack 1000.22642.1000.0

Other information

No response

lindexi commented 1 year ago

Your MyText does not changed the ComboBox. The MyText be changed by ComboBox that means the property changed will not update to the ComboBox.

CodingOctocat commented 1 year ago

@lindexi Is there any way to change this behavior? since ComboBox uses a TextBox internally, I want the behavior to be the same as a normal TextBox.

I don't want the user to be able to type spaces, and disabling the Space key won't prevent the user from pasting text with spaces unless I also monitor Ctrl+V and do Trim() in advance.

miloush commented 1 year ago

There is a couple of things happening. First, note that the ComboBox.Text actually contains the coerced value. It is just the visual representation that has "its own state" - the ComboBox.Text property and the editable textbox are two different things. This is similar to when you bind a TextBox to a value e.g. integer and then type letters into it. The data model does not get updated, but the textbox contains the typed data, it would be poor user experience if it didn't.

Second, the propety changed notification does not help, because the value has not actually changed. If you have "abc" and add a space, it gets coerced to "abc", so there is no change to propagate. Also note that the binding does re-evaluate the property after setting it, so you don't need to fire property changed if it is only for the purpose of these bindings.

Imagine you have a "abc " in the combobox list of items. If it worked the way you suggest, you would never be able to select this item by typing into the text box, or get an autocomplete from the list of items.

I don't want the user to be able to type spaces, and disabling the Space key won't prevent the user from pasting text with spaces unless I also monitor Ctrl+V and do Trim() in advance.

I wouldn't suggest going this way, you will most certainly miss a lot of ways to enter spaces. Your method already allows spaces inside the text when pasting anyway.

Before we come up with some solutions, can you tell us about your scenario, why do you need users to not be able to type spaces?

CodingOctocat commented 1 year ago

@miloush I have a ComboBox that provides some available default options while the user can enter valid custom content. Spaces are one of the invalid values, although spaces are retained on the UI of the ComboBox, and the MyText do carry Trim(), which doesn't affect my program too much in a sense. If the behavior is the same as TextBox, then it can enhance the user experience.

miloush commented 1 year ago

It sounds to me that what you need is a TextBox with autocomplete rather than a ComboBox. For workarounds, your options include retemplating the ComboBox, or just using a normal textbox and put a ComboBox next to it that only shows the dropdown button, or deriving from ComboBox and coercing the textbox text manually to the ComboBox.Text property.

I don't think the current behavior is a bug, but I also recognize it is not the most intuitive bahavior. One way to address it would be re-evaluating the Text property after updating it:

https://github.com/dotnet/wpf/blob/8526877505cf137b41ccbeb597a518f0551a4f07/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/ComboBox.cs#L775-L779

However, setting the TextBox.Text property here is fixing its value after it has already been changed, which also comes with resetting the cursor position. It's practically equivalent to the last workaround I suggested. A proper fix would need to be more complex, likely involve binding the TextBox to an intermediate property. It would also break existing applications, so I would suggest this is closed as by design.

CodingOctocat commented 1 year ago

@miloush If this is indeed not a bug, I will respect this behavior, and this behavior will not affect me not too badly. But if someone does definitely need consistent behavior with the TextBox, I think the solution is too costly, so I'll keep this issue for now.

pchaurasia14 commented 1 year ago

@CodingOctocat - Thanks for filing this issue. As @miloush stated, changing existing behavior may invite unintended regressions for other applications. Hence, we are closing this bug as designed. If you think, this is incorrect and there's a better alternative, do let us know and we'll discuss it.

CodingOctocat commented 1 year ago

@pchaurasia14 Perhaps a compatibility improvement could be made, such as adding a TextBoxInputBehavior:bool property to ComboBox to enable or disable this feature.

miloush commented 1 year ago

For the reasons above I don't think such property would be helpful, easy to implement or easy to understand. Better alternative is to provide autocomplete for text boxes @pchaurasia14!