0

我是一名专业的 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 天才能够在这件事上帮助我。

首先十分感谢。

Reagrds,阿塞拉。

4

1 回答 1

0

好的,我已经有一段时间没有在这里回答任何问题了,但你的问题绝对值得关注。

首先,关于这个:

我的职业是 Java 开发人员

忘记java。

您可能在 java 中习惯的大多数(如果不是全部)(相当繁琐和过于冗长)模式和范例在 C# 和 WPF 中几乎没有用处或根本没有用处。

这是因为,与 java 相比,C# 是一种现代的专业级语言,具有许多语言特性,可轻松开发并大大减少样板代码。

除此之外,WPF 是一个高级的专业级 UI 框架,具有大量高级功能(最著名的是数据绑定数据模板),允许您创建高级抽象并将您的代码和应用程​​序逻辑与 UI 完全分离组件,在不引入讨厌的结构或不必要的耦合的情况下实现最大的灵活性。

这种抽象是通过实现一种称为MVVM的模式来实现的。这种模式在大多数(如果不是全部)现代 UI 技术(包括 Web 和非 Web)中相当普遍,但在 Java 世界中除外,Java 世界似乎(毫不奇怪)仍然是 1990 年。

因此,与其试图从遗留技术中锤炼概念并让它们以某种方式融入 WPF,我建议您花时间去理解并接受WPF Mentality

现在,我在您的代码中看到了一些缺陷,无论是在代码本身方面,还是在您用来编写它的理念/方法方面。

首先,Height="464" Margin="444,16,10,0"在 XAML 中存在类似或类似的东西表明您使用 Visual Studio 设计器来构建这样的 UI。这作为一种学习练习很有用,但出于此处所述的原因,强烈建议不要将其用于生产代码。

我建议您花时间正确学习 XAML 并查看本教程以了解 WPF 布局系统的工作原理,以及如何编写与分辨率无关、可自动调整的 WPF UI,而不是不使用固定大小、固定位置的布局'即使在调整包含窗口的大小时也不能正确调整。

同样,开发人员在使用任何其他技术时在 WPF 中犯的典型错误在于他们如何处理事物,而不是他们如何编码。让我们分析一下您的代码:

 var currRow = ProductGrid.CurrentItem; // Current row

 InvoiceItemsDTO sel = (InvoiceItemsDTO)currRow; // Current row DTO OBJECT

 if (sel != null)
 {
    //...
 }

乍一看,这段代码(除了可以缩短的事实)很好。您正在检索底层数据对象,而不是试图弄乱 UI 元素。这是 WPF 中的正确方法。您需要对数据项而不是 UI 进行操作。

让我们将其重写为更类似于 C# 的方式:

var row = ProductGrid.CurrentItem as InvoiceItemsDTO;
if (row != null)
{
   //...
}

注意:上面的代码展示了 C# 语言级别特性(在本例中为as 运算符)如何通过允许漂亮的代码来帮助减少样板文件(我们现在有 2 行代码而不是 3 行)的示例,否则需要一堆可怕的代码对劣质技术(如 java)的黑客攻击。

好的,到目前为止一切都很好,但是由于某种原因,您会从这种以数据为中心的思维方式溜走,转而尝试操纵 UI。

想一想:您正在尝试“更新与当前选定行相对应的Value属性IntegerUpDown” 。

但是,您的 XAML 显示 的Value属性IntegerUpDown实际上是通过双向数据绑定绑定在基础数据项中调用的属性。productQuantity

因此,基本上,您的代码会产生如下结果:

get Data Item -> get UI item -> update UI item -> DataBinding updates Data Item.

看?您正在创建一个完全不必要的间接。相反,只需对您的数据项而不是 UI 进行操作,然后让双向数据绑定处理其余的事情。这就是 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 {...}属性,对吧?因此,与其在 UI 级别处理事件,(再次),为什么不简单地将逻辑放在它真正属于的地方,并摆脱 UI 依赖项,有效地使您的代码更可测试、更清晰、更简单美丽的?

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 的 DataBinding 将InvoiceItemsDTO.Selected属性的值更新为true.

3 - 您的代码为该ProductQuantity属性添加了 +1。

4 - WPF 的 DataBinding 反映ProductQuantityUI 中的更改,前提是您已正确实现INotifyPropertyChange

取消选中复选框时会发生相同的工作流程,但具有false值。

这消除了对事件处理程序、强制转换、代码隐藏和其他需要无用样板并引入不必要的、不希望的耦合的繁琐方法的需要。

底线:C# Rocks。WPF 摇滚。java是遗留的。

如果您需要进一步的帮助,请告诉我。

于 2014-09-02T23:22:18.313 回答