Programmatically create hierarchy in telerik radgridview winforms - winforms

I am wanting to create a hierarchy in a radgridview that is not data bound. So far, I haven't had any luck on figuring out how to do it.
I figured you could do something like:
Grid.Parent.Rows.Add(new object[] {});
and
Grid.Parent.Child.Rows.Add(new object[] {});
but I've had no such luck. The grid can't be data bound. I have a list of data I will loop through and create the hierarchy.
I need to know how to create parent nodes and child nodes for that parent. I saw the templates and have messed with them, but I can only make it work with a data source.
Can somebody point me in the right direction?

Take a look at the second section in this article Binding to Hierarchical Data Programmatically

// Code for data bind radgridview
void DataBInd()
{
var Source = Results.GetData(FromDate, Todate, drpLedger.Value);
radGridLedgerAccount.DataSource = null;
radGridLedgerAccount.DataSource = Source;
FormatGrid();
BindChildView();
BindBarCharView(Source);
}
//Code for bind childview i.e hierarchy
void BindChildView()
{
if (drpLedger.Value != null)
{
GridViewTemplate childview = new GridViewTemplate();
childview.Caption = "Details";
childview.DataSource = null;
childview.DataSource = Source;
this.radGridLedgerAccount.MasterTemplate.Templates.Add(childview);
FormatChildView(childview);
GridViewRelation relation = new GridViewRelation(this.radGridLedgerAccount.MasterTemplate);
relation.ChildTemplate = childview;
relation.ParentColumnNames.Add("months");
relation.ChildColumnNames.Add("NameOfMonth");
this.radGridLedgerAccount.Relations.Add(relation);
}
}

Related

How to apply a style to an hiearchial comboBox in XPages

I have a comboBox in XPages which is showing an hierarcal list of categories and values populates as a vector in SSJS.
I now want to apply a styleheet (bold) to the categories (i.e on only the categories of the generated option tags)
please note that I do not need a lesson in how stylesheets work. I need to know how to add a class or style to the categories in the outputted options tags
how can I do that?
thanks
Thomas
UPDATED MY QUESTION WITH A WORKING CLASS
Mimics a categorized view with 3 columns in a comboBox, category, label and value
public class Utils {
public static List<SelectItem> getGroupedComboboxOptions() {
try {
Database db = ExtLibUtil.getCurrentDatabase();
View vv = db.getView("ProdukterByCat");
Vector v = vv.getColumnValues(0);
List<SelectItem> groupedOptions = new ArrayList<SelectItem>();
SelectItemGroup group;
for (int i = 0; i < v.size(); i++) {
List<SelectItem> options = new ArrayList<SelectItem>();
group = new SelectItemGroup(v.get(i).toString());
ViewEntryCollection nvec = vv.getAllEntriesByKey(v.get(i), true);
ViewEntry entry = nvec.getFirstEntry();
while (entry != null) {
SelectItem option = new SelectItem(entry.getColumnValues().get(2).toString(),entry.getColumnValues().get(1).toString());
options.add(option);
entry = nvec.getNextEntry(entry);
}
group.setSelectItems(options.toArray(new SelectItem[options.size()]));
groupedOptions.add(group);
}
return groupedOptions;
} catch (NotesException e) {
e.printStackTrace();
}
return null;
}
}
A combobox in XPages is rendered using a HTML select tag. If you organise the options in optgroup's (see also Populating selectItems of the combobox (label, value) using a managed bean) you get some default styling out of the box. Example here.
You can even apply additional styling on them with standard CSS by targeting the optgroup. But support for that is limited: it doesn't work on an iPad for example.
If you want more control on how your dropdowns look, I'd suggest to use a plugin like Select2.

How to find matching words in Text Box and Datagrid when VirtualizingStackPanel.IsVirtualizing="true"?

