1

编辑:我标记了一个答案,因为它确实让我走上了正确的轨道。剩下的唯一问题似乎是设计问题,根据董事会规则,应该作为一个单独的问题提出。在此处查看我的后续问题:https ://stackoverflow.com/questions/19744475/design-issue-how-to-approach-this-specific-task 感谢您的帮助!

我正在尝试将自定义对象列表绑定到 DataGrid。直接绑定似乎很容易,但我需要为一些额外的字段指定一些复杂的公式,这些额外的字段不会直接出现在我的课堂上。我还希望能够编辑网格中的数据并获取相关字段的更新。让我给你举个例子,因为它真的很难解释。我会将其简化为带有物品的房间。每个项目可以是红色和蓝色。

我的班级看起来像这样:

public class room
{
   public string strRoomName { set; get; }
   public string strItemname { set; get; }
   public int intRedItem { set; get; }
   public int intBlueItem { set; get; }
}

现在如果我使用 dataTable.ItemSource = myList; 我得到这样的东西:

nr. | room | name | red | blue

1. living room, ball, 2, 1
2. sleeping room, bunny, 4, 1
3. living room, chair, 3, 2
4. kitchen, ball, 4, 7
5. garage, chair, 1, 4

现在对于复杂的部分,我需要帮助。我希望每个项目都是相同的数字,红色和蓝色。因为这不成立,所以我想看到每个房间的“不平衡”,并且在全球范围内是这样的:

nr. | room | name | red | blue | missing | global red | global blue | global missing 

1. living room, ball, 2, 1, 1 blue, 6, 7, 1 red
2. sleeping room, bunny, 4, 1, 3 blue, 4, 1, 3 blue
3. living room, chair, 3, 2, 1 blue, 4, 6, 2 red 
4. kitchen, ball, 4, 7, 3 red, 6, 7, 1 red
5. garage, chair, 1, 4, 3 red, 4, 6, 2 red

正如您所看到的,这听起来像 excel 公式,但是我不确定如何在 c# 代码中处理这个问题。您还可以看到我需要在同一行中使用数据,但还需要从与一个属性(项目名称)匹配的其他行获取数据。

此外,如果我将第 1 行中的蓝色值 = 1 更改为值 = 2,我希望第 1 行读取如下:

1. living room, ball, 2, 2, even, 6, 8, 2 red

和 corse line 4 需要更改为:

4. kitchen, ball, 4, 7, 3 red, 6, 8, 2 red

编辑:在阅读了您的答案并测试了我实施您的提示的技能之后,我现在将以下代码作为我的课程:

