Cycle Button Background Images in C# - c#

I have a form in C# that has a button that, when clicked, I want the background image to cycle through a set of images (which I have as resources to the project). The images are named '_1', '_2', etc. and each time I click the button I want its background image to increment to the next one and go back to "_1" when it gets to the highest. Is there a way to do this?
I tried getting button1.BackgroundImage.ToString() but that yields System.Drawing.Bitmap instead of Resources._1 like I was thinking it would (in which case I could just get the last character and switch on that to change the background to the appropriate new image).
Thanks for your help.

Why don't you just put the images in an array?

You could subclass Button and override the BackgroundImage property so you can better keep track of the current resource that represents the image. You might also override the onclick method to internally handle cycling to the next image, though that might be a little weird if the resources are handled outside of your derived button class.

class YourClass
{
private IEnumerator<Image> enumerator;
YourClass(IEnumerable<Image> images)
{
enumerator = (from i in Enumerable.Range(0, int.Max)
from image in images
select image).GetEnumerator();
enumerator.MoveNext();
}
public Image CurrentImage { get { return enumerator.Current; } }
public void OnButtonClick() { enumerator.MoveNext(); }
}
You can use this code as a backing class for your control under the assumption that user wont click the button more than two billion times.
Just note that once this class is created you cannot modify given image list outside. If you want to do such things you need to implement disposable pattern and dispose the enumerator accordingly.

Related

Change "text" property of all buttons on form incrementally

I have created a soundboard in Visual C# where I click buttons and it plays an MP3 file. I currently have buttons named SBut_01 to SBut_x appropriately.
I store paths to the MP3 files that are being associated to these buttons in an xml file, which is fed in on startup and all "captions" for the corresponding buttons (which are extracted from the path for each key) are stored in an array named ButtonCapts[x].
An example of something that could be in ButtonCapts[1] would be Foo stored as a string.
Originally when I created this soundboard, I manually specified each button's caption using a method I named AssignButtonCaptions. Which worked crudely like so:
public void AssignButtonCaptions{
SBut_01.Text = ButtonCapts[1];
SBut_02.Text = ButtonCapts[2];
...
}
However, as this soundboard expands in scope this is becoming a very large section of hardcode, so I would like to automate this as much as possible.
I've thought that I could probably use a foreach loop in the instance of ButtonCapts[], but I don't at present know a way to loop through all of my SBut_xx buttons. Is it possible to detect all buttons on a form that are prefixed by SBut_ and iterate through them from 1 to x?
It would be great if something like below could be accomplished. Please note that I have assumed that each button has been put in an array named SButList as an example of what I'm trying to accomplish. I understand there is no way this code would work in its current form and am not even sure if objects such as buttons could be stored in such a way:
//Some kind of method to put all buttons prefixed with SBut_ in to an array named ButtonList here
//Then, iterate through all of the buttons and assign the captions
int i = 1;
foreach (button SBut in ButtonList)
{
SBut.Text = ButtonCapts[i];
i++;
}
Is this possible?
Thanks
Ok, so I've figured this out with some help from Anu6is. I'll document what I found so that it may help others.
Turns out that you can easily extract all controls in a form by using the following to accomplish this:
var myButtons in Controls.OfType<Buttons>().Where(button => button.Name.Contains("Something"))
The issue I was having was that I needed to iterate through each parent directory, as my buttons were inside of a Group Box, which was in turn inside of one of many Tab Pages within a Tab Control object. I just did a quick foreach loop to iterate through all of these tabs and group boxes in order to resolve this.
My new working code looks like this, which in my opinion is much better:
public void AssignButtonCaptions()
{
foreach (TabPage Page in tabControl1.TabPages)
{
foreach(GroupBox ButtonContainer in Page.Controls.OfType<GroupBox>())
{
foreach (var SButton in ButtonContainer.Controls.OfType<Button>().Where(button => button.Name.Contains("SBut")))
{
int ButtonNum = Int32.Parse(SButton.Name.Split('_')[1]);
SButton.Text = ButtonCapts[ButtonNum];
}
}
}
}

Issue designing code to manage objects

