I am trying to bind Row to gridview which will not only contain fields of Category table but other tables as well. So I had properties set for all those fields. But since the columns in the table could change frequently I need to dynamically change the properties as well. I found the following one way to do it (using hashtable). But I can't bind the hashtable values to gridview.
How to solve this problem better?
public partial class Form1 : Form
{
public class Row
{
// properties
public Hashtable Properties = new Hashtable();
}
public Form1()
{
InitializeComponent();
DataClasses1DataContext context = new DataClasses1DataContext();
var st = from c in context.Categories
select c;
var p = from pr in context.Products
select p;
Row r = new Row();
//List<Row> listrow = new List<Row>();
foreach (var item in st)
{
r.Properties.Add(item.Description, item.Description);
}
this.gridControl1.DataSource = r.Properties.Values;
}
}
Hashtable.Values is an ICollection - but you need IList to bind data. Ideally, you really want a typed list. Why use Hashtable at all here? I doubt you have enough rows to need it...
Instead, use a typed list (List<T> or BindingList<T>). Note that you can only bind to a single type in a grid. It isn't at all clear to me what you want to display in the grid, since at the moment you are only adding the description, but at the simplest level:
this.gridControl1.DataSource = context.Categories.ToList();
or (better):
this.gridControl1.DataSource = context.Categories.ToBindingList();
This won't help you put both Products and Categories into a single grid... but then, nothing will if they aren't the same type. One thing that might work is an anonymous type of the common properties:
var query = (from c in context.Categories
select new {Id = c.CategoryId, Name = c.Category,
Description = c.Description }).Concat(
from p in context.Products
select new {Id = p.ProductId, Name = p.Product,
Description = p.ProductDescription });
this.gridControl1.DataSource = query.ToList();
Note, however, that anonymous types are immutable (non-editable) - hence no point in using ToBindingList(). The other option is to declare your own class for the purpose, which means it can be editable too.
Related
So I have the gui set up so that I have two main listboxes. I'm still figuring out what kind of gui I want for this application so there is another one but that's not relevant. One listbox is a list of options for what you want to check for a department or an employee. The other is a list of departments. Right now I have the functionality for the name option to view the names of employees for a department. I just need to know how I can filter a list so that the only employees that show up are the ones who are in the chosen department after I click on the submit button. I figured I would use a lambda expression for that and it hasn't been working for me. I really want to know how to use lambda expressions better so please only give me a solution that involves using them. If it's impossible or if it would be more efficient to do something else then let me know.
File where I put reads and set dept array to file contents
//list of employees
public static List<Employee> EmployeeList = new List<Employee>();
//array to hold the options users have for interacting with info
public static string[] OptionsArr;
//array to hold the departments
public static string[] DeptsArr;
//skipping around same file to relevant code
//set the departments array to the contents of the depts file
DeptsArr = File.ReadAllLines("..\\..\\departments.txt");
Not sure if needed
Method for populating DeptListBox
private void UpdateDeptListBox()
{
//set up for new info
DeptListBox.Items.Clear();
//prevent interfence with update
DeptListBox.BeginUpdate();
//set department listbox to depts array
DeptListBox.DataSource = Program.DeptsArr;
DeptListBox.EndUpdate();
}
Problem Method - the submit button method
List<Employee> ResultList = new List<Employee>();
//name
if (OptionsListBox.SelectedIndex == 1)
{
//user selects Marketing department
if (DeptListBox.SelectedIndex == 0)
{
//problem is either with lambda exp or Program.DeptsArr comparison
foreach (Employee empl in Program.EmployeeList.Where(empl => empl.Dept.CompareTo(Program.DeptsArr[0]) == 0).ToList())
{
//this doesn't happen
ResultList.Add(empl);
}
for (int i = 0; i<ResultList.Count; i++)
{
ResultListBox.Items.Add(ResultList[i].Lname + " " + ResultList[i].Fname + " " + ResultList[i].Dept);
}
}
}
}
For me it can be helpful when I am having issues to break stuff down and look at smaller pieces. Are you sure the issue is your lambda function? It may be your options listbox != 1 or that the data is not being read in correctly.
As far as I can tell, this part should work. Although there are some issues with it:
foreach (Employee empl in Program.EmployeeList.Where(empl =>empl.Dept.CompareTo(Program.DeptsArr[0]) == 0).ToList())
{
//this doesn't happen
ResultList.Add(empl);
}
You could start with just the Employee lambda function and hard code the values. Maybe something like this which does indeed produce the correct results (Bob and Brandon)
List<Employee> ResultList = new List<Employee>();
List<Employee> EmployeeList = new List<Employee> {
new Employee{ Name = "Bob", Dept = "Accounting" },
new Employee{ Name = "Larry", Dept = "A" },
new Employee{ Name = "Margret", Dept = "B" },
new Employee{ Name = "Brandon", Dept = "Accounting" }
};
string[] DeptsArr = new string[2];
DeptsArr[0] = "Accounting";
DeptsArr[1] = "A";
//user selects Marketing department
if (departmentIndex == 0)
{
foreach (Employee empl in EmployeeList.Where(empl => empl.Dept.CompareTo(DeptsArr[0]) == 0).ToList())
{
ResultList.Add(empl);
}
}
However your lamda function inside a foreach loop is redundant. You can think of a lambda function as an instruction for running a foreach loop. A foreach loop by itself could look like this:
List<Employee> ResultList = new List<Employee>();
foreach (Employee empl in EmployeeList)
{
if(empl.Dept == DeptsArr[0])
{
ResultList.Add(empl);
}
}
You could get the same result as the foreach loop above, by using the following lamda function:
List<Employee> ResultList = EmployeeList.Where(empl => empl.Dept == DeptsArr[0]).ToList();
A final note is that the "ToList()" on the end of that lambda function is what executes the loop and returns the result as a List. Many times this is not required. Without the "ToList()" part an IEnumerable will be returned which you may be able to use instead. Using an IEnumerable instead of calling ToList() can have better performance in many scenarios.
If you want to test whether a specific value is in an array then you call Contains on that array, e.g.
var allEmployees = new List<Employee>();
// Populate allEmployees here.
var selectedEmployees = allEmployees.Where(e => selectedDepartments.Contains(e.Department)).ToArray();
The selectedEmployees array will contain only the Employee objects from the allEmployees list with a Department property value that is contained in the selectedDepartments array/collection.
I am creating two lists of objects. One "new" list, one "old list".
I want to take the value of one property from an object on the new list and set the property on the old list on the matching object the the new value.
//Original Solution
foreach (RyderQuestion quest in myList)
{
//compare it to every question in ryder questions
foreach (RyderQuestion oldQuestion in _ryderQuestions)
{
//if the question ids match, they are the same question, and should have the right selected option
//selecting the option sets the checkbox of the MultipleChoideQuestionControl
if (oldQuestion.QuestionID == quest.QuestionID)
{
oldQuestion.SelectedOption = quest.SelectedOption;
}
}
}
I am trying to convert it to LINQ to make it more effecient using joins, but how do i update the value directly?
var x = from quest in myList
join oldquest in _ryderQuestions
on new { quest.QuestionID, quest.ShowOn, quest.QuestionOrder }
equals new { oldquest.QuestionID, oldquest.ShowOn, oldquest.QuestionOrder }
select oldquest.SelectedOption = quest.SelectedOption;
This query returns the values to the x list, but I want to actually update the object in the old list instead.
Linq is for querying, not updating. You can join the two lists to line up the object to update, but you'll still have to loop to make the changes:
var query = from quest in myList
join oldquest in _ryderQuestions
on new { quest.QuestionID, quest.ShowOn, quest.QuestionOrder }
equals new { oldquest.QuestionID, oldquest.ShowOn, oldquest.QuestionOrder }
select new {oldquest, quest};
foreach(var item in query}
item.oldquest.SelectedOption = item.quest.SelectedOption
For example:
var x = from quest in myList
join oldquest in _ryderQuestions
on new { quest.QuestionID, quest.ShowOn, quest.QuestionOrder }
equals new { oldquest.QuestionID, oldquest.ShowOn, oldquest.QuestionOrder }
select new {quest , oldquest};
foreach(var item in x)
{
item.quest.SelectedOption = item.oldquest.SelectedOption;
}
You mean this?
I am trying to add one list into another but it is giving me error of The best overloaded method match for 'System.Collection.Generic.List.AddRange(System.Collections.Generic.IEnumerable)' has some invalid arguments
My code is:
public ActionResult RegisteredEvent(string Cno)
{
if (Request.IsAjaxRequest())
{
List<tblEvent> eventlist = new List<tblEvent>();
List<RegisteredEvent> list = new List<RegisteredEvent>();
var db = new clubDataContext();
int[] eventIds = (from m in db.EventRegistrations where m.Cno == Cno select m.Event_Id).ToArray();
int i = 1;
foreach (var item in eventIds)
{
list = (from m in db.tblEvents
where item.Equals(m.EventId)
select new RegisteredEvent()
{
id = m.EventId,
caption = m.Caption,
description = m.Description,
date = m.Date.ToString()
}).ToList();
eventlist.AddRange(list); //Here I am getting error
}
ViewBag.eventDetail = eventlist;
return PartialView("RegisteredEvent");
Simply speaking, you can only concatenate lists of the same type.¹
eventlist is a List<tblEvent>
list is a List<RegisteredEvent>
¹ This is not entirely correct: Since IEnumerable is covariant, it is actually possible to add entries of a List<S> to a List<T>, if S is a subtype of T.
The T in List<T> needs to have the same type or inherent from the same base type
List<RegisteredEvent> eventlist
List<RegisteredEvent> list
or
List<tblEvent> eventlist
List<tblEvent> list
You can use IEnumerable.Select as this (I don't know the structure of tblEvent, so adapt this at your code.
eventlist.AddRange(list.Select(x => new tblEvent{ id = x.id, caption = x.caption, ... }));
But the best way is to create directly a tblEvent
//the list sent to View
eventlist = (from m in db.tblEvents
where item.Equals(m.EventId)
select new tblEvent() //here
{
id = m.EventId,
caption = m.Caption,
description = m.Description,
date = m.Date.ToString()
}).ToList();
I have a table that has 2 columns: FruitID, FruitSize. I want to write a query that takes in a list of FruitIDs and a FruitSize and that sets the new FruitSize for all the fruits.
This is what I have so far:
public void ChangeFruitSizes(List<long> TheFruitIDs, long NewFruitSize)
{
using (SomeDC MyDC = new SomeDC())
{
var TheFruits = (from f in MyDC.Fruits
where TheFruitIDs.Contains(f.FruitID)
select f).ToList();
foreach (Fruits f in TheFruits)
{
f.FruitSize = NewFruitSize;
}
MyDC.SubmitChanges();
}
}
It's currently not bugging but the fields in the database aren't updated. Thanks for your suggestions.
To write this in more concise way, you can try ForEach() in list like below:
using (SomeDC MyDC = new SomeDC())
{
(from f in MyDC.Fruits
where TheFruitIDs.Contains(f.FruitID)
select f).ToList().ForEach(F => F.FruitSize = NewFruitSize);
MyDC.SubmitChanges();
}
Just looking at the code everything is correct. Probably the error is at what is not shown: The model. I suspect you don't have a primary key defined. Define a primary key on the ID field and re-create the model (remove the table from the designer and add it back).
I have a list of employees, and all of them have another list nested which is called the EmployeeValuesCollection.
So my class is something like :-
public Employee(int employeeID, string jobTitle, int companyID,
List<EmployeeValues> employeeValuesCollection)
{
EmployeeID = employeeID;
JobTitle = jobTitle;
CompanyID = companyID;
EmployeeValuesCollection = employeeValuesCollection;
}
Now I wish to populate this object from another object with LINQ, and so far I have :-
List<DataFileRow> dataFiles = dfRow.Rows;
dataFiles
.ForEach(l => employeeList
.Add(new Employee(l.EmpID, l.JobTitle, l.CompID)));
That works however I do not know how to add the employeeValuesCollection in the statement. Is it possible to do?
So I was thinking something like :-
dataFiles
.ForEach(l => employeeList
.Add(new Employee(l.EmpID, l.JobTitle, l.CompID,
new List<EmployeeValuesCollection> .............)));
Thanks for your help and time.
It will be better to iterate through the input parameter employeeValuesCollection list inside the Employee constructor to create a local variable of type EmployeeValues and add it to the Employee class's instance list variable. If we use EmployeeValuesCollection = employeeValuesCollection;, we are actually assigning the reference. So if we modify some values of employeeValuesCollection, the same change will get reflected to EmployeeValuesCollection.
public Employee(int employeeID, string jobTitle, int companyID, List<EmployeeValues> employeeValuesCollection)
{
EmployeeID = employeeID;
JobTitle = jobTitle;
CompanyID = companyID;
foreach (var obj in employeeValuesCollection)
{
var empVal = new EmployeeValues() { Name = obj.Name};
EmployeeValuesCollection.Add(empVal);
}
And you can use the LINQ statement as
dataFiles.ForEach(l => employeeList.Add(new Employee(l.EmpID, l.JobTitle, l.CompID, l.EmployeeValuesCollection)));
You can use the Linq projection over each row. I'll do it in 'proper' linq:
employeeList = (from r in dfRow.Rows
select new Employee(r.EmpID, r.JobTitle, r.CompID,
new List<EmployeeValues>(/*parameter*/))).ToList();
Until you specify what EmployeeValues actually is (and which property on DataFileRow they're accessed from) it's difficult to say any more. But let's say it's just more properties, and I'll summise they are like key/value pairs (despite the name pluralisation on the type, that's a bit confusing).
I've taken the names here from your third comment on your question, and assumed that DataFileRow type exposes these values as an IEnumerable called EmployeeValues. Based on that - you can use an inner projection.
employeeList = (from r in dfRow.Rows
select new Employee(r.EmpID, r.JobTitle, r.CompID,
new List<EmployeeValue>((from v in r.EmployeeValues
select new EmployeeValues(v.title, v.value, v.isVisible))).ToList());