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

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.

Related

Adding multiple PictureBox to a C# Form

I am trying to add several pictures (using PictureBox) of music notes to a Music Staff Form.
In a certain Mouse Event (MouseUp), a Music Note is created, and a music note should appear on the screen. However, only when the first note is created does the image appear on the screen. For every other note created after the first one, the image does not show up.
In the below method, the Music Note is created:
private void Key_MouseUp(object sender, MouseEventArgs e)
{
foreach (MusKey mk in panel1.Controls)
{
if (sender == mk) //true for the specific key pressed on the Music Keyboard
{
if (e.Button == MouseButtons.Left)
{
timer1.Enabled = false;
sp.Stop();
string bNoteShape = null;
// ticks -> milliseconds
if (count >= 16)
{
bNoteShape = "SemiBreve";
duration = 1024;
}
else if (count >= 8 && count <= 15)
{
bNoteShape = "DotMinim";
duration = 768;
}
else if (count >= 4 && count <= 7)
{
bNoteShape = "Crotchet";
duration = 384;
}
else if (count >= 2 && count <= 3)
{
bNoteShape = "Quaver";
duration = 128;
}
else
{
bNoteShape = "Semi-Quaver";
duration = 90; //63 is too short
}
MusicNote mn = new MusicNote(mk.getMusicNote(), duration, bNoteShape, mk.getNote());
Notes.Add(mn);
mn.picturebox1.Location = new Point(xLoc1, yLoc1);
panel2.Controls.Add(mn.picturebox1); //adding MusicNote component to MusicStaff (panel2) collection
}
}
}
}
When the Note is created, it is sent to the MusicNote constructor:
public MusicNote(int iNotepitch, int iDuration, String iBnoteShape, String iNote)
{
notepitch = iNotepitch;
noteduration = iDuration;
noteshape = iBnoteShape;
note = iNote;
picturebox1.ImageLocation = NoteImage_path + noteshape + ".bmp";
picturebox1.BackColor = Color.Transparent;
picturebox1.Location = new Point(xLoc, yLoc);
xLoc = xLoc + 15;
}
I have tried initializing the location (and incrementing xLoc) both through the constructor, and also in the form itself in method Key_MouseUp, but neither seems to make a difference.
The logic seems to be correct, as the picture of the first music note always loads, but I cannot understand why every other note after the first does not show up on my screen.
Any help will be appreciated, thanks!
Edit: perhaps maybe there is another alternative to PictureBox that I could use to store the music notes?
well it looks like your not initializing a new "picturebox1"
picturebox1 = new PictureBox(); //Add this
picturebox1.ImageLocation = NoteImage_path + noteshape + ".bmp";
edit:
Ok in general we need a few things to add a control to a form.
the new Control (look like you have that) 2
Adding it to the parent control (looks like you have that)
panel2.Controls.Add(mn.picturebox1); //adding MusicNote component to MusicStaff (panel2) collection
redraw the control --
this can happen automatically when you resize the form or you can call refresh(on the panel) after the new control is added to the panel.
panel2.Controls.Add(mn.picturebox1);
panel2.Refresh();

C# WPF Pop is being drawn multiple times

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.

Pictureboxes not becoming visible

