Windows Forms - Add Items to Combo Box Through Code - winforms

I am trying to dynamically add items to a Combobox in Windows Forms when pushing a button. I have tried several different ways and I am getting some odd behaviour. In one case I could see an item in the combobox but when I ran ComboBox.Items.Count it gave me 0 even though there was an item in the box. I tried using combobox.Refresh() to see if maybe there was some kind of sync issue but that didn't do anything either.
I am using the following code but it doesn't display anything in the box. ComboBox1.Items.Count gives me 10.
List<string> items = new List<string>();
for (int i = 0; i <= 9; i++)
{
items.Add("item " + i.ToString());
}
ComboBox1.DataSource = items;
I am using Visual Studio 2019.

Related

Datagridview rowcount showing 0 even when there is a valid datasource

I have a dynamically created DataGridView that has a valid DataSource with one row bound to it. However, it is returning me 0 when I am doing a rowcount on the DataGridView.
dgResult.DataSource = resultDt; // a datatable containing one row
flowLayoutPanel.Controls.Add(dgResult);
int rows = dgResult.Rows.Count; // returning 0 always!
Can someone please tell me where I may be going wrong here?
I found the issue. I was displaying the grid in a tabbed page that was not selected. Unless the grid is visible, it does not raise the rowadded event (which is weird!) durnig databinding. I selected the tab page before doing the databind, and the rowcount worked.
Use this code instead:
BindingSource bindingSource = new BindingSource();
bindingSource.DataSource = resultDt;
dgResult.DataSource = bindingSource;
flowLayoutPanel.Controls.Add(dgResult);
var c = dgResult.Rows.Count;
The binding source is what's responsible for syncing your data with the control. You want to use it, rather than trying to assign the table directly to the control.

How to hide the Group Tree of a Crystal Report in WPF?

I'm using VS2010 and Crystal reports 13.
Is there any way to collapse/hide the group tree box that appears on the left hand side of my generated report? I saw a couple of proposed solutions but none seem to work for me.
Thanks in advance.
There also is a property on report viewer you can set as follows:
yourViewer.ToggleSidePanel = Constants.SidePanelKind.None;
I think this is a bit safer in case the Crystal Reports team decides to rename that button.
I finally found a solution that works, by manually finding the side panel and then hiding it:
var sidepanel = crystalReportsViewer1.FindName("btnToggleSidePanel") as ToggleButton;
if (sidepanel != null) {
crystalReportsViewer1.ViewChange += (x, y) => sidepanel.IsChecked = false;
}
adding this namespace:
using System.Windows.Controls.Primitives;
The problem was that the WPF ReportViewer is slightly diferent to the Win Forms one, some properties (such as ToolPanelView and ShowGroupTreeButton) have been removed, I tried many different things but the above was the only that did the trick.
You can change it from the designer by changing the 'ToolPanelView' to 'None' and hide the button by changing 'ShowGroupTreeButton' to 'false'. Previous versions had a method to explicitly hide the group tree but I believe it's been deprecated in the version you are using. To change the properties in code behind:
crystalreportviewer.ToolPanelView = TooPanelViewType.None;
crystalreportviewer.ShowGroupTreeButton = false;
there is a property DisplayGroupTree . and you can avoid the free space by using this code
CrystalReportViewer1.DisplayGroupTree = false;
CrystalReportViewer1.HasToggleGroupTreeButton = false;
Use the command to hide the panel.
CrystalReportViewer1.ToolPanelView = CrystalDecisions.Windows.Forms.ToolPanelViewType.None
I ran into the same issue as Crystal Report changes the convention. In older version of the Crystal report would hide the button and not show the panel on the left hand side.
CrystalReportViewer1.ShowGroupTreeButton = False
<Viewer:CrystalReportsViewer ToggleSidePanel="None"/>
Use the following properties in your webpage:
- ToolPanelView="None"
- HasToggleGroupTreeButton="false"
<CR:CrystalReportViewer ID="CRViewer" runat="server" HasCrystalLogo="False" ToolPanelView="None" HasToggleGroupTreeButton="false" BestFitPage="True" AutoDataBind="true" />
Group tree panel and its toggle will be hidden. It works well in my environment - ASP.Net 4.0, Crystal Report version 13.0.13
For asp.net
CrystalReportViewer1.ToolPanelView=CrystalDecisions.Web.ToolPanelViewType.None;

