Доступ к компоненту столбца в DataGrid
Я по профессии разработчик Java, и мне дали несколько заданий в.NET в качестве пилотного проекта.
Это небольшое приложение для выставления счетов, которое должно быть разработано с помощью WPF и EntityFramework.
Одна из моих задач состоит в том, чтобы показать список счетов-фактур в окне и после нажатия "изменить" для любого счета-фактуры, я должен показать детали этого счета-фактуры вместе с элементами счета-фактуры, которые назначены этому счету-фактуре.
Ниже приведен фрагмент кода XAML, показывающий элементы счета.
<DataGrid x:Name="ProductGrid" AutoGenerateColumns="False" HorizontalAlignment="Stretch"
HorizontalContentAlignment="Stretch" ColumnWidth="*" Height="464" VerticalAlignment="Top" Margin="444,16,10,0" CanUserAddRows="false">
<DataGrid.Columns>
<DataGridTemplateColumn Width="55" Header="Selected">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox Margin="2,0,2,0" HorizontalAlignment="Center" VerticalAlignment="Center"
Checked="Product_Selected" Unchecked="Product_Deselected" IsChecked="{Binding Path=selected}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Width="60" Header="Quantity">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<xctk:IntegerUpDown x:Name="UPDOWN" Increment="1" Minimum="0" HorizontalAlignment="Center" ValueChanged="Quantity_Changed"
VerticalAlignment="Center" Width="50" Value="{Binding productQuantity, Mode=TwoWay}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Header="Product Name" Width="250" Binding="{Binding Path=productName}"/>
<DataGridTextColumn Header="Weight" Binding="{Binding Path=productWeight}"/>
<DataGridTextColumn Header="Size" Binding="{Binding Path=productSize}"/>
<DataGridTextColumn Header="Sale price" Binding="{Binding Path=productSalePrice}"/>
</DataGrid.Columns>
</DataGrid>
Теперь мне нужно добиться того, чтобы при установке флажка код позади него автоматически увеличивал значение компонента IntegerUpDown до 1. Кроме того, если я снял флажок, код позади должен автоматически сбрасывать значение компонента IntegerUpDown до 0.
Ниже приведен фрагмент кода для события Product_Selected.
private void Product_Selected(object sender, RoutedEventArgs e)
{
var currRow = ProductGrid.CurrentItem; // Current row
InvoiceItemsDTO sel = (InvoiceItemsDTO)currRow; // Current row DTO OBJECT
if (sel != null)
{
if (sel.productQuantity == 0) // The user is trying to assign a new item to the invoice
{
int currentRowIndex = ProductGrid.Items.IndexOf(currRow); // Current row index
DataGridRow currentRow = ProductGrid.ItemContainerGenerator.ContainerFromIndex(currentRowIndex) as DataGridRow;
IntegerUpDown prop = ProductGrid.Columns[1].GetCellContent(currentRow) as IntegerUpDown; // Here I get a NULL for "prop"..!! :(
prop.Value = 1; // Automatically increase the value of IntegerUpDown from zero to one
}
}
}
Для этого мне нужно получить доступ к компоненту IntegerUpDown выбранной строки. К сожалению, я понятия не имею, делать это.
Я надеюсь, что некоторые из вас.NET гении могут помочь мне в этом вопросе.
Большое спасибо заранее.
Реагрдс, Асела.
1 ответ
Хорошо, прошло некоторое время с тех пор, как я ответил на любые вопросы здесь, но ваш, безусловно, заслуживает некоторого внимания.
Прежде всего, относительно этого:
Я по профессии разработчик Java
Забудь о Яве.
Большинство (если не все) из (довольно громоздких и чрезмерно многословных) шаблонов и парадигм, к которым вы могли бы привыкнуть в java, мало или вообще не используются в C# и WPF.
Это связано с тем, что, в отличие от Java, C# - это современный язык профессионального уровня со многими языковыми функциями, которые обеспечивают простоту разработки и значительно сокращают шаблон.
В дополнение к этому, WPF представляет собой расширенную инфраструктуру пользовательского интерфейса профессионального уровня с множеством расширенных функций (в частности, привязки данных и шаблонов данных), которые позволяют создавать высокоуровневую абстракцию и полностью отделять код и логику приложения от интерфейса пользователя. компоненты, обеспечивающие максимальную гибкость без использования неприятных конструкций или ненужного сцепления.
Такая абстракция достигается путем реализации шаблона под названием MVVM. Эта модель довольно повсеместна в большинстве (если не во всех) современных технологиях пользовательского интерфейса, как веб-, так и не-веб-, за исключением мира Java, который вместо этого, кажется, считает (неудивительно), что это все еще 1990 год.
Таким образом, вместо того, чтобы пытаться вбить понятия из унаследованных технологий и как-то вписать их в WPF, я предлагаю вам потратить время на то, чтобы понять и освоить WPF Mentality.
Теперь я вижу несколько недостатков в вашем коде, как с точки зрения самого кода, так и с точки зрения философии / подхода, который вы используете для его написания.
Прежде всего, наличие таких вещей, как Height="464" Margin="444,16,10,0"
или что-то подобное в XAML означает, что вы использовали конструктор Visual Studio для создания такого интерфейса. Это полезно в качестве учебного упражнения, но крайне нежелательно для производственного кода по причинам, указанным здесь.
Я предлагаю вам уделить время для правильного изучения XAML, а также взглянуть на этот учебник, чтобы понять, как работает система макетов WPF и как писать независимые от разрешения автоматически настраиваемые пользовательские интерфейсы WPF, а не фиксированные по размеру макеты с фиксированным положением, которые не не правильно настроить даже при изменении размера содержащего окна.
Опять же, типичная ошибка, которую делают разработчики в WPF, когда исходят из любых других технологий, заключается в том, как они подходят к вещам, а не в том, как они их кодируют. Давайте проанализируем ваш код:
var currRow = ProductGrid.CurrentItem; // Current row
InvoiceItemsDTO sel = (InvoiceItemsDTO)currRow; // Current row DTO OBJECT
if (sel != null)
{
//...
}
Этот код (помимо того, что он может быть сокращен), на первый взгляд, очень хорош. Вы извлекаете базовый объект данных, а не пытаетесь связываться с элементами пользовательского интерфейса. Это правильный подход в WPF. Вам нужно работать с вашими элементами данных, а не с пользовательским интерфейсом.
Давайте перепишем его в стиле C#:
var row = ProductGrid.CurrentItem as InvoiceItemsDTO;
if (row != null)
{
//...
}
Примечание. Приведенный выше код демонстрирует пример того, как функции уровня языка C# (в данном случае оператор as) помогают уменьшить шаблон (теперь у нас есть 2 строки кода вместо 3), допуская красивый код, который в противном случае требует кучу ужасных хаки в низших технологиях, таких как Java.
Ладно, пока все хорошо, но затем вы уходите от этого ориентированного на данные мышления, пытаясь по какой-то причине манипулировать пользовательским интерфейсом.
Подумайте об этом: вы пытаетесь "обновить Value
собственность IntegerUpDown
что соответствует выбранной в данный момент строке ".
Но ваш XAML показывает, что Value
собственность IntegerUpDown
на самом деле связан через двустороннюю привязку данных к свойству productQuantity
в базовом элементе данных.
Итак, в основном ваш код приводит к чему-то вроде этого:
get Data Item -> get UI item -> update UI item -> DataBinding updates Data Item.
Увидеть? вы создаете совершенно ненужную косвенность. Вместо этого просто работайте с вашим элементом данных, а не с пользовательским интерфейсом, и пусть двусторонняя привязка данных позаботится обо всем остальном. Это менталитет WPF.
var row = ProductGrid.CurrentItem as InvoiceItemsDTO;
if (row != null)
{
row.productQuantity++;
}
Посмотрите, насколько проще жизнь, когда вы имеете дело с современными технологиями?
Но это даже не заканчивается там.
Ваш XAML также показывает, что CheckBox
вы имеете дело с это IsChecked
свойство привязано к свойству под названием selected
в базовом элементе данных:
<CheckBox [...] IsChecked="{Binding Path=selected}"/>
Это означает, что ваш InvoiceItemsDTO
класс имеет public bool selected {...}
право собственности? Итак, вместо того, чтобы иметь дело с событиями на уровне пользовательского интерфейса, (опять же), почему бы вам просто не поместить логику туда, где она действительно принадлежит, и избавиться от зависимостей пользовательского интерфейса, эффективно делая ваш код более тестируемым, намного чище и проще прекрасный?
public class InvoiceItemsDTO
{
private bool _selected;
public bool Selected
{
get { return _selected; }
set
{
_selected = value;
//This is where your code should be.
if (value)
ProductQuantity++;
else
ProductQuantity--;
}
}
}
Кроме того, обратите внимание на использование надлежащего корпуса. CamelCasing ужасен, и поэтому зарезервирован для
private
члены только в C#. Неpublic
из них.
Увидеть? простой, чистый, проверяемый и красивый, и это просто работает.
Но как это работает?
1 - Когда пользователь нажимает на флажок, значение IsChecked обновляется.
2 - Привязка данных WPF обновляет значение InvoiceItemsDTO.Selected
собственность на true
,
3 - Ваш код добавляет +1 к ProductQuantity
имущество.
4 - Связывание данных WPF отражает ProductQuantity
изменить пользовательский интерфейс, если вы правильно внедрили INotifyPropertyChange
,
Тот же рабочий процесс происходит при снятии флажка, но с false
значение.
Это устраняет необходимость в обработчиках событий, приведении, коде и других обременительных подходах, которые требуют бесполезного шаблона и вводят ненужную, нежелательную связь.
Итог: C# Скалы. WPF Rocks. Ява наследие.
Дайте мне знать, если вам нужна дополнительная помощь.