I have created a Form using the VS 2013 designer.
I added a Listview control and added 13 columns via the Properties window.
I then wrote a program in C# and I am able to access all of those columns using the item.SubItems[N] construct. Also, all of those column headers appear in the Listview on the form.
Later I added 2 new columns. The new columns show up in the Listview but throw an invalid index error when I try to access them. Also the SubItems.Count does not reflect the increase of columns in the collection. I also notice that, in the form.Designer.cs file, those new columns are not there.
What can I do to make sure the designer file gets updated and my new columns are added to the collection? (note: this is not related to an 'off-by-one' error or a mistake in indexing number) Thanks in advance.
The reason for this behaviour is that the ListView Columns do not provide you with a regular matrix or grid like a Table or a DataGridView.
Instead they are what you would call a 'Jagged Array', meaning, that not all rows have the same number of columns.
So while you have provided / increased the maximum number of Columns the ListView will display, each Item has only as many SubItems as you have added.
If you add Columns at runtime you can display and access added SubItems, but only where you have actually added them to the Item. Before adding the extra SubItems they don't exist!
This is different from, say a DataGridView where you can freely assign values to each Cell, incuding those that belong to Columns you have created just in the previous line of code..
This is quite similar to the two types of Arrays, Jagged Arrays and Multidimensional Arrays..
But to get back at your title: The actual listView.Columns.Count has increased; what has not is the ListViewItem.SubItems.Count for any of the items.
Related
I have a DataGridView which is bound to a datasource dtDataMain , I have roughly 18 columns - the last two columns 17 and 18 are merged into the dtDataMain (if that matters to some one).
I also have a checkedlistbox which is a representation of selected items in an intermediary table dtSelections.
I have some pseudo code:
dtDataMain.Merge(dtSelections, MissingSchemaAction.AddWithKey)
MyDataGridViewBindingSource.DataSource = dtDataMain
MyDataGridView.DataSource = MyDataGridViewBindingSource
I merged the two tables because I wanted the database to provide the initial csv(Comma Separated Values) data that will populate all of the rows of the datagridview.
I use a checkedlistbox for the possible selections that make up that csv text and depending on my checked or not checked status I set the values in the datgridview cells and also in my bindingsource.
My Problem here is datgridview fires databinding complete - which I do not want it to do. Is there any way that I can accept changes in the COLUMN 17 and 18 of the row - and any changes in the rest of the columns are persisted?
EDIT 1: 9/27/2016
I need to add this here as it is too lengthy for the comment field:
I know the rows have changed since I merged the tables I acceptChanges on the merged table (lefthandside) to clear that.
I ran getChanges to verify all was good - no changes. I add the datasource to the dataGridView - it binds and then fires the bindingcomplete event,
in the bindingcomplete event I find that somehow 2 rows changed row(0)
and Row(1) - there are 30 rows. I do not know why this happens.
So I am trying to decouple the columns that I merged from the DataGridView , I merged them because the SQL server supplies the CSV nicely,and when I save the data the DataGridView comes back with those two added columns with data.
Previously with the two columns unbound when I did a save of the data the datagridview would not show the unbound data - just empty columns. That means every save I have to write all the rows of data in the format and order just like I am getting from the db and I also need to do it on every load and make sure it is synced with the data in each of those rows. Which is the reason for my strange question. I also have controls bound to the datasource.
So I am at the chicken and the egg -
Maybe someone has a better way to do this?
I am trying to do something like this but I don't know how:
I have a table in c# which I increase and decrease during runtime.
Each row contains data, that is being parsed from an object.
I want to get an access to the object that the line is parsing, some a pseudo of it would be:
if (table.row.object == selectedObject)
I can't do it from the row number or something because the rows are being removed and inserted, each time with different objects.
any ideas, please?
EDIT: I am using TableLayoutPanel to work with a table, and this is how I add rows and removes them:
I am adding rows to the table dynamically, I have set of buttons and when I click one it deletes all old rows from the table and adds new rows that are related to this button.
This is the way I add new rows:
tlp.RowCount++;
tlp.RowStyles.Add(new RowStyle(SizeType.AutoSize));
tlp.Controls.Add(new Label() { ... }, cellIDX, rowIDX);
// adding more columns //
and to remove old rows I loop through all rows from bottom to top, removes all related controls of the current cell, then I remove style and row num like so:
tlp.RowStyle.RemoveAt(rowNum);
tlp.RowCount--;
thank you
you can also see a previous question I had related to this subject:
C# change table row color when mouse hover
You can try having a new column in the table (call it original_row_no) and set the row values into that, before you start your insertion/deletion process. That way you can reference the row directly (this is in response to your final line which says "I can't do it from the row number or something because the rows are being removed and inserted, each time with different objects."
A better way is to have a unique id in the table and run your references against that.
It is much more easier to help if you show the code you are trying.
I have built a program in WPF using Visual Basic that allows users to select from a series of items. The list of items may change as I add more items or remove older ones. Originally, I would just add and remove these items in the project code, and rebuild the executable.
Over time, however, the list of items grew and became tedious and impractical to maintain in the code. It occurred to me that it would be easier to store the items in a database table which the program can read, and the list can just be updated by adding and removing items from this database.
I have the database built, and I've added it as a Data Source. I have the data connection set up and everything is ready to go.
What I need to know is how to set the Content property of a label to a cell from a table in the database. I want to do this in the codebehind, not in markup (XAML).
My guess is that I will need to set up a sort of query with a for loop to find the cell I want.
The name of the database is ItemsDB. It contains a table called ItemsTable, and the fields in the table have a unique ID (key) with an AutoNumber data type. The column containing the data I want is named ItemLabel.
So, to sum it all up, I want a label to show the content from a single cell in the database, specified in the codebehind. Either C# or VB is fine.
EDIT
I guess I should've mentioned that the labels themselves actually function as buttons, and I don't really want to display a ListView if I don't have to. I know it's not helpful to not have any code posted, but I don't even know where to start, so there's no code to post.
Took a while longer than I expected, hope this is still useful. So, what you want to do is iterate through a collection and create a new Label for each row.
You will need a container for the Labels, I used WrapPanel, but you can use any Panel you want. I'm replacing your Data Source with a simple DataTable, you'll get the point from this:
foreach (DataRow row in YourItemsTable.Rows)
{
Label newLabel = new Label();
newLabel.Content = row["ItemLabel"].ToString();
// If you need some events, attach the handlers here
yourParentContainerPanel.Children.Add(newLabel);
}
This should be all you need.
I've encountered a performance issue with the C# DataGridView control, which appears to be the result of the unsharing of a large number of rows. The DataGridView is bound to a bindingsource, which is, in turn, bound to a dataview. I've gone through the laundry list of dos and don'ts from the MSDN info (not using rows.count() etc), but I just can't get it to play the game.
Even if I start with a brand new DataGridView control, on a fresh form, and bind it to a bindingsource that has a basic test table as its source, just showing the form seems to cause all of the displayed rows to become unshared. Then, every single row I select causes that row to become unshared. I'm wondering if I'm wasting my time trying to prevent this?
In my actual DataGridView control, it's necessary for me to have at least one column that is not visible, which contains ID values. It's also necessary for me to search all the values in this column for a matching ID. It would appear that, as soon as I attempt to iterate through the values to find a match, this is causing every single row to become unshared. Is there a way to do this that does not cause all the rows to become unshared as I search?
I've got kind of a conceptual question. I am in the process of wrapping some statistics classes I wrote into WPF.
For that I have two DataGrid(-Views, currently in WinForms). In one DataGrid each row represents a column in the other. There I can set-up different variables (as in mathematical/statistical variables) with fields like "Header", "DataType", "ValidationBehaviour", "DisplayType". There I can also set-up how it should be displayed. Some Columns can automatically be set to ComboBoxColumns, some TextBoxColumns, and so on and so forth.
So, now once I've set-up these Columns I can go to the other grid and enter my data. I may, for instance, have generated (in grid 1) one Column called "Annual Gross Salary" with input of numerical values. Another Column called "Education" with "0=NoEducation", "1=College Level", "3=Universitary" etc. These labels are displayed as text in the combobox and my statistics engine behind then selects the respective value (0-3) for calculations (i.e. ordinal, nominal variables).
Sooo. In WinForms I could basically generate all the columns by hand in code and then add my data in the respective cells/rows. Now in WPF I thought that must be easy to realise. However, yesterday I got started with ICustomPropertyDescriptor which (maybe I was too thick) didn't give me the results I was looking for.
Basically, I just need to be able to dynamically generate columns (and rows) with different Layout, Controls (ComboBox, simple Input, DateTimes) based on the data that I have. But I don't really know how to go about it?
So here in summary:
DataGrid 1
Purpose is to display columns that have been specified in DataGrid 2
In rows, the user can add any kind of data in the rows below the columns that is allowed as to the columns specifications
DataGrid 2
Each row in this grid represents a column in DataGrid 1
Contains fields like Name/Header, DataType, Validation Behaviour, Default Value, Data Formatting, etc.
Also contains a function to be able to set-up how it should be displayed. The user can select from, for instance, ComboBoxColumn (and also add the available options), DateTime, normal TextBox, CheckBox etc.
After finishing adding a row it will automatically appear as a new column in DataGrid 1
I'd appreciate any kind of pointer into the right direction. Thanks very, very much in advance! :)
Look up DataTemplates. They do exactly this. The UI is determined by the related type.
Here is an MSDN article...