C# WPF Pop is being drawn multiple times - c#

All I have created a custom hex viewer tool for viewing a particular file type.
As part of the requirements I need to highlight certain bit values once I hover over a hex range (which is implemented via C# Run class).
The problem is about 50% of the time I get multiple popups drawn on top of each other rather than one.
See below:
Here my relevant code snippet in C#:
private Popup popup = new Popup();
void ToolTip_MouseEnter(object sender, EventArgs e)
{
//TODO: base popup action on data value
if (popup.IsOpen!=true)
{
if (sender is Run && HexDocumentUIHelperUtility.zftSequenceBitsMouseUp)
{
Run runControl = sender as Run;
if (runControl != null)
{
//popup.HorizontalAlignment = HorizontalAlignment.Center;
//popup.VerticalAlignment = VerticalAlignment.Center;
TextBox textBox = new TextBox();
textBox.Text = this.getZftBitsVisualization().getBinaryString();
int startHighlight = this.getZftBitsVisualization().getHighlightIndex();
int length = this.getZftBitsVisualization().getHighlightLength();
//textBox.SelectionStart = startHighlight;
//textBox.SelectionLength = length;
textBox.SelectionBrush = Brushes.Gold;
textBox.Select(startHighlight, length);
textBox.FontSize = 15;
popup.Child = textBox;
//get the current mouse position
//I adjusted the mouse Y coordinate by minus 20 pixels in order to avoid the popup vbeing displayed on top of the hex range
int mouseYCoordinate = System.Windows.Forms.Control.MousePosition.Y + 20;
popup.HorizontalOffset = System.Windows.Forms.Control.MousePosition.X;
popup.VerticalOffset = mouseYCoordinate;
popup.IsOpen = true;
textBox.Focus();
}
}
}//if the pop is not already opened
}
void ToolTip_MouseLeave(object sender, EventArgs e)
{
if (sender is Run)
{
Run runControl = sender as Run;
if (runControl != null)
{
if (popup != null)
{
popup.IsOpen = false;
popup.Child = null;
}
if (highlightedRunList != null)
{
highlightedRunList.Clear();
}
}
}
}

You are testing if the popup is already open at the top of the method but not actually setting that it is until much further down.
This gives the mouse enter event chance to fire several times before finally setting IsOpen to true preventing further popups opening.
Move the setting of IsOpen to immediately after the test for the popup not being open. You can always set it back to false if the popup fails.

Related

Word VSTO add-in: Change task pane width on position changed

I have a UserControl which by default is anchored at the bottom like this:
var customTaskPaneContent = new CustomTaskPaneContent(jobId, _ipcClient, document, AddCustomTaskPane);
var customTaskPane = CustomTaskPanes.Add(customTaskPaneContent, CustomTaskPaneTitle, document.ActiveWindow);
customTaskPane.DockPosition = Office.MsoCTPDockPosition.msoCTPDockPositionBottom;
customTaskPane.Height = 130;
customTaskPane.Visible = true;
customTaskPane.VisibleChanged += CustomTaskPane_VisibleChanged;
customTaskPane.DockPositionChanged += CustomTaskPane_DockPositionChanged;
private void CustomTaskPane_DockPositionChanged(object sender, EventArgs e)
{
var customTaskPane = sender as Microsoft.Office.Tools.CustomTaskPane;
if(customTaskPane != null)
{
if (customTaskPane.DockPosition == Office.MsoCTPDockPosition.msoCTPDockPositionFloating)
{
//ATTEMPT 1
//customTaskPane.Width = 1000;
//ATTEMPT 2
var userControl = customTaskPane.Control;
var size = new System.Drawing.Size(1500, 400);
userControl.Size = size;
}
}
}
When I drag the panel and its position changes to msoCTPDockPositionFloating the assigned dimensions are too small and I would like to change its width.
I have made several attempts but the dimensions are never changed. Which is the correct way to change the Width size?
Try to change the Size property of the content as well in the following way:
private void myCustomTaskPane_DockPositionChanged(object sender, EventArgs e)
{
Microsoft.Office.Tools.CustomTaskPane taskPane =
sender as Microsoft.Office.Tools.CustomTaskPane;
if (taskPane != null)
{
// Adjust sizes of user control and flow panel to fit current task pane size.
MyUserControl userControl = taskPane.Control as MyUserControl;
System.Drawing.Size paneSize = new System.Drawing.Size(taskPane.Width, taskPane.Height);
userControl.Size = paneSize;
userControl.FlowPanel.Size = paneSize;
// Adjust flow direction of controls on the task pane.
if (taskPane.DockPosition ==
Office.MsoCTPDockPosition.msoCTPDockPositionTop ||
taskPane.DockPosition ==
Office.MsoCTPDockPosition.msoCTPDockPositionBottom)
{
userControl.FlowPanel.FlowDirection =
System.Windows.Forms.FlowDirection.LeftToRight;
}
else
{
userControl.FlowPanel.FlowDirection =
System.Windows.Forms.FlowDirection.TopDown;
}
}
}
This code example assumes that the task pane contains a UserControl named MyUserControl, and the UserControl contains a FlowLayoutPanel named FlowPanel.
See CustomTaskPane Interface for the full sample code.

problems updating picture box control

I have two controls on the same form. Both controls contain an ObjectListView control. The one listview was created with the graphical editor in visual studio. This one is not causing any issues. The other listview is created programmatically at run-time. I have defined an event handler for each control that gets called when the hot item changes and they are both firing when they should. Both event handlers call the same code to update a picturebox control. The problem is that the picturebox does not get updated when the programmatically defined listview is asking it to. I am positive the event handler is getting called because my code writes to a text file as well as updating the picture box. The text file gets updated but the picture box does not. I have tried updating, invalidating, and refreshing the PicutureBox as well as the parent form, but I just can not get it to update.
I am not sure if this is an ObjectListView issue or a standard WinForms problem. I realize my question is very vague but I am not sure how to clarify it without posting all my code. Any advice would be appreciated.
Here is the code that the event handler calls:
public void ShowBitmap(object sender, HotItemChangedEventArgs e, ObjectListView lv, string type)
{
ObjectListView olv = sender as ObjectListView;
if (sender == null)
{
return;
}
switch (e.HotCellHitLocation)
{
case HitTestLocation.Nothing:
break;
case HitTestLocation.Group:
break;
case HitTestLocation.GroupExpander:
break;
default:
if (e.HotColumnIndex == 0)
{
pictureBox1.Hide();
pictureBox1.BorderStyle = BorderStyle.FixedSingle;
int rowIndex = e.HotRowIndex;
string text = "";
if (type == "Main Parts")
{
TypedObjectListView<MainRadanProjectPartsPart> tlist = new TypedObjectListView<MainRadanProjectPartsPart>(lv);
text = tlist.Objects[rowIndex].Symbol;
}
else if (type == "Parts")
{
TypedObjectListView<RadanProjectPartsPart> tlist = new TypedObjectListView<RadanProjectPartsPart>(lv);
text = tlist.Objects[rowIndex].Symbol;
}
else if (type == "Nests")
{
TypedObjectListView<MainRadanProjectNestsNest> tlist = new TypedObjectListView<MainRadanProjectNestsNest>(lv);
text = tlist.Objects[rowIndex].FileName;
}
if (text != null)
{
Point screenCoords = Cursor.Position;
Point controlRelatedCoords = lv.PointToClient(screenCoords);
if (controlRelatedCoords.Y < oldCursorPosition.Y)
{
pictureBox1.Location = controlRelatedCoords;
int xPos = controlRelatedCoords.X;
int yPos = controlRelatedCoords.Y + 60;
pictureBox1.Location = new Point(xPos, yPos);
}
else if (controlRelatedCoords.Y > oldCursorPosition.Y)
{
pictureBox1.Location = controlRelatedCoords;
int xPos = controlRelatedCoords.X;
//int yPos = controlRelatedCoords.Y - pictureBox1.Height;
int yPos = controlRelatedCoords.Y - pictureBox1.Height + 30;
pictureBox1.Location = new Point(xPos, yPos);
}
pictureBox1.Show();
pictureBox1.BringToFront();
olvTreeViewMainParts.Focus();
lv.Focus();
pictureBox1.Visible = true;
DrawSymbol(text);
oldCursorPosition = controlRelatedCoords; // save the cursor position to track cursor direction between calls
}
else
{
DrawSymbol("");
}
}
else
{
pictureBox1.Hide();
}
break;
}
}
Here is the event handler for the programmaticaly defined listview:
// track the cursor as it moves over the items in the listview
private void olvPartsListView_HotItemChanged(object sender, HotItemChangedEventArgs e)
{
ShowBitmap(sender, e, olvPartsListView, "Parts");
}

Switching Panels via Index Methods

I've been trying to solve my issue for quite a while and to be honest am getting nowhere. What i would like is when the user clicks the 'top' button on my panel it automatically goes to the top( and swaps with the one there.) and when they click the bottom button it automatically goes to the bottom. I'm setting the index panel manually but of course this doesnt work because its only viable for one panel (i have ten). Greatly appreciate some help in finding a method that can send the panel to the top of the stack regardless of its position.
Here is a image (basic) to help understand
Control ctrlToMove = (Control)this.bookControls[bookName];
int ctrlToMoveIndex = bookPanel.Controls.IndexOf(ctrlToMove);
int ctrlToSwapIndex = ctrlToMoveIndex - 5;
Control ctrlToSwap = bookPanel.Controls[ctrlToSwapIndex];
this.bookPanel.Controls.SetChildIndex(ctrlToMove, ctrlToSwapIndex);
this.bookPanel.Controls.SetChildIndex(ctrlToSwap, ctrlToMoveIndex);
Based on your drawing, I made a UserControl with a button on it:
void uc_ButtonClicked(object sender, EventArgs e) {
UserControl1 uc = sender as UserControl1;
if (uc != null) {
int childIndex = flowLayoutPanel1.Controls.GetChildIndex(uc);
if (childIndex > 0) {
UserControl1 ucTop = flowLayoutPanel1.Controls[0] as UserControl1;
flowLayoutPanel1.Controls.SetChildIndex(uc, 0);
flowLayoutPanel1.Controls.SetChildIndex(ucTop, childIndex);
}
}
}
According to your picture you have one control per row in panel. Thus I suggest you to use TableLayoutPanel instead of FlowLayoutPanel. Also I'd create user control for items in panel. E.g. it will have name PriorityUserControl and four buttons to increase, decrease, maximize, minimize it's 'priority' (I placed buttons horizontally just to save place on screen):
Next, create four events in this user control:
public event EventHandler PriorityMaximized;
public event EventHandler PriorityIncreased;
public event EventHandler PriorityDecreased;
public event EventHandler PriorityMinimized;
And rise appropriate event when button clicked:
private void topButton_Click(object sender, EventArgs e)
{
if (PriorityMaximized != null)
PriorityMaximized(this, EventArgs.Empty);
}
That's it. We have user control which tells whether it want to move up or down. Now add user controls to TableLayoutPanel (either manually or dynamically) and subscribe same event handlers of these four events to ALL user controls. Something like:
// create user control and attach event handlers
PriorityUserControl control = new PriorityUserControl();
control.PriorityMaximized += priorityUserControl_PriorityMaximized;
control.PriorityMinimized += priorityUserControl_PriorityMinimized;
control.PriorityIncreased += priorityUserControl_PriorityIncreased;
control.PriorityDecreased += priorityUserControl_PriorityDecreased;
// add another row to table
panel.RowStyles.Add(new RowStyle(SizeType.AutoSize));
panel.RowCount = panel.RowStyles.Count;
// add control table layout panel
panel.Controls.Add(control);
panel.SetRow(control, panel.RowCount - 1);
Good. All you should do now is implement these event handlers. It's simple. E.g. decreasing priority (i.e. moving down):
private void priorityUserControl_PriorityDecreased(object sender, EventArgs e)
{
// sender is a control where you clicked Down button
Control currentControl = (Control)sender;
// get position in panel
var position = panel.GetPositionFromControl(currentControl);
// just to be sure control is not one at the bottom
if (position.Row == panel.RowCount - 1)
return;
// we want to switch with control beneath current
Control controlToSwitch = panel.GetControlFromPosition(0, position.Row + 1);
// move both controls
panel.SetRow(currentControl, position.Row + 1);
panel.SetRow(controlToSwitch, position.Row);
}
Now implementation of maximizing priority (i.e. moving to top):
private void priorityUserControl_PriorityMaximized(object sender, EventArgs e)
{
Control currentControl = (Control)sender;
var position = panel.GetPositionFromControl(currentControl);
if (position.Row == 0 || panel.RowCount < 2)
return;
Control topControl = panel.GetControlFromPosition(0, 0);
panel.SetRow(currentControl, 0);
panel.SetRow(topControl, position.Row);
}
I believe you will create rest two handlers by yourself.
The key of what you want is setting up a clear and extendable algorithm capable to deal with the different positions of the Panels. Here you have a simple code showing certain approach to this problem:
public partial class Form1 : Form
{
int[] panelLocations;
Point[] pointLocations;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
panelLocations = new int[5];
pointLocations = new Point[5];
panelLocations[1] = 1;
panelLocations[2] = 2;
panelLocations[3] = 3;
pointLocations[1] = new Point(panel1.Left, panel1.Top);
pointLocations[2] = new Point(panel2.Left, panel2.Top);
pointLocations[3] = new Point(panel3.Left, panel3.Top);
}
private void relocate(int curPanel, bool goTop)
{
int curLoc = panelLocations[curPanel];
int newLoc = curLoc - 1;
if (!goTop)
{
newLoc = curLoc + 1;
}
if (newLoc < 1) newLoc = 3;
if (newLoc > 3) newLoc = 1;
if (newLoc != curLoc)
{
int otherIndex = Array.IndexOf(panelLocations, newLoc);
panelLocations[curPanel] = newLoc;
relocatePanel(curPanel);
panelLocations[otherIndex] = curLoc;
relocatePanel(otherIndex);
}
}
private void relocatePanel(int curIndex)
{
if (curIndex == 1)
{
panel1.Location = pointLocations[panelLocations[1]];
}
else if (curIndex == 2)
{
panel2.Location = pointLocations[panelLocations[2]];
}
else if (curIndex == 3)
{
panel3.Location = pointLocations[panelLocations[3]];
}
}
private void buttonTop1_Click(object sender, EventArgs e)
{
relocate(1, true);
}
private void buttonBottom1_Click(object sender, EventArgs e)
{
relocate(1, false);
}
}
Open a new project, add 3 panels (Panel1, Panel2 and Panel3... better put different background colors) and include two buttons (buttonUp and buttonDown). This code will make the Panel1 to go up and down (by changing its position with the other panels).
The idea is pretty simple: at the start you store the positions of all the Panels in an array. In another array, you store where each panel is located every time (1 is the original position of Panel1, etc.).
It is a quite simple code which you can improve and extend as much as required, but the idea is pretty reliable and you can use it in any case: a set of fixed positions through which the panels will be moving.

