I've seen this question circulate around the internet a lot, but haven't found an answer that has solved my problem. I am basically using SpicIE.Controls.Toolbar (C#) to create an explorer bar for IE. It has a few text-boxes on it among other things. The problem I have is that when I am typing in the text-box and hit backspace, the browser handles it and instead of deleting the character on the text-box, it goes back one page on the browser. In order to fix this problem, I created my own custom text box to be able to handle backspace properly:
public class MyTextBox:TextBox
{
protected override void OnKeyUp(KeyEventArgs e)
{
if (e.KeyValue == 8)
{
this.Text = this.Text.Substring(0, this.Text.Length - 1);
this.SelectionStart = this.Text.Length + 1;
e.Handled = true;
}
}
}
The code does work as it is supposed to but the browser still goes back one page even after e.Handled is set to be true.
Can anyone help me with this? I feel like I have been to all forums where they discuss this, and still haven't found an answer
Thanks for your help guys!
Generally when this happens it means the extension did not implement IInputObject correctly. I'm not familiar with the SpicIE framework, so I'm not sure if you need to implement it or they do.
Related
I am working from the sample project here: http://www.codeproject.com/Articles/8086/Extending-the-save-file-dialog-class-in-NET
I have hidden the address/location bar at the top and made other modifications but I can't for the life of me manage to disable the button that lets you go up to the parent folder. Ist is in the ToolbarWindow32 class which is the problem. This is what I have at the moment but it is not working:
int parentFolderWindow = GetDlgItem(parent, 0x440);
//Doesn't work
//ShowWindow((IntPtr)parentFolderWindow, SW_HIDE);
//40961 gathered from Spy++ watching messages when clicking on the control
// doesn't work
//SendMessage(parentFolderWindow, TB_ENABLEBUTTON, 40961, 0);
// doesn't work
//SendMessage(parentFolderWindow, TB_SETSTATE, 40961, 0);
//Comes back as '{static}', am I working with the wrong control maybe?
GetClassName((IntPtr)parentFolderWindow, lpClassName, (int)nLength);
Alternatively, if they do use the parent folder button and go where I don't want them to, I'm able to look at the new directory they land in, is there a way I can force the navigation to go back?
Edit: Added screenshot
//Comes back as '{static}', am I working with the wrong control maybe?
You know you are using the wrong control, you expected to see "ToolbarWindow32" back. A very significant problem, a common one for Codeproject.com code, is that this code cannot work anymore as posted. Windows has changed too much since 2004. Vista was the first version since then that added a completely new set of shell dialogs, they are based on IFileDialog. Much improved over its predecessor, in particular customizing the dialog is a lot cleaner through the IFileDialogCustomize interface. Not actually what you want to do, and customizations do not include tinkering with the navigation bar.
The IFileDialogEvents interface delivers events, the one you are looking for is the OnFolderChanging event. Designed to stop the user from navigating away from the current folder, the thing you really want to do.
While this looks good on paper, I should caution you about actually trying to use these interfaces. A common problem with anything related to the Windows shell is that they only made it easy to use from C++. The COM interfaces are the "unfriendly" kind, interfaces based on IUnknown without a type library you can use the easily add a reference to your C# or VB.NET project. Microsoft published the "Vista bridge" to make these interfaces usable from C# as well, it looks like this. Yes, yuck. Double yuck when you discover you have to do this twice, this only works on later Windows versions and there's a strong hint that you are trying to do this on XP (judging from the control ID you found).
This is simply not something you want to have to support. Since the alternative is so simple, use the supported .NET FileOk event instead. A Winforms example:
private void SaveButton_Click(object sender, EventArgs e) {
string requiredDir = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
using (var dlg = new SaveFileDialog()) {
dlg.InitialDirectory = requiredDir;
dlg.FileOk += (s, cea) => {
string selectedDir = System.IO.Path.GetDirectoryName(dlg.FileName);
if (string.Compare(requiredDir, selectedDir, StringComparison.OrdinalIgnoreCase) != 0) {
string msg = string.Format("Sorry, you cannot save to this directory.\r\nPlease select '{0}' instead", requiredDir);
MessageBox.Show(msg, "Invalid folder selection");
cea.Cancel = true;
}
};
if (dlg.ShowDialog() == DialogResult.OK) {
// etc...
}
}
}
I don't this is going to work. Even if you disable the button they can type ..\ and click save and it will take them up one level. You can't exactly disable the file name text box and maintain the functionality of the dialog.
You'd be better off either using the FolderBrowserDialog and setting it's RootFolder property and asking the user to type the filename in or auto generating it.
If the folder you are wanting to restrict the users to isn't an Environment.SpecialFolder Then you'll need to do some work to make the call to SHBrowseForFolder Manually using ILCreateFromPath to get a PIDLIST_ABSOLUTE for your path to pass to the BROWSEINFO.pidlRoot
You can reflect FolderBrowserDialog.RunDialog to see how to make that call.
Since you want such custom behaviors instead of developing low level code (that is likely yo break in the next versions of windows) you can try to develop your file picker form.
Basically it is a simple treeview + list view. Microsoft has a walk-through .
It will take you half a day but once you have your custom form you can define all behaviors you need without tricks and limits.
public virtual void Send(string keysToType, ActionListener actionListener)
{
if (heldKeys.Count > 0) keysToType = keysToType.ToLower();
CapsLockOn = false;
foreach (char c in keysToType)
{
short key = VkKeyScan(c);
if (c.Equals('\r')) continue;
if (ShiftKeyIsNeeded(key)) SendKeyDown((short) KeyboardInput.SpecialKeys.SHIFT, false);
if (CtrlKeyIsNeeded(key)) SendKeyDown((short) KeyboardInput.SpecialKeys.CONTROL, false);
if (AltKeyIsNeeded(key)) SendKeyDown((short) KeyboardInput.SpecialKeys.ALT, false);
Press(key, false);
if (ShiftKeyIsNeeded(key)) SendKeyUp((short) KeyboardInput.SpecialKeys.SHIFT, false);
if (CtrlKeyIsNeeded(key)) SendKeyUp((short) KeyboardInput.SpecialKeys.CONTROL, false);
if (AltKeyIsNeeded(key)) SendKeyUp((short) KeyboardInput.SpecialKeys.ALT, false);
}
actionListener.ActionPerformed(Action.WindowMessage);
}
I need to send a list of keyboard shortcuts to that method
CTRL + A, CTRL + End, etc.
But I don't know how to build such a string.
This far I wrote this:
string shortcuts;
// shortcuts = "\CTRL + A" + "\CTRL + End";
Send(shortcuts, myactionlistener)
Perhaps a 'normal' string is not the best format to do this?
If the string contains 'characters that need to be sent', perhaps a list of KeyEventArgs or some custom built data class could be sent to your Send method. Then you can loop over the list and execute one by one. If you need combinations (like CTRL+K + CTRL+K (=toggle bookmark in Visual Studio)) you might need a composite.
Another option is to create your own DSL.
Too late for Serge but for all the googlers out there...
Serge is referring to the Keyboard class in TestStack.White. The Send function can only be used to send strings literally to another window. You cannot specify control keys here. They are just needed internally sometimes. E.g. Send("{") on my keyboard layout would internally translate to "AltKeyIsNeeded" and "7".
You can send keyboard shortcuts CTRL+A, CTRL+End like this:
myWindow.Keyboard.HoldKey(KeyboardInput.SpecialKeys.CONTROL);
myWindow.Keyboard.Enter("A");
myWindow.Keyboard.LeaveKey(KeyboardInput.SpecialKeys.CONTROL);
myWindow.Keyboard.HoldKey(KeyboardInput.SpecialKeys.CONTROL);
myWindow.Keyboard.HoldKey(KeyboardInput.SpecialKeys.END);
myWindow.Keyboard.LeaveKey(KeyboardInput.SpecialKeys.END);
myWindow.Keyboard.LeaveKey(KeyboardInput.SpecialKeys.CONTROL);
I've done something similar in an application, with the Windows key' shortcuts. I'm used a WH_KEYBOARD_LL hook and when I've got a specific shortcut I call the method. Maybe this could help you.
In my experience of UI automation there were some cases where solution of #m3tikn0b didn't work for me, particularly ALT+DOWN and ALT+UP, not sure why. To make them work I had to use WinForm's SendKeys, for example for ALT+DOWN it is:
SendKeys.SendWait("%({DOWN})");
It has pretty rich syntax described in docs for Send method. However it was not suitable in all my cases as well, so I had to use TestStack.White for the most cases and SendKeys for some rare exclusions.
I am attempting to implement a Language Service in a VSPackage using the MPF, and it's not working quite as I understand it should.
I have several implementations already, such as ParseSource parsing the input file with a ParseRequest. However, when it finds an error, it adds it with AuthoringSink.AddError. The documentation for this implies it adds it to the Error List for me; it doesn't.
I also have a simple MySource class, a subclass of Source. I return this new class with an overridden LanguageService.CreateSource method. The documentation for OnCommand says it's fired 'when a command is entered'. However, it's not.
There's obviously some intermediate step which I haven't done correctly. I've already rambled enough, so I'll be glad to give any additional details by request.
Any clarification is much appreciated.
For the AuthoringSink error list question, I use this behavior in my Language Service. In ParseSource, the ParseRequest class has an AuthoringSink. You can also create a new ErrorListProvider if you want to work outside of the parser's behavior. Here is some example code:
error_list = new ErrorListProvider(this.Site);
error_list.ProviderName = "MyLanguageService Errors";
error_list.ProviderGuid = new Guid(this.errorlistGUIDstring.);
}
ErrorTask task = new ErrorTask();
task.Document = filename;
task.CanDelete = true;
task.Category = TaskCategory.CodeSense;
task.Column = column;
task.Line = line;
task.Text = message;
task.ErrorCategory = TaskErrorCategory.Error;
task.Navigate += NavigateToParseError;
error_list.Tasks.Add(task);
I hope this was helpful.
OnCommand should be firing every time there is a command, in your MySource class you can do something like this (pulled from working code):
public override void OnCommand(IVsTextView textView, VsCommands2K command, char ch)
{
if (textView == null || this.LanguageService == null
|| !this.LanguageService.Preferences.EnableCodeSense)
return;
if (command == Microsoft.VisualStudio.VSConstants.VSStd2KCmdID.TYPECHAR)
{
if (char.IsLetterOrDigit(ch))
{
//do something cool
}
}
base.OnCommand(textView, command, ch);
}
If that doesn't work double check that CodeSense = true in your ProvideLanguageService attribute when you setup your LanguageService package. A whole lot of what is cool to do in the LanguageService requires these attributes to be correctly turned on. Some even give cool behaviors for free!
Another thing to be careful of is that some behaviors like colorizer don't function correctly in the hive in my experience. I don't think these were ones that gave me trouble, but I implemented these a couple of years ago so I'm mostly just looking back at old code.
AuthoringSink.AddError only adds errors to the error list if ParseRequest.Reason is ParseReason.Check. When your ParseSource function attempts to add errors while parsing for any other ParseReason, nothing will happen.
It's possible that your language service is never calling ParseSource with this ParseReason. As far as I know, the only way to get a ParseReason of Check (outside of manually calling BeginParse or ParseSource yourself) is to proffer your service with an idle timer.
I am trying to display a simple message, which I have done probably thousands of times in the past, and NOW... The full string of text is NOT being displayed in the MessageBox. I'm not doing anything differently, so I don't see the problem. Here's my code:
if (MessageBox.Show("The text in this file has changed. Do you want to save changes?",
"TextEditor - Confirmation", MessageBoxButtons.YesNo, MessageBoxIcon.Hand, MessageBoxDefaultButton.Button1) == DialogResult.Yes)
{ //Do stuff
} else {
// Do stuff }
Now, when the messagebox is displayed, the only text that is visible is this:
The text in this file has changed.
NOTE: The Yes/No buttons are visible, and the messagebox looks normal, it doesn't look broken or anything, so I have no idea why I can't display a simple dam question in there anymore!?... Does anybody know about this? Have you experienced this before?
Thanks
OK, THIS IS WIERD... (EDITED)
I have just changed the text for the above messagebox text and now it displays the following:
The text in this file has changed. Do you wa
But the most important part of the question is still not being displayed...
Couple of things to try:
1) If running the debug version, try compiling and running the release version
2) Try creating a whole new project and copying the code to the new project and run it (could be a project setting was changed, then you could diff the files)
3) Try disabling any anti-virus software you have.
have you tried - just to be sure - to escape the whole string by prefixing it with a #-sign?
like so:
if (MessageBox.Show(#"The text in this file has changed. Do you want to save changes?",
#"TextEditor - Confirmation", MessageBoxButtons.YesNo, MessageBoxIcon.Hand, MessageBoxDefaultButton.Button1) == DialogResult.Yes)
{ //Do stuff
} else {
// Do stuff }
Did you try to put your text in a variable juste to see if it work?
string message = #"The text in this file has changed. Do you want to save changes?";
string title = "TextEditor - Confirmation";
if (MessageBox.Show,(message, title, MessageBoxButtons.YesNo, MessageBoxIcon.Hand, MessageBoxDefaultButton.Button1) == DialogResult.Yes){ //Do stuff } else { // Do stuff }
Its something odd/stupid - its time to act back by being stupid
First question - are all of your message boxes affected? If not then this case has something wrong with it. If they are all affected then ... well I don't know what to suggest really. More coffee?
Best thing to do is to reduce the problem down to the smallest possible. Create a new message box and only enter your current text (copy and paste it). Dont set any of the other parameters and take it out of the if statement
If that works, then the problem is with the parameters
- slowly add the parameters until it breaks
If it doesnt then the problem is with the text
- delete the text and retype it - there may be a strange character there - e.g. has the text been near MS Word...
- if that works, then you are golden
- otherwise, delete word by word until it starts working
I reckon you'll find out it something really stupid
The space between "changed." and "Do" wouldn't be some weird character (say NULL), would it? Try to delete the whole text and then type it again by hand.
Hmm... just remembered some weird old bug with McAffee antivirus and .NET whereupon the whole contents of messageboxes would disappear. This was however more than 5 years ago...
Maybe try updating your PC? And - you wouldn't happen to be running McAffee, would you? :)
Idea No. 3: Send us your compiled .EXE and the source files?
Idea No. 4: Compile it, then rip it open with Reflector and check how it has been compiled. Compilers have bugs too...
I've tried it too and it seems to work fine.
Maybe check the regional/language settings on the machine your running it on?
There's no set size for message boxes in the form.Designer.cs is there?
Have you tried creating another solution with 1 form and the following code - btw works form me vs2008 winXP en-gb lang
using System;
using System.ComponentModel;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
if (MessageBox.Show("The text in this file has changed. Do you want to save changes?", "TextEditor - Confirmation", MessageBoxButtons.YesNo, MessageBoxIcon.Hand, MessageBoxDefaultButton.Button1) == DialogResult.Yes)
{
MessageBox.Show("yes");
}
else
{
MessageBox.Show("no");
}
}
}
}
Could you possibly try a newline \n after "changed"?
I have just solved this problem. I am using Windows XP Home Edition and am also using Stardock's WindowBlinds to pretty-up the 500-year old WindowsXP interface. This has never caused any problems in the past, I have been using WindowBlinds for years, and also doing C# stuff for about a year and a half, and this is the first time that WindowBlinds has caused any problems what so ever.
The reason why only part of the text was showing in the MessageBox still ramins a mystery, BUT as soon as I decided to try and Close WindowBlinds and apply the standard XP theme again... All MessageBox's work properly in C#.
Thank you ALL for your good suggestions they are very much appreciated. :o)
Jason Pezzimenti
In my c# .Net application, I've been trying to be able to retrieve the currently selected text in the currently focused window. (Note that it can be any window open in windows, such as word, or safari).
I'm able to retrieve the handle to the currently focused control. (Using a couple of interop calls to user32.dll, and kernel32.dll).
However, I've been unable to consistently be able to get back the selected text.
I've tried using SENDMESSAGE and GET_TEXT. However this only seems to work for some applications (works for simple applications like wordpad, doesn't work for more complex applications like firefox, or word).
I've tried using SENDMESSAGE and WM_COPY. However, again this only seems to work on some controls. (I would think that WM_COPY, would cause the exact same behaviour as manually pressing CTRL-C, but it doesn't).
I've tried using SENDMESSAGE and WM_KEYUP+WM_KEYDOWN to manually stimulate a copy command. BUt this doesn't constantly work either. (Perhaps of an overlap with the actual hotkey pressed by a user to invoke my applications).
Any ideas on consistently being able to retrieve the currently selected text ? (on any application).
I got this working by a combination of a couple of things. So:
Wait for whatever modifiers are currently held down to be released.
Send control+c (using this answer Trigger OS to copy (ctrl+c or Ctrl-x) programmatically)
bool stillHeld = true;
int timeSlept = 0;
do
{
// wait until our hotkey is released
if ((Keyboard.Modifiers & ModifierKeys.Control) > 0 ||
(Keyboard.Modifiers & ModifierKeys.Alt) > 0 ||
(Keyboard.Modifiers & ModifierKeys.Shift) > 0)
{
timeSlept += 50;
System.Threading.Thread.Sleep(timeSlept);
}
else
{
stillHeld = false;
}
} while (stillHeld && timeSlept < 1000);
Keyboard.SimulateKeyStroke('c', ctrl: true);
I'm using WPF so Keyboard.Modifiers is System.Windows.Input.Keyboard, whereas Keyboard.SimulateKeyStroke is from Chris Schmick's answer.
Note, timeSlept is my max time to wait for the user to let go of the key before continuing on its merry way.
I managed to get text for wordpad/notepad and anything that supports UI automation.
The code below may work for you in some cases. I'm going to get a start on using Reflector to see how windows does it for textboxes in the TextBoxBase.SelectedText property.
public static string SelectedText
{
get
{
AutomationElement focusedElement = AutomationElement.FocusedElement;
object currentPattern = null;
if (focusedElement.TryGetCurrentPattern(TextPattern.Pattern, out currentPattern))
{
TextPattern textPattern = (TextPattern)currentPattern;
TextPatternRange[] textPatternRanges = textPattern.GetSelection();
if (textPatternRanges.Length > 0)
{
string textSelection = textPatternRanges[0].GetText(-1);
return textSelection;
}
}
return string.Empty;
}
set
{
AutomationElement focusedElement = AutomationElement.FocusedElement;
IntPtr windowHandle = new IntPtr(focusedElement.Current.NativeWindowHandle);
NativeMethods.SendMessage(windowHandle, NativeMethods.EM_REPLACESEL, true, value);
}
}
I don't believe that it is possible, the currently focused may not contain any selected text. (It may not even contain any text at all). Or the current selection could be an icon, or an image.
Perhaps requiring the user to copy the selected text to the clipboard first may be a solution.
I've possibly misunderstood the question, but could you just send Ctrl+c? If you know the window is always foremost and the text to be copied is selected?
SendKeys.SendWait("^c");
Once copied to the clipboard, it's not tricky to programatically retrieve the contents (you could even check it's actually text at that point).
I think creating clipboard monitor is a good option. I suggest you to look at Klipper (KDE clipboard module), it copied everything you select in the clipboard list, either content is file, folder or some text.
More details can be found here.
Hm... you find it to be easy? How come?
The best alternative to SendKeys.SendWait("^c"); I found was this one:
http://www.c-sharpcorner.com/Forums/ShowMessages.aspx?ThreadID=46203
However, it only works for a few apps, like notepad. For web browsers, it just crashes.
Anyone got anything better?
Try the GetWindowText() API on controls for which the other methods do not work.