Click here to Skip to main content
15,885,546 members
Articles / Desktop Programming / XAML

Silverlight 1.1 Hebrew and Arabic Language Support

Rate me:
Please Sign up or sign in to vote.
4.05/5 (8 votes)
31 Jan 2008Ms-PL7 min read 44K   290   10  
An article presenting Silverlight 1.1 Hebrew and Arabic language support
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using JustinAngelNet.SilverlightRTL.RTL;
using JustinAngelNet.Silverlight.Framework;
using NBiDi.Extensions;


namespace JustinAngelNet.SilverlightRTL.RTL
{
    public class RTLExtender : ControlExtenderBase
    {
        public RTLExtender()
            : base()
        {
            this.BaseLoaded += new EventHandler(RTLExtender_Loaded);
        }

        void RTLExtender_Loaded(object sender, EventArgs e)
        {
            if (this.Enabled)
            {
                if (!HasCanvasesOrTextBlocks)
                {
                    SearchEntireXAMLFileForTextBlcoksAndRTLThem();
                }
                else
                {
                    if (HasCanvases)
                    {
                        SearchTargetNonExcludedCanvasesForTextBlocksAndRTLThem();
                    }
                    if (HasTextBlocks)
                    {
                        SearchNonExcludedTargetTextBlocksAndRTLThem();
                    }
                }
            }
        }

        private void SearchEntireXAMLFileForTextBlcoksAndRTLThem()
        {
            List<TextBlock> textBlocksOnCanvas = new List<TextBlock>();
            IterateOverCanvasAndFindTextBlocksToRTL(this.RootCanvas, textBlocksOnCanvas);
            foreach (TextBlock textBlockOnCanvas in textBlocksOnCanvas)
                if (!ExcludedTextBlocks.Contains(textBlockOnCanvas))
                    ChangeTextBlockToRTL(textBlockOnCanvas as TextBlock, false);
        }

        private void SearchNonExcludedTargetTextBlocksAndRTLThem()
        {
            foreach (TextBlock targetTextBlock in TargetTextBlocks)
            {
                if (!ExcludedTextBlocks.Contains(targetTextBlock))
                    ChangeTextBlockToRTL(targetTextBlock, false);
            }
        }

        private void SearchTargetNonExcludedCanvasesForTextBlocksAndRTLThem()
        {
            List<TextBlock> textBlocksOnCanvas = new List<TextBlock>();
            foreach (Canvas targetCanvas in TargetCanvases)
            {
                if (!ExcludedCanvases.Contains(targetCanvas))
                    IterateOverCanvasAndFindTextBlocksToRTL(targetCanvas, textBlocksOnCanvas);
            }
            foreach (TextBlock textBlockOnCanvas in textBlocksOnCanvas)
                if (!ExcludedTextBlocks.Contains(textBlockOnCanvas))
                    ChangeTextBlockToRTL(textBlockOnCanvas as TextBlock, false);
        }

        private static Dictionary<TextBlock, List<TextBlock>> _RTLTextBlockForNonRTLTextBlock =
            new Dictionary<TextBlock, List<TextBlock>>();
        public static Dictionary<TextBlock, List<TextBlock>> RTLTextBlockForNonRTLTextBlock
        {
            get { return RTLExtender._RTLTextBlockForNonRTLTextBlock; }
            set { RTLExtender._RTLTextBlockForNonRTLTextBlock = value; }
        }

        private static List<TextBlock> textBlocksAllreadyRTLed = new List<TextBlock>();
        private void ChangeTextBlockToRTL(TextBlock textBlockToRTL, bool Force)
        {
            if (string.IsNullOrEmpty(textBlockToRTL.Text))
                return;

            if ((CheckTextBlockNotAllreadyDone(textBlockToRTL)
                 && TextBlockDoesNotHaveAnyAnimations(textBlockToRTL, Force)
                 && TextBlockDoesNotHaveRotateTransform(textBlockToRTL, Force))
                || Force)
                DoRTLOnTextBlock(textBlockToRTL);

        }

