Can not retrieve a WinTree in the code with foreach loop with codedui - coded-ui-tests

I can not iterate though a WinTree that took record. When I debug on childNodes I see some innerlist, lists on it but I can not get them in the code.
#region Variable Declarations
WinTree uITVTree = his.UIProMANAGEWindow.UIReportsWindow.UITVWindow.UITVTree;
#endregion
ITestControlCollection childNodes = uITVTree.Nodes;
none of the foreach loops below works, debugger does not go inside loops even though I tried with var
foreach (var links in childNodes)
{
MessageBox.Show(links.ToString());
}
foreach (UITestControl links in childNodes)
{
MessageBox.Show(links.ToString());
}
foreach (WinTreeItem treeItem in childNodes)
{
MessageBox.Show(treeItem.ToString());
}

If Wintree is not expanded, it will not get its child nodes.so just click on the tree once and then try the same code

Related

Refresh the view to avoid the error another process has updated ARRegister

Upon deletion of my custon item LELettering in which I have a view with ARRegister lines, I want to :
- Reverse the payment application for every Invoice ARRegister lines for the payment also included in my ARRegister lines.
- Clean the LetteringCD I did put in a custom field in my ARRegister extension.
Now when I pick one or the other alone, it works.
My problem is when I do both : reverseapplication() do it's job, but this, as a side effect, updates the ARRegister records when I call the reverseapplication method from the ARPaymentEntry.
Which lead to an error : "Another process has update the ARRegister record and your changes will be lost", when I try to update the ARRegister records to clean my custom field LetteringCD.
I think my problem is my view Lines is not refreshed once reverseApplication is called, so it still has the not yet updated records of ARRegister.
I tried ClearQueryCache() but it doesnt seem to work, how to I force a refresh on my view Lines so I can update them again ?
public PXSelect<LELettering> Piece;
public PXSelect<ARRegister> Lines;
protected virtual void LELettering_RowDeleting(PXCache sender, PXRowDeletingEventArgs e)
{
// Cancel the lettering by removing every LetteringCD from the ARRegister lines and reverse application paiements
cancelLettering();
}
protected void cancelLettering()
{
reverseApplication();
eraseLetteringCD();
}
protected void reverseApplication()
{
string refNbr = "";
List<ARRegister> lines = new List<ARRegister>();
foreach (ARRegister line in PXSelect<ARRegister, Where<ARRegisterLeExt.lettrageCD,
Equal<Required<ARRegisterLeExt.lettrageCD>>>>.Select(this, Piece.Current.LetteringCD))
{
if (line.DocType == "PMT") refNbr = line.RefNbr;
else lines.Add(line);
}
ARPaymentEntry graphPmt = getGraphPayment(refNbr, "PMT");
foreach(ARAdjust line in graphPmt.Adjustments_History.Select())
{
graphPmt.Adjustments_History.Current = line;
graphPmt.reverseApplication.Press();
}
graphPmt.release.Press();
graphPmt.Actions.PressSave();
}
// Here is my problem
protected void eraseLetteringCD()
{
foreach (var line in Lines.Select())
{
line.GetItem<ARRegister>().GetExtension<ARRegisterLeExt>().LettrageCD = null;
Lines.Current = Lines.Update(line);
}
Actions.PressSave();
}
protected ARPaymentEntry getGraphPayment(string refNbr, string docType)
{
ARPaymentEntry graphPmt = CreateInstance<ARPaymentEntry>();
ARPayment pmt = PXSelect<ARPayment, Where<ARPayment.refNbr, Equal<Required<ARPayment.refNbr>>,
And<ARPayment.docType, Equal<Required<ARPayment.docType>>>>>
.Select(this, refNbr, docType);
if (pmt == null) throw new PXException(Constantes.errNotFound);
graphPmt.Document.Current = pmt;
return graphPmt;
}
Edit:
The problem comes from the fact the records ARRegister are saved two times, once with the reversepaymentapplication, and once in the eraseLetteringCD, but I dont know how to avoid this in my case.
Some things I might try...
I do see that there are multiple graphs involved. The second graph will need to refresh the results before it can process. There are a few ways of doing this that I try...
You can try to clear the query cache as shown below. My guess when you call Lines.Select it has an old cached value?
protected void cancelLettering()
{
reverseApplication();
Lines.Cache.ClearQueryCache()
eraseLetteringCD();
}
I find it helpful some items in reverse if the select is not returning the cached results to find the cached row myself. The reverse could be to use PXSelectReadonly<> as your foreach select statement as this should use the records from the DB vs any cached values.
protected void eraseLetteringCD()
{
// Also try PXSelectReadonly<> in place of Lines.Select()
foreach (ARRegister line in Lines.Select())
{
//Get cached row
var cachedRow = (ARRegister)Lines.Cache.Locate(line) ?? line;
cachedRow.GetExtension<ARRegisterLeExt>().LettrageCD = null;
Lines.Update(cachedRow );
}
Actions.PressSave();
}
After the first press save you could also try to just clear the cache and the query cache to setup for the next call. Ideally if possibly just do one persist.
If the first graph is running a long operation you can make the code pause to wait for the operation to complete. This would be useful when using multiple graphs. The second graph persist should not run until the first long running process has finished. Example using the ID of your graphPmt instance:
graphPmt.release.Press();
PXLongOperation.WaitCompletion(graphPmt.UID)

