This is a migrated thread and some comments may be shown as answers.

RichTextBox doesn't support Japanese IME when hosted in WinForms

13 Answers 654 Views
RichTextBox
This is a migrated thread and some comments may be shown as answers.
Brian
Top achievements
Rank 1
Brian asked on 11 Jun 2015, 03:50 PM

I tried hosting your WPF RichTextBox in a WinForms application and the Japanese IME doesn't work properly with it. I used the Microsoft IME and the characters start repeating themselves.

 

13 Answers, 1 is accepted

Sort by
0
Todor
Telerik team
answered on 12 Jun 2015, 12:13 PM
Hi Brian,

We are aware of such issue when RadRichTextBox is hosted in a WinForms application. The problem is in the default Caret class which handles the input in RadRichTextBox and the way the text input events are fired in that case. However, since our latest 2014 Q3 release, you can handle such scenarios and now you are able to define and plug in the RadRichTextBox your own custom Caret, which inherits the default one, so a workaround can be found. You can check our Custom Caret SDK example on that matter.
 
For your convenience, I've created a sample demo with above mentioned changes and now the problem should be gone. You can find it attached. 
Please note, that in the sample project, the used approach will works only when an IME is used, because we set the InputState property to Ime. If other input languages will be used (e.g. English), you should implement your own logic for the InputState setting.

I hope this helps.
If you have further questions, I'll be glad to assist you.

Regards,
Todor
Telerik
Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Feedback Portal and vote to affect the priority of the items
0
Brian
Top achievements
Rank 1
answered on 09 Jul 2015, 02:04 PM

Thanks.  I modified this a bit to also add support for Chinese as that also did not work.

 Do you plan on formally supporting Japanese and Chinese in this product? I imagine a lot of legacy applications will be using WPF hosted as transition work since full rewrites are generally out of budget.

 

Also, I found this major error. Could you please provide guidance on a fix?

Microsoft IME: Japanese: Hirigana

Defect details: when typing Japanese with MS IME, need to hit space to change to Japanese character(still with dots under the characters), if type the next words, the previous ones disappeared.

for example: I wanted to type 日本語は難しい(nihonngohamuzukasii), when type 'nihionngo', then hit space key, it will change to Japanese character(日本語) with dots under it, then if keep typing 'ha', it changed to は、but the previous 日本語 disappeared.                                                 

 Thanks.

 

 

0
Tanya
Telerik team
answered on 10 Jul 2015, 04:05 PM
Hi Brian,

Could you, please explain what do you mean when saying "formally supporting Japanese and Chinese"? We think that this is not a common case for using our controls and the workaround Todor suggested you is a good one.

As to the behavior you are experiencing, I tried to reproduce it but to no avail. We are not specialists with the Japanese language, but I followed the steps you are describing and didn't observe the mentioned issue. For more clarity below I will list the exact steps I used. 

1. I started the demo Todor sent you and switched to Japanese ().
2. Inserted "" and hit Space - this results in 日本語.
3. Inserted "hamuzukasii" and hit Space - the result is 日本語ほ難しい.

Please check these steps and let me know if I am missing something.

Regards,
Tanya
Telerik
Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Feedback Portal and vote to affect the priority of the items
0
Brian
Top achievements
Rank 1
answered on 10 Jul 2015, 05:19 PM

