Resize winrt page when on on screen keyboard showing - c#

I have a Windows 8.1 C# app that shows a page with a pretty big textbox (covering almost all of the page; its a writing app).
When the on screen keyboard shows up, it overlays half of the textbox.
I would like to resize the textbox (or even the whole page) so it isnt covered by the keyboard.
I am now thrying to achieve this using the static InputPane and subscribing to its showing and hiding events. I then try to change the margins of my textbox using the occuled rectangle provided in the eventargs. This works, but since my page is still the height of the screen it scrolls it halfway to the bottom.
public MainPage()
{
var inputPane = InputPane.GetForCurrentView();
inputPane.Showing += this.InputPaneShowing;
inputPane.Hiding += this.InputPaneHiding;
}
void InputPaneHiding(InputPane sender, InputPaneVisibilityEventArgs args)
{
this.EditBox.Margin = new Thickness();
}
private void InputPaneShowing(InputPane sender, InputPaneVisibilityEventArgs args)
{
this.EditBox.Margin = new Thickness(0, 0, 0, args.OccludedRect.Height);
}
While trying this out I'm getting the feeling this isnt the ideal solution, but I havent got a better idea. Ideally I'd think it would be something like the vertical split when you have to apps opened, but then horizontally with the keyboard at the bottom and the app only the size available above the keyboard.
Any idea if this is possible?

Since you are handling the interface occlusion yourself you should set the property EnsuredFocusedElementInView from InputPane.Showing eventargs to true. It would prevent interface from automatically scrolling.
private void InputPaneShowing(InputPane sender, InputPaneVisibilityEventArgs args)
{
args.EnsuredFocusedElementInView = true;
this.EditBox.Margin = new Thickness(0, 0, 0, args.OccludedRect.Height);
};

Related

Prevent Form from showing outside the working area

I created a custom form to show it as a ToolTip but to show data on it, the show will be display when the user hovers on a Button and disappear on mouse leave, everything works perfectly but how can I prevent the Form from display outside the screen area??
Here is the code I used to show the Form:
Info_Form tooltip = new Info_Form();
private void Button131_MouseHover(object sender, EventArgs e)
{
Button b = (Button)sender;
tooltip = new Info_Form();
tooltip.Height = tooltip.Height + 30;
tooltip.Location = new Point(
b.Right,
b.Top);
tooltip.Show();
}
private void Button131_MouseLeave(object sender, EventArgs e)
{
tooltip.Close();
}
I suggest to:
declare your ToolTip Form in Program.cs
add static Methods to show and hide it
Of course you can use a dedicated class that exposes static methods to handle your ToolTip Form
You can then reach your ToolTip Form from anywhere in your application, just call ShowToolTip() and HideToolTip() when the Mouse pointer enters or leaves a specific Control: the bounds of this Control, translated to Screen coordinates, are used as reference to position the ToolTip.
Here, I'm using a simplified method to determine whether the ToolTip should be shown to the right or left and to the top or bottom of the reference Control:
if the reference Control is positioned to the left half of the screen, then the left potion of the Form is: ToolTip.Left = [ref Control].Left
otherwise, ToolTip.Left = [ref Control].Right - ToolTip-Width
the same logic applies to the top position of the ToolTip
Adjust this simple calculation to position your ToolTip Form using a different logic, if needed.
▶ There's no need to dispose of the ToolTip Form: it's automatically disposed of when the Form instance passed to Application.Run() is closed.
Note: If the Application is not DpiAware and the Screen where the Application's Windows are shown is scaled, any measure relative to either the Screen or the Windows/Forms may be virtualized, so you receive wrong results and any calculation will be off.
See the notes here:
Using SetWindowPos with multiple monitors
using System.Drawing;
using System.Threading.Tasks;
using System.Windows.Forms;
public static frmToolTip tooltip = null;
[STAThread]
static void Main() {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
tooltip = new frmToolTip();
Application.Run(new SomeForm());
}
public async static Task ShowToolTip(Control control, string title) {
await Task.Delay(400);
if (tooltip.Visible) return;
Rectangle refBounds = control.RectangleToScreen(control.ClientRectangle);
if (!refBounds.Contains(Cursor.Position)) {
HideToolTip();
}
else {
var screen = Screen.GetBounds(control);
var leftPos = refBounds.Left <= screen.Width / 2 ? refBounds.Left : refBounds.Right - tooltip.Width;
var topPos = refBounds.Top <= screen.Height / 2 ? refBounds.Bottom + 2: refBounds.Top - tooltip.Height - 2;
tooltip.Location = new Point(leftPos, topPos);
tooltip.Text = title;
tooltip.Show(control.FindForm());
}
}
public static void HideToolTip() => tooltip.Hide();
In any Form, use a Control's MouseEnter and MouseLeave events to show/hide your ToolTip.
Note: I'm using Task.Delay() to delay the presentation of the ToolTip, so it doesn't show up if you briefly move the Mouse Pointer over the Control that shows it.
It also verifies whether the Form ToolTip is already shown (you cannot show a Form twice) or the Mouse Pointer has moved outside the bounds of the reference Control in the meanwhile (the ToolTip is not shown in this case).
Change this behavior as required.
In ShowToolTip(), I'm passing a string, meant to be used as the Title of the ToolTip. It's just an example, to show that you may pass any other parameter to this method (a class object, maybe) to setup the ToolTip Form in a custom way, different based on the caller requirements.
using System.Threading.Tasks;
public partial class SomeForm : Form
{
// [...]
private async void SomeControl_MouseEnter(object sender, EventArgs e)
{
await Program.ShowToolTip(sender as Control, "Some Title Text");
}
private void SomeControl_MouseLeave(object sender, EventArgs e)
{
Program.HideToolTip();
}
}

