Home > C# > Extending LightSwitch TextBox : Walk-Through

Extending LightSwitch TextBox : Walk-Through

***** WISH YOU A HAPPY NEW YEAR *****

In this post, we’ll create a simple, LightSwitch ‘Character  Casing” TextBox. This extension will allow us to set following ‘case’

  • Normal
  • Upper Case
  • Lower Case
  • Proper Case
  • Title Case
  • Sentence Case

For this walk-through we are creating a “VALUE” control. So you have to follow this MSDN article to create your start-up project. You also will follow several steps from the same article. Keep that tab open.

Steps:

01. Create a LS Extensible project. Follow the above article. Name your project as “XTextBoxExtension“. And name your control as “XTextBoxControl“.

02. Update the Control Icon. Follow the article

03. Specify Supported Data Types. Our control needs to support just “STRING” property. So just update this line in your LSML file.

    <Control.SupportedDataTypes>
      <SupportedDataType DataType=":String" />
    </Control.SupportedDataTypes>

04. Bind to LightSwitch Data.

05. Allow User Code to Access the Control

06. Add Read-Only Support. Follow the article.

07. Alright. We done following that article. Now we need to create a “CharacterCasing” Property. Go back to your XTextBoxControl.LSML file and add this. This section creates a CharacterCasing property, its ENUM values and also a default value. LightSwitch will automatically create a DropDown list with below values.

    <Control.Properties>
      <ControlProperty
                Name="CharacterCasing"
                PropertyType=":String"
                CategoryName="Appearance"
                EditorVisibility="PropertySheet">
        <ControlProperty.Attributes>
          <DisplayName Value="Character Casing" />
          <Description Value="Character Casing" />
          <SupportedValuesExclusive />
          <SupportedValue DisplayName="Normal" Value="Normal"/>
          <SupportedValue DisplayName="Upper Case" Value="Upper"/>
          <SupportedValue DisplayName="Lower Case" Value="Lower"/>
          <SupportedValue DisplayName="Proper Case" Value="ProperCase"/>
          <SupportedValue DisplayName="Title Case" Value="TitleCase"/>
          <SupportedValue DisplayName="Sentence Case" Value="SentenceCase"/>
        </ControlProperty.Attributes>

        <ControlProperty.DefaultValueSource>
          <ScreenExpressionTree>
            <ConstantExpression ResultType=":String" Value="Normal"/>
          </ScreenExpressionTree>
        </ControlProperty.DefaultValueSource>
      </ControlProperty>
    </Control.Properties>

When we deal with String Formats, such as “Proper Case”, “Title Case”, or “Sentence Case”, we don’t have proper rules to follow.  Each one of our requirements might be unique.  So for a greater flexibility, lets add three more Control Properties.

WordSeperators : We supply a series of “chars” as a one single string (no comma delimiters) to determine what might be our word separators.

SentenceSeperators : Same as above, but this time we declare it for sentences with “Comma Delimited Values.

ExceptionWords :  This is for Title Case Property.  Title Cases are different from Proper Cases. For example,

  • Title Case : This is a Sample Title Case.
  • Proper Case : This Is A Sample Proper Case