        private bool TextBlockDoesNotHaveRotateTransform(TextBlock textBlockToRTL, bool force)
        {
            RotateTransform rotate = null;

            if (textBlockToRTL.RenderTransform is RotateTransform)
                rotate = textBlockToRTL.RenderTransform as RotateTransform;

            if (textBlockToRTL.RenderTransform is TransformGroup)
                foreach (Transform curTransformToCheckIsRotateTransformAndCheckItsAngle in ((TransformGroup)textBlockToRTL.RenderTransform).Children)
                    if (curTransformToCheckIsRotateTransformAndCheckItsAngle is RotateTransform)
                        rotate = curTransformToCheckIsRotateTransformAndCheckItsAngle as RotateTransform;
            if (rotate != null)
                if (rotate.Angle != 0.0)
                    ThrowException(force, GetRotateAngleErrorMessage(textBlockToRTL, rotate));

            return true;
        }

        private string GetRotateAngleErrorMessage(TextBlock textBlockToRTL, RotateTransform rotateThatIsNotZeroAngle)
        {
            return
                string.Format("TextBlock {0} should not have a RotateTransform angle other then 0 becuase it interfers with the RTLExtender. Currently it has an RotateTransform angle of {1} Please set it back to 0 and use a Loaded Animation to rotate the TextBlock's Canvas", textBlockToRTL.Name, rotateThatIsNotZeroAngle.Angle.ToString());
        }

        private bool TextBlockDoesNotHaveAnyAnimations(TextBlock textBlockToRTL, bool Force)
        {
            List<string> AnimationsNameOnTextBlock = new List<string>();
            IterateOverCanvasAndCheckIfHasAnimatiosOnTextBlock(textBlockToRTL, AnimationsNameOnTextBlock, this.RootCanvas);

            if (AnimationsNameOnTextBlock.Count != 0)
                ThrowException(Force, GetErrorMessageForAnimbationAndTextBlockToRTL(AnimationsNameOnTextBlock, textBlockToRTL));

            return true;
        }

        private void ThrowException(bool ForceNotThrowException, string errorMsg)
        {
            if (ForceNotThrowException)
                System.Diagnostics.Debug.WriteLine(errorMsg);
            else
                throw new SilverlightToolboxException(errorMsg);

        }


        private string GetErrorMessageForAnimbationAndTextBlockToRTL(List<string> animationsThatThrowErrorOn, TextBlock textBlockToRTL)
        {
            string AnimationsName = string.Empty;
            foreach (string curAnimationToAddToError in animationsThatThrowErrorOn)
            {
                if (!string.IsNullOrEmpty(curAnimationToAddToError))
                    AnimationsName += curAnimationToAddToError + " ,";
            }
            if (string.IsNullOrEmpty(AnimationsName))
                AnimationsName = "Unknown";

            return
                string.Format("TextBlock {0} should not have any animations on it because it will be replaced by the RTLExtender. Animations names: {1}. Please place the TextBlock in a canvas (Right Click --> Group --> Canvas) and point all animations to the Canvas.", textBlockToRTL.Name, AnimationsName);
        }


        private void IterateOverCanvasAndCheckIfHasAnimatiosOnTextBlock(TextBlock textBlockToRTL, List<string> AnimationsNameOnTextBlock, UIElement objectToIterateOver)
        {
            IterateOverResourcesAndTriggersAndfindAnimations(textBlockToRTL, AnimationsNameOnTextBlock, objectToIterateOver);

            IterateOverCanvasChildren(textBlockToRTL, AnimationsNameOnTextBlock, objectToIterateOver);
        }

        private void IterateOverCanvasChildren(TextBlock textBlockToRTL, List<string> AnimationsNameOnTextBlock, UIElement objectToIterateOver)
        {
            Canvas childCanvasToIterateOver = objectToIterateOver as Canvas;
            if (childCanvasToIterateOver != null)
            {
                foreach (Visual visualToIterateOVer in childCanvasToIterateOver.Children)
                    if (visualToIterateOVer is UIElement)
                        IterateOverCanvasAndCheckIfHasAnimatiosOnTextBlock(textBlockToRTL, AnimationsNameOnTextBlock,
                                                                           (UIElement)visualToIterateOVer);

            }
        }