WPF Datagrid read a cell value

I am trying to find out how to read the value of my WPF datagrid cells.
something along the lines of
String myString = myDataGrid.Cells[1][2].ToString();
the datagrid has been created in XAML and i have populated the datagrid with row data by using
reportGrid.Items.Add(new cbResultRow() { ... });
now I want to go back and read cell values in my datagrid.
I have seen some examples of reading data from a selected row or cell, by I don't have any selection (the user doesnt interact with the datagrid).
i have also seen code like
foreach(DataGridRow myrow in myDataGrid.Rows)
however the compiler says Rows is not a member of datagrid.
I have searched for several hours to try to find out how to do what I would have thought was a very simple thing!
please help,
Thanks,
will.
The WPF datagrid was built to bind to something like a DataTable. The majority of the time, you will modify the DataTable, and the Rows/Columns within the DataTable that is bound to the DataGrid.
The DataGrid itself is the Visual Element for the DataTable. Here is a pretty good tutorial on how to do this. To modify data within a loop would look something like this.
foreach(DataRow row in myTable.Rows)
{
row["ColumnTitle"] = 1;
}
This would simply make all the values in Column "ColumnTitle" equal to 1. To access a single cell it would look something like this.
myTable.Rows[0][0] = 1;
This would set the first cell in your DataTable to 1.
This might help someone else.
foreach (DataRowView row in dgLista.SelectedItems)
{
string text = row.Row.ItemArray[index].ToString();
}
Good luck!
Here's a summary of the solution.
Winform
Type: System.windows.Forms.DataGridView
// C#
foreach (DataGridViewRow row in dataGridView1.Rows)
{
//"Column1" = column name in DataGridView
string text = row.Cells["Column1"].value.ToString();
}
WPF equivalent
Type: DataGrid
// C#
foreach (DataRowView row in dataGrid.Items)
{
string text = row.Row.ItemArray[index].ToString();
}

WPF UI automation - Doesn't support rows? (or how can I select and deselect an entire row)

Note: This is no longer an issue, the .NET 4 built-in DataGrid solves this problem
I have a WPF app which is using a DataGrid; I'm using the WPF ui automation API to write some automated tests for it.
The DataGrid is the WPFToolkit one, I'm using the .NET 3.5SP1 with VS2008, and, the datagrid has multi-select enabled.
Where I'm at is that from my test can find the datagrid, and I can find individual cells within the grid using the GridPattern.GetItem method, and select them by setting SelectionItemPattern.Select. Method
The code looks somewhat like this:
AutomationElement mainGrid = // find the grid in the window
var columnCount = (int)mainGrid.GetCurrentPropertyValue(GridPattern.ColumnCountProperty);
var mainGridPattern = (GridPattern)mainGrid.GetCurrentPattern(GridPattern.Pattern);
var rowToSelect = 2;
// select just the first cell
var item = mainGridPattern.GetItem(rowToSelect, 0);
var itemPattern = (SelectionItemPattern)item.GetCurrentPattern(SelectionItemPattern.Pattern);
itemPattern.Select();
This seems to work, but it only selects the first individual cell, not the entire table row (which has 10 columns) but I can't work out how to deselect an item. The only thing I can find that looks like it might work is to call SelectionItemPattern.AddToSelection() on the itemPattern, or the corresponding RemoveFromSelection but when I do either of these, the following exception is raised:
=> Cannot change cell selection when the SelectionUnit is FullRow.
at MS.Internal.Automation.ElementUtil.Invoke(AutomationPeer peer, DispatcherOperationCallback work, Object arg)
at MS.Internal.Automation.SelectionItemProviderWrapper.AddToSelection()
The underlying root issue appears to be that (as far as I can see) the WPF UI automation API has no concept of a grid Row, only cells. This seems somewhat problematic - Is this right?
Sidenote: I had previously been using the White UI automation framework - this doesn't use UI automation to select grid rows, instead it moves the mouse to the row location and clicks it - which caused random failures of our tests - is this why they're using the mouse to do the selection though?
If you use UISpy to look at the structure of your DataGrid as UIAutomation sees it, you'll notice that the DataGrid element contains a RowsPresenter, and the RowsPresenter contains a number of DataGridRows, each containing a DataGridCell.
I suspect what's happening is that var item = mainGridPattern.GetItem(rowToSelect, 0); is returning the item representing the cell, whereas you want the item representing the whole row.
You can get this by calling item.CachedParent - then Select() that.
This now seems to have been fixed in .NET 4. Rows are now exposed as real objects that support the SelectionItemPattern, so you can now select/deselect a row. Hooray