On the above, “is” & “a” need to still maintain their lower case, when it comes to Title Case. The list might vary for what you need.  With ExceptionWords property, we can build our own Exception Dictionary, of course with Comma Delimiters.  Here is the xaml code those three properties. Just add them under “Dictionary” property. These properties also declared few default values.

      <ControlProperty
                Name="WordBreakers"
                PropertyType=":String"
                CategoryName="Appearance"
                EditorVisibility="PropertySheet">
        <ControlProperty.Attributes>
          <DisplayName Value="Word Breakers List" />
          <Description Value="Word Breakers List" />
        </ControlProperty.Attributes>

        <ControlProperty.DefaultValueSource>
          <ScreenExpressionTree>
            <ConstantExpression ResultType=":String" Value=" .!?"/>
          </ScreenExpressionTree>
        </ControlProperty.DefaultValueSource>
      </ControlProperty>

      <ControlProperty
                Name="SentenceBreakers"
                PropertyType=":String"
                CategoryName="Appearance"
                EditorVisibility="PropertySheet">
        <ControlProperty.Attributes>
          <DisplayName Value="Sentence Breakers List (Use Comma)" />
          <Description Value="Sentence Breakers List (Use Comma)" />
        </ControlProperty.Attributes>

        <ControlProperty.DefaultValueSource>
          <ScreenExpressionTree>
            <ConstantExpression ResultType=":String" Value=" ,. ,! ,? "/>
          </ScreenExpressionTree>
        </ControlProperty.DefaultValueSource>
      </ControlProperty>

      <ControlProperty
                Name="ExceptionWords"
                PropertyType=":String"
                CategoryName="Appearance"
                EditorVisibility="PropertySheet">
        <ControlProperty.Attributes>
          <DisplayName Value="Exception Words (Use Comma)" />
          <Description Value="Exception Words (Use Comma)" />
        </ControlProperty.Attributes>

        <ControlProperty.DefaultValueSource>
          <ScreenExpressionTree>
            <ConstantExpression ResultType=":String" Value="a,is,was,are,were,been"/>
          </ScreenExpressionTree>
        </ControlProperty.DefaultValueSource>
      </ControlProperty>

08. Almost done. Now go to your XTextBoxControl.xaml file. Select the textbox and wire-up its “TextChanged” event. Your updated XAML should be like this now.

    <framework:StatesControl HorizontalAlignment="Stretch">
        <TextBox x:Name="TextBox"
                Text="{Binding StringValue, Mode=TwoWay}"
                IsReadOnly="{Binding IsReadOnly}"
                TextAlignment="{Binding Properties[Microsoft.LightSwitch:RootControl/TextAlignment]}"
                HorizontalAlignment="Stretch"
                ToolTipService.ToolTip="{Binding Description}"
                 TextChanged="TextBox_TextChanged" >
        </TextBox>
    </framework:StatesControl>

09. Now Right Click on the “TextBox_TextChanged” text, and click “Navigate to Event Handler“. That’ll open the Code Behind file. Update the “TextBox_TextChanged” event with below code.

        private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
        {
            OnTextChanged();
        }

        private void OnTextChanged()
        {
            TextBox _textBox = this.TextBox;

            int selectionStart = _textBox.SelectionStart;

            if (this.CharacterCasing == "Upper")
                _textBox.Text = CultureInfo.CurrentCulture.TextInfo.ToUpper(_textBox.Text);
            else if (this.CharacterCasing == "Lower")
                _textBox.Text = CultureInfo.CurrentCulture.TextInfo.ToLower(_textBox.Text);
            else if (this.CharacterCasing == "ProperCase")
                _textBox.Text = ProperCase(_textBox.Text);
            else if (this.CharacterCasing == "TitleCase")
                _textBox.Text = TitleCase(_textBox.Text);
            else if (this.CharacterCasing == "SentenceCase")
                _textBox.Text = SentenceCase(_textBox.Text);

            _textBox.SelectionStart = selectionStart;
        }

10. We’ll use ToUpper & ToLower inbuilt functions, but we need to create the other methods. Remember, none of these below methods will satisfy 100% of your desire and far from perfect. So you need to work on the code to make it perfect for ur needs.

        #region Character Case Helpers
        public string ProperCase(string txt)
        {
            string properCaseStr = String.Empty;
            bool isCaps = true;

            foreach (char c in txt.ToCharArray())
            {
                properCaseStr += isCaps ? c.ToString().ToUpper() : c.ToString();
                isCaps = this.WordBreakers.Contains(c.ToString());
            }

            return properCaseStr;
        }

        public string TitleCase(string txt)
        {
            txt = ProperCase(txt);

            string titleCaseStr = String.Empty;
            foreach (string sentenceBreaker in this.SentenceBreakers.Split(','))
            {
                foreach (string titleCaseException in this.ExceptionWords.Split(','))
                {
                    string findWord = String.Empty;

                    //SPACE is not a Sentence Breaker. So change the Non Noun word to lowercase
                    if (sentenceBreaker == " ")
                    {
                        findWord = sentenceBreaker + ProperCase(titleCaseException);
                        txt = txt.Replace(findWord, sentenceBreaker + titleCaseException.ToLower());
                    }
                    else
                    {
                        findWord = sentenceBreaker + titleCaseException.ToLower();
                        txt = txt.Replace(findWord, sentenceBreaker + ProperCase(titleCaseException));
                    }
                }
            }

            return txt;
        }

        public string SentenceCase(string txt)
        {
            string sentenceCaseStr = String.Empty;
            bool isCaps = true;

            char prevChar = char.MinValue;
            foreach (char c in txt.ToCharArray())
            {
                sentenceCaseStr += isCaps ? c.ToString().ToUpper() : c.ToString();
                isCaps = (c == ' ' && this.WordBreakers.Contains(prevChar));
                prevChar = c;
            }

            return sentenceCaseStr;
        }
        #endregion

