Создание TextBox с наименованием
У меня имеется много TextBox полей, к каждому из которых имеется свой TextBlock с названием поля. Из-за того, что XAML разрастается до невероятных размеров, а все что указывается в TextBlock - это поле Text (остальное забил общим стилем). Назрел вопрос: как бы сделать элемент управления состоящий из TextBox и TextBlock с форматированием, имеющий в себе все свойства TextBox + поле Text для TextBlock. Пробовал создать его через UserControl, но в таком случае нужно прописывать очень много. Поэтому хотелось бы как-то унаследовать от TextBox. Если кто знает как - пожалуйста кодом. Спасибо.
Ответы (1 шт):
Пример
Предположим, у нас есть такой вид
Написан он следующим образом
XAML
<StackPanel>
<StackPanel Margin="5">
<TextBlock x:Name="name1" FontWeight="Medium" />
<StackPanel Orientation="Horizontal">
<TextBlock Text="Лет: " />
<TextBox x:Name="age1" />
</StackPanel>
</StackPanel>
<StackPanel Margin="5">
<TextBlock x:Name="name2" FontWeight="Medium" />
<StackPanel Orientation="Horizontal">
<TextBlock Text="Лет: " />
<TextBox x:Name="age2" />
</StackPanel>
</StackPanel>
<StackPanel Margin="5">
<TextBlock x:Name="name3" FontWeight="Medium" />
<StackPanel Orientation="Horizontal">
<TextBlock Text="Лет: " />
<TextBox x:Name="age3" />
</StackPanel>
</StackPanel>
<StackPanel Margin="5">
<TextBlock x:Name="name4" FontWeight="Medium" />
<StackPanel Orientation="Horizontal">
<TextBlock Text="Лет: " />
<TextBox x:Name="age4" />
</StackPanel>
</StackPanel>
</StackPanel>
C#
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
name1.Text = "Вася";
age1.Text = "13";
name2.Text = "Петя";
age2.Text = "18";
name3.Text = "Маша";
age3.Text = "7";
name4.Text = "Оля";
age4.Text = "24";
}
}
Многие новички именно так и пишут свой проект, не задумываясь о правильности того, что они делают. Ну а делают они все в корне неверно, ибо самое ключевое в WPF проекте, это XAML и Binding (привязки), если вы ими пренебрегаете, вы очень многого лишаетесь, как в плане удобства, так и в плане производительности.
Как поступить
Первым делом давайте взглянем на вид и подумаем, а не коллекция случаем это? Ведь если что-то имеет повторяющиеся "паттерны", то значит это можно описать одним классом и создать его множество раз, поместив в коллекцию, верно? Сделаем это, получив уже такой C# код:
public record User(string Name, int Age);
public partial class MainWindow : Window
{
public List<User> Users { get; set; }
public MainWindow()
{
InitializeComponent();
Users = [new("Вася", 13), new("Петя", 18), new("Маша", 7), new("Оля", 24)];
}
}
Смотрите какой простой и логичный код, где есть конкретный класс User, имеющее в себе только те данные, которые относятся к конкретному человеку. А также есть простая коллекция Users, которая хранит в себе всех этих людей.
Имея коллекцию, давайте теперь адаптируем под нее XAML. За вывод коллекции отвечает ListBox (если нужно выделение) и ItemsControl (если выделение нам не нужно), также есть всякие DataGrid и пр., которые имеют разный вид отображения коллекции. В данном случае нам нужно просто продублировать определенный вид столько раз, сколько объектов в коллекции, а значит подойдет простой ItemsControl. XAML превратится тогда в такое:
<ItemsControl ItemsSource="{Binding Users}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Margin="5">
<TextBlock FontWeight="Medium" Text="{Binding Name}" />
<StackPanel Orientation="Horizontal">
<TextBlock Text="Лет: " />
<TextBox Text="{Binding Age}" />
</StackPanel>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Заметьте, все дубликаты ушли, я только раз написал нужный мне вид и привязал его к коллекции, ничего более.
Чтобы это все заработало, нам надо указать DataContext окну, это некий источник данных, в котором будут искаться все свойства для привязки. В коде выше, коллекцию я создал прям в классе окна, это плохо, лучше вынести в отдельный класс, но для примера сойдет, ну а раз данные у нас в окне, то и указываем окну в конструкторе DataContext = this;.
Как все сделали, запускаем проект и видим все тот-же вид, который и был, с тем-же набором данных, но уже написанный правильно, с использованием привязок. Обратите внимание, в XAML у меня нет ни одного x:Name.
UserControl или как сделать TextBox с наименованием
Поняв теперь как избавиться от дубликатов в коде, можно "сгруппировать" вид нашего "юзера" в один конкретный UserControl, делается это очень просто:
Кликаем ПКМ на название проекта и выбираем "Добавить" - "Пользовательский элемент управления.
Задаем ему нужное имя, в моем случае это будет просто
UserView.Открываем
.xaml.csфайл и пишем там после конструктораpropdpи жмем TAB, вам студия вставит заготовку для свойства зависимости.Указываем этому свойству тип, имя, название
UserControl, значение по умолчанию.В моем примере, я сделаю 2 свойство, заголовок и значение, получится следующее
public string Title { get => (string)GetValue(TitleProperty); set => SetValue(TitleProperty, value); } public object Value { get => GetValue(ValueProperty); set => SetValue(ValueProperty, value); } public static readonly DependencyProperty TitleProperty = DependencyProperty.Register("Title", typeof(string), typeof(UserView), new PropertyMetadata(default)); public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(object), typeof(UserView), new PropertyMetadata(default));Открываем теперь
.xamlфайл и переносим туда вид.В
UserControlтак просто не привязать данные, вот тут имя и может пригодиться. Задаем самомуUserControlзначениеx:Name, а привязки меняем на{Binding Title, ElementName=uc}(uc- имя, заданное вx:Name).Все, с контролом закончили. Возвращаемся в основной XAML и от куда забирали вид, меняем на
<local:UserView Title="{Binding Name}" Value="{Binding Age}"/>? запускаем и радуемся результатом.
В итоге, весь наш изначальный XAML, с кучей дубликатов и тесной связью с C# кодом, превратился в простой и лаконичный:
<ItemsControl ItemsSource="{Binding Users}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<local:UserView Title="{Binding Name}" Value="{Binding Age}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Вот примерно такое у вас и должно быть.
