Hierarchical data using Custom objects (telerik RadGridView) - c#

Suppose i have
class Person
{
public int Id {get;set;}
public string Name {get;set;}
public List<Person> All {get;set;}
public Person()
{
}
public List<Person> GetAll()
{
//fills the list with person and returns
}
}
and that i have:
class Address
{
public int PersonId {get;set;}
public string theAddress {get;set;}
public List<Address> All {get;set;}
//constructor, etc
public List<Address> GetAll()
{
//fills the address list and returns
}
}
What im trying to do is exactly the following:
//filling the maintemplate with data
radGridView1.DataMember = "Person";
radGridView1.DataSource = new Person().GetAll();
//address template, the child one
GridViewTemplate template = new GridViewTemplate();
template.DataSource = new Address().GetAll();
template.DataMember = "Address";
radGridView1.MasterTemplate.Templates.Add(template);
//now the relation between those 2 classes
GridViewRelation relation = new GridViewRelation(radGridView1.MasterTemplate);
relation.ChildTemplate = template;
relation.RelationName = "PersonAddress"; //just a name
relation.ParentColumnNames.Add("Id"); //field to be "joined" to create the relation
relation.ChildColumnNames.Add("PersonId"); //same as above
radGridView1.Relations.Add(relation);
and what i get is exactly a gridview with a "+" sign by the side of each Person
The problem is, the "child" grid is EMPTY, and if i try to add data (its, by default, allowed with an empty constructor in the class) i throw an NullArgumentException
Any ideas? im almost giving up. My problem is: i use custom objects on all projects, its not like "yo use datasets, its ready to use etc", i know that, but i would like to know if there's a way to use CUSTOM OBJECTS, or if im done and should try datasets...
Thanks guys

It looks like you are using the WinForms implementation. If that's right, then this works for me fine. Please give this a go
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Telerik.WinControls.UI;
namespace RadGridView_Hierarchy_CS
{
public partial class Form1 : Form
{
private List<Person> people = new List<Person>();
private List<Address> addresses = new List<Address>();
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
FillPeople();
FillAddresses();
radGridView1.DataSource = people;
GridViewTemplate template = new GridViewTemplate();
template.DataSource = addresses;
radGridView1.MasterTemplate.Templates.Add(template);
GridViewRelation relation = new GridViewRelation(radGridView1.MasterTemplate);
relation.ChildTemplate = template;
relation.RelationName = "PersonAddress";
relation.ParentColumnNames.Add("Id");
relation.ChildColumnNames.Add("PersonId");
radGridView1.Relations.Add(relation);
}
private void FillPeople()
{
Person richard = new Person();
richard.Name = "Richard";
richard.Id = 1;
people.Add(richard);
Person bob = new Person();
bob.Name = "Bob";
bob.Id = 2;
people.Add(richard);
Person mike = new Person();
mike.Name = "Mike";
mike.Id = 3;
people.Add(mike);
}
private void FillAddresses()
{
Address house1 = new Address();
house1.PersonId = 1;
house1.Id = 1;
house1.theAddress = "1 The Mews";
addresses.Add(house1);
Address house2 = new Address();
house2.PersonId = 2;
house2.Id = 2;
house2.theAddress = "2 The Mews";
addresses.Add(house2);
}
}
class Person
{
public int Id {get;set;}
public string Name {get;set;}
public Person()
{
}
}
class Address
{
public int Id { get; set; }
public int PersonId {get;set;}
public string theAddress {get;set;}
public Address()
{
}
}
}

stumbled over you post when searching for a solution to this, so ill add my solution in case someone needs it ... ( using version Q1 2011 ).
in some initialisation method of your UC/Grid you could do something like
//setup the template
GridViewTemplate subtemplate = new GridViewTemplate();
subtemplate.AutoSizeColumnsMode = GridViewAutoSizeColumnsMode.Fill;
subtemplate.EnableFiltering = false;
subtemplate.EnableGrouping = false;
subtemplate.AutoGenerateColumns = false;
//define / add the cols
GridViewTextBoxColumn atextcol = new GridViewTextBoxColumn("Name");
//further properties of atextcol
//add the cols to the template
subtemplate.Columns.Add(atextcol);
//add the template to the grid
thegrid.Templates.Add(subtemplate);
//add a HierarchyDataProvider && subscribe to the RowSourceNeeded-Event
subtemplate.HierarchyDataProvider = new GridViewEventDataProvider(subtemplate);
thegrid.RowSourceNeeded += new GridViewRowSourceNeededEventHandler(thegrid_RowSourceNeeded);
then, in the eventhandler fill the row/rows
protected void thegrid_RowSourceNeeded(object sender, GridViewRowSourceNeededEventArgs e)
{
e.Template.Rows.Clear();
patentdata cparent = e.ParentRow.DataBoundItem as patentdata;
foreach (subdataobject sub in parentdata.subs)
{
GridViewRowInfo row = e.Template.Rows.NewRow();
row.Tag = sub;
row.Cells["Name"].Value = sub.Name;
e.SourceCollection.Add(row);
}
}
so, that would be it. critics ?

