When reading some RichTextControl properties inside Application.Idle some IME won't work.
Given this simple code:
_richTextControl = new RichTextControl();
Application.Idle += delegate(object sender, EventArgs e) {
btnCopy.Enabled = _richTextControl.SelectionLength > 0;
btnPaste.Enabled = _richTextControl.CanPaste();
};
It'll work fine with most IME I tried but, at least, for Chinese (Traditional, Taiwan) with Microsoft Chinese Traditional Array (6.0) and Chinese Traditional DaYi (6.0) it'll prevent IME to work properly (I tried only with Windows 7, in English and Taiwanese). You can start typing but when Idle code is executed (reading SelectionLength property and invoking CanPaste() method, here I wrote them both but each one is enough to reproduce this) it'll stop working (you type but pop-up window is closed immediately and nothing is sent to RichTextControl).
I checked MSDN about EM_GETSELTEXT and EM_CANPASTE but it says nothing about this issue. I also tried to replace SelectionLength property manually sending EM_GETSELTEXT with SendMessage() but it doesn't change this odd behavior (however I checked WordPad - which uses rich editor - and it works properly even if (AFAIK) MFC commands use idle time to update UI status).
Of course I can move some code into SelectionChanged event and update UI both from Idle and from SelectionChanged (and it'll work) or I may update some flags in SelectionChanged and change UI in Idle but this will break existing code and it'll force a big change in UI library (all commands have not knowledge of Idle or SelectionChanged, they simply return true in a CanExecute() method when their target control allows their execution). If nothing else is viable then I may derive a class from RichTextControl to make these methods/properties callable in Idle (using flags updated elsewhere) but (to avoid a big refactoring) I would know if it's a knows issue, a bug related to IME itself or if there is something else I missed.
Related
I have some software which manipulates text on the Windows clipboard. Specifically it will remove an underscore _ (and return characters) from the front of clipboard text, then check if the remaining text is a URL. If it is, the URL without the underscore is stored on the clipboard, otherwise the original text is left. To monitor the clipboard for changes, I am using code from the answer to this question, provided by DBKK. It works fine, but randomly stops working after an amount of time which can be anywhere between 5 mins and ~24 hours.
Below is the code which is called on the clipboard event trigger:
private void clipboardMonitor_ClipboardChanged(object sender, ClipboardAssist.ClipboardChangedEventArgs e)
{
lock(lockVar)
{
if (locked)
{
return;
}
else
{
locked = true;
//this.clipboardMonitor.ClipboardChanged -= new System.EventHandler<ClipboardAssist.ClipboardChangedEventArgs>(this.clipboardMonitor_ClipboardChanged);
count++;
label1.Text = count.ToString();
removeUnderscore();
//this.clipboardMonitor.ClipboardChanged += new System.EventHandler<ClipboardAssist.ClipboardChangedEventArgs>(this.clipboardMonitor_ClipboardChanged);
locked = false;
}
}
}
I have added various debugging code. Firstly the counter, which made me realise when I write back to the clipboard it triggers the event again. I added the two lines that unset then set the event, so modifying the clipboard doesn't call the event again while it is running. I also added code which would block the event from running if it hadn't finished the previous run (the if(lock) part). Eventually the event stops triggering (as can be seen by a counter not being incremented). It is definitely a problem with the event, I added removeUnderscore() to a button call and that still works fine when the event has stopped triggering.
My theory is that because of the weird way it can call its self, something is going wrong and not adding the event again. I seem unable to lock the the function using a lock object or the boolean value. Any ideas on what might be going wrong?
Edit:
I created another program with the same even which only counts clipboard changes. It appears to run fine indefinitely, so the problem is definitely caused by the event calling it's self. I need a robust way to suspend the even while it is executing.
I can't get a form to remain on top, in .net
I have checked How to make form always on top in Application and the answer there mentions form1.TopLevel = true; and I've checked How to make a window always stay on top in .Net? and it says Form.ActiveForm.TopMost so i've tried Form.ActiveForm.TopMost = true; and this.TopMost = true;
private void Form1_Load(object sender, EventArgs e)
{
this.TopLevel = true; //default anyway
Form.ActiveForm.TopMost = true;
this.TopMost = true;
}
But as you can see, a notepad window or any window can cover it.
Added
I have tried every suggestion made so far.
In response to Han's advice, "Of all the possible places to set TopMost property, the Load event is the worst. It should be set in the constructor, so the window gets created top-most right away. Or it should be set after it is visible, so after the Load event. Use the constructor. ". I tried putting those lines in the constructor.
public Form1()
{
InitializeComponent();
this.TopLevel = true; //default anyway
//Form.ActiveForm.TopMost = true; (commented to prevent a System.InvalidOperationException, presumably the form isn't yet active at this stage)
this.TopMost = true;
}
And the the other two suggestions, from others-
I tried setting the Form's TopMost field in the designer to True.
And running the EXE directly rather than just clicking play in visual studio.
Same issue.
And if you find that hard to believe, i've taken a 1min video here showing just that. https://screencast-o-matic.com/watch/cbXXrw2oTN
A potentially useful comment was mentioned..
Steve comments - "OK something definitively odd is happening here. I have tried to create a simple topmost form using linqpad and at the first run I get your same behavior. At the second run though everything works as expected."
A workaround, is this, following on from Han's point to put not put it in Load. Load is indeed too early. I've been finding that (at least on the system with the issue), The Constructor is also too early. I find that putting it in the Shown event, works.
One possible solution to this, is to run this patch, https://support.microsoft.com/en-us/help/2587473/topmost-windows-are-not-always-in-the-topmost-position-in-windows-7-or But be warned, if you want to uninstall it, I have some doubts whether it uninstalls correctly. And it's also not clear to me whether the patch is working or working sporadically. It likely works but it's hard for me to tell.
One commenter thought it's just my system, though that's not the case, as Steve ran into the same issue the first time he ran it. I find it may be most prone to happen after windows restarts, So, the program is running very fresh. But I find that putting that code in the Shown event, is fine and the form stays on top.
I tried instead of TopMost=true, using SetWindowPos, to set the window on top, I tried it fairly early like in the constructor, and late, like in the Shown event or on a button click, and I found it was fine in the Shown event or on a button click. So the issue was related to the TopMost=true line, or SetWindowPos line, firing too early, prior to the window it is setting appearing.
When calling SetWindowPos later one can use either this.Handle or GetForegroundWindow(), the former is easier as it's native. When calling it earlier one must use this.Handle. And using this.TopMost=true avoids all winAPI calls completely.
So in short, you could try the patch, though note that it might not uninstall right.. Or you can try the workaround of putting this.TopMost=true in the Shown event of the form.
I'm aware there is a bug with .Net 2.0 with LCG which generates AccessViolationException when certain IEnumerable<> and similar objects are used; and that there is a hotfix.
I have a project in .Net 4.0 using WinForms running on Windows 7 x64. I have a very simple form - nothing but a form and a RichTextBox docked to take up the whole client size.
From a background thread, I periodically call an update method on my form that does something similar to the following:
public static void Log(string text)
{
Invoke((MethodInvoker)(() => {
lock(richTextBox) {
richTextBox.Text += text;
}
}));
}
I do actually have both the inside of the invoke and the outside in a try/catch (Yes, I know this is horrible!) and I'm doing some additional things like putting the caret to the end and scrolling to it. I'm also using StringBuilder but this is all beside the point:
Rarely, but inevitably, while debugging, my IDE detects an AccessViolationException somewhere in the code that's updating the text box. It's sometimes in the bit that updates the text, it's sometimes at the bit that makes the selection, and it's sometimes at the bit that scrolls to the caret. If I press F5 to continue, I usually don't see the problem for some time and the application continues as before.
There is nothing fancy happening with this text box. There is no race condition (first of all because I have a lock), but also because there is simply nothing in my code that would try and write to the text box at the same time as something else is trying to.
Any idea why this may be happening? I'm sorry, I don't know what other information I can put here, as I'm not even sure why this is happening in the first place.
Not sure what the rest of your code is doing but I reckon the problem is with lock(richTextBox). If the GUI thread is doing something with the RichTextBox, the lock will not work Or if you are accessing this RichTextBox in some other thread.
My application uses the UiElement.TouchDown event in various places, one of them is to let the user stop a spinning wheel. In this situation, one can easily notice a short delay of about 1/3s between the actual touching of the screen and the TouchDown event.
I have set Stylus.IsPressAndHoldEnabled to false.
In order to troubleshoot this problem, I've written a test tool that reports WPF events and native window messages, and I noticed that as soon as I touched the screen, messages with id 0x02CC, 0x011B and 0x011A are generated, about 100 to 300 ms before the TouchDown event. This leads me to believe that the drivers report the touch quickly, and the delay is introduced somewhere later in the WPF translations.
Is there a way to make the touch interaction more responsive? Please ask for any information you need!
Unless Flicks are needed set both in XAML or by manually setting in code behind: (I assume you meant to say IsPressAndHoldEnabled not IsTouchAndHoldEnabled)
Stylus.IsPressAndHoldEnabled="False" Stylus.IsFlicksEnabled="False"
Furthermore consider handling the TouchDown Event and also capturing it to the relevant UIElement (don't forget to release it in TouchUp).
uielement.CaptureTouch(e.TouchDevice);
e.Handled = true;
The AxAcroPDF swallows all key-related events as soon as it gets focus, including shortcuts, key presses, etc. I added a message filter, and it doesn't get any key-related messages either. It's a COM component, could that be relevant?
Is there any way to catch these before the control starts swallowing them?
Hans is correct, the Acrobat Reader spawns two child AcroRd32 processes which you have no direct access to from within your managed code.
I have experimented with this and you have three viable options:
You can create a global system hook, and then look for and filter out / respond to WM_SETFOCUS messages sent to your child AcroRd32 windows. You can accomplish some of this from within C# by using a wrapper library, such as the one here: http://www.codeproject.com/KB/system/WilsonSystemGlobalHooks.aspx
You also need to identify the correct processes as there may be more than one instance of your application, or other instances of AcroRd32. This is the most deterministic solution, but because your application will now be filtering messages sent to every single window in existence, I generally don't recommend this approach because then your program could negatively affect system stability.
Find an alternate PDF viewing control. See this answer for a few commercial components: .net PDF Viewer control , or roll your own: http://www.codeproject.com/KB/applications/PDFViewerControl.aspx
Find an acceptable hack. Depending on how robust your application needs to be, code such as the following may be suitable (it was suitable for my case):
DateTime _lastRenav = DateTime.MinValue;
public Form1()
{
InitializeComponent();
listBox1.LostFocus += new EventHandler(listBox1_LostFocus);
}
private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
{
axAcroPDF1.src = "sample.pdf"; //this will cause adobe to take away the focus
_lastRenav = DateTime.Now;
}
void listBox1_LostFocus(object sender, EventArgs e)
{
//restores focus if it were the result of a listbox navigation
if ((DateTime.Now - _lastRenav).TotalSeconds < 1)
listBox1.Focus();
}
I might finally have a ridiculously simple answer. So far in testing this is working.
Having suffered from this problem for quite some time and having built a complex system of each custom control recording which of them last had focus and using a timer to flip focus back (when acropdf grabbed it) I revisited this problem and read a great number of answers (looking for recent solutions). The information gleaned helped me with the idea.
The idea is to disable the (acropdf) control whilst it is loading as in the following example (code reduced for clarity)
AxAcroPDF_this.Enabled = False
AxAcroPDF_this.src = m_src
Then on a timer, after say 1 second.
AxAcroPDF_this.Enabled = True
Basically the idea is to tell Windows not to let users use the acropdf control until allowed, so asking Windows to prevent it from getting focus (because users are not allowed in there).
So far this is holding up, I will edit this if anything changes. If it doesn't work completely for you then maybe the idea points into a useful direction.
It is an out-of-process COM component, that's the problem. Completely in violation of Windows SDK requirements as laid out in SetParent(). Once its window gets the focus, the message loop in the acroread.exe process gets all the messages, your message filter cannot see any messages anymore.
Technically it is fixable by using SetWindowsHookEx() to inject a DLL into the process and monitor messages with WH_GETMESSAGE. But you can't write such a DLL in the C# language.
Major suck, I know. There never seems to be any lack of it with that program.
For some reason Tim's answer, disabling the AxAcroPDF control directly, didn't work in my case. The Leave event on the previously-selected Textbox would never fire, either.
What is working is nesting the AxAcroPDF control inside of a disabled GroupBox. Since the users of my application need to only see the PDF, not interact with it, the GroupBox's Enabled property is set to False in the designer.