Search code examples
wpfxamlkinectnuikinect.toolbox

Kinect toolbox and magnetic controlls when navigating between pages


I am using Kinect.Toolbox mouse and magnetic controls. It works perfectly fine in a single page. However, when I have different pages which I navigate between them I get an error: InvalidOperationException Unhandeled in user code - The specified visual is not an ancestor in this visual. This happens in MouseController.cs Line 158 :

var position = element.TransformToAncestor(rootVisual).Transform(new Point(0, 0));

With some debugging I understood that the magnetic controlls from the previous page are still in the list and that causes the problem. So I tried clearing them before navigating to the next page by:

MouseController.Current.MagneticsControl.Clear();

However, still I get the same error. if I clear the list before navigating I get the error since I am still in the same page and the magneticControls list gets empty, and if I clear them after navigation I don't get the error but my magnetic controlls don't get recognized since they are cleared from the list. Does anyone have a solution for this? And where is the correct place to clear this list?

Here is the XAML where I have the magnetic controls:

 <Grid>
    <Button Content="1" local:MagneticPropertyHolder.IsMagnetic="True" Click="Button_Click"/> 
</Grid>

and on Button_Click I navigate into another page which also has some magnetic controll:

private void Button_Click(object sender, RoutedEventArgs e)
    {
        MouseController.Current.MagneticsControl.Clear();
        keyboard pageKeyboard = new keyboard();
        NavigationService navigationService = NavigationService.GetNavigationService(this);
        navigationService.Navigate(pageKeyboard);
    }

Solution

  • OK, I solved my problem like this: I understood that this problem is caused because the converter is being called while the visual tree is still being assembled, thus your Visual is not yet a descendant of the Window. There are some solutions like doing the conversion once your visual tree is already built. This is done by registering a Dispatcher callback using Dispatcher.BeginInvoke(DispatcherPriority.Render, ...) and doing your work inside the callback.

    Since I didn't want to dive into the source code, and I am not still good enough with WPF to do advanced complicated stuff, I used a solution of my own, which probably is not the best solution ever. instead of clearing the Magnetic control list I decided to set my magnetic controlls programatically instead of setting them in XAML. In this way I could make sure that I set the magnetic controlls when the visual tree is already built. So, in Page_Loaded event I set the magnetic controlls and push them into the magnetic controll list (Not sure if this last part is neccessary):

    private void Page_Loaded(object sender, RoutedEventArgs e)
    {
      foreach (Button btn in MagneticButtons)
            {
                btn.SetValue(MagneticPropertyHolder.IsMagneticProperty, true);
                MouseController.Current.MagneticsControl.Add(btn);
            }
    }
    

    As my only magnetic controls are buttons, you can also set other controlls like this. When I am navigating from the page to another page I unset all the magnetic buttons and remove them from the magnetic control list:

    foreach (Button btn in MagneticButtons)
    {
        btn.SetValue(MagneticPropertyHolder.IsMagneticProperty, false);
        MouseController.Current.MagneticsControl.Remove(btn);
    }
    

    For getting the controls in the windows or a page you can use this:

    public static IEnumerable<T> FindVisualChildren<T>(DependencyObject depObj) where T : DependencyObject
        {
            if (depObj != null)
            {
                for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
                {
                    DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
                    if (child != null && child is T)
                    {
                        yield return (T)child;
                    }
    
                    foreach (T childOfChild in FindVisualChildren<T>(child))
                    {
                        yield return childOfChild;
                    }
                }
            }
        }
    

    And in my case for getting the buttons for example:

    private IEnumerable<Button> MagneticButtons = FindVisualChildren<Button>(this);