How do I find a dynamically added TableLayoutPanel control in tab control of a winform - findcontrol

Need help finding a TableLayoutPanel added at run time to a tab control of a winform. Please find example code below. Any help would be appreciated.
private void GenerateControls()
{
TableLayoutPanel tp = new TableLayoutPanel();
tp.Name = "tpName";
tab1.Controls.Add(tp);
}
private void findTablePanelControl()
{
TableLayoutPanel tp = (TableLayoutPanel)this.Controls.Find("tpName", true)[0];
string name = tp.Name;
}
I receive the follow error message: Index was outside the bounds of the array.
I also tried the following code, but get this error (Object reference not set to an instance of an object) on the "string name =" line:
TableLayoutPanel tpParseSchema = (TableLayoutPanel)this.Controls.Find("tpParseSchema", true).FirstOrDefault();

I found the problem. The provided example code actually will work. The problem with my real code was that I mistakenly keyed in the wrong name value for the TableLayoutPanel. I ended up figuring this out by recursively stepping through all of the child controls of the tab control. Here is example code of how I did that.
foreach (Control ctrl in tab1.Controls)
{
string ctrlname = ctrl.Name;
}

Related

Finding a ListView row in coded UI causes 'No row was specified as search container for the control.'

I have a ListView inside of a Popup control (seems to be significant that it's in a Popup). The coded test is pretty basic, click a ToggleButton to open the popup, and then select an item in the ListView.
Except it seems that it can't find the item in the ListView.
System.ArgumentException: No row was specified as search container for
the control. To search for a cell control using 'ColumnIndex', you
must specify row as a container element or add 'RowIndex' to the
search property of the cell. Parameter name: SearchProperties Result
StackTrace: at
Microsoft.VisualStudio.TestTools.UITesting.ALUtility.ThrowDataGridRelatedException(String
errorString, String propertyName) at
Microsoft.VisualStudio.TestTools.UITesting.WpfControls.WpfCell.GetUITestControlsForSearch()
at
Microsoft.VisualStudio.TestTools.UITesting.UITestControl.get_QueryId()
at
Microsoft.VisualStudio.TestTools.UITesting.UITestControlSearchArgument.get_SingleQueryString()
at
Microsoft.VisualStudio.TestTools.UITesting.SearchHelper.GetUITestControlRecursive(Boolean
useCache, Boolean alwaysSearch, ISearchArgument searchArg, IList`1
windowTitles, Int32& timeLeft)
The generated code is failing at this point
uIItemCell.Checked = this.MyListBoxCellParams.UIItemCellChecked;
where uIItemCell comes from this property
public WpfCell UIItemCell
{
get
{
if ((this.mUIItemCell == null))
{
this.mUIItemCell = new WpfCell(this);
#region Search Criteria
this.mUIItemCell.SearchProperties[WpfCell.PropertyNames.ColumnHeader] = null;
this.mUIItemCell.SearchProperties[WpfCell.PropertyNames.ColumnIndex] = "1";
this.mUIItemCell.WindowTitles.Add("CodedUITestWindow");
#endregion
}
return this.mUIItemCell;
}
}
So I guess this is where the criteria should be specified, but what how? And should row be hard coded somehow? Why didn't the test editor set the row?
If it helps, this is the .ctor where UIItemCell (above) is specified, seems like more search params
public UIMyCellDataItem(UITestControl searchLimitContainer) :
base(searchLimitContainer)
{
#region Search Criteria
this.SearchProperties[UITestControl.PropertyNames.ControlType] = "DataItem";
this.SearchProperties["HelpText"] = "MyCell's helptext property, this is correctly specified, but the HelpText is used only as a tooltip";
this.WindowTitles.Add("CodedUITestWindow");
#endregion
}
Thanks
I've normally seen ListView treated as a List, and the items as ListItem. You might want to use inspect.exe or the UI Test Builder to look at the properties. You can try to manually code the control. Sorry for posting speculation as an answer. Too long to be a comment.
WpfWindow PopUpWindow = new WpfWindow();
PopUpWindow.SearchProperties[WpfWindow.PropertyNames.Name] = "Pop Up Window Name";
WpfList List = new WpfList(PopUpWindow);
List.SearchProperties[WpfList.PropertyNames.Name] = "List Name";
WpfListItem ItemToSelect = new WpfListItem(List);
ItemToSelect.SearchProperties[WpfListItem.PropertyNames.Name] = "List Item Name";
// Click button to activate pop up window
ItemToSelect.Select();
// Creating the controls.
WpfWindow mainWindow = new WpfWindow();
// Add search properties.
WpfTable table = new WpfTable(mainWindow);
// Add search properties
WpfRow row = new WpfRow(table);
// Add search properties.
WpfCell cell = new WpfCell(row);
// Add search properties.
// Just adding the table and row as containers.
row.Container = table;
cell.Container = row;
}

Is there a way to force a DataGridView to fire its CellFormatting event for all cells?

We use the CellFormatting event to colour code cells in various grids all over our application.
We've got some generic code which handles export to Excel (and print) but it does it in Black & White. Now we want to change this and pick up the colour from the grids.
This question & answer has helped (and it works) ... except there's a problem with larger grids that extend beyond a single screen. The portions of the grid which haven't yet been displayed on screen are (logically) never getting their CellFormatting code fired, and so their underlying colour never gets set. As a result, in Excel, the colour coding fizzles out half way down the page.
Seems there are three solutions:
1) Tell the user he has to scroll to all parts of the grid before doing an Export to Excel. Ha! Not a serious solution
2) Programmatically scroll to all parts of the grid before doing an Export to Excel. Only slighly less horrible than (1)
3) In our Export to Excel code, fire something at the top which tells the DataGridView to paint/format its entire area e.g.
MyDataGridView.FormatAllCells()
Is there something that does something like this???
Oh, and there is a fourth option but this will involve touching a massive amount of existing code:
4) Stop using CellFormatting event, format the cells at load time. Problem with this is we'd have to retool every grid in our application since CellFormatting is the way we've done it since year dot.
As noted in the other answers, accessing the DataGridViewCell.FormattedValue is indeed an easy way to force the CellFormatting event to be (re-)called for a specific cell. In my case, however, this property was also leading to undesirable side-effects involving the auto-resizing of the columns. While searching a long time for a workable solution, I finally encountered the following magic methods that work perfectly: DataGridView.Invalidate(), DataGridView.InvalidateColumn(), DataGridView.InvalidateRow(), and DataGridView.InvalidateCell().
These 4 methods force the CellFormatting event to be re-called only for the specified scope (cell, column, row, or whole table), and also without causing any nasty auto-resizing artifacts.
I have a possible solution - In your export function access the Cell.FormattedValue property of each cell. According to Microsoft, this forces the CellFormatting event to fire.
Assuming, as #DavidHall suggests, there is no magic .FormatAllCells our only option is to stop using CellFormatting.
However, new problem here is that applying cell style formatting during load doesn’t seem to have any effect. Lots of posts out there if you Google it. Also they point out that if you put the same code under a button on the form and click it after loading (instead of in the load, the code will work ... so the grid has to be visible before styling can apply). Most advice on the topic suggests you use ... drumroll ... CellFormatting. Aargh!
Eventually found a post which suggests using the DataBindingComplete event of the grid. And this works.
Admittedly, this solution is a variant of my unwanted option "4".
I had the same problem and I've ended up with something quite similar to your solution #4.
like you, I've used the DataBindingComplete event. but, Since I've used Extension method, the changes in the existing code are bearable:
internal static class DataGridViewExtention
{
public static void SetGridBackColorMyStyle(this DataGridView p_dgvToManipulate)
{
p_dgvToManipulate.RowPrePaint += p_dgvToManipulate_RowPrePaint;
p_dgvToManipulate.DataBindingComplete += p_dgvToManipulate_DataBindingComplete;
}
// for the first part - Coloring the whole grid I used the `DataGridView.DataBindingComplete` event:
private static void p_dgvToManipulate_DataBindingComplete(object sender, DataGridViewBindingCompleteEventArgs e)
{
foreach (DataGridViewRow objCurrRow in ((DataGridView)sender).Rows)
{
// Get the domain object from row
DomainObject objSelectedItem = (DomainObject)objCurrRow.DataBoundItem;
// if item is valid ....
if objSelectedItem != null)
{
// Change backcolor of row using my method
objCurrRow.DefaultCellStyle.BackColor = GetColorForMyRow(objSelectedItem);
}
}
}
// for the second part (disabling the Selected row from effecting the BackColor i've setted myself, i've used `DataGridView.RowPrePaint` event:
private static void p_dgvToManipulate_RowPrePaint(object sender, DataGridViewRowPrePaintEventArgs e)
{
// If current row about to be painted is selected by user
if (((DataGridView)sender).Rows[e.RowIndex].Selected)
{
// Get current grid row
var objGridRow = ((DataGridView)sender).Rows[e.RowIndex];
// Get selectedItem
DomainObject objSelectedItem = (DomainObject)objGridRow.DataBoundItem;
// if item is valid ....
if (objSelectedItem != null && objSelectedItem.ObjectId != 0)
{
// Set color for row instead of "DefaultCellStyle" (same color as we used at DataBindingComplete event)
objGridRow.DefaultCellStyle.SelectionBackColor = GetColorForMyRow(objSelectedItem);
}
// Since the selected row is no longer unique, we need to let the used to identify it by making the font Bold
objGridRow.DefaultCellStyle.Font = new Font(((DataGridView)sender).Font.FontFamily, ((DataGridView)sender).Font.Size, FontStyle.Bold);
}
// If current row is not selected by user
else
{
// Make sure the Font is not Bold. (for not misleading the user about selected row...)
((DataGridView)sender).Rows[e.RowIndex].DefaultCellStyle.Font = new Font(((DataGridView)sender).Font.FontFamily,
((DataGridView)sender).Font.Size, FontStyle.Regular);
}
}
}
A possible solution if you do want to reuse the formatting provided during the Cellformatting-event (e.g. the cellstyle-elements like fontbold and backgroundcolor). These cellstyles seem to be only available between the 'cellformatting' and 'cellpainting' events but not in the datagridview-cell's style itself..
Capture the cellstyles during the cellformatting-event with a second handler like this:
in the exportmodule add a shared list, array or dictionary to store the cellstyles:
Dim oDataGridFormattingDictionary as Dictionary(Of String, DataGridViewCellStyle) = nothing
initialize the dictionary and add a second handler to the datagridview in your printing or export-code. In vb.net something like this:
oDataGridFormattingDictionary = New Dictionary(Of String, DataGridViewCellStyle)
AddHandler MyDatagridviewControl.CellFormatting, AddressOf OnPrintDataGridView_CellFormatting
Add the code for the handler
Private Sub OnPrintDataGridView_CellFormatting(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewCellFormattingEventArgs)
If e.RowIndex > -1 AndAlso e.ColumnIndex > -1 AndAlso Not e.CellStyle Is Nothing Then
If Not oDataGridFormattingDictionary Is Nothing andalso oDataGridFormattingDictionary.ContainsKey(e.RowIndex & "_" & e.ColumnIndex) = False Then
oDataGridFormattingDictionary.Add(e.RowIndex & "_" & e.ColumnIndex, e.CellStyle)
End If
End If
End Sub
Very important: to make sure the original cellformating-event (AND the second cellformatting-handler after that) are actually called you have to request the formattedvalue for each cell that you want to print (e.g.
oValue = Datagridview.rows(printRowIndex).Cells(printColumnIndex).FormattedValue)
!
When printing you can now check if the cell has formatting. E.g.:
if not oDataGridFormattingDictionary is nothing andalso oDataGridFormattingDictionary.ContainsKey(printRowIndex & "_" & printColumnIndex) Then
... the cellstyle is accesible via:
oDataGridFormattingDictionary(printRowIndex & "_" & printColumnIndex)
end if
at the end of the export or printcode remove the handler and set the dictionary to nothing
RemoveHandler DirectCast(itemToPrint.TheControl, DataGridView).CellFormatting, AddressOf OnPrintDataGridView_CellFormatting
oDataGridFormattingDictionary = nothing

How to detect the sender(button) of dynamical created bindings

I create some RibbonButtons dynamically and add them to a group according to an xml file. The follwoing function is carried out as often as entries found in the xml file.
private void ExtAppsWalk(ExternalAppsXml p, AppsWalkEventArgs args)
{
RibbonButton rBtn = new RibbonButton();
rBtn.Name = args.Name;
Binding cmdBinding = new Binding("ExtAppCommand");
rBtn.SetBinding(RibbonButton.CommandProperty, cmdBinding);
Binding tagBinding = new Binding("UrlTag");
tagBinding.Mode = BindingMode.OneWayToSource;
rBtn.SetBinding(RibbonButton.TagProperty, tagBinding);
rBtn.Label = args.Haed;
rBtn.Tag = args.Url;
rBtn.Margin = new Thickness(15, 0, 0, 0);
MyHost.ribGrpExtern.Items.Add(rBtn);
}
I tried to use the Tag property to store the Url's to be started when the respective button is clicked. Unfortunately the binding to the Tag property gives me the last inserted Url only.
What would be the best way to figure out which button is hit or to update the Tag property.
The datacontext is by default the context of the Viewmodel. The RibbonGroup to which the Buttons are added is created in the xaml file at designtime. I use that construct:
MyHost.ribGrpExtern.Items.Add(rBtn);
to add the buttons. It maight not really be conform with the mvvm pattern. May be someone else has a better idea to carry that out.
I foud a solution for my problem here and use the RelayCommand class. So I can pass objects (my Url) to the CommandHandler.
RibbonButton rBtn = new RibbonButton();
rBtn.Name = args.Name;
Binding cmdBinding = new Binding("ExtAppCommand");
rBtn.SetBinding(RibbonButton.CommandProperty, cmdBinding);
rBtn.CommandParameter = (object)args.Url;
private void ExtAppFuncExecute(object parameter)
{
if (parameter.ToString().....//myUrl

Silverlight InlineCollection.Add(InlineUIContainer) missing?

I'm having difficulty adding the inline of specific type InlineUIContainer into the InlineCollection (Content property) of a TextBlock. It appears the .Add() method of InlineCollection doesn't accept this type, however you can clearly set it through XAML without explicitly marking the content as a InlineContainer, as demonstrated in many examples:
http://msdn.microsoft.com/en-us/library/system.windows.documents.inlineuicontainer.aspx
Is it possible to programatically add one of these as in the following?
Target.Inlines.Add(new Run() { Text = "Test" });
Target.Inlines.Add(new InlineUIContainer() {
Child = new Image() { Source = new BitmapImage(new Uri("http://example.com/someimage.jpg")) } });
Target.Inlines.Add(new Run() { Text = "TestEnd" });
I have a feeling what's going on is that Silverlight is using a value converter to create the runs when specified in XAML as in the example which doesn't use InlineContainer, but I'm not sure where to look to find out.
The specific error I'm getting is as follows:
Cannot add value of type 'System.Windows.Documents.InlineUIContainer' to a 'InlineCollection' in a 'System.Windows.Controls.TextBlock'.
As pointed out by Jedidja, we need to use RichTextBox to do this in Silverlight.
You can't Add() Runs directly, but you can add Spans containing Runs.
Interestingly, you can also do this:
textBlock.Inlines.Clear();
textBlock.Inlines.Add(new Span());
textBlock.Inlines[0] = new Run();
Not that it's a good idea to hack around what the framework is actively trying to prevent you from doing.
P.S. If you can't figure out what XAML is doing, inspect the visual tree.

How to set the ColumnIndex of a newly added DataGridViewButton column

I have a really annoying issue with a button cell in a DataGridView control. I'm binding the grid to a dataset at runtime. Some of the rows in the grid will be linked to pdf documents. I create a button column and add it to the grid, then I loop through the rows and based on the value of a certain column I set the text of the cell in the button column. When I step through the code I can see the ColumnIndex of the button column is 10. However when the form appears, the button text values for the rows I want are blank.
When I click the button I check in the CellContentClick event to see if the ColumnIndex is 10 (which is the button column) it tells me the ColumnIndex is 0, even though it's the last column. Then when I reload the grid I call the BindHistoryGrid method again which drops the column if it exists and re-adds it. This time it sets the button text correctly. Is there some strange behavior going on that I can't see? How do I set the button ColumnIndex to 10 the first time I add it (even though it tells me that it's 10)?
private DataGridViewButtonColumn PDFButtonColumn;
private void BindHistoryGrid()
{
dataGridViewStmt.DataSource = ah.getAccountHistory(0, dateTimePicker1.Value, dateTimePicker2.Value);
if (dataGridViewStmt.Columns["GetPDFFile"] != null)
dataGridViewStmt.Columns.Remove("GetPDFFile");
dataGridViewStmt.Columns[0].DisplayIndex = 0;
dataGridViewStmt.Columns[0].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
dataGridViewStmt.AutoResizeColumns(DataGridViewAutoSizeColumnsMode.AllCells);
dataGridViewStmt.Columns[0].Visible = false;
dataGridViewStmt.Columns[1].Visible = false;
dataGridViewStmt.Columns.Add(PDFButtonColumn);
dataGridViewStmt.RowHeadersVisible = false;
dataGridViewStmt.ReadOnly = true;
dataGridViewStmt.AllowUserToAddRows = false;
foreach (DataGridViewRow row in dataGridViewStmt.Rows)
{
//if (((string)row.Cells[5].Value).Contains("Invoice"))
if (((int)row.Cells[9].Value) > 0)
{
((DataGridViewButtonCell)(row.Cells[10])).Value = "Get Invoice";
}
else
{
((DataGridViewButtonCell)(row.Cells[10])).Value = "";
}
}
}
private void dataGridViewStmt_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
if (e.ColumnIndex == 10 && dataGridViewStmt.CurrentRow.Cells[6].Value != System.DBNull.Value)
{
string pdfFile = "";
int docID = 0;
pdfFile = (string)dataGridViewStmt.CurrentRow.Cells[5].Value + ".pdf";
docID = (int)dataGridViewStmt.CurrentRow.Cells[9].Value;
if (docID > 0)
{
getPDFFile(docID, pdfFile, "pdf");
}
else
{
MessageBox.Show("No invoice available for this item"; }
}
}
I called my bindGrid() method from the two place one after the InitializeComponent() in form's constructor as well as from form1_load(). it works for me.
hope this will also helps you.
I didn't get any replies here so I posted on another forum. I eventually got an answer of sorts, but the whole thing is still pretty vague. The answer I got stated that in order to preserve resources, the grid doesn't always refresh itself. An example is if you have a form with a tab control that has 2 tabs, place a grid on the 1st tab and set column properties after binding in Form Load. This will work. However, when you place the grid on the 2nd tab, using the same binding won't work:
http://social.msdn.microsoft.com/Forums/en-US/csharpgeneral/thread/99ab9fbf-9eaa-4eef-86b8-8f4e49fa81c5
I still haven't found out how or when it decides to preserve resources, if there's a way to bypass this behaviour, if this behaviour is documented anywhere etc. If anyone can throw more light on it I'm all ears.
I had the very same issue. I originally had a DataGridView on a separate form and it worked perfectly with the button column - which I add in code after setting the datasource. However, when I decided to move the grid onto another form with a Tabbed Control (on to the Tab(2) page as it happens), the button column index kept reverting to zero. It looked perfectly OK on the grid of course, i.e. in the correct physical location, and if I stepped through the code in debug mode the Index didn't change, but when I ran the program it did change! Very frustrating.
I solved it by setting the tab page to the page that my grid was located BEFORE setting the datasource.
My simple process was like this (I use VB10):
TabControl1.SelectedIndex = 2 ' this is where the datagridview is
MyGrid.DataSource = Nothing
MyGrid.Columns.Clear
' I execute an Sql command into a DataReader, then fill a DataTable and then assign it to the grid
MyGrid.DataSource = MyDataTable
' Now add button column
Dim btnCol as New DatGridViewButtonColumn
MyGrid.Columns.Add(btnCol)

Resources