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);
}
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);