编辑:我标记了一个答案,因为它确实让我走上正轨。唯一的问题似乎是一个设计的东西,根据议事规则应该被问为一个单独的问题。请在此处查看我的后续问题: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");
答案 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));
}
}
现在,只要您的RedItemCount
或BlueItemCount
属性值发生变化,用户界面就会更新这些值和 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#的过程中需要考虑。