Not sure how to explain this, but:
there is a control MyPopup, made out of ToolStripDropDown;
there are many based on MyPopup controls (call them popups);
there are no problems to open popup from the Form;
but there is is a problem to open popup from popup.
Problem is what after child popup is closed, parent popup stays on screen even when its parent Form get focus. The only way to close that stuck parent popup is to get focus to it (with the mouse) and hit Esc.
To have popup able to show another popup I have to trick Closing event:
void Popup_Closing(object sender, ToolStripDropDownClosingEventArgs e)
{
// prevent from closing when stay
if (_stay && e.CloseReason != ToolStripDropDownCloseReason.CloseCalled )
{
e.Cancel = true;
return;
}
}
Before of after closing child popup, parent popup has:
value of _stay is False;
the value of Popup.AutoClose is True;
I tried to "bring" mouse focus back to the parent popup with the following:
TopLevel=true no luck;
Focus(); no luck;
Focused=true; no luck;
AutoClose=true; no luck;
Captured=true; no luck;
Also tried setting above value to False and then to True, still no luck.
And here is some more code, which could be useful or not:
public class MyPopup : UserControl
{
protected bool _stay = false;
private ToolStripDropDown _popup;
private ToolStripControlHost _host;
public MyPopup()
{
// create popup
_popup = new ToolStripDropDown();
_popup.Margin = _popup.Padding = Padding.Empty;
_popup.AutoSize = false;
_popup.Closing += Popup_Closing;
// add host
_host = new ToolStripControlHost(this);
_host.Margin = _host.Padding = Padding.Empty;
_host.AutoSize = false;
_popup.Items.Add(_host);
}
public void Show(Control parent, int x, int y)
{
_popup.Show(parent, x, y);
}
public new void Hide()
{
_popup.Close();
}
private void Popup_Closing(object sender, ToolStripDropDownClosingEventArgs e)
{
// prevent from closing when stay
if (_stay && e.CloseReason != ToolStripDropDownCloseReason.CloseCalled )
{
e.Cancel = true;
return;
}
}
protected void PopupChildClosedDefaultEvent(object sender, EventArgs e)
{
// hide popup if mouse is outside of client region when closing child popup
if (!ClientRectangle.Contains(PointToClient(MousePosition)))
Hide();
else
{
// >> here I am trying different things <<
_popup.AutoClose = false;
_popup.AutoClose = true;
}
}
}
public class PopupParent: MyPopup
{
private void TestChildren()
{
_stay = true;
PopupChild popup = new PopupChild();
popup.Show(button1, 0, 0);
popup.Closed += PopupChildClosedDefaultEvent;
_stay = false;
}
}
public class PopupChild: MyPopup
{
}
Question: Is there any way to fix "broken" popup after it has lost its ability to autoclose on mouse event (clicking somewhere outside of the client area)?
Ok, morning bring some freshness to brains and I managed to solve that issue with following:
_popup.Close();
_popup.Show(_parent, _x, _y);
So I have to reshow popup to have it functioning as before. It flickers, but works.
Related
I'm trying to understand what is executing before the MouseWheel event.
What I've done:
I have a form which has AutoScroll property set to true. There is a control (ZEDGRAPH) at the top and the bottom of this form.
To overcome the issue of scrolling and zooming at the same time I captured the mousewheel += new MouseEvenHandler(mymethod) for the form.Then using a bool variable I keep track of when the control (ZEDGRAPH) has focus and when it does not.
When it has focus I make verticalscroll.value = (int)mydesiredposition;
This works in accomplishing what I wanted which is to ignore the mousewheel event in the form and focus on the control.
What I am struggling with is the fact that when I scroll the form flickers every time and scrolls down before coming to the set scrollbar value.
So what I am wondering is what is getting triggered before this mouseeventhandler that causes it to flicker and is there a relatively simple workaround this?
My code snapshot:
public Form(Form1 f)
{
InitializeComponent();
this.MouseWheel += new MouseEventHandler(mousewheel);
}//end of constructor
//
//
bool mousehoverZedGraph1 = false;
bool mousehoverZedGraph2 = false;
//
//
private void zedGraphControl1_MouseHover(object sender, EventArgs e)
{
mousehoverZedGraph1 = true;
return;
}
private void mousewheel(object sender, MouseEventArgs e)
{
if (mousehoverZedGraph1 == true)
{
VerticalScroll.Enabled = false;
VerticalScroll.Value = 0;
return;
}
else if (mousehoverZedGraph2 == true)
{
VerticalScroll.Value = 429;
VerticalScroll.Enabled = false;
}
else
{
//VerticalScroll.Value += e.Delta;
}
}
private void Form_MouseEnter(object sender, EventArgs e)
{
mousehoverZedGraph1 = mousehoverZedGraph2 = false;
VerticalScroll.Enabled = true;
}
A small video highlighting the flicker:
I try to avoid the XY Problem by saying immediately what I want and then what I get. 😛
So, first of all, I minimize my MainWindow and thoroguh its NotifyIcon ContextMenu I want that my MainWindow reappears.
The problem: the MainWindow doesn't appear/show as Window, but it appears as Icon in the toolbar (see figure 2).
The code:
This is the TrayIcon initializer:
private void InitializeTrayIcon()
{
KyactusTrayIcon = new NotifyIcon();
KyactusTrayIcon.Icon = AppIcon;
KyactusTrayIcon.Visible = true;
KyactusTrayIcon.ContextMenu = new ContextMenu(new []
{
new MenuItem("Chiudi", ExitApplication),
new MenuItem("Mostra", ShowMainWindow),
});
ShowNotification(#"Ciao " + Globals.CurrentUser.Name + #"!", #"Benvenuto su Kyactus");
}
This is the delegate responsible to show the minimized MainWindow (not working at all):
private void ShowMainWindow(object sender, EventArgs e)
{
WindowState = WindowState.Normal;
Topmost = true;
Show();
Activate();
}
This is what happens when the MainWindow is minimized by clicking the [-] button (ie the Hide() method):
private void MainWindow_OnStateChanged(object sender, EventArgs e)
{
switch (this.WindowState)
{
case WindowState.Maximized:
ShowNotification("Bleah!", "Questo è proprio brutto! :(");
break;
case WindowState.Minimized:
Hide();
ShowNotification("Avviso", "L'applicazione è ora minimizzata qui");
break;
case WindowState.Normal:
break;
}
}
Step one. The method MainWindow_OnStateChanged will be invoked when click on [-]:
Step two. The window disappears (ok) and the Tray icon appears (ok). Then I click on 'Mostra' (translated as 'Show') and the ShowMainWindow delegate will be invoked
Step three. This is the final step, that is, what I do not expect. The MainWindos 'lives' as an Icon in the toolbar. But I can't see it as a Window.
Please note that I have not this problem when I close the window by clicking [X] instead of [-]. So, my suspect is the MainWindow's Window.State. I tried to restore it implementing the WindowState.Normal into the ShowMainWindow, but nothing.
Update: if is use WindowState.Maximized in the ShowMainWindow method,
I can see the window again, but it is maximized and this is bad and ugly.
Just change the order of operation when showing the window
private void ShowMainWindow(object sender, EventArgs e)
{
Show();
WindowState = WindowState.Normal;
Topmost = true;
Activate();
}
Simply,create some class-level integer variables and store the height,width and positioning values there.Then use them to get back the size of your window :
int height;
int width;
double left;
double top;
private void MainWindow_SizeChanged
{
height = this.Height;
width = this.Widthl
left = this.Left;
top = this.Top;
}
private void ShowMainWindow(object sender, EventArgs e)
{
this.Height = height;
this.Width = width;
this.Left = left;
this.Top = top;
}
I am trying to make my custom ComboBox inheriting from ContainerControl. I used this article as a base but rewrote it, but I use a ToolStripControlHost, my own custom ListBox & a ToolStripDropDown.
Now the ComboBox is a button where you click on to show the DropDowncontaining my ListBox, works fine with overriding OnMouseClick.
The problems starts when I try to close the DropDown, with the DropDown's 'AutoClose' property to true, the DropDown closes if you click somewhere outside the DropDown (including the button) ...
protected override void OnMouseClick(MouseEventArgs e)
{
base.OnMouseClick(e);
/* listboxControl = ToolStripDropDown */
if (!listboxControl.Visible)
{
listboxControl.Show(this, GetDropLocation(), ToolStripDropDownDirection.BelowRight);
//listbox.Capture = true;
}
}
This is the code for the click on the button .. so what happens if you click it ?
If the DropDown is shown, it first closes the DropDown, then it fires the OnMouseClick event. Meaning: listboxControl.Visible is already false & it will show the DropDown again. All of this causing a quick close-open.
I have been stuck with this problem for some time now and google doesn't seem to know a lot about this subject (that article on CodeProject has the same bug).
What I have tried is disabling AutoClose and capturing the mouse after I show the DropDown, this works partially but it affects the working of my hosted ListBox. The ListBox contains a set of controls (the items), these items have a hover paint effect. Capturing the mouse in the ListBox control prevents the OnMouseEnter to be fired.
All input would be greatly appreciated !
You need a variable to track the cursor position when the DropDown is closing.
Here is a quick and dirty example control:
public class CustomDropBox : Control {
private ListBox box = new ListBox() { IntegralHeight = false };
private ToolStripControlHost host;
private ToolStripDropDown drop;
private bool wasShowing = false;
public CustomDropBox() {
box.MinimumSize = new Size(120, 120);
box.MouseUp += box_MouseUp;
box.KeyPress += box_KeyPress;
box.Items.AddRange(new string[] { "aaa", "bbb", "ccc" });
host = new ToolStripControlHost(box) { Padding = Padding.Empty };
drop = new ToolStripDropDown() { Padding = Padding.Empty };
drop.Closing += drop_Closing;
drop.Items.Add(host);
}
private Rectangle GetDownRectangle() {
return new Rectangle(this.ClientSize.Width - 16, 0, 16, this.ClientSize.Height);
}
void drop_Closing(object sender, ToolStripDropDownClosingEventArgs e) {
if (e.CloseReason == ToolStripDropDownCloseReason.AppClicked) {
wasShowing = GetDownRectangle().Contains(this.PointToClient(Cursor.Position));
} else {
wasShowing = false;
}
}
void box_KeyPress(object sender, KeyPressEventArgs e) {
if (e.KeyChar == (char)Keys.Enter && box.SelectedIndex > -1) {
drop.Close();
}
}
void box_MouseUp(object sender, MouseEventArgs e) {
int index = box.IndexFromPoint(e.Location);
if (index > -1) {
drop.Close();
}
}
protected override void OnMouseDown(MouseEventArgs e) {
if (e.Button == MouseButtons.Left && GetDownRectangle().Contains(e.Location)) {
if (wasShowing) {
wasShowing = false;
} else {
drop.Show(this, new Point(0, this.Height));
}
}
base.OnMouseDown(e);
}
protected override void OnPaint(PaintEventArgs e) {
e.Graphics.Clear(Color.White);
ControlPaint.DrawComboButton(e.Graphics, GetDownRectangle(), ButtonState.Normal);
base.OnPaint(e);
}
}
I have made a custom control and when a condition is met, I want to show a tooltip:
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
var plannedItem = GetPlannedItemByPosition(e.Location);
if (plannedItem != null)
_tooltip.SetToolTip(this, plannedItem.Description);
else
_tooltip.RemoveAll();
}
This code works fine, excepts for the face that the tooltip flickers.
This custom control, paints all the information in the OnPaint event, maybe this has something to do with it? And if it does, how can I prevent the tooltip from flickering?
Remember last mouse position and set the tooltip only when the mouse position changes.
public partial class Form1 : Form
{
private int lastX;
private int lastY;
private void button1_MouseMove(object sender, MouseEventArgs e)
{
if (e.X != this.lastX || e.Y != this.lastY)
{
toolTip1.SetToolTip(button1, "test");
this.lastX = e.X;
this.lastY = e.Y;
}
}
This will happen when you display the tooltip at the mouse cursor position. As soon as the tip window shows up, Windows notices that the mouse is located in that window and posts a MouseMove message. Which makes the tooltip disappear. Which makes Windows send a MouseMove message to your control, running your OnMouseMove() method. Which makes the tooltip appear again. Etcetera, you'll see the tooltip rapidly flickering.
Solve this by any of the following methods:
show the tooltip well away from the mouse position so it won't overlap the mouse cursor
only update/show the tooltip when it needs to be changed
set the control's Capture property to true so the tooltip won't get a MouseMove message
Since this is a painted custom control, I think it might be easier to just have a variable hold the last shown tip, and instead of always "setting" the tooltip, just show it.
Simple example (using just a form):
public partial class Form1 : Form {
private List<TipRect> _Tips = new List<TipRect>();
private TipRect _LastTip;
private ToolTip _tooltip = new ToolTip();
public Form1() {
InitializeComponent();
_Tips.Add(new TipRect(new Rectangle(32, 32, 32, 32), "Tip #1"));
_Tips.Add(new TipRect(new Rectangle(100, 100, 32, 32), "Tip #2"));
}
private void Form1_Paint(object sender, PaintEventArgs e) {
foreach (TipRect tr in _Tips)
e.Graphics.FillRectangle(Brushes.Red, tr.Rect);
}
private void Form1_MouseMove(object sender, MouseEventArgs e) {
TipRect checkTip = GetTip(e.Location);
if (checkTip == null) {
_LastTip = null;
_tooltip.Hide(this);
} else {
if (checkTip != _LastTip) {
_LastTip = checkTip;
_tooltip.Show(checkTip.Text, this, e.Location.X + 10, e.Location.Y + 10, 1000);
}
}
}
private TipRect GetTip(Point p) {
TipRect value = null;
foreach (TipRect tr in _Tips) {
if (tr.Rect.Contains(p))
value = tr;
}
return value;
}
}
Here is the TipRect class I created to simulate whatever your PlannedItem class is:
public class TipRect {
public Rectangle Rect;
public string Text;
public TipRect(Rectangle r, string text) {
Rect = r;
Text = text;
}
}
I imagine your mouse does move a little when you think it is still. I suggest you do some kind of caching here - only call _tooltip.SetToolTip if the plannedItem has changed.
For the visitors of this thread, here is what I did, following suggestions above (VB.NET):
Dim LastToolTip As String
Private Sub PictureBox1_MouseMove(sender As Object, e As MouseEventArgs) Handles PictureBox1.MouseMove
Dim NewToolTip = CalculateTooltipText(e.X, e.Y)
If LastToolTip <> NewToolTip Then
ToolTip1.SetToolTip(PictureBox1, NewToolTip)
LastToolTip = NewToolTip
End If
End Sub
It stopped the flickering.
c# (works on tooltip chart):
Point mem = new Point();
private void xxx_MouseMove(MouseEventArgs e){
// start
Point pos = e.Location;
if (pos == mem) { return; }
// your code here
// end
mem = pos
}
I have a Winforms app in C# that calls calls a method asynchronously and uses a callback.
I would like to display an animated gif to let the end user know that work is being done.
I would like to have the animated gif hover over the center of the form.
How can I do this?
Update:
Thanks. I guess the step I was missing was to use a Picture Box to hold the gif.
The following seems to be doing the trick of showing the gif and like jmatthews3865 said below I can just set the visible property of the PictureBox to false to hide it.
private ShowAnimatedGif()
{
PictureBox pb = new PictureBox();
this.Controls.Add(pb);
pb.Left = (this.Width / 2) - (pb.Width / 2);
pb.Top = (this.Height / 2) - (pb.Height / 2);
pb.Image = Resources.AnimatedGifHere;
pb.Visible = true;
}
in your form, simply include the image with it's visible property set to false.
from the event which calls the long running async process (button1_click etc.), set the images visibility property to true. event fires, image appears, async process runs and your ui thread should still be responsive.
in your callback event set the images visible property to false to indicate that the process is complete.
Need some code to give an exact answer, but this is fairly trivial, insert the gif before you make the asynchronous call, then remove it in the callback.
This is the answer. I'm using LoadingCircle which is an animated gif component.
public partial class Form1 : Form
{
public delegate void ProcessAnimation(bool show);
ProcessAnimation pa;
public Form1()
{
InitializeComponent();
pa = this.ShowAnimation;
}
private void button2_Click(object sender, EventArgs e)
{
Thread tr = new Thread(FlushToServer);
tr.Start();
}
private void ShowAnimation(bool show)
{
if (show)
{
loadingCircle1.Visible = true;
loadingCircle2.Active = true;
}
else
{
loadingCircle1.Visible = false;
loadingCircle1.Active = false;
}
}
private void FlushToServer()
{
this.Invoke(this.pa,true);
//your long running process
System.Threading.Thread.Sleep(5000);
this.Invoke(this.pa,false);
}
}
i modify the above code a bit and it will not throw error "c# invoke or begininvoke cannot be called on a control until the window handle has been created."
namespace AnimateUI
{
public partial class Form1 : Form
{
public delegate void ProcessAnimation(bool show);
ProcessAnimation pa;
public Form1()
{
InitializeComponent();
pa = this.ShowAnimation;
pictureBox1.Visible = false;
}
private void ShowAnimation(bool show)
{
if (show)
{
pictureBox1.Visible = true;
}
else
{
pictureBox1.Visible = false;
}
}
private void button1_Click(object sender, EventArgs e)
{
Thread tr = new Thread(StartTask);
tr.Start();
}
private void StartTask()
{
if (!this.IsHandleCreated)
this.CreateControl();
this.Invoke(this.pa, true);
System.Threading.Thread.Sleep(15000);
this.Invoke(this.pa, false);
}
}
}