Determining the page number of an inline element while using FlowDocumentPageViewer? - c#

I have a FlowDocumentPageViewer control in my application that programmatically advances through each block and inline element in a FlowDocument (this is because it's part of a typing application and doing so gives visual cues which tell the user what to type). Each time I change the inline element I'm focused on, I want to check what page the inline element is on, and if it's not on the current page, to navigate to the page it is on.
If this is not possible, please suggest any alternate solutions.
Also, if it matters, every inline element I'm dealing with is a Run element.

Are you just trying to automatically navigate to the page? If so we don't need to know the page number and should be able to just use BringIntoView? I'm assuming you have a reference to the block?
The following code navigates to the page the 301st block is on when the button is pressed
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.Loaded += new RoutedEventHandler(MainWindow_Loaded);
}
void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
FlowDocument fd = new FlowDocument();
for (int i = 0; i < 1000; i++)
{
fd.Blocks.Add(new Paragraph(new Run(i.ToString())));
}
view.Document = fd;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
(view.Document as FlowDocument) .Blocks.Skip(300).First().BringIntoView();
}
}
If you really, really want the page number you could do:
var previousPage = view.MasterPageNumber;
(view.Document as FlowDocument) .Blocks.Skip(300).First().BringIntoView();
var pageOfControl = view.MasterPageNumber;
view.GoToPage(previousPage);
It didn't flicker or anything in the test app lol! My mate didn't like that though so he suggested:
var ddp = (DynamicDocumentPaginator)view.Document.DocumentPaginator;
var position = ddp.GetObjectPosition(document.Blocks.Skip(300).First());
var page = ddp.GetPageNumber(position);
Just be aware that it is 0 indexed as opposed to the "lol" method which starts at 1

Related

Where to start generating buttons on WPF form

I have WPF form with Grid and inside that Grid in Row(1).Column(1) i have StackPanel.
Inside that StackPanel i want to generate buttons.
I don't know how many buttons will be generated, since form(with grid and stackPanel) can be of different size.
Code below works, buttons are getting generated if i run that piece of code on Button_Click for example.
But buttons are not generated if I run this piece of code after InitializeComponent().
I guess, that after InitializeComponent WPF form is still not drawn(or finished loading) so my stPanel.ActualHeigh =="0", and since I can't divide with zero nothing acctualy happens.
Can you suggest some workaround, or even better proper solution?
public partial class frmReceipt : Window
{
public frmReceipt()
{
InitializeComponent();
addButtonGrp(); //am i too fast :)
}
private void addButtonGrp()
{
//Calculate size of container to determine numbers of button
int btnMinimumHeightSize = 30;
int btnNumberCreated = (Convert.ToInt16(stPanel.ActualHeight) / btnMinimumHeightSize);
for (int i = 0; i < btnNumberCreated; i++)
{
CreateGroupButtons btn = new CreateGroupButtons();
var btnX = new Button();
btnX = (btn.addButton(i, btnMinimumHeightSize, Convert.ToInt16(stPanel.ActualWidth)));
btnX.Click += ClickHandlerGrp;
if (i == btnNumberCreated - 1)
{
btnX.Height = btnMinimumHeightSize + ((Convert.ToDouble(stPanel.ActualHeight) / btnMinimumHeightSize) % 1) * (btnNumberCreated);
}
stPanel.Children.Add(btnX);
}
}
private void ClickHandlerGrp(object sender, RoutedEventArgs e)
{
var button = sender as Button;
MessageBox.Show("Clicked button number: " + button.Tag);
string test = Convert.ToString(button.Tag);
switch (test)
{
case "PLUGrp":
addButtonGrp(); //this is just for test, i don't want to generate buttons this way
break;
default:
break;
}
}
}
}
Thanks a lot!
I think you're right about running your code before the form has displayed. (It would be easy to check by putting a breakpoint on the for loop)
You can use the Loaded event of the form. Put this in your XAML for the window
Loaded="MainWindowView_OnLoaded"
and this in your C#
private void MainWindowView_OnLoaded(object sender, RoutedEventArgs e)
{
addButtonGrp();
}
This should then fire after the form is displayed, when you know the height of your stack panel.

