I am attempting to populate a grid in my MainPage with web browser items which are added in the code behind. For some reason, I cannot get the grid to remember which elements have been added already. I am unsure of how to store this information so that when the MainPage is navigated from, and then back to, the grid knows which values were previously added. To better explain, what I have is as follows:
MainPage.xaml
<Grid x:Name="BrowserHost" Grid.Row="1">
//Items will be added in code behind
</Grid>
MainPage.xaml.cs
private void ShowTab(int index)
{
this.currentIndex = index;
int count = Settings.BrowserList.Value.Count;
if (count <= 0)
{
MessageBox.Show("count <= 0");
}
if (currentIndex <= (count - 1)) //in correct range
{
// BrowserHost does not remember previously added children elements added previously when navigated back to this page and performing this check
if(!BrowserHost.Children.Contains(Settings.BrowserList.Value[currentIndex].Browser))
//Attempt to add browser if it was already added previously throws InvalidOperationException
BrowserHost.Children.Add(Settings.BrowserList.Value[currentIndex].Browser);
}
if (currentIndex > (count - 1))
{
MessageBox.Show("currentIndex > (count - 1)");
}
for (int i = 0; i < Settings.BrowserList.Value.Count; i++)
{
if (Settings.BrowserList.Value.ElementAt(i) != null)
{
Settings.BrowserList.Value.ElementAt(i).Browser.Visibility = i == this.currentIndex ? Visibility.Visible : Visibility.Collapsed;
}
}
}
Essentially, the ShowTab method is called with a specific index value everytime the MainPage loads. A check is performed to see if Settings.BrowserList.Value[currentIndex].Browser (a collection with web browser instances) exists at the currentIndex in the list (these are added in another page). If the BrowserHost grid on MainPage already contains the child element (a browser) with a particular index, then it should skip over the check and continue to show that browser instance on the screen. This is supposed to basically implement a tabbed browsing scenario for a custom web browser control I have created. My issue is, I can consecutively add new browsers to BrowserHost and show them on the screen, but when I try to call a previously added webbrowser at certain index in my collection, I get an InvalidOperationException. How would I fix this? My debugging shows that in ShowTab when I check to see if a browser has been added, regardless of it has or not, it jumps into attempting to add that browser to BrowserHost child element. If the browser has been created previously, thats when the InvalidOperationException occurs.
I don't think what you are doing is a good idea. However, the issue seems to be that the control is already parented by a different control and so you cannot add it again. Try doing the following:
var browser = Settings.BrowserList.Value[currentIndex].Browser;
if(!BrowserHost.Children.Contains(browser))
{
var grid = (Grid)browser.Parent;
if (grid != null)
{
grid.Children.Remove(browser);
}
BrowserHost.Children.Add(browser);
}
You also need to make sure that the Browser is placed in a Grid in your other form (if it's not directly inside a Grid, just wrap it in one).
This may work, but I strongly suggest you find a different way of doing it.
Related
While I was trying to implement an infinite scroll page I've encountered a strange behaviour with the CurrentItemChanged event of Xamarin.Forms.CarouselView.
While the user scrolls, new items are added to the ItemSource and old items are removed (for low memory consumption).
After I encountered the strange behaviour I've debugged and narrowed down the problem.
So here are the steps to replicate the situation.
Create a CarouselView.
Create a ObservableCollection<T> in the code behind and assign it to the ItemSource.
Create a method and subscribe it to the CurrentItemChanged event of the CarouselView. This method must at some point remove an element from the ItemSource that has an index between 0 and the index of the CurrentItem.
Now deploy the app and swipe the CarouselView once. This will result in an endless loop of scrolls that will keep going untill all of the items are removed from the ItemSource.
The method from step 3 must look like below.
bool FirstTime = true;
private void StateChanged(object s, EventArgs e)
{
// Pass the first call which is made right after the Carousel is initialized.
if (FirstTime) { FirstTime = false; return; }
var currentItem = (Model)Carousel.CurrentItem; // For debug.
var index = Models.IndexOf(currentItem); // Same.
// Step 3's requirement
Models.RemoveAt(0);
}
When you instead for example add a button to the page and assign the method you've created at the step 3 to it's Clicked event, and continue with the 4th step and manually press button after each scroll the endless loop won't occur.
I don't know if this is a feature or a bug but this certainly was unexpected, at least for me. I would love to figure out how to overcome this problem and learn why it works like this.
Note: I'm aware that removing the current item will cause such problem but the described behaviour occurs either ways. Also CarouselView.CurrentItem is updated before the CurrentItemChanged event is fired.
This will result in an endless loop of scrolls that will keep going untill all of the items are removed from the ItemSource.
This is because ObservableCollection has CollectionChanged event which will be called when the data collection is changed. When you remove the first item, the index is refershed and the event will also be triggered.
For this function, you could detect if the current item is the last one to update the data collection. Check the code:
public partial class Page1 : ContentPage
{
CustomViewModel viewModel = new CustomViewModel();
ObservableCollection<CustomModel> collection;
public Page1()
{
InitializeComponent();
BindingContext = viewModel;
collection = viewmodel.DataCollection;
}
private void CarouselView_CurrentItemChanged(object sender, CurrentItemChangedEventArgs e)
{
var item = e.CurrentItem as CustomModel;
var index = collection.IndexOf(item);
if (collection.Count == (index + 1))
{
collection.RemoveAt(0);
collection.Add(new CustomModel { ... });
}
}
}
So, i am trying to use Selenium to scrape this webpage that updates every couple seconds. I am looking to click on the most recent link which, in this case, is the link by Edward Researcher. This list will update multiple times and get updated with new links stacking at the top.
My current code basically takes the top most link of the entire page when I want it to take links that are shown on the page starting from the top
allLinks = driver.FindElements(By.XPath("/html/body/div[6]//a")).ToHashSet();
This will get all the clickable links going top down using a tags to grab the links. My problem is trying to only get links from one place on the webpage and grab that link everytime it updates. Not really sure how to do that since the code and xPath changes with every added link. Any help would be greatly appreciated
The bottom most mark in red is the current link that I inspected that is topmost but when the page gets updated with new Links the two top red marks are where they will be updated in another DIV.
The Code that I have a t the moment works to grab the first link on the page but I would prefer to start at the Logged hits section as shown in the first image. Here is the code that makes it work:
public static void searchAllLinks()
{
//HitForker is labelled as '0'
//PandaCrazy tab is labellled as '1'
//Start index at 1
int listIndex = 1;
//Different numbers need for the first run down the list
bool firstRun = true;
//HashSet to store all IWebElements found on page at runtime
HashSet<IWebElement> allLinks;
//Get browser tabs open at current time
var browserTabs = driver.WindowHandles;
//Switch to HF
driver.SwitchTo().Window(driver.WindowHandles[0]);
//Grab links from page starting at Logged Hits Most recent Div
allLinks = driver.FindElements(By.XPath("/html/body/div[6]//a")).ToHashSet();
//Loop through all links in hash
//Hashset contains every link on the page. Only need the middle link to access the content
foreach(IWebElement value in allLinks)
{
if(firstRun == true)
{
//Second link in the hash
if(listIndex == 2)
{
value.Click();
firstRun = false;
listIndex = 0;
whatToClick(value);
}
}
//When linkIndex is 5 then click the value and reset to 0
if(listIndex == 5)
{
value.Click();
whatToClick(value);
listIndex = 0;
}
listIndex++;
}
}
//Method to find which webpage opened on successfull click
public static void whatToClick(IWebElement currentLink)
{
//Grabs the browser handles open
var browserTabs = driver.WindowHandles;
//When the link is clicked on switch to that tab
driver.SwitchTo().Window(driver.WindowHandles[2]);
//2 options
//Hit is not available then add to PC
//Hit is available then accept
try
{
if (driver.FindElement(By.XPath("/html/body/div[3]/div[2]/div/div/div/div[2]/p/span/span/button[2]")).Displayed == true)
{
driver.FindElement(By.XPath("/html/body/div[3]/div[2]/div/div/div/div[2]/p/span/span/button[2]")).Click();
driver.Close();
}
else
{
driver.Close();
driver.SwitchTo().Window(driver.WindowHandles[0]);
}
//Switch Back to the HF
driver.SwitchTo().Window(driver.WindowHandles[0]);
//Catch exception to catch if the hit cannot be accepted
} catch (OpenQA.Selenium.NoSuchElementException e )
{
if (driver.FindElement(By.XPath("/html/body/div[2]/div[1]/nav/div/div[1]/div/div[3]/span/span/button")).Displayed == true)
{
driver.FindElement(By.XPath("/html/body/div[2]/div[1]/nav/div/div[1]/div/div[3]/span/span/button")).Click();
driver.Close();
}
driver.SwitchTo().Window(driver.WindowHandles[0]);
}
catch (OpenQA.Selenium.NoSuchWindowException w)
{
Console.WriteLine("Window Not open");
}
}
Please click the element using relative xpath as follows
driver.FindElement(By.Xpath("//div[#id='log_table']/div[0]/div/div[1]/span[1]//a")).Click()
The above xpath will focus on the first of the table and accesses the hyperlink of that.
I need to implement a simple UIPageViewController using Xamarin in Visual Studio 2017 for mac.
As the documentation says in https://developer.xamarin.com/api/type/MonoTouch.UIKit.UIPageViewController/ I need to connect the previous and next views, using UIPageViewControllerDataSource.GetNextViewController and UIPageViewControllerDataSource.GetPreviousViewController from the UIPageViewControllerDataSource delegate, but I'm not clear where should I do that, nor I see any where to do that using the storyboard designer.
The idea to use the UIPageViewController is to add a quick tutorial of 4 pages before advancing to another View, at the last view of the UIPageViewController there would be a button that calls the segway to the next page.
You can add a UIPageViewController in the Storyboard. Configure the style in the Properties window => Widget(Navigation, Transition Style, Spine Location).
Then you can add the DataSource in the corresponding CS file. Add the firstly shown UIViewController using: SetViewControllers(new UIViewController[] { viewController }, UIPageViewControllerNavigationDirection.Forward, true, null);. Please notice that when you set the Spine Location to Mid, this array should contain at least two UIViewControllers. If not, add just one.
but I'm not clear where should I do that, nor I see any where to do
that using the storyboard designer.
When you want to navigate to the next page, the event GetNextViewController() will fire(i.e. first page - second page). And you should return which Controller will show in this event. Also GetPreviousViewController() means which Controller you want to show when navigate back. Here is my sample:
public class MyPageViewDataSource : UIPageViewControllerDataSource
{
List<UIViewController> pageList;
public MyPageViewDataSource(List<UIViewController> pages)
{
pageList = pages;
}
public override UIViewController GetNextViewController(UIPageViewController pageViewController, UIViewController referenceViewController)
{
var index = pageList.IndexOf(referenceViewController);
if (index < pageList.Count - 1)
{
return pageList[++index];
}
else
{
//when navigate to the last page, return null to disable navigate to next.
return null;
}
}
public override UIViewController GetPreviousViewController(UIPageViewController pageViewController, UIViewController referenceViewController)
{
var index = pageList.IndexOf(referenceViewController);
if (index == 0)
{
//when navigate to the first page, return null to disable navigate to previous.
return null;
}
else
{
return pageList[index - 1];
}
}
}
At last set your UIPageViewController's DataSource to this:DataSource = new MyPageViewDataSource(pageList);
I am working on an application that has a GridView item on an ASP.net page which is dynamically generated and does a partial post-back as items are updated within the grid-view. This partial post-back is causing the tab indices to be lost or at the very least ignored as the tab order appears to restart. The grid view itself already has the pre-render that is being caught to calculate the new values from the modified items in the grid-view. Is there a way to get what element had the focus of the page prior to the pre-render call? The sender object is the grid-view itself.
You can try using this function, which will return the control that caused the postback. With this, you should be able to reselect it, or find the next tab index.
private Control GetControlThatCausedPostBack(Page page)
{
//initialize a control and set it to null
Control ctrl = null;
//get the event target name and find the control
string ctrlName = Page.Request.Params.Get("__EVENTTARGET");
if (!String.IsNullOrEmpty(ctrlName))
ctrl = page.FindControl(ctrlName);
//return the control to the calling method
return ctrl;
}
Here's an instance where I had dynamically generated inputs that updated totals via AJAX on change. I used this code to determine the next tab index, based on the tab index of the control that caused the postback. Obviously, this code is tailored to my usage, but with some adjustments I think it could work for you as well.
int currentTabIndex = 1;
WebControl postBackCtrl = (WebControl)GetControlThatCausedPostBack(Page);
foreach (PlaceHolder plcHolderCtrl in pnlWorkOrderActuals.Controls.OfType<PlaceHolder>())
{
foreach (GuardActualHours entryCtrl in plcHolderCtrl.Controls.OfType<GuardActualHours>())
{
foreach (Control childCtrl in entryCtrl.Controls.OfType<Panel>())
{
if (childCtrl.Visible)
{
foreach (RadDateInput dateInput in childCtrl.Controls.OfType<RadDateInput>())
{
dateInput.TabIndex = (short)currentTabIndex;
if (postBackCtrl != null)
{
if (dateInput.TabIndex == postBackCtrl.TabIndex + 1)
dateInput.Focus();
}
currentTabIndex++;
}
}
}
}
}
I have the following code:
private void dgv_Checks_CellClick(object sender, DataGridViewCellEventArgs e)
{
DoThis();
if (e.RowIndex < 0)
return;
if (dgv_Checks.CurrentCell.OwningColumn.Name == "CopyBalance")
dgv_Checks.Rows[e.RowIndex].Cells["CheckAmountColumn"].Value = dgv_Checks.Rows[e.RowIndex].Cells["AccountBalanceColumn"].Value;
// ****** THIS IS WHERE I AM HAVING ISSUES ******
if (e.RowIndex > -1 && dgv_Checks.Columns[e.ColumnIndex].Name == "SearchColumn")
{
var acctno = dgv_Checks.Rows[e.RowIndex].Cells["AccountNumberColumn"].Value.ToString().Trim();
if (acctno.Length == 7)
{
var acctname = GetAccountName(acctno);
if (acctname.Trim().Length > 0)
{
dgv_Checks.Rows[e.RowIndex].Cells["NameColumn"].Value = acctname;
dgv_Checks.Rows[e.RowIndex].Cells["AccountBalanceColumn"].Value = GetAccountBalance(acctno);
}
else
{
AccountSearchScreen(dgv_Checks.CurrentRow);
}
}
else
{
AccountSearchScreen(dgv_Checks.CurrentRow);
}
}
dgv_Checks.Rows[e.RowIndex].Cells["CheckAmountColumn"].Value = dgv_Checks.Rows[e.RowIndex].Cells["AccountBalanceColumn"].Value;
}
When it reaches this line:
if (e.RowIndex > -1 && dgv_Checks.Columns[e.ColumnIndex].Name == "SearchColumn")
I am getting back the previous cell's index so I am always 1 click behind. Am I not using the right event? I just want to capture a button click and only that one in the "SearchColumn".
Thanks as usual.
If I recall correctly, it has nothing to do with the handler code, and something to do with other page events, master pages, validation, etc. Something is hijacking your request somewhere in the .net framework that causes it to get delayed, but I can't remember where...
Take your gridview and datasource, and put them in a separate file with no master pages or other confounding factors and see if the problem goes away.
If it is now working normally, either add in components from your original page until you can replicate the problem again, or go back to your original page and step through everything it does. This has happened to me at least twice, and all I can remember is that it's something that seems completely unrelated.
Not only have I provided a fairly useless answer, but I've demonstrated once again why I should really keep a (b)log of this crap...