Thread.Start vs Action in ThreadStart

I have faced this problem while changing a code block.
List<Entity> entities = new List<Entity>();
//Some values added to the list ....
foreach(var entity in entities)
{
Thread th = new Thread(new ThreadStart(SomeMethod));
th.Start(entity);
}
SomeMethod is taking Entity and changing on it. somthing like
private void SomeMethod(Entity entity)
{
//Some operation on entity
}
This is a .NET 2.0 code, while changing it to 4.0 I did a small change i.e.
foreach(var entity in entities)
{
Thread th = new ThreadStart(() => SomeMethod(entity));
th.Start();
}
This is not working, from error it looks like entity variable getting changed between threads and resulting some null reference exception. I havnt changed anything else, as soon as code reverted to the previous way, it is fine. Can anyone explain this?
Please try to use a new local variable in such a way:
foreach(var entity in entities)
{
var copy = entity;
Thread th = new ThreadStart(() => SomeMethod(copy));
th.Start();
}
The approach with a new variable will not work if you introduce the variable in such a way
Thread th = new ThreadStart(() => {var copy = entity; SomeMethod(copy)) };
since that code will still capture the loop variable that is changed in each iteration of the loop.
Also please note that you can get the error you described if the entities collection contains the same element twice (that is two references to the same Entity object). Please make sure that it's not the case.

Need help in Sharepoint workflow

I am new to sharepoint development and i have task in hand to do. I need to add few lines of code for the following logic.
Need to check if previous title and new title of task items are same.
If Not, then query the Task list
Find all the items which contain the previous title
Update their titles.
Here is my Pseudocode:
public override void ItemUpdating(SPItemEventProperties properties)
{
try {
this.DisableEventFiring();
//Need to write my logic here
base.ItemUpdating(properties);
}
catch (Exception ex) {
}
finally {
this.EnableEventFiring();
}
}
Can somebody guide me how to write the code for the above mentioned logic? If you have any sample code's with similar logic, please share it. It will be helpful for me.
Thanks in Advance!
This code might help you out. Maybe you need to adapt it for your needs, but the properties you need to access are the same.
public override void ItemUpdating(SPItemEventProperties properties)
{
//this will get your title before updating
var oldName = properties.ListItem["Title"].ToString();
//and this will get the new title
var newName = properties.AfterProperties["Title"].ToString();
if (newName != oldName)
{
using (var site = new SPSite("http://yoursitename"))
using (var web = site.OpenWeb())
{
var list = web.Lists["Tasks"];
var items = list.Items.OfType<SPListItem>().Where(i => (string) i["Title"] == oldName);
foreach (var item in items)
{
item["Title"] = newName;
item.Update();
}
}
}
base.ItemUpdating(properties);
}