11. Now we need to create a Local Properties and register them to listen if there is any change on appropriate properties.

        #region Properties
        public string CharacterCasing
        {
            get { return (string)GetValue(CharacterCasingProperty); }
            set { SetValue(CharacterCasingProperty, value); }
        }

        public string WordBreakers
        {
            get { return (string)GetValue(WordBreakersProperty); }
            set { SetValue(WordBreakersProperty, value); }
        }

        public string SentenceBreakers
        {
            get { return (string)GetValue(SentenceBreakersProperty); }
            set { SetValue(SentenceBreakersProperty, value); }
        }

        public string ExceptionWords
        {
            get { return (string)GetValue(ExceptionWordsProperty); }
            set { SetValue(ExceptionWordsProperty, value); }
        }

        #endregion

        #region Dependency Properties
        public static readonly DependencyProperty CharacterCasingProperty =
            DependencyProperty.Register("CharacterCasingProperty",
                                    typeof(string), typeof(XTextBoxControl),
                                    new PropertyMetadata(OnCharacterCasingPropertyChanged));

        public static readonly DependencyProperty WordBreakersProperty =
            DependencyProperty.Register("WordBreakersProperty",
                                    typeof(string), typeof(XTextBoxControl),
                                    new PropertyMetadata(OnWordBreakersPropertyChanged));

        public static readonly DependencyProperty SentenceBreakersProperty =
            DependencyProperty.Register("SentenceBreakersProperty",
                                    typeof(string), typeof(XTextBoxControl),
                                    new PropertyMetadata(OnSentenceBreakersPropertyChanged));

        public static readonly DependencyProperty ExceptionWordsProperty =
            DependencyProperty.Register("ExceptionWordsProperty",
                                    typeof(string), typeof(XTextBoxControl),
                                    new PropertyMetadata(OnExceptionWordsPropertyChanged));
        #endregion

        #region Metadata
        private static void OnCharacterCasingPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            //Do what you want
        }

        private static void OnWordBreakersPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            //Do what you want
        }

        private static void OnSentenceBreakersPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            //Do what you want
        }

        private static void OnExceptionWordsPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            //Do what you want
        }
        #endregion

12. And finally, bind the Dependency property from the control’s constructor.

        public XTextBoxControl()
        {
            InitializeComponent();
            this.SetBinding(CharacterCasingProperty, new Binding("Properties[XTextBoxExtension:XTextBoxControl/CharacterCasing]"));
            this.SetBinding(WordBreakersProperty, new Binding("Properties[XTextBoxExtension:XTextBoxControl/WordBreakers]"));
            this.SetBinding(SentenceBreakersProperty, new Binding("Properties[XTextBoxExtension:XTextBoxControl/SentenceBreakers]"));
            this.SetBinding(ExceptionWordsProperty, new Binding("Properties[XTextBoxExtension:XTextBoxControl/ExceptionWords]"));
        }

THATS IT!!!!!!!! Now test your control.

Advertisement
Categories: C#
  1. January 1, 2012 at 4:39 pm | #1

    This is great. Thanks a bunch!

  2. powerbala
    January 1, 2012 at 4:47 pm | #2

    Thanks Paul.

  3. January 5, 2012 at 8:23 am | #3

    Verry good !

    I was just looking for some example to teach me how to set my own stuff for a control.

  4. Kivito
    January 12, 2012 at 11:06 am | #4

    great stuff Bala! keep rockin!

  1. January 2, 2012 at 7:10 am | #1

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.