Как найти потомков элемента управления пользователя вместо окна - заменив Window.FindName
В настоящее время у меня есть проект WPF, который имеет одно главное окно и множество пользовательских элементов управления, которые являются дочерними элементами этого окна. Многие из детей этого окна являются вкладками. Я успешно заменил свое главное окно на пользовательский элемент управления, который реализует почти ту же функциональность, что и главное окно.
Замена окна на UserControl представила одну проблему - в настоящее время наше приложение определяет, какую вкладку программирования отображать на основе родительского окна, используя Window.FindName
Метод показан ниже. Поэтому мне нужно заменить Application.Current.MainWindow
с соответствующим описанием моего основного пользовательского контроля. См. Ошибочный метод C# ниже и wpf создание вкладок для пояснения.
Примечание. Что касается метода Window.FindName() - причина, по которой он не работает после того, как я заменил его на UserControl, заключается в том, что метод FindName осуществляет поиск вверх в визуальном дереве, как описано здесь.
Кто-нибудь знает, как найти пользовательский элемент управления на основе x:Name, аналогично Application.Current.MainWindow
? Кроме того, есть ли лучший способ найти UserControl, чем поиск строки x: Name на случай, если она будет переименована?
Как мы в настоящее время находим MainWindow - теперь нужно найти 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;
}
Создание вкладок программирования.
(xaml)
<Grid>
<ProgrammingControl:ProgrammingTab x:Name="ProgrammingTab1" Program="1" IsVisibleChanged="ProgrammingTab_IsVisibleChanged" />
<ProgrammingControl:ProgrammingTab x:Name="ProgrammingTab2" Program="2" IsVisibleChanged="ProgrammingTab_IsVisibleChanged" />
</Grid>
3 ответа
Я нашел обходной путь к этой проблеме: я создал новый алгоритм, основанный на алгоритме другого пользователя Stackru, который рекурсивно находил любые дочерние объекты DependencyObject. Найдите мое решение здесь. Если вы объявите метод FindChild() в моем другом посте в public static class UIHelper {}
Вы можете решить проблему, выполнив это:
ProgrammingTab programmingTab1 = UIHelper.FindChild<ProgrammingTab>(Application.Current.MainWindow, "ProgrammingTab1");
ProgrammingTab programmingTab2 = UIHelper.FindChild<ProgrammingTab>(Application.Current.MainWindow, "ProgrammingTab2");
При этом все еще используется процедурный код вместо декларативного XAML для привязок, как предложил RayBurns. Если это сработает, его решение будет гораздо более эффективным, поскольку оно будет обходить не целое дерево, а просто изменять видимость вкладок на основе конвертера. Сейчас я проверю это решение и посмотрю, как оно получится.
Причина, по которой FindName() не работает должным образом, описана в посте здесь.
Похоже, ваше приложение разработано в стиле WinForms. Чтобы придерживаться этого стиля и просто ответить на свой вопрос, вы можете найти FindName(), чтобы найти UserControl, и снова найти вкладку ProgrammingTab, например:
var userControl = (MyUserControl)Application.Current.MainWindow.FindName("userControlName");
var programmingTab1 = (ProgrammingTab)userControl.FindName("ProgrammingTab1");
var programmingTab2 = (ProgrammingTab)userControl.FindName("ProgrammingTab2");
...
Однако я бы порекомендовал вам использовать расширенные возможности WPF, такие как привязка данных. Вы можете иметь DependencyProperty "CurrentProgram" для одноэлементного объекта, на который ссылается статическое свойство, и просто привязать Visiblity к нему с помощью конвертера.
<ProgrammingTab Visibilty="{Binding CurrentProgram,
Source={x:Static MyState.Instance},
Converter={x:Static VisibleIfEqualConverter},
ConverterParameter=1}" ...>
...
С этим изменением ваша SetCurrentProgram становится просто:
public void SetCurrentProgram(int num)
{
MyState.Instance.CurrentProgram = num;
}
Прелесть этой техники в том, что любая вкладка ProgrammingTab в любом месте вашего приложения будет автоматически появляться или исчезать каждый раз, когда изменяется значение MyState.Instance.CurrentProgram, без необходимости искать их с помощью FindName() или иным образом.
Эта статья может вам помочь: http://blog.lexique-du-net.com/index.php?post/2010/09/14/UserControl/Control-how-to-get-a-reference-to-an-element-of-the-template