C# winforms webbrowser not going to url's asked for

I was asked by a friend to develop a winform app to be able to extract data. I figured it would be easy enough - how wrong I was!
In my winform, I have included a webbrowser control and some buttons. The URL for the webbrowser is http://www.racingpost.com/greyhounds/card.sd and as you can imagine, it is the place to get data for greyhounds. When on the page above, there are a number of links within this area which are specific to a race time. If you click on any of these, it takes you to that race, and its this data that I need to extract. So, my initial thoughts were to get ALL links off the link above, then store them in a list, then just have a button available to take in whatever link it is, and then take the webbrowser to that location. Once there, I can then look to extract the data and store it as needed.
So, in the first instance, I use
//url = link above
wb1.Url = new Uri(url);
grab the data (which are links for each race on that day)
once I have this, use a further button to go to the specific race
wb1.Url = new Uri("http://www.racingpost.com/greyhounds/card.sd#resultday=2015-01-17&raceid=1344640");
then, once there, click another button to capture the data, after which, return to the original link above.
The problem is, it will not go to the location present in the link. BUT, if I click the link manually within the webbrowser, it goes there no problem.
I have looked at the properties of the webbrowser, and these all look fine - although I can't qualify that tbh!
I know if I try to go to the links manually, I can, but if I try to do it through code, it just wont budge. I can only assume I have done something wrong in the code.
Hope some of that makes sense - first posting, so apologies if I made a mess of it. I will provide all code no problem, but cant seem to figure out how to post the code in 'code format'?
//here is the code
public partial class Form1 : Form
{
Uri _url;
public Form1()
{
InitializeComponent();
wb1.Url = new Uri("http://www.racingpost.com/greyhounds/card.sd");
wb1.Navigated +=new WebBrowserNavigatedEventHandler(wb1_Navigated);
}
classmodules.trackUrl tu;
private void btnGrabData_Click(object sender, EventArgs e)
{
classmodules.utility u = new classmodules.utility();
rtb1.Text = u.GetWebData("http://www.racingpost.com/greyhounds/card.sd");
HtmlDocument doc = wb1.Document;
string innerText = (((mshtml.HTMLDocument)(doc.DomDocument)).documentElement).outerHTML;
innerText = Regex.Replace(innerText, #"\r\n?|\n", "");
rtb1.Text = innerText;
tu = new classmodules.trackUrl();
u.splitOLs(ref tu, innerText);
classmodules.StaticUtils su = new classmodules.StaticUtils();
su.SerializeObject(tu, typeof(classmodules.trackUrl)).Save(#"d:\dogsUTL.xml");
classmodules.ExcelProcessor xl = new classmodules.ExcelProcessor();
xl.createExcel(tu);
}
private void wb1_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
WebBrowser wb1 = sender as WebBrowser;
this.Text = wb1.Url.ToString();
}
void wb1_Navigated(object sender, WebBrowserNavigatedEventArgs e)
{
_url = e.Url;
}
private void btnGoBack_Click(object sender, EventArgs e)
{
goBack();
}
private void goBack()
{
wb1.Url = new Uri("http://www.racingpost.com/greyhounds/card.sd");
}
private void btnGetRaceData_Click(object sender, EventArgs e)
{
HtmlDocument doc = wb1.Document;
string innerText = (((mshtml.HTMLDocument)(doc.DomDocument)).documentElement).outerHTML;
rtb2.Text = innerText;
}
//###############################
//OK, here is the point where I want to take in the URL and click a button //to instruct the webbrowser to go to that location. I add an initial //counter to 0, and then get the first url from the list, increment the //counter, then when I click the button again, urlNo wil be 1, so then it //tries the second url
int urlNo = 0;
private void btnUseData_Click(object sender, EventArgs e)
{
if (tu.race.Count > urlNo)
{
string url = tu.race[urlNo].url;
wb1.Url = new Uri(url);
lblUrl.Text = url;
urlNo++;
}
else
{
lblUrl.Text = "No More";
}
}
Did you try the Navigate(...) method? In theory, the behavior of Navigate and Url is the same, but I can infer that they behave a bit different.
http://msdn.microsoft.com/en-us/library/system.windows.forms.webbrowser.navigate(v=vs.110).aspx

Handling an array of buttons

I'm creating a simple game where a melody is played, and buttons that corresponds to notes are supposed to be highlighted.
Then the user is to push the button, and during each click the buttons are to highlighted again. I would like to place all the buttons in the GUI graphically.
Can I add a highLight method to the buttons in the GUI? I know I probably could create a new class that ineherits from some button class and create the buttons in the code but I would prefer to do it graphically.
What is the neatest way to handle the button outputs? I know I could paste in code for each button like
private void button_withIndexA(object sender, EventArgs e)
{
checkIfThisNoteWasCorrect();
highLightThisButton();
setHighLightOffForAllOtherButtons();
}
However, I think it would be neater to collect all buttons in some sort of container class and make a function like
class buttonArrayHandler
{
/*constructors etc*/
private void someButtonWasClicked(object sender)
{
/*Check which button was clicked, and do stuff accordingly*/
}
}
However I don't know how to do that. Suggestions?
You could change the button colors to create a highlight effect, but if it's a game you can use images / graphics for buttons, and swap them to another graphic when clicked.
When you double click on a button / graphic / control, by default it creates a method and links it to the click action for you. Instead click on each control, then the little lightning icon, and under the click action, pick the same method for all of them.
Then in your method cast the object sender to get the original control, for example:
var clickedButton = (Button)sender;
Where (Button) may be (Graphics) or whatever type of control you used as a button.
EDIT:
If you need to access a group of controls, you can either keep a global list of names at the top of the form and loop through them:
public List<string> buttonList = new List<string>() { "button1", "button2" };
void SomeMethod()
{
foreach (var controlName in buttonList)
{
this.Controls[controlName].Text = "TEST";
}
}
Or use a fixed name and number range:
void SomeMethod()
{
for (int i = 1; i <= 2; i++)
{
this.Controls["button" + i].Text = "TEST";
}
}
I would recommend adding the buttons programatically just like Carlos487 mentioned, here is the code segment that I made and could work to your advantage:
public Form1()
{
InitializeComponent();
int topMod = 0;
for (int i = 0; i < 5; i++)
{
MakeButton(i,topMod);
topMod += 20;
}
}
public void MakeButton(int index, int margin)
{
Button currentButton = new Button();
currentButton.Text = "Note" + index;
currentButton.Top += margin;
currentButton.Click += OnButtonClick;
panel1.Controls.Add(currentButton);
}
public void OnButtonClick(object sender, EventArgs e)
{
//checkIfThisNoteWasCorrect();
//highLightThisButton();
//setHighLightOffForAllOtherButtons();
MessageBox.Show("My Action was activated!");
}
As you can see you can place the common functions within the OnButtonClick method like I did here, giving all of the buttons the same event sequence. If you do not want to go through the whole process of programming buttons then you could also just do this:
currentButton.Click += OnButtonClick;

How to disable every navigation in WebBrowser?

I have a WebBrowser control which I dinamically refresh/change url based on user input. I don't want to let the user to navigate, so I set AllowNavigation to false. This seems to be OK, however the below link is still "active":
Close Page
The issue here is: If the user clicks it, and confirms closure in the pop-up window I can't manage WebBrowser anymore. Looks like it is closed though the last page is still visible. Also I can't remove this link as the site is not managed by me.
Disable the control? Nope, I have to allow the user to highlight and copy text from the webpage.
Do I have any other option to disable literally ALL links?
#TaW: here is my code based on yours. So I have to set the url from my code and call a custom one:
button_click()
{
webBrowser1_load_URL("http://website/somecheck.php?compname=" + textBoxHost.Text);
}
Here it is the function:
private void webBrowser1_load_URL(string url)
{
string s = GetDocumentText(url.ToString());
s = s.Replace(#"javascript:window.close()", "");
webBrowser1.AllowNavigation = true;
webBrowser1.DocumentText = s;
}
The rest is exaclty what's in your answer:
private void webBrowser1_DocumentCompleted(object sender,
WebBrowserDocumentCompletedEventArgs e)
{
webBrowser1.AllowNavigation = false;
}
public string GetDocumentText(string s)
{
WebBrowser dummy = new WebBrowser(); //(*)
dummy.Url = new Uri(s);
return dummy.DocumentText;
}
Still it's not working. Please help me to spot the issue with my code.
If you have control over the loading of the pages you could grab the pages' text and change the code to disable rogue scripts. The one you showed can simply be deleted. Of course you might have to forsee more than the one..
Obviously this could be eased if you could do without javascript alltogether, but if that is not an option go for those that do real or pseudo-navigation..
private void webBrowser1_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
webBrowser1.AllowNavigation = false;
}
private void loadURL_Click(object sender, EventArgs e)
{
webBrowser1.AllowNavigation = true;
string s = File.ReadAllText(textBox_URL.Text);
s = s.Replace("javascript:window.close()", "");
webBrowser1.DocumentText = s;
}
If the pages are not in the file system, the same trick should work, for instance by loading the URL into a dummy WebBrowser like this:
private void cb_loadURL_Click(object sender, EventArgs e)
{
string s = GetDocumentText(tb_URL.Text);
s = s.Replace("javascript:window.close()", "");
webBrowser1.AllowNavigation = true;
webBrowser1.DocumentText = s;
}
public string GetDocumentText(string s)
{
WebBrowser dummy = new WebBrowser(); //(*)
dummy.Url = new Uri(s);
return dummy.DocumentText;
}
Note: According to this post you can't set the DocumentText quite as freely as one would think; probably a bug.. Instead of creating the dummy each time you can also move the (*) line to class level. Then, no matter how many changes you had to make, you would always have an unchanged version, th user could e.g. save somewhere..

Get source of Webpage in Webbrowser C#

Currently I have a website loaded in a WebBrowser component, which keeps changing stuff inside a certain <a> inside the page. In order for me to get the data, I have to create another WebRequest each 5 seconds, just to refresh the data (I think they're called dynamic pages). I've tried fetching the data from the WebBrowser (WebBrowser.DocumentText), but the value just remained the same, even though I am pretty sure it changed, because I can see it changed. I think the webrequest each 5 seconds takes up unnecesary memory space, and that this can be done easier.
Do you guys maybe know a way for me to do this?
Guessing at Winforms. You'll want to use the Document property to read back the DOM. Here's an example. Start a new Winforms project and drop a WebBrowser on the form. Then a label and a timer. Make the code look like this:
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
webBrowser1.Url = new Uri("http://stackoverflow.com/questions/10781011/get-source-of-webpage-in-webbrowser-c-sharp");
webBrowser1.DocumentCompleted += webBrowser1_DocumentCompleted;
timer1.Interval = 100;
timer1.Tick += new EventHandler(timer1_Tick);
}
void timer1_Tick(object sender, EventArgs e) {
var elem = webBrowser1.Document.GetElementById("wmd-input");
label1.Text = elem.InnerText;
}
void webBrowser1_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e) {
timer1.Enabled = true;
}
}
The browser will navigate to your question. Type something in the Answer box, note how the label displays what you typed.
You'll need to tweak this code to work with your specific web page, alter the "wmd-input" element name. Use a DOM inspection tool to find the name. I like Firebug.
You could try to get the source via JavaScript.
Use the InvokeScript method to execute return document.documentElement.outerHTML;
This will return an Object which you should be able to type cast to a String.

Categories

Resources