How to properly change the image of a button I just clicked?

Here is my dilemma. I have a set of 10 buttons that I use to rate something. They have a star image that when pressed gets changed from a grey one to a red one. If I press the star number 5 all the previous ones also get changed to red.
My problem is that the star that is clicked does not change its image so I found a workaround to introduce a pause inside a dispatcher block. I don't see this very elegant but it works and I was wondering if someone has a better approach.
Even if nobody finds a better way at least this code will help other people to do stuff with multiple buttons that do almost the same thing or browsing through controls in a panel.
Here is the code of the click event:
private void btnStar_Click(object sender, RoutedEventArgs e)
{
//First we get the number of the star from the control name
String strNum = (sender as Button).Name;
strNum = strNum.Replace("btnStar", "");
int idxSelected = int.Parse(strNum);
Debug.WriteLine("Selected star #" + strNum);
//We store the image ON an OFF to be used later
ImageBrush imgbrON = new ImageBrush();
imgbrON.ImageSource = new BitmapImage(new Uri("images/estrella_on.png", UriKind.Relative));
imgbrON.Stretch = Stretch.None;
ImageBrush imgbrOFF = new ImageBrush();
imgbrOFF.ImageSource = new BitmapImage(new Uri("images/estrella_off.png", UriKind.Relative));
imgbrOFF.Stretch = Stretch.None;
//If we pressed the first star when only the first was selected we reset all
if (idxSelected == 1 && iCurrentNumberOfStars == 1)
{
idxSelected = 0; //In order to deselect all stars
Debug.WriteLine("Deselect all");
}
else
{
//Here is the code to do the WORKAROUND to select the clicked star
Dispatcher.BeginInvoke(() =>
{
Thread.Sleep(500);
(sender as Button).Background = imgbrON;
});
}
iCurrentNumberOfStars = idxSelected;
foreach (UIElement child in ContentPanel.Children)
{
Thread.Sleep(10);
if (child.GetType().Name == "Button")
{
Button tmpButton = (child as Button);
Image content = tmpButton.Content as Image;
strNum = tmpButton.Name;
if (strNum.StartsWith("btnStar") == true)
{
strNum = strNum.Replace("btnStar", "");
int idxtmp = int.Parse(strNum);
if (idxtmp > idxSelected )
{
Debug.WriteLine(tmpButton.Name + ":OFF");
tmpButton.Background = imgbrOFF;
}
else
{
Debug.WriteLine(tmpButton.Name +":ON");
tmpButton.Background = imgbrON;
}
}
}
}
}
}
The reason why it's happens - Button has a visual states which manipulate the background property. When you try to set the background to your own - animation overrides your changes. If you remove background animation from visual states - it will work without a Delay.

