tags:

views:

74

answers:

2

I have a WPF combobox

<ComboBox Name="cmbExpression" IsEditable="True"/> 

Now I have written some text over there say fx(a,b,c) ,f(x),x,p,mv,P#,Fx(X),x,sometext

Now my requirement is that, whenever the cursor or the mouse (arrow keys viz -> and <-) will be placed on the characters, I should be able to get the value.

Some Test cases:

Case 1:

User put the cursor or mouse pointer on x of f(x).

Output will be f(x).

Case 2:

User put on v of mv

Output: mv

Case 3:

User put the cursor on t of sometext.

Output: sometext

Case 4:

User put the cursor on ( on Fx(X)

Output: Fx(X)

Case 5:

    User put the cursor on ',' of  fx(a,b,c) 

   Output: fx(a,b,c)

I am using C#3.0 and WPF

Help needed.

Thanks

+3  A: 

TextBox has a method called GetCharacterIndexFromPoint which returns the character index at the supplied position, TextBlock has a similar method which returns a TextPointer. You can use these methods to find the what is under the cursor.

<ComboBox IsEditable="True" MouseMove="ComboBox_MouseMove" />

Code

private void ComboBox_MouseMove(object sender, MouseEventArgs e)
{
    var combobox = (ComboBox)sender;
    //An editable ComboBox uses a TextBox named PART_EditableTextBox 
    var textbox = (TextBox)combobox.Template.FindName("PART_EditableTextBox", combobox);

    var pos = textbox.GetCharacterIndexFromPoint(e.GetPosition(textbox),true);
    var text = textbox.Text;
    if (string.IsNullOrEmpty(text))
        return;

    txt.Text = GetWordAtPos(text,pos);
}

private static string GetWordAtPos(string str, int pos)
{
    Stack<char> matches = new Stack<char>();
    int wordStart = 0, wordEnd = 0;
    for (int i = 0; i < str.Length - 1 && wordEnd == 0; i++)
    {
        char c = str[i];

        switch (c)
        {
            case ',':
                if (matches.Count == 0)
                {
                    if (i > pos)
                        wordEnd = i;
                    else
                        wordStart = i;
                }
                break;
            case '(':
                matches.Push(')');
                break;
            case ')':
                if (matches.Count == 0 || matches.Peek() != c)
                    throw new ArgumentException("Found ) without matching ( character.");
                else
                    matches.Pop();
                break;
        }
    }

    string word;
    if (wordEnd == 0)
        word = str.Substring(wordStart);
    else
        word = str.Substring(wordStart, wordEnd - wordStart);

    return word.Trim(',');
}

A different approach you may want to look into if you need more control over the appearance of each token would be to generate a Run element for each token, but this seems to work well for the cases you described.

Kris
The situation fails for fx(a,b,c)
priyanka.sarkar_2
I updated the code to handle the the new cases you added. It should raise an error if encounters a closing ) without an opening one, I don't know what you want to happen in that situation. I'm also unsure whether you need to handle nested functions, this code returns the outermost function. If the cases get more complex you may want to use a parser.
Kris
Thanks a lot for your timely response
priyanka.sarkar_2
A: 

For Keyboard navigation(-> & <-)

private void comboBox1_KeyUp(object sender, KeyEventArgs e)
        {
            var combobox = (ComboBox)sender;
            //An editable ComboBox uses a TextBox named PART_EditableTextBox  
            var textbox = (TextBox)combobox.Template.FindName("PART_EditableTextBox", combobox);

             //This is the way of getting the charecter position
            **var pos = textbox.CaretIndex - 1;**

            var text = textbox.Text;
            if (string.IsNullOrEmpty(text))
                return;

            txt.Text = GetWordAtPos(text, pos); 
        } 
priyanka.sarkar_2