Issue with magnetic controls when navigating between pages

Oct 3, 2012 at 8:55 AM

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 a predecessor 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?

Oct 3, 2012 at 1:24 PM

Ok, 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);