Hot to get cursor position relative to upper left corner of the control?

When I clicked on a control,
How to get cursor position relative to upper left corner of a (winforms) control ?
C#, VS 2005
PS:
I'm asking on context of tooltip "show" method which need that coordinates ..
This is my code to set tooltips on a composite control, might give you a clue (LED derivers from UserControl):
public LED()
{
InitializeComponent();
m_Image = global::AdvAdmittance.Controls.Properties.Resources.ledgray_small;
m_ToolTip = new ToolTip();
m_ToolTip.AutoPopDelay = 5000;
m_ToolTip.InitialDelay = 1000;
m_ToolTip.ReshowDelay = 500;
m_ToolTip.ShowAlways = true;
m_LedPictureBox.MouseHover += new EventHandler(m_LedPictureBox_MouseHover);
m_LedPictureBox.MouseLeave += new EventHandler(m_LedPictureBox_MouseLeave);
m_LedPictureBox.Click += new EventHandler(m_LedPictureBox_Click);
}
void m_LedPictureBox_MouseHover(object sender, EventArgs e)
{
if (m_ToolTipText != string.Empty)
{
Point toolTipPoint = this.Parent.PointToClient(Cursor.Position);
toolTipPoint.Y -= 20;
m_ToolTip.Show(m_ToolTipText, this.Parent, toolTipPoint);
}
}
void m_LedPictureBox_MouseLeave(object sender, EventArgs e)
{
m_ToolTip.Hide(this.m_LedPictureBox);
}
Ahh, Thanks for an answer.
All I need is a PointToClient method.
I hope (maybe) it will be useful for other people, here "my" code.
I took almost all code from http://support.microsoft.com/kb/322634 and modified three lines:
void treeView1_MouseMove(object sender, MouseEventArgs e)
{
// Get the node at the current mouse pointer location.
TreeNode theNode = this.treeView1.GetNodeAt(e.X, e.Y);
// Set a ToolTip only if the mouse pointer is actually paused on a node.
if ((theNode != null))
{
// Verify that the tag property is not "null".
if (theNode.Tag != null)
{
// Change the ToolTip only if the pointer moved to a new node.
if (theNode.Tag.ToString() != this.toolTip1.GetToolTip(this.treeView1))
{
//this.toolTip1.SetToolTip(this.treeView1, theNode.Tag.ToString());
Point c = System.Windows.Forms.Cursor.Position;
Point p = treeView1.PointToClient(c);
this.toolTip1.Show(theNode.Tag.ToString(), treeView1, p);
}
}
else
{
this.toolTip1.SetToolTip(this.treeView1, "");
}
}
else // Pointer is not over a node so clear the ToolTip.
{
this.toolTip1.SetToolTip(this.treeView1, "");
}
}
Have a look at
Windows Forms Coordinates
Control.PointToClient Method
C# Get a control’s position on a
form
Control PointToClient() vs
PointToScreen()

Categories