        private static void IterateOverResourcesAndTriggersAndfindAnimations(TextBlock textBlockToRTL, List<string> AnimationsNameOnTextBlock, UIElement objectToIterateOver)
        {
            foreach (DependencyObject curObjectToCheckNotSetOnTextBlock in objectToIterateOver.Resources)
            {
                CheckForAnimatiosAndStoryBoard(textBlockToRTL, AnimationsNameOnTextBlock, curObjectToCheckNotSetOnTextBlock);
            }
            foreach (EventTrigger curObjectToCheckNotSetOnTextBlock in objectToIterateOver.Triggers)
            {
                foreach (BeginStoryboard beginStoryboard in curObjectToCheckNotSetOnTextBlock.Actions)
                {
                    CheckForAnimatiosAndStoryBoard(textBlockToRTL, AnimationsNameOnTextBlock, beginStoryboard.Storyboard);
                }
            }
        }

        private static void CheckForAnimatiosAndStoryBoard(TextBlock textBlockToRTL, List<string> AnimationsNameOnTextBlock, DependencyObject curObjectToCheckNotSetOnTextBlock)
        {
            Animation curAnimation = curObjectToCheckNotSetOnTextBlock as Animation;
            if (curAnimation != null)
            {
                if (curAnimation.GetValue(Storyboard.TargetNameProperty).ToString() == textBlockToRTL.Name)
                    AnimationsNameOnTextBlock.Add(curAnimation.Name);
            }

            Storyboard curStoryboard = curObjectToCheckNotSetOnTextBlock as Storyboard;
            if (curStoryboard != null)
            {
                foreach (Animation timelineToCheckChildren in curStoryboard.Children)
                {
                    if (timelineToCheckChildren.GetValue(Storyboard.TargetNameProperty).ToString() == textBlockToRTL.Name)
                    {
                        AnimationsNameOnTextBlock.Add(timelineToCheckChildren.Name);
                        if (!AnimationsNameOnTextBlock.Contains(curStoryboard.Name))
                            AnimationsNameOnTextBlock.Add(curStoryboard.Name);
                    }
                }
            }
        }

        private void DoRTLOnTextBlock(TextBlock textBlockToRTL)
        {
            if (textBlockToRTL.TextWrapping == TextWrapping.NoWrap)
            {
                //TextBlock newTextBlock = SilverlightExtensions.Clone<TextBlock>(textBlockToRTL);
                textBlockToRTL.Text = NBidi.NBidi.LogicalToVisual(textBlockToRTL.Text);

                //Canvas textBlockParent = (Canvas) textBlockToRTL.Parent;
                //textBlockParent.Children.Remove(textBlockToRTL);
                RTLTextBlockForNonRTLTextBlock[textBlockToRTL] = new List<TextBlock>(new TextBlock[] { textBlockToRTL });

            }
            else // (textBlockToRTL.TextWrapping != TextWrapping.NoWrap)
            {
                List<TextBlock> TextBlocksAttachedToTextBlockParent = SilverlightAlignmenetParagraphsToRowsAlgorithem.RtlTextBlockToWrappingScenarios(textBlockToRTL, ForceAllParagraphsAsRTL);
                foreach (TextBlock textBlocksAttachedToCanvas in TextBlocksAttachedToTextBlockParent)
                {
                    textBlocksAllreadyRTLed.Add(textBlocksAttachedToCanvas);
                }
                RTLTextBlockForNonRTLTextBlock[textBlockToRTL] = TextBlocksAttachedToTextBlockParent;
            }
        }

        private static bool CheckTextBlockNotAllreadyDone(TextBlock textBlockToRTL)
        {
            if (!textBlocksAllreadyRTLed.Contains(textBlockToRTL))
            {
                textBlocksAllreadyRTLed.Add(textBlockToRTL);
                return true;
            }
            return false;

        }


        public void RTLCanvas(Canvas canvasToRTL)
        {
            RTLCanvas(canvasToRTL, ForceAllParagraphsAsRTL);
        }

        public void RTLCanvas(Canvas canvasToRTL, bool forceRTL)
        {
            List<TextBlock> textBlocksOnCanvas = new List<TextBlock>();
            IterateOverCanvasAndFindTextBlocksToRTL(canvasToRTL, textBlocksOnCanvas);
            foreach (TextBlock textBlockOnCanvas in textBlocksOnCanvas)
                ChangeTextBlockToRTL(textBlockOnCanvas, forceRTL);

        }

