数据绑定,使用不寻常的绑定公式?

时间:2013-11-02 01:27:28

标签: c# wpf

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

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

My Class看起来像这样:

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第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:还修复了资源,即使我发誓我昨天使用完全相同的代码,但现在它正在工作。哦,好吧。

只剩下一个真正的问题:

如何根据OTHER对象属性设置属性。 “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");

2 个答案:

答案 0 :(得分:1)

如果您打算使用WPF,那么您需要在数据类型类{您的Room类上实现INotifyPropertyChanged Interface。您还应该使用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));
    }
}

现在,只要您的RedItemCountBlueItemCount属性值发生变化,用户界面就会更新这些值 MissingCount值。如果您只是按照每个字段的示例进行操作,那么您应该能够获得所需的内容。


更新&gt;&gt;&gt;

好的,所以你似乎缺少一件至关重要的事情。在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 setter,您可以访问整个集合选择的项目,你可以计算你需要的任何东西。

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

答案 1 :(得分:1)

  

我的课程中没有直接显示的额外字段。

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

如果部分类不可用,则创建一个继承原始类的类,并实现所需的扩展属性。 只要原始类不是sealed阻止它。

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

  

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

您需要让您的课程遵守INotifyPropertyChanged。这将允许更改发生并显示在屏幕上,而无需您直接参与。

  

构建我的班级的更好方法。

执行基本的MVVM(模型,视图,视图模型方法)。它只是一种将数据组织在屏幕逻辑之外的方法。

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

上显示了VM和View

注释接口

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