Change "text" property of all buttons on form incrementally - c#

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];
}
}
}
}

Related

How do I add access rights to my c# windows forms application?

First of all, I am really sorry if the question has been asked somewhere already but I couldn't find an answer anywhere at all after looking. I am fairly new to coding as well so sorry if it isn't actually possible or something.
I have created a windows forms application in c# with multiple panels that themselves contain elements like textboxes and labels. For example, I have a chat panel and a calendar panel. I would like to somehow build access rights into this based on a user's privileges (access levels are stored in a database that is already connected to the application). Ideally, I would like that once the user logs in, the panels are then initialized and created as (from a security point of view) this would be better.
I can't really provide screenshots or much code as it is for an assessed piece of work that I am not allowed to put on the internet.
Thanks so much in advance :)
If i got you right then what you want to do is if(userHasSomePermission) { CreateComponent }; instead of creating it before and if user doesn't have permission disable/hide it
If this is the case it is not much of science but it is bit tricky.
Inside your Form constructor you have InitializeComponents() method which is method that is stored in you Form.Designer.cs file. Inside that file you are creating your controls.
What you can do is create more methods inside your Form.cs like
private void CreatePanel1()
{
Panel p = new Panel();
p.Location = new Point(3, 3);
p.Size = new Size(50, 50);
p.BackgroundColor = Color.Black;
this.Controls.Add(p);
}
and then inside your constructor call it if needed:
public Form()
{
InitializeComponents();
if( checkIfUserHavePermission )
CreatePanel1();
}
This way components inside our method will be create only if needed.
Tricky part of this is that you will not see components inside designer window since only components that are located in Form.Designer.cs/InitializeComponents() are drawn inside it. So any change you want to make will need to be done by hand through code.
Otherwise if you are concerned about security and do not want just to hide/disable some control, you could remove it if needed.
So you could use Tag property of each control and add let's say Admin_C to each control's Tag which is meant only to admins and then do this:
public Form()
{
InitializeComponents();
if(userIsNotAdmin)
{
foreach (Control item in this.Controls)
{
if(item.Tag.ToString() == "Admin_C")
this.Controls.Remove(item);
}
}
}

Inherit properties to a WPF Page element

This may be a very simple question, so my apologies.
My problem here is that I want to build a Multilingual WPF App using C#, but I don't know how to make my different Page elements inherit the same method which makes my MainWindow translate to different languages. The app is done, I'm just translating it to English (My native language is Spanish).
I'm using Resource files to translate it.
Code for the language translation:
private void Languages_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
//This is the combobox in which you select the language to display the app in
Set_Language();
}
//This is the method to invoke the Resource Manager and all the stuff from the resource file.
private void Set_Language()
{
if (!boolInit)
{
strLanguage = "LeitProjekteV2._0.Languages." + ((ComboBoxItem)LanguageSel.SelectedItem).Name.ToString();
ResourceManager LocRm = new ResourceManager(strLanguage, typeof(MainWindow).Assembly);
//Menu buttons
lblMenu.Content = LocRm.GetString("strMainMenu"); //The names inside the "" are the names of the resource in the Resource file which, depending on the language selected(Spanish, English and German)
//Change the text of whatever I choose; in this case, a Label named 'lblMenu'
MapButt.Content = LocRm.GetString("strMapButt");
BuscButt.Content = LocRm.GetString("strBusButt");
AgeButt.Content = LocRm.GetString("strAgeButt");
ComButt.Content = LocRm.GetString("strComButt");
InfButt.Content = LocRm.GetString("strInfButt");
LoginButt.Header = LocRm.GetString("strLoginButt");
RegisterButt.Header = LocRm.GetString("strRegisterButt");
ContacButt.Header = LocRm.GetString("strContacButt");
MasButt.Header = LocRm.GetString("strMoreButt");
//Here go the names of everything the Pages contain that I want to translate, just like above
//Have no idea how to inherit this method to all the pages
}
}
Now, I have several pages embedded in the same MainWindow.xaml, so that you click the button "Map", a Frame changes it's content to a Page named Map.xaml, and so on for other buttons.
But how do I make those Pages also translate?
Since the Set_Language() method takes the string value of the Combobox in order to select the correct Resource File, I don't want to create one combobox for every Page that I have, albeit that would eliminate my problem.
Any help? Sorry for the horrible way of asking, I'm still getting the hint here.
Thanks.
Use the below:
var wnd = Window.GetWindow(this); //get current window
Cast it to your window class and expose your language as a public property
Use your page to get the property by finding the current window
You may create a parent page class that do the above, and inheriting it for all your pages so you dont repeat code
I can see the difficulty you are facing is that you can't find a way to share the combobox across the main window and the pages hosted in the frame.
You can set a global variable that is accessible from the whole application, a good place is in application settings. Then when you make a selection with the Combobox, you just update the selected value to that variable.
Then call each page's Set_Language() method when they are being loaded into the Frame. In the Set_Language() method of each page, you can query what is been set to the variable stored in the application settings.
If you want to quick solution, create a static class to hold the selected language is also OK.
static class UserSelections
{
public static string Language { get; set; }
}

