1

I'm trying to send a Control as a ComandParamter so that I can set focus on it. These controls are in a GridViewColumn HeaderTemplate and tabbing cannot go across headers as far as I can tell. My research has led me to utilize x:reference because ElementName fails due to naming scope. The command is properly bound, it does run when I don't bind a CommandParameter.

With the binding shown in xaml below, I receive this error:

Attempt to reference named object(s) 'resourcetypeSrch' which have not yet been defined. Forward references, or references to objects that contain forward references, are not supported on directives other than Key.

How can I bind the ComboBox with x:Name resourcetypeSrch to the TextBox KeyBinding CommandParameter?

<GridViewColumn DisplayMemberBinding="{Binding Name }">
    <GridViewColumn.HeaderTemplate>
        <DataTemplate>
            <DockPanel>
                <TextBlock Text="{StaticResource Name}" />
                <TextBox Text="{Binding DataContext.Foo, RelativeSource={RelativeSource AncestorType=Page}}"                                             
                         Style="{StaticResource SearchBox }" Width="200">
                    <TextBox.InputBindings>
                        <KeyBinding Key="Tab" 
                                    Command="{Binding DataContext.SearchNavigationCmd, RelativeSource={RelativeSource AncestorType=Page}}"
                                    CommandParameter="{Binding {x:Reference resourcetypeSrch}}"/>
                    </TextBox.InputBindings>

                </TextBox>
            </DockPanel>
        </DataTemplate>
    </GridViewColumn.HeaderTemplate>
</GridViewColumn>

<GridViewColumn Width="350" DisplayMemberBinding="{Binding ResourceTypeLookup.TypeName }">
    <GridViewColumn.HeaderTemplate>
        <DataTemplate>
            <DockPanel>
                <TextBlock Text="{StaticResource ResourceType}" />
                <ComboBox x:Name="resourcetypeSrch" Width="300" HorizontalAlignment="Left" IsSynchronizedWithCurrentItem="True"
                      ItemsSource="{Binding DataContext.SrchResourceTypeLookups, RelativeSource={RelativeSource AncestorType=Page}, Mode=OneTime}" 
                      DisplayMemberPath="TypeName"
                      SelectedValuePath="Bar"
                      SelectedValue="{Binding DataContext.Fizz, RelativeSource={RelativeSource AncestorType=Page}}" >
                </ComboBox>
            </DockPanel>
        </DataTemplate>
    </GridViewColumn.HeaderTemplate>
</GridViewColumn>
Crowcoder
  • 11,250
  • 3
  • 36
  • 45

3 Answers3

0

You need to use RelativeSource binding for your CommandParameter in KeyBinding:

<KeyBinding Key="Tab" 
        Command="{Binding DataContext.SearchNavigationCmd, RelativeSource={RelativeSource AncestorType=Page}}"
        CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ComboBox}}}"/>

This code will look up the controls tree until it finds the control of type ComboBox. In your particular case the first ComboBox it's going to find is going to be the one you need.

Matt
  • 93
  • 1
  • 6
Nick Sologoub
  • 724
  • 1
  • 7
  • 21
  • I'll give it a try but I'm not loving the idea of relying on position. I have other column header controls that I will need to operate in the same manner. – Crowcoder Apr 01 '16 at 22:28
  • Fare enough. You can reference your control by name if you wish: CommandParameter="{Binding ElementName=resourcetypeSrch}" – Nick Sologoub Apr 01 '16 at 22:46
  • `ElementName` is the first thing I tried. The `CommandParameter` comes in as null which is how I came to try `x:reference`. – Crowcoder Apr 02 '16 at 14:40
  • I see. I've missed the point that the ComboBox is also inside a HeaderTemplate. In that case I don't believe there is a way to access that control. But there is a work around. – Nick Sologoub Apr 02 '16 at 15:38
0

I believe you want to be able to tab navigate through controls in headers of the grid. What you can do to achieve this is create a property in your dataSource for each column (i.e. Column1IsFocused, Column2IsFocused).

Then you can create an extension to focus your control (for example here is one) from viewmodel. You will bind the property IsFocused of your extension to each of properties from dataSource and then in the Command handler you just set one or the other property to true. I believe this might work.

Community
  • 1
  • 1
Nick Sologoub
  • 724
  • 1
  • 7
  • 21
0

I just thought about another posibility. You can just traverse the visual tree in the command handler method and find your control by name. Here is a nice implementation of a method that goes through VisualTree of an object and looks for a control with specified method:

public static T FindChild<T>(DependencyObject parent, string childName)
   where T : DependencyObject
{    
  // Confirm parent and childName are valid. 
  if (parent == null) return null;

  T foundChild = null;

  int childrenCount = VisualTreeHelper.GetChildrenCount(parent);
  for (int i = 0; i < childrenCount; i++)
  {
    var child = VisualTreeHelper.GetChild(parent, i);
    // If the child is not of the request child type child
    T childType = child as T;
    if (childType == null)
    {
      // recursively drill down the tree
      foundChild = FindChild<T>(child, childName);

      // If the child is found, break so we do not overwrite the found child. 
      if (foundChild != null) break;
    }
    else if (!string.IsNullOrEmpty(childName))
    {
      var frameworkElement = child as FrameworkElement;
      // If the child's name is set for search
      if (frameworkElement != null && frameworkElement.Name == childName)
      {
        // if the child's name is of the request name
        foundChild = (T)child;
        break;
      }
    }
    else
    {
      // child element found.
      foundChild = (T)child;
      break;
    }
  }

  return foundChild;
}

So in your command you just need to do:

FindChild<ComboBox>(ListView, "resourcetypeSrch")

Substitute "ListView" with name of any control that is parent to the data you are looking.

Nick Sologoub
  • 724
  • 1
  • 7
  • 21
  • Thank you, this works, however it requires me to have a Command for each control I want to tab to because I have no way to pass both the control name and the parent DependencyObject in the `CommandParameter`. I think I'm just going to take the search fields out of the column headers and save me all the trouble. – Crowcoder Apr 04 '16 at 19:11