How to Find Children of a UserControl instead of a Window - replacing Window.FindName

9k views Asked by At

I currently have a WPF project which has one main Window, and many UserControls which are children of this Window. Many of the children of this window are Tabs. I have successfully replaced my main Window with a User Control that implements almost exactly the same functionality as the Main Window.

Replacing the Window with a UserControl introduced one problem - currently our application determines which programming tab to display based on the parent window by using the Window.FindName method shown below. Therefore I need to replace the Application.Current.MainWindow with an appropriate description of my main user control. See the erroring C# method below and wpf instantiation of the tabs for clarification.

Note Regarding Window.FindName() method - the reason why it does not work after I replaced it with a UserControl is because the FindName method searches upwards in the visual tree, as described here.

Does anyone know how to find a user control based on the x:Name, similar to Application.Current.MainWindow ? Also, is there a better way to find the UserControl than looking for the x:Name string in case it gets renamed?

How we currently find the MainWindow - need to now find MainUserControl:

(C#)
private static void SetCurrentProgram(int num)
    {
        Window window = Application.Current.MainWindow;
        ProgrammingTab programmingTab1 = window.FindName("ProgrammingTab1") as ProgrammingTab;
        ProgrammingTab programmingTab2 = window.FindName("ProgrammingTab2") as ProgrammingTab;

        programmingTab1.Visibility = num == 1 ? Visibility.Visible : Visibility.Collapsed;
        programmingTab2.Visibility = num == 2 ? Visibility.Visible : Visibility.Collapsed;

    }

Instantiation of programming tabs.

(xaml)
<Grid>
 <ProgrammingControl:ProgrammingTab x:Name="ProgrammingTab1" Program="1" IsVisibleChanged="ProgrammingTab_IsVisibleChanged" />
 <ProgrammingControl:ProgrammingTab x:Name="ProgrammingTab2" Program="2" IsVisibleChanged="ProgrammingTab_IsVisibleChanged" />
</Grid>
3

There are 3 answers

0
CrimsonX On BEST ANSWER

I figured out a workaround to this problem: I created a new algorithm based on another StackOverflow user's algorithm that recursively found any children of a DependencyObject. Find my solution here. If you declare the FindChild() method in my other post within public static class UIHelper {} you can then solve the problem by doing this:

  ProgrammingTab programmingTab1 = UIHelper.FindChild<ProgrammingTab>(Application.Current.MainWindow, "ProgrammingTab1");
  ProgrammingTab programmingTab2 = UIHelper.FindChild<ProgrammingTab>(Application.Current.MainWindow, "ProgrammingTab2");

This still uses procedural code instead of declarative XAML for bindings like RayBurns suggested. If it works, his solution will be much more efficient as it wont be traversing a whole tree but rather just changing the visibility of tabs based on a converter. I'll test that solution now and see how it turns out.

The reason why FindName() doesn't work properly is described in the post here.

5
Ray Burns On

It sounds like your app is developed in a very WinForms-like style. To stick with that style and simply answer your question, you can FindName() to find the UserControl and again to find the ProgrammingTab, like this:

var userControl = (MyUserControl)Application.Current.MainWindow.FindName("userControlName");
var programmingTab1 = (ProgrammingTab)userControl.FindName("ProgrammingTab1");
var programmingTab2 = (ProgrammingTab)userControl.FindName("ProgrammingTab2");
...

However I would recommend you look into using WPF's advanced capabilities such as data binding. You can have a DependencyProperty "CurrentProgram" on a singleton object referenced by a static property, and simply databind Visiblity to it using a converter.

<ProgrammingTab Visibilty="{Binding CurrentProgram,
   Source={x:Static MyState.Instance},
   Converter={x:Static VisibleIfEqualConverter},
   ConverterParameter=1}" ...>
  ...

With this change, your SetCurrentProgram becomes simply:

public void SetCurrentProgram(int num)
{
   MyState.Instance.CurrentProgram = num;
}

The beauty of this technique is that any ProgrammingTab anywhere in your application will automatically appear or disappear every time MyState.Instance.CurrentProgram's vaulue changes, with no need to find them with FindName() or otherwise.