Showing tab with lots of ComboBox controls is slow with WinForms

I have set up a dialog with several tabs. One of these contains twenty combo boxes, each with over 100 items, added like this :
foreach (var x in collection)
{
string text = FormatItem (x);
combo.Items.Add (text);
}
so there is nothing fancy at all with the items. They are plain strings and the combo boxes get filled when the dialog is created. This happens almost instantenously.
However, when the user clicks on the tab containing all these combo boxes for the very first time, the GUI freezes for several seconds (and I am running on a really beefy machine).
I loaded the symbols for System.Windows.Forms and tried to break into the debugger while the program is stuck. What I have discovered is a stack trace with the following calls:
System.Windows.Forms.Control.CreateHandle()
System.Windows.Forms.ComboBox.CreateHandle()
System.Windows.Forms.Control.CreateControl(...) x 3
System.Windows.Forms.Control.SetVisibleCore(true)
System.Windows.Forms.TabPage.Visible.set(true)
which results in plenty of native transitions, WndProc calls, etc. I suppose this happens for every single item in every combo box. Phew.
Obviously, I cannot optimize WinForms. But maybe I can take some actions in order to avoid all this hell getting lose on my poor GUI? Any ideas?
Nota bene:
I've no event handlers attached on the combo boxes which could be called when the controls get created for real.
If I try to access the Handle property of the combo boxes just after having created and populated the form, I pay the penalty at that moment, rather than when the tab becomes visible for the first time. But having to wait several seconds when creating the form is not acceptable either. I really want to get rid of the long set up time.
The idea of applying BeginUpdate and EndUpdate does not apply here: these should be used to prevent the control from repainting when its items list gets filled. But in my case, the problem happens well after the control has been set up.
What you're saying is not consistent with anything I ever observed... :s
But have you tried using .BeginUpdate / .EndUpdate ?
Another thing you coud try is not populate the boxes until needed. Delay it until the box gets focus for example... (If you trap the dropdown event some user might be annoyed that the up/down arrow keys won't work.)
Everything I tried failed up to now to speed up the first display of the tab containing all the combo boxes. Data binding didn't help either.
I finally decided to fix the issue by doing a trick, similar to what danbystrom proposed, i.e. only populate the Items collection when the focus first arrives on a combo. This still produces a noticeable delay, the time all items get created (within a BeginUpdate and EndUpdate pair of method calls), but it is tolerable (approx. 200ms versus several seconds in my original scenario).
Instead of iteration your collections, would't setting the ComboBox.DataSource be a viable, and much faster alternative?
comboBox1.DataSource = myCollection1;
comboBox2.DataSource = myCollection2;
comboBox3.DataSource = myCollection3;
// and so on...
Here is a more complete example:
public class Entity
{
public string Title { get; set; }
public override string ToString()
{
return Title;
}
}
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
List<Entity> list = new List<Entity>
{
new Entity {Title = "Item1"},
new Entity {Title = "Item2"},
new Entity {Title = "Item3"}
};
comboBox1.DataSource = list;
}
I've just been coming up against this same problem, where populating a combobox with around 4000k items was unacceptably slow.
I was filling the combo in the OnLoad event handler of a form, however, when I shifted this code to constructor, after InitializeComponent(), there was no delay at all.
I guess doing this operation in the OnLoad was causing a redraw of the combo to fire, hence the delay? Anyway, just thought I'd add this in case it's of use to anyone else in this situation.
Lots of controls on a form can be a problem. I once had a form that dynamically created between 50-100 textbox controls. It was slow to load.
We solved that problem by using a datagrid instead. It's a control that is optimized for lots of data. I don't know what your exact requirements are, but it might work.

Resources