        public void RTLTextBlock(TextBlock textBlockToRTL)
        {
            RTLTextBlock(textBlockToRTL, ForceAllParagraphsAsRTL);
        }

        public void RTLTextBlock(TextBlock textBlockToRTL, bool forceRTL)
        {
            ChangeTextBlockToRTL(textBlockToRTL, forceRTL);
        }

        private void IterateOverCanvasAndFindTextBlocksToRTL(Canvas canvasToIterateOver, List<TextBlock> foundTextBlocks)
        {
            foreach (Visual curChildToCheckIfTextBlockOrCanvas in canvasToIterateOver.Children)
            {
                if (curChildToCheckIfTextBlockOrCanvas is TextBlock)
                    if (!ExcludedTextBlocks.Contains((TextBlock)curChildToCheckIfTextBlockOrCanvas))
                        foundTextBlocks.Add(curChildToCheckIfTextBlockOrCanvas as TextBlock);
                if (curChildToCheckIfTextBlockOrCanvas is Canvas)
                    if (!ExcludedCanvases.Contains((Canvas)curChildToCheckIfTextBlockOrCanvas))
                        IterateOverCanvasAndFindTextBlocksToRTL(curChildToCheckIfTextBlockOrCanvas as Canvas, foundTextBlocks);
            }
        }

        private bool HasCanvasesOrTextBlocks
        {
            get
            {
                return HasCanvases || HasTextBlocks;
            }
        }

        private bool HasTextBlocks
        {
            get
            {
                return TargetTextBlocks.Count != 0;
            }
        }

        private bool HasCanvases
        {
            get
            {
                return TargetCanvases.Count != 0;
            }
        }

        private List<Canvas> _TargetCanvases = new List<Canvas>();
        [FillXamlElementssByName("TargetCanvasesName")]
        internal List<Canvas> TargetCanvases
        {
            get { return _TargetCanvases; }
            set { _TargetCanvases = value; }
        }


        public string TargetCanvasesName
        {
            get;
            set;
        }

        private List<TextBlock> _TargetTextBlocks = new List<TextBlock>();
        [FillXamlElementssByName("TargetTextBlocksName")]
        internal List<TextBlock> TargetTextBlocks
        {
            get { return _TargetTextBlocks; }
            set { _TargetTextBlocks = value; }
        }

        public string TargetTextBlocksName
        {
            get;
            set;
        }

        private List<Canvas> _ExcludedCanvases = new List<Canvas>();
        [FillXamlElementssByName("ExcludedCanvasesName")]
        internal List<Canvas> ExcludedCanvases
        {
            get { return _ExcludedCanvases; }
            set { _ExcludedCanvases = value; }
        }

        public string ExcludedCanvasesName
        {
            get;
            set;
        }

        private List<TextBlock> _ExcludedTextBlocks = new List<TextBlock>();
        [FillXamlElementssByName("ExcludedTextBlocksName")]
        internal List<TextBlock> ExcludedTextBlocks
        {
            get { return _ExcludedTextBlocks; }
            set { _ExcludedTextBlocks = value; }
        }

        public string ExcludedTextBlocksName
        {
            get;
            set;
        }

        public bool ForceAllParagraphsAsRTL
        {
            get;
            set;
        }
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Microsoft Public License (Ms-PL)


Written By
JustinAngel.Net, Senior .Net consultant
Israel Israel
Justin-Josef Angel is a C# Microsoft Most Valuable professional, a Senior .Net consultant in Israel with 4 years of .Net experience and has 8 years of Web experience.

Justin's been working this past year on two Enterprise sized Silverlight projects with his customers. During that time he's gained a real-insight into Silverlight's inner workings and how to integrate Silverlight into the real world of software development. Additionally, During that time he's developed a few well-known projects like the "Silverlight 1.0 Javascript Intellisense", "Silverlight 1.1 Hebrew & Arabic Languages support" and a few others you might know.

Justin is also a seasoned presenter with an impressive track-record of talking in front of thousands of people in Israel.

Justin owns the first .Net blog written in Hebrew - http://www.JustinAngel.Net .
And he also owns an additional blog with mixed Hebrew & English content - http://blogs.Microsoft.co.il/blogs/JustinAngel.

A full list of his articles (all 100+ of them) can be found at: http://www.JustinAngel.Net/#index




Comments and Discussions