im starting to code with c# and i have a problem. Basically, i want to jump colors clockwise. For example, if i click the orange button the gold button turns white and the second button turns gold. I want to do that for all buttons until the button bellow gold button be gold and the others stay white (clockwise)
You can see what i explained above in this image.
The only code that i have for now is:
if (button9.Enabled)
{
button2.BackColor = Color.Gold;
button1.BackColor = Color.White;
}
Button 9 is the jump button.
If you could help me i would be very grateful.
Here is an simple WinForm example
You can follow the serial number (1.1 ~ 4.2) in the code comment to get the code idea :)
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
namespace WinFormsApp1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
// 2.1 initialize a list, and add all buttons into it.
this.Buttons.Add(button2);
this.Buttons.Add(button3);
this.Buttons.Add(button4);
// 2.2. initialize high light index.
HighlightIndex = 0;
// 2.3. refresh all buttons' color.
RefreshColor();
}
// 1.1 we use this List to store all buttons.
private List<Button> Buttons = new List<Button>();
// 1.2 indicated the index of the highlighted button
private int HighlightIndex = 0;
private void button1_Click(object sender, System.EventArgs e)
{
// 4.1 when user click the jump button, increme the highlight index.
HighlightIndex = HighlightIndex + 1;
if (HighlightIndex >= Buttons.Count)
{
HighlightIndex = 0;
}
// 4.2 refresh all buttons' color.
RefreshColor();
}
private void RefreshColor()
{
// 3.1 loop all buttons by index.
for (int i = 0; i < Buttons.Count; i++)
{
if (i == HighlightIndex)
{
// 3.2 if the index equals the highlight button index, update BackColor as Gold.
Buttons[i].BackColor = Color.Gold;
}
else
{
// 3.3 set normal button BackColor as White.
Buttons[i].BackColor = Color.White;
}
}
}
}
}
Related
I am trying to reproduce the operation of the Control Expander WPF, or as shown in the menu of Outlook, Vertical Web Menu etc., since in WindowsForms this control does not exist. Here I leave the sample code: Menu_Expader.zip link GoogleDrive.
I have managed to do it using the following controls:
Panels
FlowLayoutPanel
1 Time Control
Button Vectors
Labels Vectors ...
This works perfectly, but it happens that to each panel I must establish a
Maximum Size and Minimum Size therefore every time I add an item inside I must modify the size of the panel where I add it, and the item are very close to each other is a bit annoying for the user's vision.
Example this is what I currently have:
EDIT
Code Sample:
// The state of an expanding or collapsing panel.
private enum ExpandState
{
Expanded,
Expanding,
Collapsing,
Collapsed,
}
// The expanding panels' current states.
private ExpandState[] ExpandStates;
// The Panels to expand and collapse.
private Panel[] ExpandPanels;
// The expand/collapse buttons.
private Button[] ExpandButtons;
// Initialize.
private void Form1_Load(object sender, EventArgs e)
{
// Initialize the arrays.
ExpandStates = new ExpandState[]
{
ExpandState.Expanded,
ExpandState.Expanded,
ExpandState.Expanded,
};
ExpandPanels = new Panel[]
{
panModule1,
panModule2,
panModule3,
};
ExpandButtons = new Button[]
{
btnExpand1,
btnExpand2,
btnExpand3,
};
// Set expander button Tag properties to give indexes
// into these arrays and display expanded images.
for (int i = 0; i < ExpandButtons.Length; i++)
{
ExpandButtons[i].Tag = i;
ExpandButtons[i].Image = Properties.Resources.expander_down;
}
}
// Start expanding.
private void btnExpander_Click(object sender, EventArgs e)
{
// Get the button.
Button btn = sender as Button;
int index = (int)btn.Tag;
// Get this panel's current expand
// state and set its new state.
ExpandState old_state = ExpandStates[index];
if ((old_state == ExpandState.Collapsed) ||
(old_state == ExpandState.Collapsing))
{
// Was collapsed/collapsing. Start expanding.
ExpandStates[index] = ExpandState.Expanding;
ExpandButtons[index].Image = Properties.Resources.expander_up;
}
else
{
// Was expanded/expanding. Start collapsing.
ExpandStates[index] = ExpandState.Collapsing;
ExpandButtons[index].Image = Properties.Resources.expander_down;
}
// Make sure the timer is enabled.
tmrExpand.Enabled = true;
}
// The number of pixels expanded per timer Tick.
private const int ExpansionPerTick = 7;
// Expand or collapse any panels that need it.
private void tmrExpand_Tick(object sender, EventArgs e)
{
// Determines whether we need more adjustments.
bool not_done = false;
for (int i = 0; i < ExpandPanels.Length; i++)
{
// See if this panel needs adjustment.
if (ExpandStates[i] == ExpandState.Expanding)
{
// Expand.
Panel pan = ExpandPanels[i];
int new_height = pan.Height + ExpansionPerTick;
if (new_height >= pan.MaximumSize.Height)
{
// This one is done.
new_height = pan.MaximumSize.Height;
}
else
{
// This one is not done.
not_done = true;
}
// Set the new height.
pan.Height = new_height;
}
else if (ExpandStates[i] == ExpandState.Collapsing)
{
// Collapse.
Panel pan = ExpandPanels[i];
int new_height = pan.Height - ExpansionPerTick;
if (new_height <= pan.MinimumSize.Height)
{
// This one is done.
new_height = pan.MinimumSize.Height;
}
else
{
// This one is not done.
not_done = true;
}
// Set the new height.
pan.Height = new_height;
}
}
// If we are done, disable the timer.
tmrExpand.Enabled = not_done;
}
I want to get a result similar to this - Bootstrap Menu Accordion:
Imitate that operation panels expand according to the quantity of item that it contains as long as it does not protrude from the screen, in which case it will show the scroll bar. I know there are software that provide custom controls like DVexpress, DotNetBar Suite among others, but they are Licensed Software I do not want to use it illegally pirate. Can you help me optimize it or create it in another way?
Environment: Visual Studio 2010 & .NET NetFramework 4.
The original question I made it in StackOverFlow in Spanish.
Modulo (Module)
Menu Principal (Main menu)
Mantenimientos (Maintenance)
Procesos (Processes)
Consultas (Queries)
Reportes (Reports)
Note: If someone speaks Spanish and English and can do a better translation, please edit the question. (Excuse the advertising on the image, I recorded the screen with a software trial version).
What i'm doing is capturing a screenshot of windows in the background.
In a ListBox i have items of windows i'm taking screenshots.
The idea is that the windows are in the background so i don't need to move the windows to the foreground each time.
In form1 constructor:
this.listBoxSnap.Items.AddRange(WindowSnap.GetAllWindows(true, true).ToArray());
Then in form1 i also have a RefreshWindowsList method:
private void RefreshWindowsList()
{
Graphics g;
g = Graphics.FromImage(img);
g.Clear(Color.Transparent);
buttonSnap.Enabled = true;
this.listBoxSnap.Items.Clear();
this.listBoxSnap.Items.AddRange(WindowSnap.GetAllWindows(true, true).ToArray());
for (int i = listBoxSnap.Items.Count - 1; i >= 0; i--)
{
string tt = listBoxSnap.Items[i].ToString();
if (tt.Contains(" ,"))
{
listBoxSnap.Items.RemoveAt(i);
}
}
string[] myList = new string[listBoxSnap.Items.Count];
for (int i = 0; i < listBoxSnap.Items.Count; i++)
{
string tt = listBoxSnap.Items[i].ToString();
int index = tt.LastIndexOf(",");
myList[i] = tt.Substring(0, index);
}
textBoxIndex.Text = listBoxSnap.Items.Count.ToString();
if (this.listBoxSnap.Items.Count > 0)
{
this.listBoxSnap.SetSelected(0, true);
}
listBoxSnap.Select();
}
The problem is that if i will make a refresh if i will call this method every X seconds/minutes the ListBox will blink.
So i was wondering if there is a way to refresh only a specific window form the WindowSnap.GetAllWindows collection and not to refresh the ListBox.
So if i refresh the specific hWnd window and save the window image as screenshot on the hard disk i will not see any changes on the ListBox but i will have a collection of images saved on the hard disk.
The class WindowSnap is a bit long so i will give a link for it i don't want to add it all here:
WindowSnap.cs
And it's using also the class WindowSnapCollection.cs
WindowSnapCollection.cs
The idea again is somehow to refresh specific hWnd window frol the collection and to refresh the ListBox.
To disable flicker while clearing and reloading items of ListBox, you can use BeginUpdate() and EndUpdate() methods of ListBox control, for example in a timer with interval 1000 I wrote this code and there was no flicker in ListBox:
private void timer1_Tick(object sender, EventArgs e)
{
this.listBox1.BeginUpdate();
this.listBox1.Items.Clear();
for (int i = 0; i < 100; i++)
{
this.listBox1.Items.Add(i);
}
this.listBox1.EndUpdate();
}
Also you can put your new items in a list and then, add them using :
listBox1.Items.AddRange(yourItemsList.Cast<object>().ToArray());
newbie programmer here after hours of searching has left me stumped.
I'm having trouble with referencing a control inside a tab created at RunTime with a button press. Basically what I have is a tabletop RPG calculator, using a Windows Form, that has a tabControl holding tab pages, with each tab page holding user-inputted stats for that individual enemy to be used in calculations.
The problem is that I want the user to be able to click a button to generate a new enemy tab page. Here is my code for generating an enemy tab page with a TextBox.
int enemyNumber = 0;
// Creates a new Enemy Tab
private void button2_Click_1(object sender, EventArgs e)
{
// Create a new TabPage
var newTabPage = new TabPage()
{
Text = "Enemy " + enemyNumber,
};
// Add Enemy Name Box
var newEnemyNameBox = new TextBox()
{
Name = "enemyNameBox" + enemyNumber,
Text = "",
Location = new Point(127, 11),
Size = new Size(133, 20)
};
// Add the controls to the new Enemy tab
newTabPage.Controls.Add(newEnemyNameBox);
// Add the TabPage to the TabControl
tabControl1.TabPages.Add(newTabPage);
// Increases the enemy's "reference number" by 1
// So that enemy tabs will be generated in order enemyTab0, enemyTab1, etc.
enemyNumber += 1;
}
This all works nicely. Unfortunately, after this point things have gotten ugly. I need to reference that TextBox named "enemyNameBox" + enemyNumber, and I'm not sure how to do so.
What I did was create "archVariables" to store the values from whatever enemy tab is selected, then use the appropriate archVariable in the program's calculations. IE: archEnemyName. The idea is that whatever tab the user is currently selected on (determined via SelectedIndex) the TextBox from that page will be used for the program's output.
Here are the two things I've tried after researching the matter:
// Attempt 1
private void defendCalcButton_Click(object sender, EventArgs e)
{
for (int i = 0; i < tabControl1.SelectedIndex; i++)
{
archEnemyNameBox = ((TextBox)Controls["enemyNameBox" + i]).Text;
}
}
This code simply throws a NullReferenceException when I press the button. So after researching more I tried this:
// Attempt 2
private void defendCalcButton_Click(object sender, EventArgs e)
{
for (int i = 0; i < tabControl1.SelectedIndex; i++)
{
TextBox tb2 = new TextBox();
tb2 = ((TextBox)(enemyTab.Controls.Find("enemyNameBox" + i, true)));
archEnemyNameBox = tb2.Text;
}
}
This time I got an Error: Cannot convert type 'System.Windows.Forms.Control[]' to 'System.Windows.Forms.TextBox'
I feel like the second method I have here is probably closer to the correct way to do this, but apparently I'm still not getting it right. I've learned a lot by searching the information on stackoverflow and msdn.microsoft but nothing has gotten me past this problem.
Any help would be appreciated.
basically the problem with your second attemp is that enemyTab.Controls.Find("enemyNameBox" + i, true) returns an array of Controls Control[] and you're trying to convert that to a Control here is the problem, you should get the first control in that array and then convert it to a Control so it should be like this:
private void defendCalcButton_Click(object sender, EventArgs e)
{
for (int i = 0; i < tabControl1.SelectedIndex; i++)
{
TextBox tb2 = new TextBox();
tb2 = ((TextBox)(enemyTab.Controls.Find("enemyNameBox" + i, true)[0]));
archEnemyNameBox = tb2.Text;
}
}
but it is not the BestWay to do so it seems that everytime a user adds a new tabPage it will have the same Controls right? so why not create an userControl with any Control you have on your TabPage? so when you press the user press to add a new tab your code should be like so:
private void CreateNewEnemyTab()
{
var newTabPage = new TabPage()
{
Text = "Enemy " + enemyNumber,
};
EnemyTabUserControl enemyTab = new EnemyTabUserControl(enemyNumber);
here the EnemyTabUserControl should have all the components you need;
newTabPage.Controls.Add(enemyTab);
tabControl1.TabPages.Add(newTabPage);
}
and the code to bring the TextBox from the current tab could be as follow (you are going to need to reference LINQ)
using System.Linq;
//First Lets create this property, it should return the selected EnemyTabUserControl inside the tabControl
public EnemyTabUserControl CurrentTab {
get {
return tabControl1.SelectedTab.Controls.OfType<EnemyTabUserControl>().First();
}
}
// then if we make the textbox you want to reference from outside the code we can do this
CurrentTab.NameOfTheTextBox;
Patrick has solved your fundamental problem, but I don't think you need the loop in there at all. Here I've broken the steps out so you can see what needs to happen a little better:
private void defendCalcButton_Click(object sender, EventArgs e)
{
Control[] matches = this.Controls.Find("enemyNameBox" + tabControl1.SelectedIndex.ToString(), true);
if (matches.Length > 0 && matches[0] is TextBox)
{
TextBox tb = (TextBox)matches[0];
archEnemyNameBox = tb.Text;
}
}
I am trying to create some kind of linkage between some buttons on the form so that when i click a button it highlights all the previous buttons to it [some kind of volume controller]
Imagine it as a volume controller. All these colored buttons will be gray and what i want to achieve is as you click on a button it colorizes all the buttons before it; However IDK what is the best way to make a behavior like this without involving tons of useless codes...
First you will need to add all the buttons to an array then handle it from there
Code
//Create an array of buttons and hook up the Click event of each of them
private Button[] VolumeButtons { get; set; }
public Main()
{
InitializeComponent();
//Assuming that you have 21 buttons as it appears in your picture...
VolumeButtons = new Button[21];
VolumeButtons[0] = button1;
VolumeButtons[1] = button2;
VolumeButtons[2] = button3;
VolumeButtons[3] = button4;
VolumeButtons[4] = button5;
VolumeButtons[5] = button6;
VolumeButtons[6] = button7;
VolumeButtons[7] = button8;
VolumeButtons[8] = button9;
VolumeButtons[9] = button10;
VolumeButtons[10] = button11;
VolumeButtons[11] = button12;
VolumeButtons[12] = button13;
VolumeButtons[13] = button14;
VolumeButtons[14] = button15;
VolumeButtons[15] = button16;
VolumeButtons[16] = button17;
VolumeButtons[17] = button18;
VolumeButtons[18] = button19;
VolumeButtons[19] = button20;
VolumeButtons[20] = button21;
foreach (var volumeButton in VolumeButtons)
volumeButton.Click += VolumeButton_Click;
}
void VolumeButton_Click(object sender, EventArgs e)
{
//Find the index of the clicked button
int index = Array.FindIndex(VolumeButtons, 0, VolumeButtons.Length, button => button == ((Button)sender));
//Set the color of all the previous buttons to Aqua, and all the forward buttons to gray [ you may play with it to match your colors then ]
for (int i = 0; i < VolumeButtons.Length; i++)
VolumeButtons[i].BackColor = i <= index ? Color.Aqua : Color.Gray;
}
Put the buttons in an array
Create a click event that looks up the index n of the button (sender) in the array and sets the style of each button 0 - n appropriately.
Wire up each button to the click event
Do as much as you can on your own and come back with specific questions as necessary.
I have a System.Windows.Forms.Panel with some content.
I am trying to programmatically scroll the panel (vertically) either up or down.
I have tried setting the AutoScrollPosition property to a new Point on the panel but that doesn't seem to do it.
I have the AutoScroll property set to true.
I even tried to set the VerticalScroll.Value twice as suggested here, but that doesn't seem to work either.
This is what I am currently doing:
//I have tried passing both positive and negative values.
panel.AutoScrollPosition = new Point(5, 10);
The X and Y values on AutoScrollPosition remain 0 and 0.
Any help or direction on this would be greatly appreciated it.
Thanks in advance,
Marwan
Here is a solution. I guess you can scroll your Panel by arbitrary position using Win32 however there is a simple trick to help you achieve your requirement here:
public void ScrollToBottom(Panel p){
using (Control c = new Control() { Parent = p, Dock = DockStyle.Bottom })
{
p.ScrollControlIntoView(c);
c.Parent = null;
}
}
//use the code
ScrollToBottom(yourPanel);
Or use extension method for convenience:
public static class PanelExtension {
public static void ScrollToBottom(this Panel p){
using (Control c = new Control() { Parent = p, Dock = DockStyle.Bottom })
{
p.ScrollControlIntoView(c);
c.Parent = null;
}
}
}
//Use the code
yourPanel.ScrollToBottom();
UPDATE
If you want to set the exact position, modifying the code above a little can help:
//This can help you control the scrollbar with scrolling up and down.
//The position is a little special.
//Position for scrolling up should be negative.
//Position for scrolling down should be positive
public static class PanelExtension {
public static void ScrollDown(this Panel p, int pos)
{
//pos passed in should be positive
using (Control c = new Control() { Parent = p, Height = 1, Top = p.ClientSize.Height + pos })
{
p.ScrollControlIntoView(c);
}
}
public static void ScrollUp(this Panel p, int pos)
{
//pos passed in should be negative
using (Control c = new Control() { Parent = p, Height = 1, Top = pos})
{
p.ScrollControlIntoView(c);
}
}
}
//use the code, suppose you have 2 buttons, up and down to control the scrollbar instead of clicking directly on the scrollbar arrows.
int i = 0;
private void buttonUp_Click(object sender, EventArgs e)
{
if (i >= 0) i = -1;
yourPanel.ScrollUp(i--);
}
private void buttonDown_Click(object sender, EventArgs e)
{
if (i < 0) i = 0;
yourPanel.ScrollDown(i++);
}
Another solution you may want to use is using Panel.VerticalScroll.Value. However I think you need more research to make it work as you expect. Because I can see once changing the Value, the scrollbar position and control position don't sync well. Notice that Panel.VerticalScroll.Value should be between Panel.VerticalScroll.Minimum and Panel.VerticalScroll.Maximum.
This surprisingly works! NOTE THE MINUS SIGN in the code. There is strange behavior in setting scroll position. If you set the position to exact value (50), it goes negative when you read it next time (-50). So you have to invert it before setting new scroll value.
Scroll down:
private void ButtonScrollDown_OnClick(object sender, EventArgs e)
{
Point current = yourScrollPanel.AutoScrollPosition;
Point scrolled = new Point(current.X, -current.Y + 10);
yourScrollPanel.AutoScrollPosition = scrolled;
}
Scroll up similarly, (-current.Y - 10)
If you have a class that derives from Panel, then call these two protected methods to scroll the panel:
// The bottom is off screen; scroll down. These coordinates must be negative or zero.
SetDisplayRectLocation(0, AutoScrollPosition.Y - item.BoundingRect.Bottom + ClientRectangle.Bottom);
AdjustFormScrollbars(true);
In my example, item.BoundingRect.Bottom is the Y coordinate of the bottom of a thumbnail, and I need to scroll the panel down so that the whole thumbnail is visible.
#King King's solution of creating a temporary Control just so that scrolling could be done seemed "heavy" to me. And #Hans Passant's suggestion of setting AutoScrollMinSize and AutoScrollPosition didn't work for me.
Leave AutoScroll to its default value of 'true'.
Try this:-
panel.ScrollControlIntoView(childcontrol);
This should work. childcontrol is the particular control that you want to show in your display area.
Setting the value of the HorizontalScroll property and then using the method ScrollControlIntoView works for me:
lpanel.HorizontalScroll.Value = 100;
lpanel.ScrollControlIntoView(lpanel);
Use #King King Answered Code and if you want to hide horizontal and vertical scroll bar, just apply the below code in the constructor or initialization.
yourPanel.AutoScroll = false;
yourPanel.HorizontalScroll.Maximum = 0;
yourPanel.HorizontalScroll.Visible = false;
yourPanel.VerticalScroll.Maximum = 0;
yourPanel.VerticalScroll.Visible = false;
yourPanel.AutoScroll = true;
I had an issue where I couldnt get my panel to scroll back to top . I tried many things to try and get the panel to scroll back to the top after populating it with many controls.
Nomatter what I did it always put the VScroll bar to the bottom.
After exhaustive testing I found it was because my controls had the TabStop property set to true (default on user controls) was causing the issue.
Setting TabStop to false fixed it.
Create an control that sits slightly outside the visible area (so -1 at the top and clientsize+1 ) and then call ScrollControlIntoView:
public static class PanelExtension {
public static void ScrollDown(this Panel p)
{
using (Control c = new Control() { Parent = p, Height = 1, Top = p.ClientSize.Height + 1 })
{
p.ScrollControlIntoView(c);
}
}
public static void ScrollUp(this Panel p )
{
using (Control c = new Control() { Parent = p, Height = 1, Top = -1})
{
p.ScrollControlIntoView(c);
}
}
}
//use the code, suppose you have 2 buttons, up and down to control the scrollbar instead of clicking directly on the scrollbar arrows.
private void buttonUp_Click(object sender, EventArgs e)
{
yourPanel.ScrollUp();
}
private void buttonDown_Click(object sender, EventArgs e)
{
yourPanel.ScrollDown();
}
with yourpanel.SetAutoScrollMargin(1, 1); you can set very fine scrolling steps and then take a timer to call the srolling when buttons are down