Related

Issue on Rendering List in Grid in C#

I am trying to render a simple List into a Grid like
var sr = new BindingSource();
sr.DataSource = str;
dataGridView1.DataSource = sr;
I am not getting any error but not able to display the list in Grid. Here is the entire code
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Enum
{
public enum Sex {Male, Female, Other };
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
List<Sex> str = new List<Sex>();
str.Add(Sex.Female);
str.Add(Sex.Male);
var sr = new BindingSource();
sr.DataSource = str;
dataGridView1.DataSource = sr;
}
}
}
DataGridView cannot bind to a list of primitive values (like int, decimal, DateTime, enum, string etc.) because it requires a list containing objects with properties.
The easiest way is to use LINQ projection to an anonymous type with single property like this (BindingSource is not needed at all):
private void button1_Click(object sender, EventArgs e)
{
List<Sex> str = new List<Sex>();
str.Add(Sex.Female);
str.Add(Sex.Male);
dataGridView1.DataSource = str.Select(value => new { Sex = value }).ToList();
}
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
public class Person
{
public string Name { get; set; }
public string Lastname { get; set; }
public Sex Sex { get; set; }
}
public enum Sex { Male, Female, Other };
private void button1_Click(object sender, EventArgs e)
{
BindingList<Person> persons = new BindingList<Person>();
persons.Add(new Person() { Name = "Joe", Lastname = "Doe" , Sex = Sex.Male});
persons.Add(new Person() { Name = "Nancy", Lastname = "Foo" , Sex = Sex.Female});
dataGridView1.DataSource = persons;
}
}
I don't think you can bind an enum to GridView. This is what I could get working
public class Person
{
public Sex Gender { get; set; }
}
You need to use BindingList as list does not implement IBindingList
var list = new List<Person>()
{
new Person { Gender = Sex.Male, },
new Person { Gender = Sex.Female, },
};
var bindingList = new BindingList<Person>(list);
var source = new BindingSource(bindingList, null);
dataGridView1.DataSource = source;

How can I use iQueryable in a class with a private constructor?

I've just started learning Linq, and I'm trying to map a database table to a class, but I can't get it to work with a private constructor.
My code:
namespace LinqTest
{
using System.Collections.Generic;
using System.Data.Linq;
using System.Data.Linq.Mapping;
using System.Linq;
[Table(Name = "tblCity")]
public class City
{
private City()
{
}
[Column(IsPrimaryKey = true, Name = "CityId")]
public int Id { get; private set; }
[Column(Name = "CityName")]
public string Name { get; private set; }
public static List<City> GetList()
{
var dataContext = new DataContext(Database.ConnectionString());
var cities = dataContext.GetTable<City>();
var iQueryable =
from city in cities
select city;
return iQueryable.ToList();
}
}
}
I've also tried mapping to a new instance:
var list = new List<City>();
foreach (var iCity in iQueryable)
{
var city = new City
{
Id = iCity.Id,
Name = iCity.Name
};
list.Add(city);
}
return list;
What can I do to make this work? I'm open to alternative methods.
Thanks
You cant use iqueryable object in foreach. Because this is just a query. You must get objects with ToList(). Pls try this:
var list = new List<City>();
foreach (var iCity in iQueryable.ToList())// return iqueryable to list
{
var city = new City
{
Id = iCity.Id,
Name = iCity.Name
};
list.Add(city);
}
return list;
Or You can search automapper. One example here

C#, WinForms ListBox - Programatically setting DisplayMember property - can text be added?

I am working in WinForms with a ListBox. I have questions about the DisplayMember property.
If I understand correctly - if I do not provide a DisplayMember property then the ListBox uses the objects ToString() method. However if I specify DisplayMember then the ListBox will show the specified property of the objects in the collection.
However, is it possible to add some static text?
What I mean is, if my Property returns a first name such as "Dave", is there a way for me to prefix this with "First Name: Dave" through using the DisplayMember property or some other means?
I ask because the collection of objects I am working with is from a class I don't have control over - therefore I can't easily create a new property or override the ToString() method.
I thought briefly about extending the class and providing my own overridden ToString() method. However in my case I would need the ability to cast the parent class to the child class (which is not possible). I thought shortly about writing a static method that would accept the parent class as a parameter, copy its contents and return the child class but I just don't know if that is considered clean and best practice.
Any thoughts?
The best way to do this is to create a separate wrapper object that you'll use. You can make that object generic and use it on every place where you have that same problem. What I'll do is something like that.
namespace Demo
{
using System;
public class Person
{
public string FirstName { get; set;}
public string LastName { get; set; }
}
public class ItemWrapper<T>
{
public T Item { get; private set; }
public string DisplayMember { get; private set; }
public ItemWrapper(T item, Func<T, string> displayFactory) {
if (item == null) {
throw new ArgumentNullException("item");
}
if (displayFactory == null) {
throw new ArgumentNullException("displayFactory");
}
this.Item = item;
this.DisplayMember = displayFactory(item);
}
public override string ToString() {
return this.DisplayMember;
}
/// <summary>
/// This method is just an example and should be removed
/// </summary>
public static void Example() {
var person1 = new Person() { FirstName = "Johny", LastName = "Bravo" };
var person2 = new Person() { FirstName = "Johny2", LastName = "Bravo" };
var person3 = new Person() { FirstName = "Johny3", LastName = "Bravo" };
var item1 = new ItemWrapper<Person>(person1, p => "First Name: " + p.FirstName);
var item2 = new ItemWrapper<Person>(person2, delegate(Person p) { return "First Name: " + p.FirstName; });
var item3 = new ItemWrapper<Person>(person3, DisplayFactory);
}
private static string DisplayFactory(Person p) {
return "First Name" + p.FirstName;
}
}
}
Lets say that you have a form with a ListBox on it. Then you can use it like that:
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
var p1 = new Person() {FirstName = "Johny", LastName = "Bravo"};
var p2 = new Person() {FirstName = "Alex", LastName = "Flo"};
var list = new List<ItemWrapper<Person>>
{
new ItemWrapper<Person>(p1, p => p.FirstName),
new ItemWrapper<Person>(p2, p => p.FirstName)
};
var bs = new BindingSource(list, "Item");
this.listBox1.DataSource = bs;
}
private void button1_Click(object sender, EventArgs e)
{
var person = this.listBox1.SelectedValue as Person;
}
}
}