I have Datagrid and Text Box in my Form. Datagrid is showing me existing items in my stock. I use Text Box to search and set focus to that row which is matching with my Text Box. Now it is working fine when VirtualizingStackPanel.IsVirtualizing="false" but it is very slow and getting a lot RAM resource.
Here is my code for this.
public IEnumerable<Microsoft.Windows.Controls.DataGridRow> GetDataGridRows(Microsoft.Windows.Controls.DataGrid grid)
{
var itemsSource = grid.ItemsSource as IEnumerable;
if (null == itemsSource) yield return null;
foreach (var item in itemsSource)
{
var row = grid.ItemContainerGenerator.ContainerFromItem(item) as Microsoft.Windows.Controls.DataGridRow;
if (null != row) yield return row;
}
}
private void SearchBoxDataGrid_TextChanged(object sender, TextChangedEventArgs e)
{
var row = GetDataGridRows(AssortDataGrid);
/// go through each row in the datagrid
foreach (Microsoft.Windows.Controls.DataGridRow r in row)
{
DataRowView rv = (DataRowView)r.Item;
// Get the state of what's in column 1 of the current row (in my case a string)
string t = rv.Row["Ассортимент"].ToString().ToLower();
if (t.StartsWith(SearchBoxDataGrid.Text.ToLower()))
{
AssortDataGrid.SelectedIndex = r.GetIndex();
AssortDataGrid.ScrollIntoView(AssortDataGrid.SelectedItem);
break;
}
}
}
What I want is to make it VirtualizingStackPanel.IsVirtualizing="true" but in this case my method is not working. I know why it is not working, my code will work only for showing part of Datagrid.
What do you recommend? How to fix this issue? Any idea will be appreciated. If you give any working code it will be fantastic. I hope I could explain my problem.
Virtualization means that WPF will reuse the UI components, and simply replace the DataContext behind the components.
For example, if your Grid has 1000 items and only 10 are visible, it will only render around 14 UI items (extra items for scroll buffer), and scrolling simply replaces the DataContext behind these UI items instead of creating new UI elements for every item. If you didn't use Virtualization, it would create all 1000 UI items.
For your Search to work with Virutalization, you need to loop through the DataContext (DataGrid.Items) instead of through the UI components. This can either be done in the code-behind, or if you're using MVVM you can handle the SeachCommand in your ViewModel.
I did a little coding and make it work. If anyone needs it in future please, use it.
Firstly I am creating List of Products
List<string> ProductList;
Then on Load Method I list all my products to my Product List.
SqlCommand commProc2 = new SqlCommand("SELECT dbo.fGetProductNameFromId(ProductID) as ProductName from Assortment order by ProductName desc", MainWindow.conn);
string str2;
SqlDataReader dr2 = commProc2.ExecuteReader();
ProductList = new List<string>();
try
{
if (dr2.HasRows)
{
while (dr2.Read())
{
str2 = (string)dr2["ProductName"];
ProductList.Insert(0, str2.ToLower ());
}
}
}
catch (Exception ex)
{
MessageBox.Show("An error occured while trying to fetch data\n" + ex.Message);
}
dr2.Close();
dr2.Dispose();
After that I did some changes in SearchBoxDataGrid_TextChanged
private void SearchBoxDataGrid_TextChanged(object sender, TextChangedEventArgs e)
{
int pos = 0;
string typedString = SearchBoxDataGrid.Text.ToLower();
foreach (string item in ProductList)
{
if (!string.IsNullOrEmpty(SearchBoxDataGrid.Text))
{
if (item.StartsWith(typedString))
{
pos = ProductList.IndexOf(item);
AssortDataGrid.SelectedIndex = pos;
AssortDataGrid.ScrollIntoView(AssortDataGrid.SelectedItem);
break;
}
}
}
}
Now it works when VirtualizingStackPanel.IsVirtualizing="true".
That is all.

Set DisplayMemberPath and SelectedValue in WPF with Code

