Renumber footnotes with c#/openxml - c#

My program should renumber footnotes in a word file.
We have a VBA-Macro, that does the same but it´s too slow.
Dim fussnote As Footnote
For Each fussnote In ActiveDocument.Footnotes
fussnote.Reference.Select
With Selection
With .FootnoteOptions
.Location = wdBottomOfPage
.NumberingRule = wdRestartContinuous
.StartingNumber = 1
.NumberStyle = wdNoteNumberStyleArabic
.NumberingRule = wdRestartSection
End With
.Footnotes.Add range:=Selection.range, Reference:=""
End With
Next
I tried to clone all footnotes, then go through them (and their references) and change their ID (is Id the right property?)
My Code looks like this (not working):
public override void Work(WordprocessingDocument args)
{
var __allFootnotes = (Footnotes)args.MainDocumentPart
.FootnotesPart.Footnotes.Clone();
var footnotes = __allFootnotes.Elements<Footnote>()
.SkipWhile(f => !(f.Id.Value > 0)).ToList();
RenumberFootnotes(footnotes,
args.MainDocumentPart.Document.Body.Descendants<Paragraph>().ToList());
var __styles = args.MainDocumentPart
.StyleDefinitionsPart.Styles;
for (int i = 0; i < footnotes.Count(); i++)
{
//var footnote = footnotes[i];
}
args.MainDocumentPart.FootnotesPart
.Footnotes = __allFootnotes;
}
private void RenumberFootnotes(List<Footnote> footnotes, List<Paragraph> paragraphs)
{
var __p = paragraphs.Where(p => p.Descendants<FootnoteReference>().Any());
var __references = __p.SelectMany(p => p.Descendants<FootnoteReference>());
for (int i = 1; i < footnotes.Count; i++)
{
var __tempId = footnotes[i].Id.Value;
footnotes[i].Id.Value = i;
var __reference = __references.First(fr => fr.Id.Value == __tempId);
__reference.Id.Value = i;
}
}

Solution extracted from question:
You have to add a new 'FootnoteReferenceMark' Object into a Run in your Footnote.
'footnote' is my variable for a footnote. Then i just take the first descendant of type 'Run' and append a new child of 'FootnoteReferenceMark'.
footnote.Descendants<Run>().First().AppendChild(new FootnoteReferenceMark());

Related

How to insert to database in bulk instead of inserting it 1 by 1

How do you make the list receives more than 1 value based on the quantity of the SelectedList.Count.
Code:
for (int i = 0; i < SelectedList.Count; i++)
{
lastSeriesNo++;
string assetcodegen = string.Format("{0}-{1}-{2}", List[i].AssetCategoryID, CurrentApplication.Now.Year, lastSeriesNo.ToString().PadLeft(5, '0'));
AssetCodeOfficial[i] = assetcodegen;
var list = (from x in ctx.DataContext.AssetRegistryEntities
where x.AssetCode == SelectedList[i].AssetCode
select x
).AsEnumerable();
foreach (var asset in list)
{
asset.SeriesNumber = (short)lastSeriesNo;
asset.Status = 'A';
asset.IsTemp = false;
asset.UpdatedBy = CurrentApplication.CurrentUserId;
asset.UpdatedDate = asset.AssetCreatedDate = CurrentApplication.Now;
AssetCodetemp[i] = asset.AssetCode;
depreciationInMonths = asset.DepnInMonths;
ctx.DataContext.SubmitChanges();
}
}
Thank you all guys for the help, I manage to fix my problem. It already saves the data to the database as bulk not 1 by 1 saving.
So I use lambda expression for my list and use the .addRange to add item to the list.
list.AddRange(ctx.DataContext.AssetRegistryEntities.Where(x=>x.AssetCode.Trim() == SelectedList[i].AssetCode.Trim()));
Code:
List<NXpert.FixedAsset.DataAccess.AssetRegistryEntity> list = new List<NXpert.FixedAsset.DataAccess.AssetRegistryEntity>();
for (int i = 0; i < SelectedList.Count; i++)
{
lastSeriesNo++;
string assetcodegen = string.Format("{0}-{1}-{2}", List[i].AssetCategoryID, CurrentApplication.Now.Year, lastSeriesNo.ToString().PadLeft(5, '0'));
AssetCodeOfficial[i] = assetcodegen;
list.AddRange(ctx.DataContext.AssetRegistryEntities.Where(x=>x.AssetCode.Trim() == SelectedList[i].AssetCode.Trim()));
AssetCodetemp[i] = list[i].AssetCode;
}
foreach (var asset in list)
{
asset.SeriesNumber = (short)lastSeriesNo;
asset.Status = 'A';
asset.IsTemp = false;
asset.UpdatedBy = CurrentApplication.CurrentUserId;
asset.UpdatedDate = asset.AssetCreatedDate = CurrentApplication.Now;
depreciationInMonths = asset.DepnInMonths;
}
ctx.DataContext.SubmitChanges();

