Как создать пользовательский элемент управления WPF с содержимым NAMED
У меня есть набор элементов управления с прикрепленными командами и логикой, которые постоянно используются повторно. Я решил создать пользовательский элемент управления, который содержит все общие элементы управления и логику.
Однако мне также нужен элемент управления для хранения содержимого, которое можно назвать. Я попробовал следующее:
Однако, кажется, что любой контент, размещенный внутри пользовательского элемента управления, не может быть назван. Например, если я использую элемент управления следующим образом:
- Как получить доступ к управлению текстовым полем Winform из другого classа?
- Виртуализация элемента управления ItemsControl?
- Что такое разработка, основанная на компонентах?
- Лучший способ получить доступ к элементу управления в другой форме в Windows Forms?
- В чем разница между атрибутами classа и экземпляра?
Я получаю следующую ошибку:
Невозможно установить значение атрибута Name ‘buttonName’ на элемент ‘Button’. «Кнопка» находится под элементом «UserControl1», который уже имеет имя, зарегистрированное, когда оно было определено в другой области.
Если я удалю buttonName, тогда он компилируется, однако мне нужно иметь возможность называть содержимое. Как я могу это достичь?
- Перенос слов для ярлыка в Windows Forms
- Неразрешенный внешний символ для статических членов classа
- Общие элементы в двух списках
- Найти элемент управления в Windows Forms по имени
- Почему элементы управления не хотят удаляться?
- В чем разница между User Control, Custom Control и Component?
- C # Получить позицию элемента управления в форме
- Android ListView с элементами onClick
Ответ заключается в том, чтобы не использовать UserControl для этого.
Создайте class, расширяющий ContentControl
public class MyFunkyControl : ContentControl { public static readonly DependencyProperty HeadingProperty = DependencyProperty.Register("Heading", typeof(string), typeof(HeadingContainer), new PropertyMetadata(HeadingChanged)); private static void HeadingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { ((HeadingContainer) d).Heading = e.NewValue as string; } public string Heading { get; set; } }
затем используйте стиль для указания содержимого
и, наконец, использовать его
Кажется, это невозможно, если используется XAML. Пользовательские элементы управления кажутся чрезмерными, когда у меня есть все необходимые элементы управления, но вам просто нужно сгруппировать их вместе с небольшим количеством логики и разрешить именованный контент.
Решение в блоге JD как маккир предлагает, кажется, лучший компромисс. Способ расширения решения JD, позволяющего контролировать элементы управления в XAML, может быть следующим:
protected override void OnInitialized(EventArgs e) { base.OnInitialized(e); var grid = new Grid(); var content = new ContentPresenter { Content = Content }; var userControl = new UserControlDefinedInXAML(); userControl.aStackPanel.Children.Add(content); grid.Children.Add(userControl); Content = grid; }
В моем примере выше я создал пользовательский элемент управления UserControlDefinedInXAML, который определяется как любые обычные пользовательские элементы управления, использующие XAML. В моем UserControlDefinedInXAML у меня есть StackPanel, называемый aStackPanel, внутри которого я хочу, чтобы появилось мое именованное содержимое.
Другая альтернатива, которую я использовал, – это просто установить свойство Name
в событии Loaded
.
В моем случае у меня был довольно сложный элемент управления, который я не хотел создавать в коде, и он искал дополнительный элемент управления с определенным именем для определенного поведения, и, поскольку я заметил, что могу установить имя в DataTemplate
Я решил, что могу сделать это и в событии Loaded
.
private void Button_Loaded(object sender, RoutedEventArgs e) { Button b = sender as Button; b.Name = "buttonName"; }
Иногда вам может понадобиться ссылка на элемент из C #. В зависимости от x:Uid
использования вы можете установить x:Uid
вместо x:Name
и получить доступ к элементам, вызвав метод поиска Uid, например Get, его Uid в WPF .
Вы можете использовать этот помощник для заданного имени внутри пользовательского элемента управления:
using System; using System.Reflection; using System.Windows; using System.Windows.Media; namespace UI.Helpers { public class UserControlNameHelper { public static string GetName(DependencyObject d) { return (string)d.GetValue(UserControlNameHelper.NameProperty); } public static void SetName(DependencyObject d, string val) { d.SetValue(UserControlNameHelper.NameProperty, val); } public static readonly DependencyProperty NameProperty = DependencyProperty.RegisterAttached("Name", typeof(string), typeof(UserControlNameHelper), new FrameworkPropertyMetadata("", FrameworkPropertyMetadataOptions.None, (d, e) => { if (!string.IsNullOrEmpty((string)e.NewValue)) { string[] names = e.NewValue.ToString().Split(new char[] { ',' }); if (d is FrameworkElement) { ((FrameworkElement)d).Name = names[0]; Type t = Type.GetType(names[1]); if (t == null) return; var parent = FindVisualParent(d, t); if (parent == null) return; var p = parent.GetType().GetProperty(names[0], BindingFlags.Instance | BindingFlags.Public | BindingFlags.SetProperty); p.SetValue(parent, d, null); } } })); public static DependencyObject FindVisualParent(DependencyObject child, Type t) { // get parent item DependencyObject parentObject = VisualTreeHelper.GetParent(child); // we've reached the end of the tree if (parentObject == null) { var p = ((FrameworkElement)child).Parent; if (p == null) return null; parentObject = p; } // check if the parent matches the type we're looking for DependencyObject parent = parentObject.GetType() == t ? parentObject : null; if (parent != null) { return parent; } else { // use recursion to proceed with next level return FindVisualParent(parentObject, t); } } } }
и ваше окно или контрольный код За установленным вами контролем по свойству:
public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } public Button BtnOK { get; set; } }
ваше окно xaml:
UserControlNameHelper получает ваше управляющее имя и ваше имя classа для установки Control на Property.
Я решил создать дополнительное свойство для каждого элемента, который мне нужно получить:
public FrameworkElement First { get { if (Controls.Count > 0) { return Controls[0]; } return null; } }
Это позволяет мне получить доступ к дочерним элементам в XAML:
меню
Код позади:
public TextBox BlahTextBox { get; set; } private void BlahTextBox_Loaded(object sender, RoutedEventArgs e) { BlahTextBox = sender as TextBox; }
Реальное решение было бы для Microsoft исправить эту проблему, а также всех остальных со сломанными визуальными деревьями и т. Д. Гипотетически.
Еще одно обходное решение: ссылайтесь на элемент как RelativeSource .
У меня была та же проблема с помощью TabControl, когда вы добавляли кучу именованных элементов управления.
Мое обходное решение состояло в том, чтобы использовать шаблон управления, который содержит все мои элементы управления, которые будут показаны на закладке. Внутри шаблона вы можете использовать свойство Name, а также привязать данные к свойствам именованного элемента управления из других элементов управления, по крайней мере, внутри одного и того же шаблона.
В качестве содержимого элемента управления TabItem используйте простой элемент управления и установите ControlTemplate соответственно:
Для доступа к указанному элементу управления внутри шаблона из кода позади вам нужно будет использовать визуальное дерево.