I have a C# winform which displays snapshots from a camera. The form has four PictureBox controls and When an image is taken it is placed into pictureBox1 and the previous images are bumped along to 2,3 and 4. Under each picture box is also a label which displays the time stamp and the order number (each image is given a number 1-4, that stays with it until it is bumped off in which the newest image takes that number). Currently I am doing it like below. However I feel this is very inefficient and will cause me problems later on if I decide to add key down events to change the backcolors of some of the labels (to indicate status).
Does anyone know of a better way to do this?
if (count > 4)
{
count = 0
}
count ++;
pictureBox4.image = pictureBox3.image;
pictureBox3.image = pictureBox2.image;
pictureBox2.image = pictureBox1.image;
pictureBox1.imagelocation = (#"http://192.168.X.X/image.cgi")
label4.Text =label3.text;
label3.text = label2.text;
label2.text = label1.text;
label1.text = count.ToString()+ " " + datetime.now();
I could create a new Control, most likely a Panel that contains all of these UI elements in it (PictureBox, Label, anything else). Have a constructor for your Control that takes a URL of the image. Load the image into your PictureBox, and set your label.
Have all of that logic encapsulated in your Control. So when a new one is added, you just create the new Control, and remove the last one in your row, and move the .Left properties of the 3 remaining to their new locations.
Don't forget to implement IDisposable, and Dispose of the Controls when they're removed to free up the resources of displaying the images.
EDIT
If it's not there already, you can provide references back to the top Control in each of your inner Controls (PictureBox and Label), and even to your main form in your top Control by passing this as a parameter in the constructor as well and setting a private member variable inside those controls. That way, when someone clicks on the PictureBox, you can go up the line to this.Parent and get your outer Control. You could even have that reference to your Main Form (hopefully a Panel in there that holds your 4 of these objects). That could be this.Parent.Parent to call a method on there. (I think there's already a public property of Parent on all Controls, so that's fine.)
A little bit of quick coding:
You have your main Form (mainForm). In there is a Panel (picturePanel). picturePanel holds your 4 new Panels, which we'll call customPanel. Each customPanel has a PictureBox (imageBox), and Label (fileNameLabel).
Your customPanel constructor would look like this:
public partial class CustomPanel : Panel {
private PictureBox _imageBox;
private Label _fileNameLabel;
public CustomPanel() {} // This is most likely tied into the code behind file. Sorry, It's been a while since I've done WinForms
public CustomPanel(string imageFileName, Panel parent) {
// Set the source for the PictureBox.
// Set the Text of the label.
_parent = parent;
}
}
Continue with this down the line through the PictureBox and Label. Then in your events, you have your PictureBox work up the chain. To find picturePanel. If you want to get really fancy, you could have that derive from Panel as well and just add a public property that handles all of the switching around of which customPanel sent the message.
So down in your PictureBox event, you could have a line of code like this:
if (this.Parent.Parent is PicturePanel) {
((PicturePanel)this.Parent.Parent).RemovePicture(this.Parent);
}

How can I check if a PictureBox's image has been modified?

I'm working on a super top-secret application right now that has a PictureBox on it. There are some options to edit the image inside of the PictureBox, including (but not limited to) sepia, grayscale, and some rotations.
At the moment, however, there isn't any way to check if the image has been modified - as I've been trying to create a "Would you like you save your changes??!" dialog box, which would appear if the image has been modified in any way.
I've got all of the actual modifications set in stone at the moment - all I need is a reliable method of checking if the PictureBox's image has been modified.
mmm.. Every time the user use an option to edit the image, just set a flag to true. That way you know that the image has change.. in fact, you are changing it somehow, so you know when is modified!
Does the BackgroundImageChanged Event fire? Use that.
http://msdn.microsoft.com/en-us/library/system.windows.forms.control.backgroundimagechanged.aspx
There are some options to edit the image inside of the PictureBox,
including (but not limited to) sepia, grayscale, and some rotations.
private bool isChanged = false;
void Apply_sepia() {
isChanged = true;
// apply sepia
}
void close(...) {
if(isChanged) {
if(MessageBox.Show("Are you sure?", SomethingOptions.YesNo) == MessageBoxOptions.Yes) {
Application.Exit();
}
} else {
Application.Exit();
}
}
I obviously cannot remember what the correct property names are.. however you get the gist.

Bring Winforms control to front

Are there any other methods of bringing a control to the front other than control.BringToFront()?
I have series of labels on a user control and when I try to bring one of them to front it is not working. I have even looped through all the controls and sent them all the back except for the one I am interested in and it doesn't change a thing.
Here is the method where a label is added to the user control
private void AddUserLabel()
{
var field = new UserLabel();
userContainer.Controls.Add(field);
SendLabelsToBack(); // Send All labels to back
userContainer.Controls[field.FieldName].BringToFront();
}
Here is the method that sends all of them to the back.
private void SendLabelsToBack()
{
foreach (var label in userContainer.Controls);
label.SendToBack();
}
Yeah, there's another way. The Controls.SetChildIndex() also changes Z-order. The one with index 0 is the one on top. Doesn't buy you anything though, BringToFront() uses this method.
Your SendLabelsToBack() method as given cannot work, it will also send the label to added to the back. But your next statement fixes that again.
Okay, that doesn't work, which means the BringToFront() method doesn't get executed. Look in the Output window for a "first chance exception" notification. As written, your SendLabelsToBack() will cause an exception if the user control contains any control other than a UserLabel. Also, set a breakpoint after the BringToFront() call and check the value of userContainer.Controls[0].Name when it breaks.
Controls' z-index is per-container.
If you call BringToFront on a control that is inside a container (such as a Panel), it will not bring the container to the front.
Therefore, the control will only go in front of other controls in that container.
To see what containers your controls are in, you can use the Document Outline pane in the View menu.
EDIT: Your userContainer control is probably behind a different control.
Have you tried Invalidate() after BringToFront()? BringToFront does not raise the Paint event
try this:
private void SendLabelsToBack()
{
foreach (var label in userContainer.Controls)
{
label.SendToBack();
label.Invalidate();
}
}
I think you just need to change your last line:
userContainer.Controls[field.FieldName].BringToFront();
to this:
userContainer.Controls[field.Name].BringToFront();
When you use a string as the indexer for the Controls collection, it goes by the Name property of the control (not the FieldName property).
Since you're just trying to bring the most recently-added control to the top, this would also work:
userContainer.Controls[userContainer.Controls.Count - 1].BringToFront();
From my experience looks like windows puts all controls belonging to one graphic container(pane, group box...etc) in a software collection. The collection is ordered by child index which is a property of every control in that container.
The trick is that children with the same index can and do exists. In this case windows will paint those children ordered relative to others but between them it will paint them in the reverse order they had been added to the container.
Long story short: for one container-you need to make sure controls have different indexes by changing ALL NOT just SOME of the indexes when you want to change the z-order. For example:
foreach (Control newControl in TopControl.Controls)
{
TopControl.Controls.SetChildIndex(newControl,indexlogic(newControl));
}
where indexLogic(newControl ) is your method of calculation of the index of particular control.

How can I access my applications images that are in my resources?

I'm going to briefly explain what I want my program to do.
I have a lot of Images on my form and I want the image source to change on MouseEnter event.
So, if a user moves the mouse over the button, I'd like the button to appear to be glowing. Of course I've made two images for the Image control. One normal, and one glowing. I'm trying to make a single event on mouseEnter for all of the images because I don't want to pollute my code with 60+ events all essentially doing the same thing.
Someone suggested I do something like this:
void HeroMouseEnter(object sender, EventArgs e)
{
((PictureBox)sender).Image = GetImage(((PictureBox)sender).Name)
}
Honestly, this would work exactly how I need it to. But I'm a bit confused is about the GetImage() method.
How exactly would I code this? All of my images, both the glowing and non glowing ones are already added to my resources. How would I summon them according to the PictureBox's name?
I tried making a dictionary with the key being the name of the pictureBox and the value being the resource file, but no dice.
Please help!
Something like this?
public Image GetImage(string name)
{
switch (name)
{
case "PictureBox1":
return Properties.Resources.Picture1;
case "PictureBox2":
return Properties.Resources.Picture2;
default:
return null;
}
}

Categories