Contents of a panel won't visually update within a given time, but will clear completely afterwards

I have a form with a picturebox docked to fill the whole thing. On this form, I have a panel that is normally invisible and another picturebox; on the panel, I have a label and another panel with a label.
Here is what SHOULD happen when the user hovers over the second picturebox:
The picturebox's image changes and the first panel becomes visible, making the second panel and both labels visible too
The user clicks on the second label
The second label's OnClick handler makes the first label's text change and the second panel becomes invisible
A timer ticks for a few seconds
A code segment in the timer's OnTick handler causes the image in the second picturebox to change and the first panel to become invisible
Here is what DOES happen:
The picturebox's image changes and the first panel becomes visible, making the second panel and both labels visible too
The user clicks on the second label
The second label's OnClick handler sets the first label's text to a new string and sets the second panel's Visible property to false, BUT the second panel stays visible (although you can't interact with it) and the first label's text gets written on top of the old text
A timer ticks for a few seconds
A code segment in the timer's OnTick handler causes the image in the second picturebox to change and the first panel to become invisible
I've tried everything I can think of. I've called Invalidate, Update, and Refresh on every control in the form, I've called Application.DoEvents, I've reset the image in the background PictureBox to itself, nothing. The REALLY weird part is that in step 5, when the front picturebox resets itself and all panels are set invisible, nothing gets left behind - it's just for that brief few seconds between the OnClick handler terminating and the timer's OnTick cleaning up that there are problems. I can edit this for more information if needed, but does anyone have any ideas of what to do?
Edit: It's been pointed out to me that I should probably upload the code for this. Well, that code is a hacked-together mess, but okay. Also: there are some weird extra bits (in the enum types among others), they're for later parts of the project and irrelevant right now.
bool CountingHoverTime = false;
int HoverTime = 0;
int MasterTick = 0;
enum GhostState { Stand, Speak, Pet, Ask };
GhostState curState;
public enum TalkType { HoverGen, Petted, Spont, TimerMsg, Response };
private void Form1_Load(object sender, EventArgs e)
{
FormBorderStyle = FormBorderStyle.None;
ShowInTaskbar = false;
TopMost = true;
ControlBox = false;
Text = String.Empty;
WindowState = FormWindowState.Maximized;
SetStyle(ControlStyles.SupportsTransparentBackColor, true);
this.BackColor = Color.Transparent;
this.TransparencyKey = Color.Transparent;
}
protected override void OnPaintBackground(PaintEventArgs e)
{
//base.OnPaintBackground(e);
}
private void pictureBox2_MouseHover(object sender, EventArgs e)
{
if(curState == GhostState.Stand)
{
CountingHoverTime = true;
}
}
private void timer1_Tick(object sender, EventArgs e)
{
if((curState != GhostState.Ask) && (curState != GhostState.Stand))
{
MasterTick++;
if(MasterTick > 10)
{
SetToBasic();
}
}
else
{
MasterTick = 0;
}
if (CountingHoverTime)
{
HoverTime++;
if (HoverTime > 4)
{
HoverTime = 0;
curState = GhostState.Ask;
Say("What can I do for you?", TalkType.HoverGen);
}
}
}
public void SetToBasic()
{
curState = GhostState.Stand;
ghostBox.Image = Properties.Resources.stickStand1;
TalkPanel.Visible = false;
}
public void Say(String speak, TalkType type)
{
mainText.Text = speak;
if(type == TalkType.Response || type == TalkType.Spont)
{
curState = GhostState.Speak;
}
else
{
curState = GhostState.Ask;
}
ghostBox.Image = Properties.Resources.stickTalk;
if (type == TalkType.HoverGen)
OptionPanel.Visible = true;
else
OptionPanel.Visible = false;
TalkPanel.Visible = true;
backBox.Image = Properties.Resources.background;
ghostBox.Invalidate();
TalkPanel.Invalidate();
mainText.Invalidate();
backBox.Invalidate();
ghostBox.Update();
TalkPanel.Update();
mainText.Update();
backBox.Update();
Application.DoEvents();
}
private void op1label_Click(object sender, EventArgs e)
{
curState = GhostState.Speak;
OptionPanel.Visible = false;
Say("No can do yet.", TalkType.Response);
}
Edit 2: I've put together a gif visualization of what's happening, thank you HandbagCrab.
I've approached this from a different direction. I think the issue is to do with the picturebox you have docked on the form causing some issues.
To fix it and still keep the transparency, get rid of the backBox. Set the background colour of the form to a colour you're not going to use then set the transparency key to that colour. This will make those areas of the form transparent. Now you just need your hidden panel and your labels and whatever control it is that hosts your stick man image.
I've left the backgrounds of the labels as pink here but you should change them to your background colour so that they're not transparent.
When I run the form I get the transparency still and when clicking on the grey panel (I've used a panel to simulate your stick man) shows the hidden panel with the labels. Clicking label2 updates the text on label1 (the one that contains the text "longish text"), completely replacing the text.
Here it is in use (I've not done a gif as I wanted each step to be clearly visible)
Here's the application when open:
Here it is after clicking the grey box:
Here's the updated text when clicking label2:
I've left the application border style as Sizeable just so you can see where the border lies. I also took the screenshots over a black background so there was no visual clutter.
Here's me right clicking the desktop through the transparent section:

How to set tooltip at current mouse location?

I registered Hotkey: Ctrl + Space. Hotkey message is sent to:
private void Hotkey_press()
{
... // I want to show tooltip "Hello" at current mouse location.
}
Is there any way to show this tooltip even the mouse doesnt point to any control and it is outside my Window.Form1?
Edit: That ToolTip can show even the form lost focus or hide
You want something like
ToolTip tt = new ToolTip();
IWin32Window win = this;
tt.Show("String", win, mousePosition);
Where the MousePosition can be obtained from the MouseEventArgs via
private SomeMouseEventHandler(object sender, MouseEventArgs e)
{
System.Drawing.Point mousePosition = e.Location;
...
}
or using
System.Drawing.Point mousePosition = Cursor.Position;
also, you may want to set a longer duration for which the ToolTip is displayed, well just use the overloads available for the Show method, tt.Show("String", win, mousePosition, 5000); will display the tool tip for 5 seconds.
I hope this helps.
Tooltip tip = new ToolTip();
tip.ShowAlways = true;
tip.Show("My tooltip",this,Cursor.Position.X,Cursor.Position.Y);
http://msdn.microsoft.com/en-us/library/system.windows.forms.tooltip.aspx
http://msdn.microsoft.com/en-us/library/system.windows.forms.tooltip.showalways.aspx
http://msdn.microsoft.com/en-us/library/system.windows.forms.cursor.aspx
As this answer suggests, there is no managed way to accomplish this. If you want to show a tool tip control when your program is not in focus then the "right" way to do it is to PInvoke Win32 and use CreateWindowEx. The answer linked above given by gideon shows some pointers on how to do it, but nonetheless it is very complicated.
If you don't mind using thrid party libraries, AutoIt provides a way to create tool tips easily without having to deal with Win32 yourself.
Here is a very simple example demonstrating use:
//make sure AutoItX3.dll is referenced in your project
using AutoItX3Lib;
private AutoItX3 myAutoIt = new AutoItX3();
private async void ShowToolTipAtMouse(string message)
{
//default position is bottom right of mouse pointer,
//but you can set the x and y positions yourself
myAutoIt.ToolTip(message);
//call the function again with an empty argument to close
await Task.Delay(1000);
myAutoIt.ToolTip(String.Empty);
}
This will work as long as your program is running; doesn't matter if it is in/out of focus or even hidden. Downside is you don't get the regular fade out animation (it just vanishes). Also, if you need multiple tool tips at once you need to have multiple AutoItX3 objects.
You need Show/Hide it in the mouse events, also converting mouse location from Screen coordinate to your control coordinate. the little problem is if you put tooltip exactly on mous pointer location toolTip will catch mouseEnter event and unintended mouseLeave event on your control will be triggered so my solution was adding a little offset to final location.
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
Point p = pictureBox3.PointToClient(Cursor.Position);
p.X += 5;
p.Y += 5;
toolTip1.Show("My tooltip" ,
pictureBox1, p);
}
private void pictureBox1_MouseLeave(object sender, EventArgs e)
{
toolTip1.ShowAlways = false;
Text = ("Leave");
}
private void pictureBox1_MouseEnter(object sender, EventArgs e)
{
toolTip1.ShowAlways = true;
Text=("Enter");
}
It is a shame Winforms does not have a position property for tooltips.
The easiest thing I found is to add your own mouse-over and mouse-leave handlers and then use the Show() function to set the location (x and y), in pixels, relative to the upper left corner of the second argument of the Show() function.
The second argument can be any control, but probably makes most sense to use the control containing the tooltip itself (this), the parent the control, or a child control inside the control.
You can use a Point instead of two arguments (x and y) for the position, but remember Show() will take the point's x coordinate and y coordinate and add them to the x and y coordinates of the upper left corner of the control you chose as the second argument.
private void UserControl1_MouseHover(object sender, EventArgs e)
{
toolTip1.Show("this text is so new", this, 10, 10);
}
private void UserControl1_MouseLeave(object sender, EventArgs e)
{
toolTip1.Hide(this);
}

.NET C# MouseEnter listener on a Control WITH Scrollbar

As long as the mouse is over a specific control, we show some form. When the mouse leaves the control, we hide the control after a small timeout. This is standard hover behavior.
However, when a control (for example a Treeview) has a scrollbar, and the mouse is ON or OVER the scrollbar, the events don't fire ...
If we could get a reference to the scrollbar control, this would solve our problem, as we would add the same listener events to the scrollbar. However, the scrollbar isn't accessible as far as I know ...
How can we solve this problem ?
The scrollbar is in the tree view's non-client area. When the mouse moves there, it starts generating non-client messages like WM_NCMOUSEMOVE and WM_NCMOUSELEAVE. You would have to sub-class the TreeView and override WndProc() to detect these message.
This doesn't really solve your problem though, you'll still have a hard time with edge cases. A low-tech approach with a Timer always works:
private Form frmPopup;
private void treeView1_MouseEnter(object sender, EventArgs e) {
timer1.Enabled = true;
if (frmPopup == null) {
frmPopup = new Form2();
frmPopup.StartPosition = FormStartPosition.Manual;
frmPopup.Location = PointToScreen(new Point(treeView1.Right + 20, treeView1.Top));
frmPopup.FormClosed += (o, ea) => frmPopup = null;
frmPopup.Show();
}
}
private void timer1_Tick(object sender, EventArgs e) {
Rectangle rc = treeView1.RectangleToScreen(new Rectangle(0, 0, treeView1.Width, treeView1.Height));
if (!rc.Contains(Control.MousePosition)) {
timer1.Enabled = false;
if (frmPopup != null) frmPopup.Close();
}
}
I think there are several different ways to do this, but the key is your desire to have a timeout on the action. I think a combination of two techniques might work:
Put the control on a panel, docked to fill, and use the MouseEnter of the panel to turn on your behavior -- this will include the control's scrollbar. You can use the MouseLeave event of the panel as well, but you'll have to check the cursor's position to ensure it hasn't moved into the contained control. This method is mostly reliable, but moving the mouse quickly can confuse it.
If you combine this with a timer that starts when your shown/hidden control is shown and check the cursor position periodically. This will work, but your timeout before hiding the control won't necessarily be consistent (because the timer starts when they enter the control). You could stop/start the timer on mousemoves in the control to alleviate this somewhat.
I put together a project of the different methods I tried here: http://lovethedot.s3.amazonaws.com/100609StackoverflowScrollbarQuestion.zip
By docking the control you want to track in the panel, it essentially wraps it and you'll get MouseEnter at the very edge of the tracked control:
private void panel1_MouseEnter(object sender, EventArgs e)
{
this.Text = "in";
}
private void panel1_MouseLeave(object sender, EventArgs e)
{
if (!new Rectangle(new Point(0, 0), panel1.Size).Contains(panel1.PointToClient(Control.MousePosition)))
this.Text = "out";
}
You're tracking entry into the panel surrounding the control, and exit from that panel provided the cursor isn't inside the tracked control.
To get a better "leave" experience, it's combined with a Timer that checks to see where the cursor is as well:
private void listBox3_MouseEnter(object sender, EventArgs e)
{
button1.Visible = true;
visibleTimer.Stop();
visibleTimer.Start();
}
void visibleTimer_Tick(object sender, EventArgs e)
{
if (!new Rectangle(new Point(0, 0), listBox3.Size).Contains(listBox3.PointToClient(Control.MousePosition)))
{
visibleTimer.Stop();
button1.Visible = false;
}
}

Strange behavior in FormBorderStyle between Fixed and Sizable

I have created a simple test form with FormBorderStyle = FixedToolWindow by default and added a button that will switch between FixedToolWindow and SizableToolWindow on mouse press.
Switching the FormBorderStyle between these two seems to produce a weird effect that's causing a lot of issues on my application. The problem is that the window seems to change size and I can't have that. I just want to change the border, I need the form size to remain the same.
For instance, here's the button code:
private void button1_Click(object sender, System.EventArgs e) {
if(FormBorderStyle == FormBorderStyle.FixedToolWindow) {
System.Diagnostics.Debug.WriteLine("SWITCHING: FIXED -> SIZABLE");
FormBorderStyle = FormBorderStyle.SizableToolWindow;
} else {
System.Diagnostics.Debug.WriteLine("SWITCHING: SIZABLE -> FIXED");
FormBorderStyle = FormBorderStyle.FixedToolWindow;
}
}
And to debug I use this:
private void Settings_SizeChanged(object sender, System.EventArgs e) {
System.Diagnostics.Debug.WriteLine(this.Size);
}
And here's the output when I press the switch button:
SWITCHING: FIXED -> SIZABLE
{Width=373, Height=169}
{Width=383, Height=179}
SWITCHING: SIZABLE -> FIXED
{Width=383, Height=179}
{Width=373, Height=169}
How can I fix this behavior? And by "fix", I mean, prevent this from happening if possible. I want to be able to specify my form size and to remain like that, no matter the type of border style.
Also, a solution by subclassing the Form class would be the perfect solution for me in case anyone as any ideas to solve this problem with such a method.
EDIT:
I made a little video to demonstrate the problem. The first test shows that the form size doesn't actually change (visually), only the location of the form changes a little bit; but the values for the Size property do change, as you can see on the debug output. The second test you will see on the debug output that the form Size property values change and the window size itself will also change.
Please look here:
http://screencast.com/t/0vT1vCoyx2u
Your issue is with the Location changing, not the Size. This code solves the problem seen in the video.
private void Form1_DoubleClick(object sender, EventArgs e)
{
Point _location = this.PointToScreen(this.ClientRectangle.Location);
if (this.FormBorderStyle == FormBorderStyle.SizableToolWindow)
{
this.FormBorderStyle = FormBorderStyle.FixedToolWindow;
}
else
{
this.FormBorderStyle = FormBorderStyle.SizableToolWindow;
}
Point _newloc = this.PointToScreen(this.ClientRectangle.Location);
Size _diff = new Size(_newloc) - new Size(_location);
this.Location -= _diff;
}
It appears to me that the issue of the rendered form moving when switching between those two borderstyles is a bug in the DWM.
I suspect what's happening is that Windows Forms is keeping the client size (i.e. inner area) the same while the border size changes. This is generally a good thing because it ensures that the window can still correctly fit the content that you've put on it.
If you want to maintain the same outer dimensions, you could work around it by saving the size to a variable before changing the border type, and then restoring it back. They'll probably a slight flicker, though.
This worked fine for me:
private Size _size;
private void Form1_DoubleClick(object sender, EventArgs e)
{
_size = this.Size;
if (this.FormBorderStyle == FormBorderStyle.SizableToolWindow)
{
this.FormBorderStyle = FormBorderStyle.FixedToolWindow;
}
else
{
this.FormBorderStyle = FormBorderStyle.SizableToolWindow;
}
this.Size = _size;
}
I had this same problem. The client size changes when you set the FormBorderStyle but it does not seem to get put into the clientrectangle property right away. The above listed code almost worked for me, but rather than just store size, make sure to store clientsize. Of course, in the above example maybe "this" was a reference to clientsize -- I wasn't sure. Buy anyway, this worked for me:
_frmFilter.Height = 300
Dim _size As Size = .ClientSize
_frmFilter.FormBorderStyle = FormBorderStyle.FixedToolWindow
_frmFilter.ClientSize = _size

Categories