Доступ к компоненту столбца в 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. Ява наследие.

Дайте мне знать, если вам нужна дополнительная помощь.

Другие вопросы по тегам