In my application I have a mainform. When the open button is clicked I want to show a second (borderless) form whith the text loading. I've got this working so far.
But what I want is that the loading form is centered relative to the mainform. How do I do this?
SOLUTION:
private void tsbOpen_Click(object sender, EventArgs e)
{
if (_fileDialog.ShowOpenDialog() == DialogResult.OK)
{
_progress = new frmProgress(); // _progress is a member var
backgroundWorker1.RunWorkerAsync("open");
_progress.ShowDialog(this);
}
}
You can set StartPosition to CenterParent and pass the mainform as an Owner.
I created a subform named ProcessingRequest and I put some text and an animated gif on it.
I have a Property in my main form that calculates the location my sub form should be in.
private Point ProcessingLocation { get { return new Point(this.Location.X + this.Width / 2 - new ProcessingRequest().Width / 2, this.Location.Y + this.Height / 2 - new ProcessingRequest().Height / 2); } }
I have a class that makes a new thread to show the sub form.
public class ShowProgress
{
static private System.Drawing.Point point;
static private ProcessingRequest p;
static public void ShowProgressForm(System.Drawing.Point myPoint)
{
point = myPoint;
Thread t = new Thread(new ThreadStart(ShowProgress.ShowForm));
t.IsBackground = true;
t.SetApartmentState(ApartmentState.STA);
t.Start();
}
static private void ShowForm()
{
p = new ProcessingRequest();
p.StartPosition = FormStartPosition.Manual;
p.Location = point;
p.TopMost = true;
Application.Run(p);
}
static public void CloseForm()
{
p.Invoke(new CloseDelegate(ShowProgress.CloseFormInternal));
}
static private void CloseFormInternal()
{
p.Close();
}
}
public delegate void CloseDelegate();
Then in my main form I simply put
ShowProgress.ShowProgressForm(ProcessingLocation);
//heavy processing code goes here or whatever
ShowProgress.CloseForm();
:)
Martijn try this
at the start of the method put some code like this
public sub Bah()
{
if (me.InvokeRequired)
{
me.Invoke(new action(Bah));
return
}
myform.showdialog...
}
dont know if this code compiles to 100% but you get the idea
Get the position of the main form coordinates and its size and take the size of child form and put some simple mathematics on it.
Related
In my main class I create a wpf window (p) and a function to display the window with a given message on it in a label (content).
public partial class MainWindow : Window
{
///Creates Window
public static Wils0n.Window1 p = new Wils0n.Window1();
/// <summary>
/// creates notif bar with message
/// </summary>
/// <param name="message">Message for notif bar</param>
public static void NotifProc(string message)
{
///sets global string to message
Commands.MyGlobals.inputtext = message;
p.Dispatcher.Invoke(new Action(delegate ()
{
MainWindow.p.Topmost = true;
MainWindow.p.Top = 0;
///sets label content to global string (this only works once)
MainWindow.p.content.Content = Commands.MyGlobals.inputtext;
MainWindow.p.Left = System.Windows.SystemParameters.WorkArea.Width / 2 - (MainWindow.p.Width / 2);
MainWindow.p.Show();
Thread.Sleep(2000);
while (MainWindow.p.Top != -114)
{
MainWindow.p.Top -= 3;
Thread.Sleep(15);
}
MainWindow.p.Hide();
}
));
}
}
Then in another class in another namespace I call it like such..
namespace Wilson.Utils
{
class Commands
{
public void start()
{
///creates notification window thats reads "hello world"
MainWindow.NotifProc("hello world");
///creates notification window thats reads "test1"
///For some reason reads "hello world"
MainWindow.NotifProc("test1");
///creates notification window thats reads "test2"
///For some reason reads "hello world"
MainWindow.NotifProc("test2");
}
///creates global string
public static class MyGlobals
{
public static string inputtext = "";
}
}
}
This works perfectly fine the first time only, if I call it again it with a different message it wont update the message but the notif still works it just displays the old message.
I have also had this problem with changing MainWindow.p.Opacity and MainWindow.p.Background
Without Dispatcher.Invoke I get an error reading "cannot access object because a different thread owns it".
If I remove Commands.MyGlobals.inputtext = message; and put Commans.MyGlobals.inputtext = "test1" before MainWindow.NotifProc("test1"); it still doesn't work.
I have also tried creating a class like such:
public static class ExtensionMethods
{
private static Action EmptyDelegate = delegate () { };
public static void Refresh(this UIElement uiElement)
{
uiElement.Dispatcher.Invoke(DispatcherPriority.Render, EmptyDelegate);
}
}
and adding:
p.content.Refresh();
but had no luck.
Any help would be great. :/
EDIT
I managed to find a fix although it is probably not the best:
instead of creating a new window at the beginning I create a new window each time the window animation is called. VS said the thread must be STA so I called it in an STA thread and lastly I added a check at the end of the window animation so that no new windows can be created until the first is done.
My new NotifProc function looks like this:
public static void NotifProc(string message)
{
Tools.MyGlobals.notifmessage = message;
var thread = new Thread(new ThreadStart(STAThread));
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
thread.Join();
}
private static void STAThread()
{
Wils0n.Window1 p = new Wils0n.Window1();
p.content.Content = Tools.MyGlobals.notifmessage;
p.content.Refresh();
p.Topmost = true;
p.Top = 0;
p.Left = System.Windows.SystemParameters.WorkArea.Width / 2 - (p.Width / 2);
p.Show();
Thread.Sleep(2000);
while (p.Top != -114)
{
p.Top -= 3;
Thread.Sleep(15);
}
p.Close();
}
Instead of calling Thread.Sleep in the UI thread, and thus blocking it, you should simply animate the Window position like this:
public static void NotifProc(string message)
{
p.Content.Content = message;
p.Left = SystemParameters.WorkArea.Width / 2 - (p.Width / 2);
p.Top = 0;
p.Topmost = true;
p.Show();
var animation = new DoubleAnimation
{
To = -114,
BeginTime = TimeSpan.FromSeconds(2),
Duration = TimeSpan.FromSeconds(0.57)
};
animation.Completed += (s, e) => p.Hide();
p.BeginAnimation(TopProperty, animation);
}
If you want to call the method from a thread other than the UI thread, do it like this:
Application.Current.Dispatcher.Invoke(() => NotifProc(...));
or
MainWindow.p.Dispatcher.Invoke(() => NotifProc(...));
So basically im trying to make polygons with a name entered from Form2, called Apgabala_nosaukums (it's in my language, sorry for that). I have been trying to debug this, first 2 times the name entered from Form2 did get read and i was able to see that the name was added to the Polygon. But now it is not getting in the fromVisibleChanged anymore, ending in that the polygon is not getting name. Meaning that I cannot get the bool to true, so I could add 4 points and make a square or rectangle area out of them. Any ideas? Basically the btnAdd_Click function is not working properly, rest is working fine. Any ideas?
Form1 (Main form):
namespace GMapTest
{
public partial class Form1 : Form
{
GMapOverlay polygons = new GMapOverlay("polygons");
List<PointLatLng> points = new List<PointLatLng>();
double lat;
double lng;
int clicks = 0;
bool add = false;
string nosaukums;
public Form1()
{
InitializeComponent();
}
private void gMapControl1_Load(object sender, EventArgs e)
{
gmap.MapProvider = GoogleMapProvider.Instance;
GMaps.Instance.Mode = AccessMode.ServerOnly;
gmap.SetPositionByKeywords("Riga, Latvia");
gmap.ShowCenter = false;
gmap.Overlays.Add(polygons);
}
private void gmap_MouseDown(object sender, MouseEventArgs e)
{
if (add == true)
{
if (e.Button == MouseButtons.Left)
{
lat = gmap.FromLocalToLatLng(e.X, e.Y).Lat;
lng = gmap.FromLocalToLatLng(e.X, e.Y).Lng;
clicks += 1;
points.Add(new PointLatLng(lat, lng));
}
if (clicks == 4)
{
GMapPolygon polygon = new GMapPolygon(points, nosaukums);
polygons.Polygons.Add(polygon);
clicks = 0;
points.Clear();
add = false;
}
}
}
private void btnAdd_Click(object sender, EventArgs e)
{
Apgabala_nosaukums addName = new Apgabala_nosaukums();
addName.ShowDialog();
addName.VisibleChanged += formVisibleChanged;
if (nosaukums != null)
{
this.add = true;
}
}
private void formVisibleChanged(object sender, EventArgs e)
{
Apgabala_nosaukums frm = (Apgabala_nosaukums)sender;
if (!frm.Visible)
{
this.nosaukums = (frm.ReturnText);
frm.Dispose();
}
}
}
}
Form2 (Apgabala_nosaukums):
namespace GMapTest
{
public partial class Apgabala_nosaukums : Form
{
public string ReturnText { get; set; }
public Apgabala_nosaukums()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
this.ReturnText = this.txtName.Text;
this.Visible = false;
}
}
}
The problem is in your btnAdd_Click function. When you call ShowDialog your other form is shown and the next line, addName.VisibleChanged += formVisibleChanged; isn't called until you close the new form. ShowDialog shows the form modally, you can't interact with the parent until you close the new form.
There are a couple ways you could fix this.
1) Subscribe to the VisibleChanged event before you show the form,
addName.VisibleChanged += formVisibleChanged;
addName.ShowDialog();
2) Call addName.Show() instead of addName.ShowDialog(). This shows the form in a non-modal way. The event will get subscribed to because execution continues in btnAdd_Click before the new form is closed. But, the parent form will be interactable, not sure if this is desired or not.
3) You could also get rid of the VisibleChanged event stuff and instead do ShowDialog and read the property after. This is what I'd recommend from seeing the code.
private void btnAdd_Click(object sender, EventArgs e)
{
Apgabala_nosaukums addName = new Apgabala_nosaukums();
addName.ShowDialog();
this.nosaukums = addName.ReturnText;
addName.Dispose();
}
suppose i have have two sdi form and when apps run then a sdi form show and from there i am showing another sdi form but the problem is i can drga any where the second sdi form which i do not want. in case of MDI form the mdi child form can not be drag out of mdi form boundary.so in my case i want to simulate the same thing. i want no other sdi form can not be drag out form my main sdi form's boundary. so just guide me how to do this.
i could guess that i have to work with form drag event and from there i have to check form top and left but need more suggestion.
private void Form1_Move(object sender, EventArgs e)
{
this.Location = defaultLocation;
}
i try to do it this way but it is not working.
public partial class Form2 : Form
{
Form1 _parent = null;
public Form2()
{
InitializeComponent();
}
public Form2(Form1 parent)
{
InitializeComponent();
_parent = parent;
}
private void Form2_Move(object sender, EventArgs e)
{
if((this.Location.X+this.Width) > _parent.Width)
{
this.Location = new System.Drawing.Point(_parent.ClientRectangle.Width-this.Width,this.Location.Y);
}
if ((this.Location.Y + this.Height) > _parent.Height)
{
this.Location = new System.Drawing.Point(_parent.ClientRectangle.Height - this.Height, this.Location.X);
}
if (this.Location.Y < 0)
{
this.Location = new System.Drawing.Point(this.Location.X);
}
if (this.Location.X < 0)
{
this.Location = new System.Drawing.Point(this.Location.Y);
}
}
}
please guide me where i made the mistake.
thanks
UPDATE
private void Form2_Move(object sender, EventArgs e)
{
int left = this.Left;
int top = this.Top;
if (this.Left < _parent.Left)
{
left = _parent.Left;
}
if (this.Right > _parent.Right)
{
left = _parent.Right - this.Width;
}
if (this.Top < _parent.Top)
{
top = _parent.Top;
}
if (this.Bottom > _parent.Bottom)
{
top = _parent.Bottom - this.Height;
}
this.Location = new Point(left, top);
}
I recomend to you follow the #ProgrammerV5 recomendation. But if you realy need to control the form movement, please see the use of Cursor.Clip property
here are some information: http://www.codeproject.com/Tips/375046/The-Cursor-Clip-Property
Also you may need to do a MouseCapture.
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);
}
}
}