public class RoomList : ObservableCollection<room>
{
    public RoomList() : base()
    {
        Add(new room() { strRoomName = "living room", strItemname = "ball", intRedItem = 2, intBlueItem = 1 });
        Add(new room() { strRoomName = "sleeping room", strItemname = "bunny", intRedItem = 4, intBlueItem = 1 });
        Add(new room() { strRoomName = "living room", strItemname = "chair", intRedItem = 3, intBlueItem = 2 });
        Add(new room() { strRoomName = "kitchen", strItemname = "ball", intRedItem = 4, intBlueItem = 7 });
        Add(new room() { strRoomName = "garage", strItemname = "chair", intRedItem = 1, intBlueItem = 4 });
    }
}
//rooms
public class room : INotifyPropertyChanged
{
    public string strRoomName { set; get; }
    public string strItemname { set; get; }
    private int _intRed = 0;
    private int _intBlue = 0;
    public int intRedItem
    {
        get { return _intRed; }
        set
        {
            _intRed = value;
            NotifyPropertyChanged("intRedItem", "strMissing");
        }
    }
    public int intBlueItem
    {
        get { return _intBlue; }
        set
        {
            _intBlue = value;
            NotifyPropertyChanged("intBlueItem", "strMissing");
        }
    }
    public string strMissing
    {
        get
        {
            int missingCount = intRedItem - intBlueItem;
            return missingCount == 0 ? "Even" : missingCount.ToString();
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    public void NotifyPropertyChanged(params string[] propertyNames)
    {
        if (PropertyChanged != null)
        {
            foreach (string propertyName in propertyNames)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
}

编辑 1:我立即得到了“缺失”的字段,非常感谢这个提示。它真的和我想象的一样简单,并且对未来的项目很有用。

Edit2:通过在我的类中添加私有虚拟变量修复了 StackOverflow-Error,请参见上面的代码。

Edit3:也修复了资源,即使我发誓我昨天使用了完全相同的代码,但现在它正在工作。那好吧。

只剩下一个真正的问题:

如何根据其他对象属性设置属性。“globalRed”属性需要遍历我的列表或集合,但两者似乎都不是一个选项。至少不像我通常在代码中这样做。“listRooms”不会出现在属性 getter 中。在添加新对象后,这也需要再次循环。我想然后 otify 方法也会处理这个问题,但是我首先要如何循环呢?

还有一个问题可以更好地理解:

谁能用简单的话解释一下为什么上面的集合比列表方法更可取?尽管我能够测试它们的行为相同。

//the list-Way
datagridRooms.ItemsSource = listRooms.Where(x=>x.strRoomName == "living room");
//indentical with the collection-way...
datagridRooms.ItemsSource = new RoomList().Where(x=>x.strRoomName== "living room");
4

2 回答 2

1

如果您要使用 WPF,那么您将需要在您的数据类型类上实现INotifyPropertyChanged接口……您的Room类。您还应该将ObservableCollection<T>Class用于您的收藏。这使您的 UI 能够随着数据的变化而更新,反之亦然。

现在对于您的“Excel公式”字段,您可以为每个字段创建一个属性。您可能需要调整“公式”以更准确地满足您的需求,但以您的Missing字段为例:

public string MissingCount
{
    get 
    {
        int missingCount = intRedItem - intBlueItem;
        return missingCount == 0 ? "Even" : missingCount.ToString();
    }
}

要在 UI 中进行此更新,我们需要提醒INotifyPropertyChanged.PropertyChanged更改事件,但因为我们没有设置此属性(这是我们应该通知的时间),我们必须从以下使用的属性发送通知MissingCount

public int RedItemCount
{
    get { return redItemCount; }
    set 
    {
        redItemCount = value;
        NotifyPropertyChanged("RedItemCount", "MissingCount"); 
    }
}

public int BlueItemCount
{
    get { return blueItemCount; }
    set 
    {
        blueItemCount = value;
        NotifyPropertyChanged("BlueItemCount", "MissingCount"); 
    }
}

public void NotifyPropertyChanged(params string[] propertyNames)
{
    if (PropertyChanged != null)
    {
        foreach (string propertyName in propertyNames) 
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}

现在,每当您RedItemCount或您的BlueItemCount属性值发生变化时,UI 都会更新这些值和MissingCount。如果您对每个字段都遵循此示例,您应该能够得到您想要的。


更新>>>

好的,所以你似乎错过了一件重要的事情。在 MVVM 中,我们称之为视图模型。基本上,它是一个包含所有包含您要显示的对象的属性的类。因此,虽然您几乎已经拥有了,但您至少还需要一个属性:

public Room SelectedRoom
{
    get { return selectedRoom; }
    set 
    {
        selectedRoom = value; 
        NotifyPropertyChanged("SelectedRoom"); 
        // Calculate new values here
    }
}

当然,你仍然需要你的集合属性:

public RoomList RoomList
{
    get { return roomList; }
    set { roomList = value; NotifyPropertyChanged("RoomList"); }
}

使用这些属性,您可以Bind使用该ItemsSoucre属性进行集合,也可以Bind直接使用SelectedItem集合控件(或...的CurrentItem属性)的集合DataGrid,例如:

<DataGrid ItemsSource="{Binding RoomList}" CurrentItem="{Binding SelectedRoom}" ... />

现在,每当在 UI 中选择/聚焦一个新项目时,程序执行将跳转到SelectedRoom视图模型中的设置器,您可以在其中访问整个集合所选项目,并且可以计算您需要的任何内容。

您需要将此视图模型类的实例设置DataContextUserControl/Window或“视图”。

于 2013-11-02T02:22:51.713 回答
1

没有直接出现在我的课堂上的额外字段。

如果您无法向现有类添加更多属性,那么您可以做的最好的事情之一就是从您提到的主要类中创建一个Partial类。在那里,您可以将其他列的逻辑添加为属性,并将业务逻辑放入属性 getter/setter 中以处理提到的excel类型操作。

如果部分类不可用,则创建一个继承原始类并实现所需扩展属性的类。只要原始类没有被密封以防止这种情况发生。

无论如何,您都可以使用新修改的类在网格中显示。

我还希望能够编辑网格中的数据并获取相关字段的更新。

您需要让您的班级遵守INotifyPropertyChanged。这将允许更改发生并呈现在屏幕上,而无需您的任何直接参与。

一种更好的方式来组织我的课程。

做一个基本的 MVVM(模型、视图、视图模型方法)。它只是一种组织数据远离屏幕逻辑的方法。

视图是保存网格并处理与 GUI 相关的事件的页面。ViewModel 负责获取数据,坦率地说,它只是保存要被 View 绑定的数据。那是您应该放置项目列表的地方,并将其放入Observable Collection中,当您和/或从列表中删除项目时,它会向 GUI 发送通知(如果您想订阅此类事件,您也应该这样做)。M 是模型,这只是您前面提到的类。我在题为Xaml: ViewModel Main Page Instantiation and Loading Strategy for Easier Binding 的博客文章中提供了一个显示 VM 和 View 的基本示例。

注意接口

养成用 C#接口定义类实例的习惯。如果将接口视为执行特定任务的合同,那么它本身就提供了开发的一致性,从而产生了非常可扩展的代码。使用接口可以将不同的对象汇集到一个列表中,并以一致的方式处理它们。不仅在类和实例中思考代码方面非常强大,而且在更高的理解水平上,这导致能够添加更多功能并使代码可测试(请参阅Visual Studio 中的单元测试)。使用接口需要更多的前期工作,但它会在未来得到回报,并且是你在学习 C# 时需要考虑的事情。

于 2013-11-02T02:58:35.117 回答