Consider the following simple WinForms form with a textbox and a webbrowser control. Whenever the textbox content changes, the text is pushed to the browser:
public class MainForm : Form
{
public MainForm()
{
var browser = new WebBrowser() { Dock = DockStyle.Fill };
var textbox = new TextBox() { Dock = DockStyle.Fill, Multiline = true };
var splitter = new SplitContainer() { Dock = DockStyle.Fill };
splitter.Panel1.Controls.Add(textbox);
splitter.Panel2.Controls.Add(browser);
this.Controls.Add(splitter);
textbox.TextChanged += delegate { browser.DocumentText = textbox.Text; };
textbox.Text = "<b>hello world</b>";
}
}
(I am doing something like this in my DownMarker code to build a Markdown editor with Stackoverflow's MarkdownSharp library.)
This works fine, except that the WebBrowser control insists on showing the wait cursor whenever DocumentText is set - even if updating the browser content takes only a few milliseconds. This results in mouse cursor flicker when typing in the textbox.
Is there any way to supress these mouse cursor changes? I already considered rate-limiting the DocumentText updates, but I find that the occasional flicker during an update is still annoying and I would prefer instant updates.
edit: Hans' answer pointed me in the right direction. Changing the TextChanged event handler to this seems to work without cursor flicker:
textbox.TextChanged +=
delegate
{
if (browser.Document == null)
{
browser.DocumentText = "<html><body></body></html>";
}
while ((browser.Document == null)
|| (browser.Document.Body == null))
{
Application.DoEvents();
}
browser.Document.Body.InnerHtml = textbox.Text;
};
edit2: the above still shows the wait cursor when the page is made heavier, e.g. by adding images. This might be fixable be doing more fine grained updates of just the html elements that change, but that is obviously much more complex.
Assigning the DocumentText property is a Big Deal, WebBrowser treats it like a navigation command. It can't tell the difference. Which normally takes time, hundreds of milliseconds, enough for it to justify displaying the wait cursor.
A very different approach would be to load a dummy document and alter the DOM through the Document property. That's pretty common in web pages, Ajax and javascript and what-not. No wait cursor for those. Not so sure if that will still fit your editing model, I'd guess at you wanting to load a dummy HTML document with a empty <body> and change the body content.
Should work. Back-up plan is an Update! button. Which would also avoid trying to render half-finished and thus broken HTML.
Related
I have a WinForms application that has a TextBox control (search box) at the top of it. This TextBox is constantly receiving focus during normal application use, and it is very distracting.
I would like the TextBox to only receive the focus if the user explicitly clicks on it.
I can think of a couple rather complicated ways to accomplish this:
Change an image of a text box into a text box when clicked
Keep track of mouse clicks and shift the focus away based on mouse state
Is there something simpler that I can do to accomplish this?
Edit to add better description of problem based on new understanding
Based on the answers that I have received, I now have a bit of a better understanding of what was causing this problem. As the user interacted with my application, various actions would cause controls to either be disabled or to completely disappear. If one of these controls happened to have the focus at the time, then the next control in the tab order would receive the focus.
I don't know what was the "next control" before I added the text box in question. The application has hundreds of controls on screen at any given time, and I'm pretty sure that tab order was never intentionally defined. Whatever it was before, it was innocuous. After adding the search text box, it seemed like that control would always end up with the focus.
Here is a very simple example that demonstrates what was happening:
public class Form1 : Form
{
public Form1()
{
var button = new Button
{
Location = new System.Drawing.Point(159, 67),
Size = new System.Drawing.Size(75, 23),
TabIndex = 0,
Text = #"Click me"
};
button.Click += (sender, args) => button.Enabled = false;
var textBox = new TextBox
{
Location = new System.Drawing.Point(159, 142),
Name = "textBox1",
Size = new System.Drawing.Size(174, 20),
TabIndex = 1
};
SuspendLayout();
ClientSize = new System.Drawing.Size(486, 392);
Controls.Add(textBox);
Controls.Add(_button);
ResumeLayout(false);
PerformLayout();
}
}
After starting the application, clicking on the button will force the text box to get the focus, since it is the next in the tab order. As mentioned by Handbag Crab in the accepted answer, this behavior can be avoided by setting TabStop = false on the text box.
textBox1.TabStop = false;
The above should stop it receiving focus from tabbing.
Subclass the TextBox and over WndProc function to capture the focus message and handle it. Maybe something like this:
if (m.Msg == WM_MOUSEACTIVATE) {
m.Result = (IntPtr)MA_NOACTIVATEANDEAT;
return;
}
base.WndProc(ref m);
What would be the "best" (or a better) way to do the following?
In a carousel page (say 6 content pages), I click a button, part of the action changes the text on that button, but it also has to change for all the other content pages.
I currently have this happening in the carousel pages OnCurrentPageChanged(), where I call a function and pass in "this". helpers.ChangeAll(this);
public void ChangeAll(CarouselSwipePage page)
{
foreach (SwipePageContent v in page.Children)
{
Button b = v.Content.FindByName<Button>("pause");
if (GlobalSettings.Settings.Default.CarouselCountEnabled) //this is set elsewhere and is used to determine whether the carousel is changing automatically, if it is then set the text to pause
{
b.Text = FontAwesomeFont.PauseCircleO;
}
else
{
b.Text = FontAwesomeFont.PlayCircleO;
}
}
}
This works ok on android but on ios when the user swipes to the next content page after clicking the button, the button text is momentarily the old value before changing to the new value, due to the function being called OnCurrentPageChanged().
Apart from that I'm sure there must be a better way to do this, it looks rubbish.
What about creating a style for the buttons, and just change the text value of the style?
So, using binding properties or dynamic resources, when you change the value, it is going to change all the buttons of your application that use this style. I think this approach is pretty much better and simpler than a loop.
I have decided to use templated PopUps to display validation errors for my controls, rather than ToolTips (seem to offer greater flexibility in terms of styling). I am looking for a clean way to ensure that the PopUps are anchored to the control they are referring to so when the window is moved they are moved with them...
One thought i had was to traverse the visual tree and manually set all PopUps IsOpen property to false...this seems like a bit of a "hack". It is less than ideal as it will presumably flicker among other things...Having said this a lot of the solutions i have seen so far on stackoverflow also seem to have their pitfalls. Ideas?
// Reference to the PlacementTarget.
DependencyObject myPopupPlacementTarget;
// Reference to the popup.
Popup myPopup;
Window w = Window.GetWindow(myPopupPlacementTarget);
if (null != w)
{
w.LocationChanged += delegate(object sender, EventArgs args)
{
var offset = myPopup.HorizontalOffset;
myPopup.HorizontalOffset = offset + 1;
myPopup.HorizontalOffset = offset;
};
}
From this link
So I've been working on this project, and we have a WebBrowser object on the form. The purpose of the object is that it loads in HTML Forms into it to be viewed, at this current point in time though, you are able to edit the contents of the HTML form, which is not desired.
I want to simply display this HTML form of information to the user, but not allow them to alter the textboxes or checkboxes or anything of that nature on the form.
I tried using the Navigating event and set e.cancel = true;. This haulted the control from even loading the page. And if I set it to only execute e.cancel = true; after the form had loaded, I could still change text boxes and such on the form, as it only seemed to randomly called the Navigating event.
Does anyone know of a way to get a WebBrowser object to be read only?
Cheers!
You can apply contentEditable attribute to the Body tag of the document.
Document.Body.SetAttribute("contentEditable", false);
This will make your document readonly for user.
You could try accessing all form elements on the page and set the readonly attribute on the tag. Something like:
var inputs = webBrowser1.Document.GetElementsByTagName("input");
foreach (HtmlElement element in inputs)
{
element.SetAttribute("readonly", "readonly");
}
You'd obviously have to repeat the process for all form elements (select etc.), but it should work.
I have been running into this issue as well. Thanks to steavy I have been able to come up with a solution :
Hook up to the DocumentCompleted event (you can do this in the designer) :
myWebBrowser.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(wb_procedure_DocumentCompleted);
Then make it readonly in the event :
private void myWebBrowser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
myWebBrowser.Document.Body.SetAttribute("contentEditable", "false");
}
I do this in the event when the document is fully loaded because I sometimes ran into a NullReferenceException, the body wasn't loaded yet and the line would throw.
In WPF 4.0, I can't seem to get any keyboard shortcuts to work if I swap the user control in the window after it's been loaded. A code sample says a thousand words, so here's what I'm doing:
Window window = new Window { Width = 800, Height = 600 };
window.Loaded += delegate
{
editor = new EditorRoot();
window.Content = editor;
};
app.Run(window);
window gets KeyDown events (and routed commands work fine), but editor never gets any keyboard events (nor do any controls within it). I tried:
editor.Loaded += (sender, e) => Keyboard.Focus(editor);
... but that didn't do anything. EditorRoot extends UserControl and has IsFocusable=true Any ideas what's wrong?
And if this does not work - use Dispatcher.BeginInvoke. From my experience - setting focus synchronously doesn't always work. And not only in WPF 4.
Maybe you could try the FocusManager instead of your approach. I use it and it works, you can even use it in XAML:
FocusManager.FocusedElement=editor;