views:

495

answers:

3

Hello, I am trying to get a selected text in a TextBox embedded in a listView. This seems so easy, but I couldn't really find a elegant solution.

When I click on "Create Rule" menu item, I want to get the TextBox in which the menu item resides.

I appreciate any help! I spent way too long of my time on this...

... --> ...

I want to get the text in the code behind like this...

    private void CreateRuleMenuItem_Click(object sender, RoutedEventArgs e)
    {
        TextBox txtBox = // ???
        string selectedText = txtBox.selectedText;
A: 

Is this a templated control? If not, you should be able to add a name tag, x:Name="txtTextbox" and then just address it directly, txtTextBox.SelectedText.

Since this is a templated control you don't have direct access via name.

So in your code-behind you could use a method like the following which will find the first Parent of the element specified of a particular type (TextBox). Place the following method into a helper class, or within your existing code:

    /// <summary>
    /// Finds a parent of a given item on the visual tree.
    /// </summary>
    /// <typeparam name="T">The type of the queried item.</typeparam>
    /// <param name="child">A direct or indirect child of the
    /// queried item.</param>
    /// <returns>The first parent item that matches the submitted
    /// type parameter. If not matching item can be found, a null
    /// reference is being returned.</returns>
    public static T TryFindParent<T>(this DependencyObject child)
        where T : DependencyObject
    {
        //get parent item
        DependencyObject parentObject = GetParentObject(child);

        //we've reached the end of the tree
        if (parentObject == null) return null;

        //check if the parent matches the type we're looking for
        T parent = parentObject as T;
        if (parent != null)
        {
            return parent;
        }
        else
        {
            //use recursion to proceed with next level
            return TryFindParent<T>(parentObject);
        }
    }

Then you just do this code in your code event handler:

        MenuItem menuItem = sender as MenuItem;
        if(menuItem!=null)
        {
            TextBox textBox = menuItem.TryFindParent<TextBox>();
            if(textBox!=null)
            {
                string selectedText = textBox.SelectedText;
            }
        }

This method is helpful in many of my projects, so I create a UIHelper class that I put these types of things into...

Good Luck,

Jason

Jason Stevenson
A: 

Thank you very much for your propmt help! It is a template control as shown below.

                        <GridViewColumn Header="Content">
                            <GridViewColumn.CellTemplate>
                                <DataTemplate>
                                    <TextBox Text="{Binding Path=record_content}" Width="800">
                                        <TextBox.ContextMenu>
                                            <ContextMenu>
                                                <MenuItem Name="CreateRuleMenuItem"
                                                          Header="Create Rule"
                                                          Click="CreateRuleMenuItem_Click"/>
                                                <MenuItem Name="DeleteRuleMenuItme" 
                                                          Header="Delete Rule" 
                                                          Click="DeleteRuleMenuItme_Click"/>
                                            </ContextMenu>
                                        </TextBox.ContextMenu>
                                    </TextBox>
                                </DataTemplate>
                            </GridViewColumn.CellTemplate>
                        </GridViewColumn>
A: 

I would clearly recommand you using MVVM and databinding in this case.

I see you have a class XXX with a "record_content" property which is bound to the Text Property of the TextBox. (I think that you omitted the Mode=TwoWay Binding option to ensure that changes in the TextBox change the record_content property value)

You could add a recordContentSelectedText property, bound to the SelectedText property of your TextBox:

<TextBox [...] SelectedText="{Binding recordContentSelectedText,Mode=TwoWay}"/>

the datacontext of your TextBox is an instance of XXX which record_content contains the TextBox content.... And the ContextMenu and its items have the same DataContext!

If the property value is correctly updated by your databinding, it will be very easy:

var data = this.DataContext as XXX;
var selectedText = this.recordContentSelectedText;

It will work only if your DataContext is bound only to one TextBox in the list. Otherwise, TextBox selected text synchronization will occur as a side effect (I don't know if you understand what I mean, but it could be an issue or not, depending on the behavior you expect from your app)

Maupertuis