SharePoint "Thread was being aborted" looping through a SPListItemCollection

I have a SPListItemCollection with ~500 items in. I am looping through using a for each loop, each time grabbing the file and writing it out to a pdf document. I am getting the error "Thread was being aborted". It looks to be a time out issue but I dont know how to fix it? Collections with 200-300 items work fine. Is there any way I can stop getting this error. If I cant increase the timeout I will need to batch it up.
Update
I have tried splitting up the processing in batches of 100 items. With each new 100 items using a new spweb and site and pulling the items from sharepoint. This is all done within the same method (so I am not recreating my custom object) however the same error is still occuring...
http://blogs.msdn.com/solutions/archive/2009/01/08/getting-around-thread-was-being-aborted-error-when-creating-ep-site.aspx
In a basic text editor such as Notepad, open the web.config file for example ' %SYSTEMDRIVE%\Inetpub\wwwroot
-or-
%SYSTEMDRIVE%\\Inetpub\wwwroot\wss\VirtualDirectories\80 folder
Press CTRL + F to open the Find dialog box.
Find the following tag:
<httpRuntime maxRequestLength="51200" />
Replace it with this tag:
<httpRuntime executionTimeout="6000" maxRequestLength="51200" />
This seems to have got it working for me.
Keep in mind there is a right and a very wrong way to iterate over list items, and it makes a huge difference in performance. The wrong way will cause n+1 calls to the database for the list data; the right way only makes a single call to the database.
Worst:
for (int i = 0; i < myList.Items.Count; i++)
{
SPListItem thisItem = myList.Items[i];
// yada yada yada
}
Bad:
foreach (SPListItem item in myList.Items)
{
// yada yada yada
}
Right:
SPListItemCollection myItems = myList.Items;
foreach (SPListItem item in myItems)
{
// yada yada yada
}
Updated: To reflect more accurate guidance
Iterating through an SPListItemCollection is never a good idea (but sadly sometimes the only way to go). You could consider wrapping up this code in a LongRunningOperation. Here's some code I adapted from some of my own:
ACTUAL CLASS:
using System;
using System.Collections.Generic;
using System.Globalization;
using Microsoft.SharePoint;
namespace Common.LongRunningJobs
{
/// <summary>
/// Provides a long running job wrapper around converting multiple files
/// </summary>
[CLSCompliant(false)]
public class FileToPdfLongRunningJob : LongRunningOperationJob
{
private List<string> fileUrls;
/// <summary>
/// Initializes a new instance of the <see cref="FileToPdfLongRunningJob"/> class.
/// </summary>
/// <param name="urls">The urls of the files to create pdfs from.</param>
public FileToPdfLongRunningJob(List<string> urls)
{
fileUrls = urls;
}
/// <summary>
/// Does the actual work of converting the files, while providing the user with status updates.
/// </summary>
public override void DoWork()
{
try
{
using (var currentWeb = Site.OpenWeb(parentWeb))
{
OperationsPerformed = 0;
foreach (var url in fileUrls)
{
SPFile file = currentWeb.GetFile(url);
// DO PDF OUTPUT
StatusDescription = string.Format(CultureInfo.InvariantCulture, "busy converting {0} van {1}, file: {2}...", (OperationsPerformed + 1), TotalOperationsToBePerformed, file.Name);
UpdateStatus();
OperationsPerformed++;
}
}
}
catch (Exception ex)
{
// LOG ERROR
}
}
}
}
USING ABOVE CLASS:
private void ConvertFiles()
{
const string PROGRESS_PAGE_URL = "/_layouts/LongRunningOperationProgress.aspx";
var urls = new List<string>();
foreach (SPListItem item in yourlistitemcollection)
{
urls.Add(item.File.Url);
}
var longRunningJob = new FileMoveLongRunningJob(urls)
{
Title = "Pdf conversion Long Running Job",
TotalOperationsToBePerformed = urls.Count,
RedirectWhenFinished = true,
NavigateWhenDoneUrl = urlToRedirectTo;//i.e. SPContext.GetContext(Context).List.RootFolder.ServerRelativeUrl
};
longRunningJob.Start(SPContext.Current.Web);
string url = string.Format("{0}{1}?JobId={2}", SPContext.Current.Web.Url, PROGRESS_PAGE_URL, longRunningJob.JobId);
SPUtility.Redirect(url, SPRedirectFlags.Default, Context);
}
Try creating new SPSite and SPWeb objects before getting items (and get them from new webs).
Often times that solves the problem.
There can be potential pit falls in the code that iterates list. It will be good if you can paste your code here.
Also, its better to use Caml to query for list items and select only those fields that are required for your operation rather than all.