It happens after you type with the ime. Hit enter, go to another line then try the keys again. It causes the words to drop. I think I fixed the issue with a few changes and reflection. 

  class InternationalCaret : CustomCaret
    {
        private const string JapaneseName = "ja-JP";
        private const string Chinese = "zh";

        private bool isCurrentLanguageJapanese;
        private bool isCurrentLanguageChinese;

        public InternationalCaret()
        {
            InputLanguageManager.Current.InputLanguageChanged += this.Current_InputLanguageChanged;
            this.GotKeyboardFocus += this.OnGotKeyboardFocus;

            this.isCurrentLanguageJapanese = InputLanguageManager.Current.CurrentInputLanguage.Name == JapaneseName;
            isCurrentLanguageChinese = InputLanguageManager.Current.CurrentInputLanguage.Name.StartsWith(Chinese);
            this.Focusable = true;
        }

        private void UpdateImeState()
        {
            if (this.isCurrentLanguageJapanese)
            {
                this.InputState =
                                  (InputMethod.Current.ImeConversionMode == (ImeConversionModeValues.Native | ImeConversionModeValues.FullShape | ImeConversionModeValues.Roman) ||
                                  InputMethod.Current.ImeConversionMode == (ImeConversionModeValues.Native | ImeConversionModeValues.FullShape | ImeConversionModeValues.Roman | ImeConversionModeValues.Katakana) ||
                                  InputMethod.Current.ImeConversionMode == (ImeConversionModeValues.FullShape | ImeConversionModeValues.Roman | ImeConversionModeValues.Alphanumeric)

                                  ) &&
                                  InputMethod.Current.ImeState == InputMethodState.On ?
                                  InputStates.Ime : InputStates.Standard;
            }
            else if (this.isCurrentLanguageChinese)
            {
                this.InputState =
                               ((InputMethod.Current.ImeConversionMode & ImeConversionModeValues.Native) == ImeConversionModeValues.Native
                               ) &&
                               InputMethod.Current.ImeState == InputMethodState.On ?
                               InputStates.Ime : InputStates.Standard;
            }
            else
            {
                InputState = InputStates.Standard;
            }
        }

        private void Current_InputLanguageChanged(object sender, InputLanguageEventArgs e)
        {
            this.isCurrentLanguageJapanese = e.NewLanguage.Name == JapaneseName;
            isCurrentLanguageChinese = InputLanguageManager.Current.CurrentInputLanguage.Name.StartsWith(Chinese);
            this.UpdateImeState();
        }

        private void OnGotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
        {
            this.UpdateImeState();
        }

        protected override void OnTextInputStart(object sender, TextCompositionEventArgs e)
        {
            this.UpdateImeState();

            base.OnTextInputStart(sender, e);
        }

        protected override void OnTextInput(TextCompositionEventArgs e)
        {
            if (this.InputState == InputStates.Standard)
            {
                base.OnTextInput(e);
            }
            else
            {
                if (this.LastInputEvent == InputEvents.OnTextInputUpdate)
                {
                    this.ClearText();
                }

                this.ShouldPersist = true;

                if (!string.IsNullOrEmpty(e.Text))
                {
                    this.OnTextInserted(this, new TextInsertedEventArgs(e.Text, this.ShouldPersist, this.ShouldStartNewComposition));
                }

                this.ShouldPersist = false;
                this.ShouldStartNewComposition = false;

                this.LastInputEvent = InputEvents.OnTextInput;
                Type t = typeof(Caret);
                var field = t.GetField("lastCompositionOffset", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
                field.SetValue(this, 0);

                // Added this, because the Caret is not correctly positioned otherwise.
                this.Padding = new Thickness(-3, 0, 0, 0);
            }
        }


    }

0
Brian
Top achievements
Rank 1
answered on 28 Jul 2015, 06:25 PM

It turns out there is another bug related to this issue.

 If you type into the TextBox using an IME, and then click on the text being typed before it is inserted, it crashes.

Managed Debugging Assistant 'FatalExecutionEngineError' has detected a problem in XXX
Additional information: The runtime has encountered a fatal error. The address of the error was at 0x62467c6d, on thread 0x6b64. The error code is 0x80131623. This error may be a bug in the CLR or in the unsafe or non-verifiable portions of user code. Common sources of this bug include user marshaling errors for COM-interop or PInvoke, which may corrupt the stack.

Steps:

1) Set keyboard language to Japanese with Hirigana as input

2) Type nihonngo into editor.

3) click on the text you typed.

4) Watch crash and say "BAHHH" I can't catch this type of exception.

Any ideas or will this require you to fix internally?(Guessing the latter)

 

 

 

 

 

0
Todor
Telerik team
answered on 30 Jul 2015, 03:15 PM
Hi Brian,

Indeed, I was able to reproduce the lastly described issue.

I can conclude the issue is WindowsForms specific and it occurs when the UIElement.Focus() method is invoked. However, the Caret class inherits UIElement and you could change its Focusable property. As a workaround, in the Caret.OnTextInput() event handler, you could set the Caret's Focusable property to false right before the OnTextInserted invoking and then set back its value to true after the mentioned method execution. Here is a sample example code snippet of the described approach:
protected override void OnTextInput(TextCompositionEventArgs e)
{
      ...
        if (!string.IsNullOrEmpty(e.Text))
        {
            this.Focusable = false;
            this.OnTextInserted(this, new TextInsertedEventArgs(e.Text, this.ShouldPersist, this.ShouldStartNewComposition));
            this.Focusable = true;
        }
      ...
}

I hope this helps.


Regards,
Todor
Telerik
Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Feedback Portal and vote to affect the priority of the items
0
Janos
Top achievements
Rank 1
answered on 16 Nov 2016, 02:25 PM

Hi all,

I know it is relatively old topic, however we also need a workaround like this in our product, and I have to say thanks who are in this topic for samples and descriptions.

However I would like to warn others who are thinking on to use a MyCaret implementation from the provided example, that it causes a memory leak, because every MyCaret instance subscribes to a static event (and never unsubscribes from it).

I suggest something like this:

public MyCaret()
{
    Loaded += MyCaret_Loaded;
    Unloaded += MyCaret_Unloaded;
    this.GotKeyboardFocus += this.OnGotKeyboardFocus;
 
    this.isCurrentLanguageJapanese = InputLanguageManager.Current.CurrentInputLanguage.Name == JapaneseName;
}
 
void MyCaret_Loaded(object sender, RoutedEventArgs e)
{
    InputLanguageManager.Current.InputLanguageChanged += this.Current_InputLanguageChanged;
}
void MyCaret_Unloaded(object sender, RoutedEventArgs e)
{
    InputLanguageManager.Current.InputLanguageChanged -= this.Current_InputLanguageChanged;
}

 

Best Regards,

Janos

 

0
Brian
Top achievements
Rank 1
answered on 16 Nov 2016, 03:04 PM

Yeah, I found that later.  Never posted an update .

simple fix was to use weakeventmanager to bind the events up. 

0
Boby
Telerik team
answered on 17 Nov 2016, 06:53 AM
Hello all and thanks for the updates.

I just want to add that the latest LIBs of the product contains fix for the following issue: RichTextBox: FatalExecutionEngineError is thrown when clicking in RadRichTextBox while using Microsoft Pinyin IME in Flow layout mode, which may be the one that Brian mentioned is experiencing. The first official release to contain the fix will be 2017 R1, expected in January.


Regards,
Boby
Telerik by Progress
Telerik UI for WPF is ready for Visual Studio 2017 RC! Learn more.
0
David
Top achievements
Rank 1
answered on 13 Jul 2017, 06:59 PM

Would it be safe to say that the issue is with the RichTextBox accepting input from the IME in general?

I've got users all over the world and I'd like to just omit the specific language check rather than maintain a list of potentially hundreds of language/region combinations.

0
David
Top achievements
Rank 1
answered on 13 Jul 2017, 07:26 PM

Is there a downside to just overriding the behavior in all cases:

protected override void OnTextInput(TextCompositionEventArgs e)
{
    if (this.LastInputEvent == InputEvents.OnTextInputUpdate)
    {
        this.ClearText();
    }
 
    this.ShouldPersist = true;
 
    if (!string.IsNullOrEmpty(e.Text))
    {
        this.Focusable = false;
        this.OnTextInserted(this, new TextInsertedEventArgs(e.Text, this.ShouldPersist, this.ShouldStartNewComposition));
        this.Focusable = true;
    }
 
    this.ShouldPersist = false;
    this.ShouldStartNewComposition = false;
 
    this.LastInputEvent = InputEvents.OnTextInput;
 
    this.Padding = new Thickness(-3, 0, 0, 0);
}

 

0
Boby
Telerik team
answered on 14 Jul 2017, 06:03 AM
Hi David,

The initial workaround in this thread are for the case when RadRichTextBox for WPF is hosted in WinForms application. 

The general case, when RadRichTextBox for WPF is in WPF application, is working as expected, despite the issue reported later in the thread, which I believe is the same as this one: 
RichTextBox: FatalExecutionEngineError is thrown when clicking in RadRichTextBox while using Microsoft Pinyin IME in Flow layout mode)
and was fixed in UI for WPF R2 2017 SP1.

Otherwise, the internal IME implementation is extremely complex and we cannot tell whether the workaround suggested by you would be safe in all cases without additional extensive testing.

Regards,
Boby
Progress Telerik
Want to extend the target reach of your WPF applications, leveraging iOS, Android, and UWP? Try UI for Xamarin, a suite of polished and feature-rich components for the Xamarin framework, which allow you to write beautiful native mobile apps using a single shared C# codebase.
0
Brian
Top achievements
Rank 1
answered on 14 Jul 2017, 02:04 PM

It's rarely ever a good idea to implement a fix in a general way if you can't determine the impact nor even know if most use cases exhibit the issue.

We found the Telerik products hosted in a Winforms or C++ container had a number of IME issues, but most were tied to the Asian languages. It's hard to completely blame Telerik, as Winforms and C++ wrapped WPF components don't get all the windows messages they should get, as some get filtered by the shim.

Anyway, my advice would be to implement in a targeted manner. 

The way I did it was to only set my inherited cursor IF a specific condition existed. In our case, the language support on the system was in a specific list(and some other checks). And even then, we have a flag to disable it in case the code causes issues or Telerik fixes there stuff and we can disable our custom code.

Good luck!

Tags
RichTextBox
Asked by
Brian
Top achievements
Rank 1
Answers by
Todor
Telerik team
Brian
Top achievements
Rank 1
Tanya
Telerik team
Janos
Top achievements
Rank 1
Boby
Telerik team
David
Top achievements
Rank 1
Share this question
or