How can i use a variable in a for loop in a command ? (c#)

So i have a matrix of buttons, from a1f to a10f and from a to j, so a1f is on the top left and j10 is on the buttom right.
I want to to something like this:
for (i = 1; i < 11; i++)
{
a{i}f.BackgroundImage = Properties.Resources._1mal2_1_Rebellion;
b{i}f.BackgroundImage = Properties.Resources._1mal2_2_Rebellion;
a{i}f.Enabled = false;
a{i}f.Tag = "playerShip";
b{i}f.Enabled = false;
b{i}f.Tag = "playerShip";
}
so the first loop would be:
a1f.BackgroundImage = Properties.Resources._1mal2_1_Rebellion;
b1f.BackgroundImage = Properties.Resources._1mal2_2_Rebellion;
a1f.Enabled = false;
a1f.Tag = "playerShip";
b1f.Enabled = false;
b1f.Tag = "playerShip";
the second would be:
a2f.BackgroundImage = Properties.Resources._1mal2_1_Rebellion;
b2f.BackgroundImage = Properties.Resources._1mal2_2_Rebellion;
a2f.Enabled = false;
a2f.Tag = "playerShip";
b2f.Enabled = false;
b2f.Tag = "playerShip";
and so on..
a{i}f or a[i]f isnt working.
If you can't iterate the controls, you could store them in a temp array.
But you would probably better doing by generating the controls. It might be the next level for improvement. For now, you could try this:
For example:
// create arrays which contains the controls.
var aShips = new [] { a1f, a2f, a3f, a4f, a5f, a6f, a7f, a8f, a9f, a10f };
var bShips = new [] { b1f, b2f, b3f, b4f, b5f, b6f, b7f, b8f, b9f, b10f };
// notice the 0 and the < 10, because arrays are zero-indexed
for (i = 0; i < 10; i++)
{
// now you can access them via the array.
aShips[i].BackgroundImage = Properties.Resources._1mal2_1_Rebellion;
aShips[i].Enabled = false;
aShips[i].Tag = "playerShip";
bShips[i].BackgroundImage = Properties.Resources._1mal2_2_Rebellion;
bShips[i].Enabled = false;
bShips[i].Tag = "playerShip";
}

EP Plus - Error Table range collides with table

I am building an export to excel functionality using EP plus and c# application. I am currently getting the error.
'Table range collides with table tblAllocations29'
In my code logic below, I am looping through a data structure that contains key and collection as a value.
I looping across each key and once again loop through each collection belonging to that key.
I basically need to print tabular information for each collection along with its totals.
In the current scenario, I am getting the error when it is trying to print
three arrays
The first array has 17 records
The second array has 29 records
The third array has 6 records
I have taken a note of the ranges it is creating while debugging
The ranges are
A1 G18
A20 G50
A51 G58
controller
[HttpGet]
[SkipTokenAuthorization]
public HttpResponseMessage DownloadFundAllocationDetails(int id, DateTime date)
{
var ms = GetStrategy(id);
DateTime d = new DateTime(date.Year, date.Month, 1).AddMonths(1).AddDays(-1);
if (ms.FIRM_ID != null)
{
var firm = GetService<FIRM>().Get(ms.FIRM_ID.Value);
IEnumerable<FIRMWIDE_MANAGER_ALLOCATION> allocationsGroup = null;
var allocationsGrouped = GetAllocationsGrouped(EntityType.Firm, firm.ID, d);
string fileName = string.Format("{0} as of {1}.xlsx", "test", date.ToString("MMM, yyyy"));
byte[] fileContents;
var newFile = new FileInfo(fileName);
using (var package = new OfficeOpenXml.ExcelPackage(newFile))
{
FundAllocationsPrinter.Print(package, allocationsGrouped);
fileContents = package.GetAsByteArray();
}
var result = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new ByteArrayContent(fileContents)
};
result.Content.Headers.ContentDisposition =
new ContentDispositionHeaderValue("attachment")
{
FileName = fileName
};
result.Content.Headers.ContentType =
new MediaTypeHeaderValue("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
return result;
}
return null;
#endregion
}
I have written the following utility that will try and export. It works sometimes when there are two array collections and it failed when processing three. Could somebody tell me what the problems are
FundsAllocationsPrinter.cs
public class FundAllocationsPrinter
{
public static void Print(ExcelPackage package, ILookup<string, FIRMWIDE_MANAGER_ALLOCATION> allocation)
{
ExcelWorksheet wsSheet1 = package.Workbook.Worksheets.Add("Sheet1");
wsSheet1.Protection.IsProtected = false;
int count = 0;
int previouscount = 0;
var position = 2;
int startposition = 1;
IEnumerable<FIRMWIDE_MANAGER_ALLOCATION> allocationGroup = null;
foreach (var ag in allocation)
{
allocationGroup = ag.Select(a => a);
var allocationList = allocationGroup.ToList();
count = allocationList.Count();
using (ExcelRange Rng = wsSheet1.Cells["A" + startposition + ":G" + (count + previouscount + 1)])
{
ExcelTableCollection tblcollection = wsSheet1.Tables;
ExcelTable table = tblcollection.Add(Rng, "tblAllocations" + count);
//Set Columns position & name
table.Columns[0].Name = "Manager Strategy";
table.Columns[1].Name = "Fund";
table.Columns[2].Name = "Portfolio";
table.Columns[3].Name = "As Of";
table.Columns[4].Name = "EMV (USD)";
table.Columns[5].Name = "Percent";
table.Columns[6].Name = "Allocations";
wsSheet1.Column(1).Width = 45;
wsSheet1.Column(2).Width = 45;
wsSheet1.Column(3).Width = 55;
wsSheet1.Column(4).Width = 15;
wsSheet1.Column(5).Width = 25;
wsSheet1.Column(6).Width = 20;
wsSheet1.Column(7).Width = 20;
// table.ShowHeader = true;
table.ShowFilter = true;
table.ShowTotal = true;
//Add TotalsRowFormula into Excel table Columns
table.Columns[0].TotalsRowLabel = "Total Rows";
table.Columns[4].TotalsRowFormula = "SUBTOTAL(109,[EMV (USD)])";
table.Columns[5].TotalsRowFormula = "SUBTOTAL(109,[Percent])";
table.Columns[6].TotalsRowFormula = "SUBTOTAL(109,Allocations])";
table.TableStyle = TableStyles.Dark10;
}
foreach (var ac in allocationGroup)
{
wsSheet1.Cells["A" + position].Value = ac.MANAGER_STRATEGY_NAME;
wsSheet1.Cells["B" + position].Value = ac.MANAGER_FUND_NAME;
wsSheet1.Cells["C" + position].Value = ac.PRODUCT_NAME;
wsSheet1.Cells["D" + position].Value = ac.EVAL_DATE.ToString("dd MMM, yyyy");
wsSheet1.Cells["E" + position].Value = ac.UsdEmv;
wsSheet1.Cells["F" + position].Value = Math.Round(ac.GroupPercent,2);
wsSheet1.Cells["G" + position].Value = Math.Round(ac.WEIGHT_WITH_EQ,2);
position++;
}
position++;
previouscount = position;
// position = position + 1;
startposition = position;
position++;
}
}
}
This is how the data looks when it is displayed successfully
Your issue is entirely in your Print method. You've been bitten by creating a slightly over-complicated row tracking mechanism and combining that with magic numbers. This causes you to position each table after the first one row higher than it should be. The header and subtotals are not part of the table, so you have a couple rows of leeway for the error. Tables can't overlap as you've seen, so EPPlus starts barking at you after you've exhausted your leeway.
All you need to do is keep track of the current row that you are writing to, and account for the space taken by your table header and footer (the subtotals) if you use them.
You declare these:
int count = 0;
int previouscount = 0;
var position = 2;
int startposition = 1;
But to write to the correct row, all you need is this:
var rowNumber = 1;
This will properly start writing your data in row one of the Excel sheet. As you write your table rows, you'll track and increment only the rowNumber. But what about the header and footer of each table? If you start writing at the first row of your table you'll overwrite the header, and if you don't account for both the header and footer you'll start having collisions like you've seen. So lets do this:
var showFilter = true;
var showHeader = true;
var showTotals = true;
var rowAdderForHeader = Convert.ToInt32(showHeader);
var rowAdderForFooter = Convert.ToInt32(showTotals);
These are pretty self explanatory, you'll use the rowAdders to hop the header or footer when needed. rowNumber will always be your current row to create your table and write your data. You use the count when defining your table, but we've made it irrelevant for anything else, so we move it:
var allocationList = allocationGroup.ToList();
//Moved here
var count = allocationList.Count();
Your using statement becomes:
using (ExcelRange Rng = wsSheet1.Cells["A" + rowNumber + ":G" + (count + rowNumber)])
Next, it isn't mentioned in your post, but you are going to run into a problem with the following:
ExcelTableCollection tblcollection = wsSheet1.Tables;
ExcelTable table = tblcollection.Add(Rng, "tblAllocations" + count);
Your table names have to be unique, but you could very well wind up with multiple allocations having the same count, which will cause EPPlus to throw an exception at you for duplicating a table name. So you'll want to also track the index of your current table:
var rowNumber = 1;
var tableIndex = 0;
//...
foreach (var ag in allocation)
{
tableIndex += 1;
//...
}
And use it to ensure unique table names:
ExcelTableCollection tblcollection = wsSheet1.Tables;
ExcelTable table = tblcollection.Add(Rng, "tblAllocations" + tableIndex);
We use our format control variables:
// table.ShowHeader = true;
table.ShowFilter = true;
table.ShowTotal = true;
//Changes to
table.ShowHeader = showHeader;
table.ShowFilter = showFilter;
table.ShowTotal = showTotals;
You have a small typo here:
table.Columns[6].TotalsRowFormula = "SUBTOTAL(109,Allocations])";
//Should be:
table.Columns[6].TotalsRowFormula = "SUBTOTAL(109,[Allocations])";
After you are done defining your table, you begin writing your data with a foreach loop. In order to prevent overwriting the table header if it exists, we'll have to advance one row. We also have to advance one row for each FIRMWIDE_MANAGER_ALLOCATION. If you are using the subtotals, we have to advance one row after the loop completes in order to properly position the next table:
rowNumber += rowAdderForHeader;
foreach (var ac in allocationGroup)
{
//...
rowNumber += 1;
}
rowNumber += rowAdderForFooter;
And that's it. We now properly track our position using just one variable, and we modify the position as necessary if there is a header or footer on your table.
The following is a complete working example that can be run in LinqPad as long as you add the EPPlus package through Nuget. It creates a random number of allocation groups each with a random number of allocations, and then exports them. Change the output file path to something that works for you:
void Main()
{
var dataGenerator = new DataGenerator();
var allocations = dataGenerator.Generate();
var xlFile = new FileInfo(#"d:\so-test.xlsx");
if (xlFile.Exists)
{
xlFile.Delete();
}
using(var xl = new ExcelPackage(xlFile))
{
FundAllocationsPrinter.Print(xl, allocations);
xl.Save();
}
}
// Define other methods and classes here
public static class FundAllocationsPrinter
{
public static void Print(ExcelPackage package, ILookup<string, FIRMWIDE_MANAGER_ALLOCATION> allocation)
{
ExcelWorksheet wsSheet1 = package.Workbook.Worksheets.Add("Sheet1");
wsSheet1.Protection.IsProtected = false;
IEnumerable<FIRMWIDE_MANAGER_ALLOCATION> allocationGroup = null;
var rowNumber = 1;
int tableIndex = 0;
var showFilter = true;
var showHeader = true;
var showTotals = true;
var rowAdderForHeader = Convert.ToInt32(showHeader);
var rowAdderForFooter = Convert.ToInt32(showTotals);
foreach (var ag in allocation)
{
tableIndex += 1;
Console.WriteLine(tableIndex);
allocationGroup = ag.Select(a => a);
var allocationList = allocationGroup.ToList();
var count = allocationList.Count();
using (ExcelRange Rng = wsSheet1.Cells["A" + rowNumber + ":G" + (count + rowNumber)])
{
ExcelTableCollection tblcollection = wsSheet1.Tables;
ExcelTable table = tblcollection.Add(Rng, "tblAllocations" + tableIndex);
//Set Columns position & name
table.Columns[0].Name = "Manager Strategy";
table.Columns[1].Name = "Fund";
table.Columns[2].Name = "Portfolio";
table.Columns[3].Name = "As Of";
table.Columns[4].Name = "EMV (USD)";
table.Columns[5].Name = "Percent";
table.Columns[6].Name = "Allocations";
wsSheet1.Column(1).Width = 45;
wsSheet1.Column(2).Width = 45;
wsSheet1.Column(3).Width = 55;
wsSheet1.Column(4).Width = 15;
wsSheet1.Column(5).Width = 25;
wsSheet1.Column(6).Width = 20;
wsSheet1.Column(7).Width = 20;
table.ShowHeader = showHeader;
table.ShowFilter = showFilter;
table.ShowTotal = showTotals;
//Add TotalsRowFormula into Excel table Columns
table.Columns[0].TotalsRowLabel = "Total Rows";
table.Columns[4].TotalsRowFormula = "SUBTOTAL(109,[EMV (USD)])";
table.Columns[5].TotalsRowFormula = "SUBTOTAL(109,[Percent])";
table.Columns[6].TotalsRowFormula = "SUBTOTAL(109, [Allocations])";
table.TableStyle = TableStyles.Dark10;
}
//Account for the table header
rowNumber += rowAdderForHeader;
foreach (var ac in allocationGroup)
{
wsSheet1.Cells["A" + rowNumber].Value = ac.MANAGER_STRATEGY_NAME;
wsSheet1.Cells["B" + rowNumber].Value = ac.MANAGER_FUND_NAME;
wsSheet1.Cells["C" + rowNumber].Value = ac.PRODUCT_NAME;
wsSheet1.Cells["D" + rowNumber].Value = ac.EVAL_DATE.ToString("dd MMM, yyyy");
wsSheet1.Cells["E" + rowNumber].Value = ac.UsdEmv;
wsSheet1.Cells["F" + rowNumber].Value = Math.Round(ac.GroupPercent, 2);
wsSheet1.Cells["G" + rowNumber].Value = Math.Round(ac.WEIGHT_WITH_EQ, 2);
rowNumber++;
}
//Account for the table footer
rowNumber += rowAdderForFooter;
}
}
}
public class FIRMWIDE_MANAGER_ALLOCATION
{
public FIRMWIDE_MANAGER_ALLOCATION(string name, Random rnd)
{
Name = name;
MANAGER_STRATEGY_NAME = "strategy name";
MANAGER_FUND_NAME = "fund name";
PRODUCT_NAME = "product name";
EVAL_DATE = DateTime.Now;
UsdEmv = (decimal)rnd.NextDouble() * 100000000;
GroupPercent = (decimal)rnd.NextDouble() * 100;
WEIGHT_WITH_EQ = 0;
}
public string Name { get; set; }
public string MANAGER_STRATEGY_NAME { get; set; }
public string MANAGER_FUND_NAME { get; set; }
public string PRODUCT_NAME { get; set; }
public DateTime EVAL_DATE { get; set; }
public decimal UsdEmv { get; set; }
public decimal GroupPercent { get; set; }
public decimal WEIGHT_WITH_EQ { get; set; }
}
public class DataGenerator
{
public static Random rnd = new Random();
public ILookup<string, FIRMWIDE_MANAGER_ALLOCATION> Generate()
{
var data = new List<FIRMWIDE_MANAGER_ALLOCATION>();
var itemCount = rnd.Next(1, 100);
for (var itemIndex = 0; itemIndex < itemCount; itemIndex++)
{
var name = Path.GetRandomFileName();
data.AddRange(GenerateItems(name));
}
return data.ToLookup(d => d.Name, d => d);
}
private IEnumerable<FIRMWIDE_MANAGER_ALLOCATION> GenerateItems(string name)
{
var itemCount = rnd.Next(1,100);
var items = new List<FIRMWIDE_MANAGER_ALLOCATION>();
for (var itemIndex = 0; itemIndex < itemCount; itemIndex++)
{
items.Add(new FIRMWIDE_MANAGER_ALLOCATION(name, rnd));
}
return items;
}
}

Populate TextBoxes from a List

I am trying to populate TextBoxes from a list. I have been able to populate ComboBoxes with comboList:
var comboList = new System.Windows.Forms.ComboBox[4];
comboList[0] = cmbSite1Asset;
comboList[1] = cmbSite2Asset;
comboList[2] = cmbSite3Asset;
comboList[3] = cmbSite4Asset;
List<CRCS.CAsset> assets = _rcs.Assets;
foreach (CRCS.CAsset asset in assets)
{
string id = asset.ID;
for (int i = 0; i < 4; ++i)
{
comboList[i].Items.Add(id);
}
}
But when I try and apply the same principle to TextBoxes
var aosList = new System.Windows.Forms.TextBox[8];
aosList[0] = txtAsset1;
aosList[1] = txtAsset2;
aosList[2] = txtAsset3;
aosList[3] = txtAsset4;
aosList[4] = txtAsset5;
aosList[5] = txtAsset6;
aosList[6] = txtAsset7;
aosList[7] = txtAsset8;
foreach (CRCS.CAsset asset in assets)
{
string id = asset.ID;
for (int n = 0; n < 8; ++n)
{
aosList[n].Items.Add(id);
}
}
TextBox does not like Items.Add ( aosList[n]Items.Add(id); )
I am looking fore a reference or guidance resolving this issue. Thanks!
You should use ComboBox for your problem,instead of iterating on each element,You simply use below lines to populate combobox.
comboList.DataSource=assets;
comboList.DisplayMember="ID";
comboList.ValueMember="ID";
However,if you want your values in TextBox,you can use TextBox.AppendText Method, but it will not work like ComboBox as it will contain texts+texts+texts, will not have indexes like ComboBox.
private void AppendTextBoxLine(string myStr)
{
if (textBox1.Text.Length > 0)
{
textBox1.AppendText(Environment.NewLine);
}
textBox1.AppendText(myStr);
}
private void TestMethod()
{
for (int i = 0; i < 2; i++)
{
AppendTextBoxLine("Some text");
}
}
A Combobox is a collection of items, and so has an Items property from which you can add/remove to change it's contents. A Textbox is just a control that displays some text value, so it has a Text property which you can set/get, and which denotes the string that is displayed.
System.Windows.Forms.TextBox[] aosList = new System.Windows.Forms.TextBox[8];
aosList[0] = txtAsset1;
aosList[1] = txtAsset2;
aosList[2] = txtAsset3;
aosList[3] = txtAsset4;
aosList[4] = txtAsset5;
aosList[5] = txtAsset6;
aosList[6] = txtAsset7;
aosList[7] = txtAsset8;
for (int n = 0; n < 8; ++n)
{
aosList[n].Text = assets[n].ID; // make sure you have 8 assets also!
}
int i = 1;
foreach (var asset in assets)
{
this.Controls["txtAsset" + i].Text = asset.ID;
i++;
}

Selenuim driver.Navigate().back() breaks the HTML DOM when navigating back words and forwards

The code below clicks on each button and then clicks on dynamic link produced by the button which takes me to next page however when test navigates back it fails to carry out the similar steps because of "Element not found in the cache - perhaps the page has changed since it was looked up". I need to test links and move backwards and forwards during the process.
Code to click and navigate
public static int ClickNestedLink(int ID, IList<string>allNestedlinks,IWebDriver _driver,string Url)
{
//Find the product brand and click on its brand and click on product link
foreach (string BrandName in allNestedlinks)
{
IList<string> ProductBrandButtonList = new List<string>();
//Find different brands
foreach (IWebElement ProductBrandButton in _driver.FindElements(By.ClassName("productSet")))
{
IWebElement ProductParent = ProductBrandButton.FindElement(By.ClassName("product-parent"));
String test = ProductParent.GetAttribute("data-product-id");
ProductBrandButtonList.Add(test);
//Find links for a brand
IList<string> LinksCollection = new List<string>();
foreach (IWebElement ImageLink in ProductParent.FindElements(By.ClassName("brand_image")))
{
String ImageLike2 = ImageLink.GetAttribute("src");
LinksCollection.Add(ImageLike2);
ImageLink.Click();
IWebElement productBrandClick = ProductParent.FindElement(By.ClassName("product-brand"));
String Test2 = productBrandClick.Text;
productBrandClick.Click();
_driver.Navigate().Back();
Task.Delay(20000).Wait(); }
}
}
return (ID);
}
The Click and Back actions are loading a new page with new elements making the previous references obsolete.
To overcome this issue, you need locate the elements in each loop:
// iterate the products
for (int i = 0; ; i++) {
var products = _driver.FindElements(By.CssSelector(".productSet .product-parent"));
if (i >= products.Count)
break;
// get the product for the iteration
var product = products[i];
// iterate the links
for (int j = 0; ; j++) {
var links = _driver.FindElements(By.CssSelector(".productSet .product-parent .brand_image"));
var brands = _driver.FindElements(By.CssSelector(".productSet .product-parent .product-brand"));
if (j >= links.Count)
break;
// get the link and brand for the iteration
var link = links[j];
var brand = brands[j];
// click and navigate back
}
}
Solution
public static int ClickNestedLink2(int ID, IList allNestedlinks, IWebDriver _driver, string Url)
{
var products = _driver.FindElements(By.CssSelector(".productSet .product-parent"));
var productCount = products.Count;
for (int i = 0; i < productCount; i++)
{
var product = _driver.FindElements(By.CssSelector(".productSet .product-parent"))[i];
var brandButtonsCount = product.FindElements(By.CssSelector(".productSet .product-parent .brand_image")).Count;
for (int btnIdx = 0; btnIdx < brandButtonsCount; btnIdx++)
{
product = _driver.FindElements(By.CssSelector(".productSet .product-parent"))[i];
var brandButton = product.FindElements(By.CssSelector(".productSet .product-parent .brand_image"))[btnIdx];
brandButton.Click();
var brandNameAnchor = product.FindElement(By.CssSelector(".productSet .product-parent .product-brand"));
brandNameAnchor.Click();
_driver.Navigate().Back();
OpenQA.Selenium.Support.UI.WebDriverWait wait = new WebDriverWait(_driver, new TimeSpan(0, 0, 10));
wait.Until(driver1 => ((IJavaScriptExecutor)_driver).ExecuteScript("return document.readyState").Equals("complete"));
}
}
return (ID = 0);
}
}

Categories

Resources