Will using a SPListItemCollection returned from a function reopen the SPWeb?

After reading Stefan Gossner's post about disposing objects and this question about Cross method dispose patterns, I found that I was guilty of accidentally reopening some SPWebs. I know in Stefan Gossner's post he mentions you should dispose of an SPWeb after you are finished with any child object. However, the microsoft documentation mentions Caching the SPListItemCollection object. Is the following code correct? Would the returned SPListItemCollection reopen an SPWeb object? Is there any way to tell for sure?
// is this correct????
private SPListItemCollection GetListItems()
{
SPListItemCollection items = null;
try
{
using (SPSite site = new SPSite(GetListSiteUrl()))
{
using (SPWeb web = site.OpenWeb())
{
// retrieve the list
SPList list = web.Lists[_ListName];
// more code to create the query...
items = list.GetItems(query);
}
}
}
catch (Exception e)
{
// log error
}
return items;
}
Edit 09/09/09
I am mainly referring to this part of Stefan Grossner's post:
You should dispose a SPWeb or SPSite
object after the last access to a
child object of this object.
I believe what he is saying is that if I use the SPListItemCollection after I dispose of the SPWeb that I used to get it... the SPWeb will be reopened automatically.
I found out after asking Stefan directly that the SPListItemCollection can indeed reopen the SPWeb after you dispose of it. This means that my code posted above is INCORRECT and I would only be able to dispose of the SPWeb after I use the SPListItemCollection.
Update: It is better to convert to the SPListItemCollection to something else and return that instead.
private DataTable GetListItems()
{
DataTable table = null;
try
{
SPListItemCollection items = null;
using (SPSite site = new SPSite(GetListSiteUrl()))
{
using (SPWeb web = site.OpenWeb())
{
// retrieve the list
SPList list = web.Lists[_ListName];
// more code to create the query...
items = list.GetItems(query);
// convert to a regular DataTable
table = items.GetDataTable();
}
}
}
catch (Exception e)
{
// log error
}
return table;
}
As far as I know the answer is no, but I would have written the code something like
private void FetchItems(Action<SPListItemCollection> action)
{
using(...)
{
var items = list.GetItems(query);
action(items);
}
}
By doing this, to call this method you would need to send a method along (delegate) that the SPListItemCollection should be used for, an example:
FetchItems( items => ....) or FetchItems( DoStuffWithItems(SPListItemCollection) )
If you are talking about whether you need an SPWeb in the same scope when you get around to using the SPListItemCollection, I think the answer is no.
For example, I routinely do the following:
private IEnumerable<SPListItem> AllItems;
public void GetItems()
{
var results = SPContext.Current.Web.Lists[ListName].Items.Cast<SPListItem>();
this.AllItems = results;
}
and then I use AllItems all over the place, and it works fine.
Incase you are wondering, the cast is done so I can use Linq on the result set - much much faster than submitting a query to the list, especially if you are doing multiple subselects on the data.

Resources