RichTextBox doesn't support Japanese IME when hosted in WinForms

14 posts, 0 answers
  1. Brian
    Brian avatar
    13 posts
    Member since:
    Jun 2009

    Posted 11 Jun 2015 Link to this post

    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.

     

  2. Todor
    Admin
    Todor avatar
    168 posts

    Posted 12 Jun 2015 Link to this post

    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
  3. Brian
    Brian avatar
    13 posts
    Member since:
    Jun 2009

    Posted 09 Jul 2015 in reply to Todor Link to this post

    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.

     

     

  4. Tanya
    Admin
    Tanya avatar
    531 posts

    Posted 10 Jul 2015 Link to this post

    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 (Hirigana).
    2. Inserted "nihonngo" 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
  5. Brian
    Brian avatar
    13 posts
    Member since:
    Jun 2009

    Posted 10 Jul 2015 in reply to Tanya Link to this post

    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);
                }
            }


        }

  6. Brian
    Brian avatar
    13 posts
    Member since:
    Jun 2009

    Posted 28 Jul 2015 in reply to Todor Link to this post

    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)

     

     

     

     

     

  7. Todor
    Admin
    Todor avatar
    168 posts

    Posted 30 Jul 2015 Link to this post

    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
  8. Janos
    Janos avatar
    2 posts
    Member since:
    Dec 2011

    Posted 16 Nov 2016 Link to this post

    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

     

  9. Brian
    Brian avatar
    13 posts
    Member since:
    Jun 2009

    Posted 16 Nov 2016 in reply to Janos Link to this post

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

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

  10. Boby
    Admin
    Boby avatar
    625 posts

    Posted 17 Nov 2016 Link to this post

    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.
  11. David
    David avatar
    2 posts
    Member since:
    Jan 2017

    Posted 13 Jul Link to this post

    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.

  12. David
    David avatar
    2 posts
    Member since:
    Jan 2017

    Posted 13 Jul Link to this post

    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);
    }

     

  13. Boby
    Admin
    Boby avatar
    625 posts

    Posted 14 Jul Link to this post

    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.
  14. Brian
    Brian avatar
    13 posts
    Member since:
    Jun 2009

    Posted 14 Jul in reply to David Link to this post

    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!

Back to Top