Copy Controls from a list to another without the same pointer reference

Today I would like to create a copy/paste function for a software I develop.
I have a Panel that contains Controls and I want to copy/paste.
I have a selection tool that permit the user to select different Controls and add this Controls to a List. I have called it "SelectionActuelle".
Then, when the user clicks on "Copy". I would like to add every controls that SelectionActuelle contains into a new List called "PressePapier".
But when I do, it copies the same pointer reference, and I dont want.
I throught that add a Control to another List should copy it and create a new instance but it doesn't.
I tried this example HERE but it doesn't work.
What I have now is only 6 lines (it doesn't work !!) to try to make a copy of the List :
private void bt_copier_Click(object sender, EventArgs e)
{
PressePapier.Clear();
foreach(Control ctr in SelectionActuelle)
{
PressePapier.Add(ctr);
}
bt_coller.Enabled = true;
}
How can I simply copy Control to make my Copy/Paste tool ? So is it possible (I think yes but we never know) ?
Have a good day,
Julien
Thanks to you, I found out that my structure was not great and I changed it to a simpler structure and more reutilisable.
I will apply this :
You should foreach the selected controls and check it's type, just generate a new instance of that control with it's properties.
But Dr. Coconut, my problem was that .NET does not appreciate some expressions in the code even if I have adapted it... I don't know why. Question of framework version ?

Check every TextBox for letters

Lets assume I have 5 TextBoxes like this:
textBox_Box1
textBox_Box2
textBox_Box3
textBox_Box4
textBox_Box5
And I have a function that checks if the TextBox contains only letters, like this:
public static bool OnlyLetters(string s)
{
foreach (char c in s)
{
if (!Char.IsLetter(c))
return false;
}
return true;
}
Is there an efficient way to check every textBox with this function? I do not want to write it in this style of course:
OnlyLetters(textBox_Box1.Text);
OnlyLetters(textBox_Box2.Text);
OnlyLetters(textBox_Box3.Text);
OnlyLetters(textBox_Box4.Text);
OnlyLetters(textBox_Box5.Text);
I would prefer to check it in a loop, but I do not know how to realize it at that point.
You could create an array of your TextBoxes:
private TextBox[] textBoxes = {
textBox_Box1,
textBox_Box2,
textBox_Box3,
textBox_Box4,
textBox_Box5
};
Then you can access it in a loop:
foreach (TextBox txt in textBoxes)
{
if (!OnlyLetters(txt.Text))
{
// Do something
}
}
One way would be to put all your text boxes in some sort of container and then loop through the children of the container (which will be text boxes) and preform a check on each. That way, you can add and remove text boxes from the container as needed without modifying your code.
When you are looping through the text box container, check to see if the child you are on is in fact a text box, then preform a cast on it so you may access it's text property.
I do not know which framework you are using, so I cannot provide a code sample.
You said you would like to check it in a 'Loop'. I don't know what GUI framework you are using so the types might be wrong, but you could do something like the following:
List<TextBox> textBoxes = new List<TextBox>();
// Add all your textBoxes to the list here
Then use a loop on the list of textBoxes whenever you want to check their contents. If it's a mobile platform you should probably see if it's possible to limit the type of keyboard shown, and on iOS you can automatically have the UI components added to the list so you don't have to manually write the code. Hope this helps a bit!

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.

Categories