WPF TextBox 取得還沒變成字的注音或拼音

  • 441
  • 0

教你如何從 WPF 的 TextBox 中,取得已經輸入的完整字串,包含原本就在裡面的文字以及還沒變成文字的注音符號或拼音

這陣子公司 PM 給的更新搜尋規格,新增了推薦關鍵字的功能,原先我們做的搜尋是整個打完關鍵字按下 Enter 才會開始搜尋,但是這次要在使用者打到一半時,就先將打到一半的關鍵字,加上還沒被組合成字的注音,送到 Server API 來取得建議使用者搜尋的關鍵字,我們原本的實作是將 TextBox.Text Binding 到 ViewModel 的屬性上,但是這個方法並不會將打到一半的注音符號一起 Set 到該屬性上,會變成像下面這張圖一樣,直到這個字串被 Commit 後,才會被 Set 到屬性中,請注意,後面這些 ,下面有橫線所以還在編輯狀態

我們 Google 嘗試了很多方法,像是 Win32 的 IMM 系列 API,或是 TextCompositionManager,不過都還是有點麻煩,最後在一個不小心的情況下發現了一件事情.....

直接拿 TextBox.Text 得到的字串會包含著還沒被 IME Commit 的文字

一整個早上花時間在解的問題,居然最後解法是直接拿 Text 屬性,不知道是我餵狗(Google) 技術不夠好還是眼瞎了,找了很久資料居然都沒看到這件事情 Orz,所以決定寫上來跟大家分享...

下面是這個 Demo App 的原始碼

MainWindow.xaml

<Window x:Class="WpfImeText.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:WpfImeText"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Grid Margin="10">
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <StackPanel Grid.Row="0">
            <TextBlock Text="Use binding"/>
            <TextBox Text="{Binding BindingText, UpdateSourceTrigger=PropertyChanged}"/>
            <TextBlock Text="{Binding BindingText}"/>
        </StackPanel>
        <StackPanel  Grid.Row="1">
            <TextBlock Text="Use TextBox.Text"/>
            <TextBox Name="MyTextBox" KeyUp="MyTextBox_KeyUp"/>
            <TextBlock Name="MyTextBlock"/>
        </StackPanel>
    </Grid>
</Window>

 

 MainWindow.xaml.cs

using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Input;

namespace WpfImeText
{
    public partial class MainWindow : Window, INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        private void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }

        private string bindingText;
        public string BindingText
        {
            get
            {
                return bindingText;
            }
            set
            {
                bindingText = value;
                NotifyPropertyChanged();
            }
        }

        public MainWindow()
        {
            InitializeComponent();
            DataContext = this;
        }

        private void MyTextBox_KeyUp(object sender, KeyEventArgs e)
        {
            MyTextBlock.Text = MyTextBox.Text;
        }
    }
}