Adding to a bound ListView

I'm trying to add a person to a list of campers(people). I have bound the ListView(GridView) to the database and it displays all the names, ages, and grades. But now I'm trying to add a new person and add(display) him to the ListView along with everyone else. Any help is greatly appreciated.
Here's what I have: ObservableCollection
class BindingCamper
{ // This class assist in binding campers from listview to the textboxes on the camperspage
public ObservableCollection<Camper> Campers { get; private set; }
public BindingCamper()
{
Campers = new ObservableCollection<Camper>();
}
}
Here is where I add the list of names to the listview:
MainWindow _parentForm;
public ObservableCollection<Camper> Campers { get; private set; }
public CampersPage(MainWindow parent)
{
_parentForm = parent;
InitializeComponent();
var bindMe = new BindingCamper();
for (int i = 0; i < _parentForm.allCampers.Count; i++)
bindMe.Campers.Add(new Camper { Name = "" + _parentForm.allCampers[i].getName(), Ages = _parentForm.allCampers[i].getAge(), SchoolGrade = _parentForm.allCampers[i].getGrade() });
DataContext = bindMe;
Here is where I add a new camper(person) and I'm trying to add him/her to the listview:
String nameMe;
nameMe = txtNewFirstName.Text ;
int age;
int grade;
if (nameMe != "" && IsNumber(txtNewGrade.Text) && IsNumber(txtNewAge.Text))
{
age = Convert.ToInt16(txtNewAge.Text);
grade = Convert.ToInt16(txtNewGrade.Text);
// Create New Camper
Camper person = new Camper(age, grade, nameMe);
_parentForm.allCampers.Add(person);
//_parentForm.camperPage.listViewCampers.Items.Refresh();
var bind = new BindingCamper();
// bind.Campers.Add(new Camper { Name = person.getName(), Ages = person.getAge(), SchoolGrade = person.getGrade() });
// _parentForm.camperPage.Campers.Add(new Camper { Name = person.getName(), Ages = person.getAge(), SchoolGrade = person.getGrade() });
Close();
You shouldn't refresh the database to add camper:
Camper person = new Camper(age, grade, nameMe);
if TryAddToDatabase(person)
{
bindMe.Campers.Add(person);
}
If ListView is bounded to bindMe.Campers it would refresh automatically and show new item. I also would recommend you completely read the WPFTutorial so you won't do any extra work in the future.

Trouble assigning a value to this variable

Using this code I intend to populate the variables.
http://www.dreamincode.net/forums/xml.php?showuser=146038
//Load comments.
var commentsXML = xml.Element("ipb").Element("profile").Element("comments");
this.Comments = (from comment in commentsXML.Descendants("comment")
select new Comment()
{
ID = comment.Element("id").Value,
Text = comment.Element("text").Value,
Date = comment.Element("date").Value,
UserWhoPosted = comment.Element("user").Value
}).ToList();
The problem is UserWhoPosted is an object of the User.cs class POCO I made:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace SharpDIC.Entities
{
public class Friend
{
public string ID { get; set; }
public string Name { get; set; }
public string Url { get; set; }
public string Photo { get; set; }
}
}
How would I populate that object?
I haven't tested this but can't you instantiate the Friend object when your populating the Comment object?
var commentsXML = xml.Element("ipb").Element("profile").Element("comments");
this.Comments =
(
from comment in commentsXML.Descendants("comment")
select new Comment()
{
ID = comment.Element("id").Value,
Text = comment.Element("text").Value,
Date = comment.Element("date").Value,
UserWhoPosted = new Friend()
{
ID = comment.Element("user").Descendants("id"),
Name = comment.Element("user").Descendants("name"),
Url = comment.Element("user").Descendants("url"),
Photo = comment.Element("user").Descendants("photo")
}
}
).ToList();

Categories

Resources