Sorry, but I have another problem. In my code I can now get it to randomly assign pictures to pictureboxes but unfortunately I cannot get any of the pictureboxes to become visible, upon clicking them, this event should happen:
private void pictureBox1_Click(object sender, EventArgs e)
{
// The timer is only on after two non-matching
// icons have been shown to the player,
// so ignore any clicks if the timer is running
if (timer1.Enabled == true)
{
return;
}
PictureBox clickedpicturebox = sender as PictureBox;
if (clickedpicturebox == null)
{
// If the clicked picture is visible, the player clicked
// an icon that's already been revealed --
// ignore the click
if (clickedpicturebox.Visible == true)
return;
// If firstClicked is null, this is the first icon
// in the pair that the player clicked,
// so set firstClicked to the picturebox that the player
// clicked, make it visible, and return
if (firstClicked.Tag == null)
{
clickedpicturebox = firstClicked;
firstClicked.Tag = clickedpicturebox.Tag;
firstClicked.Visible = true;
}
// If the player gets this far, the timer isn't
// running and firstClicked isn't null,
// so this must be the second icon the player clicked
// Set its property to visible
clickedpicturebox = secondClicked;
secondClicked.Tag = clickedpicturebox.Tag;
secondClicked.Visible = true;
// If the player gets this far, the player
// clicked two different icons, so start the
// timer (which will wait three quarters of
// a second, and then hide the icons)
timer1.Start();
}
}
But for some reason, even if I strip it down to just a line that says:
PictureBox clickedpicturebox = sender as PictureBox;
clickedpicturebox.Visible = true;
It still doesn't work, could it be because I selected multiple pictures to apply the event to at the same time?
Also, incase you need it, I have the properties of the first picturebox here, all other pictureboxes are essentially the same.
//
// pictureBox1
//
this.pictureBox1.BackColor = System.Drawing.Color.Transparent;
this.pictureBox1.Dock = System.Windows.Forms.DockStyle.Fill;
this.pictureBox1.Location = new System.Drawing.Point(5, 5);
this.pictureBox1.Name = "pictureBox1";
this.pictureBox1.Size = new System.Drawing.Size(125, 119);
this.pictureBox1.TabIndex = 0;
this.pictureBox1.TabStop = false;
this.pictureBox1.Visible = false;
this.pictureBox1.Click += new System.EventHandler(this.pictureBox1_Click);
EDIT: I would like to thank everyone, the problem is now resolved, I have used the labels allow me to easily interact between the foreground and background colours, allowing for easy transition between a label and a picturebox.
H W and ASh are both correct, the problem is resolved.

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");
}

Combining ClearAll with Watermark on textbox in WPF

After trying numerous methods to get watermarking to work for me, I finally found the one modified by #Beej on this page:
Watermark / hint text / placeholder TextBox in WPF
I got it placed in my project, and it works fine, with one exception. I have multiple textboxes on each tab of a tabcontrol. At the bottom is a clear button that works to clear all the textboxes on the tab. The clear button works fine, the watermark works fine, but I can't get them to work together. The window loads with the watermarks in place, and pressing the clear button clears all the boxes, but the watermarks don't reappear until after I move through the textboxes (each one gaining and losing focus.) I have tried numerous ways to solve this, such as placing a method call to the ShowWatermark function in the button MouseUp event, but nothing has worked...Help?!
This is the Clear button method I'm using:
public void ClearTextBoxes()
{
ChildControls ccChildren = new ChildControls();
foreach (object o in ccChildren.GetChildren(rvraDockPanel, 2))
{
if (o.GetType() == typeof(TextBox))
{
TextBox txt = (TextBox)o;
txt.Text = "";
}
if (o.GetType() == typeof(DigitBox))
{
DigitBox digit = (DigitBox)o;
digit.Text = "";
}
if (o.GetType() == typeof(PhoneBox))
{
PhoneBox phone = (PhoneBox)o;
phone.Text = "";
}
if (o.GetType() == typeof(DateBox))
{
DateBox date = (DateBox)o;
date.Text = "";
}
if (o.GetType() == typeof(TextBoxWatermarked))
{
TextBoxWatermarked water = (TextBoxWatermarked)o;
water.Text = "";
}
}
}
class ChildControls
{
private List<object> listChildren;
public List<object> GetChildren(Visual p_vParent, int p_nLevel)
{
if (p_vParent == null)
{
throw new ArgumentNullException("Element {0} is null!", p_vParent.ToString());
}
this.listChildren = new List<object>();
this.GetChildControls(p_vParent, p_nLevel);
return this.listChildren;
}
private void GetChildControls(Visual p_vParent, int p_nLevel)
{
int nChildCount = VisualTreeHelper.GetChildrenCount(p_vParent);
for (int i = 0; i <= nChildCount - 1; i++)
{
Visual v = (Visual)VisualTreeHelper.GetChild(p_vParent, i);
listChildren.Add((object)v);
if (VisualTreeHelper.GetChildrenCount(v) > 0)
{
GetChildControls(v, p_nLevel + 1);
}
}
}
}
Both the ChildControls class and the TextboxWatermarked class (from the above link) are in seperate class files.
The problem is not with your code, it's with the chosen watermarked text box. It only updates the watermark when it gains or loses focus, which is an obvious flaw. You'll need to find a better implementation. Have you tried the one in extended WPF toolkit?

Categories