I have a DataTable that is coming from a Web Service, which I need to bind to a ComboBox. I have not grokked doing binding in XAML yet so this question is about binding in code instead. So far I have tried
cboManager.DataContext = Slurp.DistrictManagerSelect().DefaultView;
cboManager.DisplayMemberPath = "Name";
cboManager.SelectedValuePath = "NameListId";
cboManager.SetBinding(ComboBox.ItemsSourceProperty, new Binding());
And I have tried
DataTable tbl = new DataTable();
tbl = Slurp.DistrictManagerSelect();
cboManager.ItemsSource = ((IListSource)tbl).GetList();
cboManager.DisplayMemberPath = "[Name]";
cboManager.SelectedValuePath = "[NameListId]";
DataContext = this;
In both cases I get the list of managers to show but when I select from the ComboBox I get [Name] and [NameListId] and not the values I am expecting. What am I doing wrong (other than not using XAML's DataBinding)?
Edit added after answers to my original post came in.
So (based on Rachel's response) try number three looks like this:
using (DataTable tbl = Slurp.DistrictManagerSelect())
{
List<ManagerList> list = new List<ManagerList>();
foreach (var row in tbl.Rows)
{
list.Add(new ManagerList
{
NameListId = (int)row[0],
Name = row[1].ToString()
});
}
}
Assuming I am doing what she meant the correct way I am no getting this error Cannot apply indexing with [] to an expression of type 'object'
Have you tried to just bind to the DataTable directly? Do you have columns Name and NameListId? Leave off the DataContext (you already assigned the ItemsSource).
DataTable tbl = Slurp.DistrictManagerSelect();
cboManager.ItemsSource = tbl;
cboManager.DisplayMemberPath = "Name";
cboManager.SelectedValuePath = "NameListId";
When you cast to the IListSource I suspect it is combining all the columns. If you want to bind to a list then you need to create items that have properties Name and NameListID.
I think that's because your ItemsSource is a DataTable, so each ComboBoxItem contains a DataContext of a DataRow, and the DataRow class doesn't have properties called Name or NameListId
Basically, you're trying to tell the ComboBox to display DataRow.Name, and set the value to DataRow.NameListId, both of which are not valid properties.
I usually prefer to parse data into objects, and bind the ItemsSource a List<MyObject> or ObservableCollection<MyObject>
foreach(DataRow row in tbl.Rows)
list.Add(new MyObject { Name = row[0].ToString(), NameListId = (int)row[1] });
cboManager.ItemsSource = list;

How can I get items from current page in PagedCollectionView?

I've got my objects in PagedCollectionView bound to DataGrid and DataPager.
var pcView = new PagedCollectionView(ObservableCollection<Message>(messages));
How can I easily get items from current page in PagedCollectionView from my ViewModel? I wish there were something like this:
var messagesFromCurrentPage = pcView.CurrentPageItems; // error: no such a property
There are properties like SourceCollection, PageIndex and Count but I don't find them useful in this case. What am I missing here?
If you want to get select items you can just use Linq to do it.
var items = pcView.Where(i => i.SomeCondition == true);
Make sure you add a using statement for System.Linq.
Edit: Whenever I have a question as to what is really going on I just look at the code using Reflector (or ILSpy). In this case here is the relevant code inside GetEnumerator() which is how the Select or Where gets the items in the list:
List<object> list = new List<object>();
if (this.PageIndex < 0)
{
return list.GetEnumerator();
}
for (int i = this._pageSize * this.PageIndex; i < Math.Min(this._pageSize * (this.PageIndex + 1), this.InternalList.Count); i++)
{
list.Add(this.InternalList[i]);
}
return new NewItemAwareEnumerator(this, list.GetEnumerator(), this.CurrentAddItem);
So you can see how it is returning only the items in the current page from this code.

How to maintain ComboBox.SelectedItem reference when DataSource is resorted?

This really seems like a bug to me, but perhaps some databinding gurus can enlighten me? (My WinForms databinding knowledge is quite limited.)
I have a ComboBox bound to a sorted DataView. When the properties of the items in the DataView change such that items are resorted, the SelectedItem in my ComboBox does not keep in-sync. It seems to point to someplace completely random. Is this a bug, or am I missing something in my databinding?
Here is a sample application that reproduces the problem. All you need is a Button and a ComboBox:
public partial class Form1 : Form
{
private DataTable myData;
public Form1()
{
this.InitializeComponent();
this.myData = new DataTable();
this.myData.Columns.Add("ID", typeof(int));
this.myData.Columns.Add("Name", typeof(string));
this.myData.Columns.Add("LastModified", typeof(DateTime));
this.myData.Rows.Add(1, "first", DateTime.Now.AddMinutes(-2));
this.myData.Rows.Add(2, "second", DateTime.Now.AddMinutes(-1));
this.myData.Rows.Add(3, "third", DateTime.Now);
this.myData.DefaultView.Sort = "LastModified DESC";
this.comboBox1.DataSource = this.myData.DefaultView;
this.comboBox1.ValueMember = "ID";
this.comboBox1.DisplayMember = "Name";
}
private void saveStuffButton_Click(object sender, EventArgs e)
{
DataRowView preUpdateSelectedItem = (DataRowView)this.comboBox1.SelectedItem;
// OUTPUT: SelectedIndex = 0; SelectedItem.Name = third
Debug.WriteLine(string.Format("SelectedIndex = {0:N0}; SelectedItem.Name = {1}", this.comboBox1.SelectedIndex, preUpdateSelectedItem["Name"]));
this.myData.Rows[0]["LastModified"] = DateTime.Now;
DataRowView postUpdateSelectedItem = (DataRowView)this.comboBox1.SelectedItem;
// OUTPUT: SelectedIndex = 2; SelectedItem.Name = second
Debug.WriteLine(string.Format("SelectedIndex = {0:N0}; SelectedItem.Name = {1}", this.comboBox1.SelectedIndex, postUpdateSelectedItem["Name"]));
// FAIL!
Debug.Assert(object.ReferenceEquals(preUpdateSelectedItem, postUpdateSelectedItem));
}
}
To clarify:
I understand how I would fix the simple application above--I only included that to demonstrate the problem. My concern is how to fix it when the updates to the underlying data rows could be happening anywhere (on another form, perhaps.)
I would really like to still receive updates, inserts, deletes, etc. to my data source. I have tried just binding to an array of DataRows severed from the DataTable, but this causes additional headaches.
Just add a BindingContext to the ComboBox :
this.comboBox1.DataSource = this.myData.DefaultView;
this.comboBox1.BindingContext = new BindingContext();
this.comboBox1.ValueMember = "ID";
this.comboBox1.DisplayMember = "Name";
By the way, try not keeping auto-generated names for your widgets (comboBox1, ...), it is dirty. :-P
The only promising solution I see at this time is to bind the combo box to a detached data source and then update it every time the "real" DataView changes. Here is what I have so far. Seems to be working, but (1) it's a total hack, and (2) it will not scale well at all.
In form declaration:
private DataView shadowView;
In form initialization:
this.comboBox1.DisplayMember = "Value";
this.comboBox1.ValueMember = "Key";
this.shadowView = new DataView(GlobalData.TheGlobalTable, null, "LastModified DESC", DataViewRowState.CurrentRows);
this.shadowView.ListChanged += new ListChangedEventHandler(shadowView_ListChanged);
this.ResetComboBoxDataSource(null);
And then the hack:
private void shadowView_ListChanged(object sender, ListChangedEventArgs e)
{
this.ResetComboBoxDataSource((int)this.comboBox1.SelectedValue);
}
private void ResetComboBoxDataSource(int? selectedId)
{
int selectedIndex = 0;
var detached = new KeyValuePair<int, string>[this.shadowView.Count];
for (int i = 0; i < this.shadowView.Count; i++)
{
int id = (int)this.shadowView[i]["ID"];
detached[i] = new KeyValuePair<int, string>(id, (string)this.shadowView[i]["Name"]);
if (id == selectedId)
{
selectedIndex = i;
}
}
this.comboBox1.DataSource = detached;
this.comboBox1.SelectedIndex = selectedIndex;
}
Must detach event handler in Dispose:
this.shadowView.ListChanged -= new ListChangedEventHandler(shadowView_ListChanged);
Your example sorts the data on the column it updates. When the update occurs, the order of the rows changes. The combobox is using the index to keep track of it's selected items, so when the items are sorted, the index is pointing to a different row. You'll need to capture the value of comboxBox1.SelectedItem before updating the row, and set it back once the update is complete:
DataRowView selected = (DataRowView)this.comboBox1.SelectedItem;
this.myData.Rows[0]["LastModified"] = DateTime.Now;
this.comboBox1.SelectedItem = selected;
From an architecture perspective, the SelectedItem must be cleared when rebinding the DataSource because the DataBinder don't know if your SelectedItem will persist or not.
From a functional perspective, the DataBinder may not be able to ensure that your SelectedItem from you old DataSource is the same in your new DataSource (it can be a different DataSource with the same SelectedItem ID).
Its more an application feature or a custom control feature than a generic databinding process.
IMHO, you have theses choices if you want to keep the SelectedItem on rebind :
Create a reusable custom control / custom DataBinder with a persistance option which try to set the SelectedItem with all your data validation (using a DataSource / item identification to ensure the item validity)
Persist it specifically on your Form using the Form/Application context (like ViewState for ASP.NET).
Some controls on the .NET market are helping you by rebinding (including selections) the control from their own persisted DataSource if the DataSource is not changed and DataBind not recalled. That's the best pratice.

Resources