Reducing code redundancy in C# - c#

I'm new to tinkering with C#, and so far that's the extent of it. I'm just tinkering for a small project I have.
The redundancy is driving me crazy, though. Writing long lists of similar code taking up line after line just doesn't sit well with me.
I have a few tabs with checkboxes and radio buttons in them, that's where I noticed the duplication the most. Unfortunately I... don't quite grasp every aspect of C# as well as I should, yet. So I was hoping to learn from you folks.
Example:
//setup checkbox
checkBox1.AutoSize = true;
checkBox1.Checked = false;
checkBox1.CheckState = CheckState.Unchecked;
checkBox1.Location = new Point(5, 102);
checkBox1.Text = "Check Box 1!";
checkBox1.UseVisualStyleBackColor = true;
//set radio 1
radio1.AutoSize = true;
radio1.Checked = true;
radio1.Location = new Point(5, 33);
radio1.Size = new Size(20, 20);
radio1.Text = "Radio-e-o-e-o";
radio1.UseVisualStyleBackColor = true;
//set radio 2
radio2.AutoSize = true;
radio2.Checked = false;
radio2.Location = new Point(5, 56);
radio2.Size = new Size(18, 20);
radio2.Text = "Option 2!";
radio2.UseVisualStyleBackColor = true;
My instinct would be to set up some arrays with the variable data for things like the names and the distinct data. But as I said, I'm very new, I've only been tinkering... and the resources I've come across tend to either not match what I'm looking for, or add layers of complexity I'm probably not ready for.

If I got your question right: you can make a method:
private CheckBox DoSomethingWith(CheckBox checkBox, Point location, string text)
{
checkBox.AutoSize = true;
checkBox.Checked = false;
checkBox.CheckState = CheckState.Unchecked;
checkBox.Location = location;
checkBox.Text = text;
checkBox.UseVisualStyleBackColor = true;
return checkBox;
}
then pass a checkbox to it checkBox1 = DoSomethingWith(checkBox1, new Point(10,10), "My Text");

You can make your own class that inherits from RadioButton, set your default settings, and use it.
public class MyRadioButton : public RadioButton
{
MyRadioButton()
{
UseVisualStyleBackColor = true;
AutoSize = true;
}
}
Then you just add this control instead of RadioButton.

So...you want to refactor the Form1.designer.cs file?
Anything you do to "clean up" would place an unnecessary burden on the CPU just for the sake of "clean code". Just let the designer be (unless you're talking repeat designs, in which case a user control may be the way to go).
Those are my impressions on this, anyways

If all you're trying to do is tinker, and not set up a whole application architecture, and you're writing this yourself rather than trying to mess w/ the designer-generated code, then using some kind of structure you can loop over is probably you're best option. LINQ has some really nicer operators for doing simple transformations like that.
For example,
var formElements = myArrayofElementInfo.Select(e => CheckBox(e.Point, e.Text))
That also assumes you're using something like the CheckBox method presented by #Sean87 to consolidate construction.

Maybe you could reduce the redundancy by creating methods to initialize your controls passing the controls as parameters (you could also create the control inside the method itself). For example:
public void setupCheckbox (Checkbox checkbox, boolean autoSize, boolean checked, etc.)
public void setupRadioButton (RadioButton radiobutton, boolean autoSize, boolean checked, etc.)
And then create the controls using the above methods:
setupCheckbox (checkBox1, true, false, etc.)
setupRadioButton (radio1, true, false, etc)
setupRadioButton (radio2, true, false, etc)
Anyhow, the visual attributes of the classes you are referring to normally are defined within the IDE (e.g. Visual Studio) so normally you don't care about them. Maybe you could consider storing the Text in an array for the initialization of the different controls if you find it useful.

Related

ASP.NET C# Adding LinkButtons to a List

I'm building an ASPX web page where some links will be visible or invisible depending on the user's access level. My thought was rather than create multiple functions and turn each link on or off, I could keep things neat and tidy by making List<LinkButton> members, like:
private List<LinkButton> adminButtons = new List<LinkButton>();
private List<LinkButton> guestButtons = new List<LinkButton>();
private List<LinkButton> userButtons = new List<LinkButton>();
Then I'd call one function to make them all visible or invisible.
private void DisplayButtons(List<LinkButton> linkButtons, bool displayButtons) {
for (int i = 0; i < linkButtons.Count; i++) {
linkButtons[i].Visible = displayButtons;
}
}
But I hit two snags. Firstly, I couldn't figure out how to initialize the lists with the links. For example, I've got asp:LinkButtons with IDs like ID='Link_UserManagement', ID='Link_InventoryManagement', et cetera. I can do this in the ASPX.CS file, and it works:
Link_UserManagement.Visible = false;
Link_InventoryManagement.Visble = true;
But this doesn't work:
private List<LinkButton> adminButtons = new List<LinkButton>() { Link_UserManagement }
And then if I try adding them this way, then the List count increases, but the values are null:
adminButtons.Add(Link_UserManagement);
So obviously I don't understand something about how these links work. My question is, why isn't this working the way I thought it would?
Secondly, if there's a better way to go about hiding and showing content based on a user's access level, I'm open to suggestions.

Can't find controls that don't have enough defined properties in Coded UI

This may be a noobish question, but in my records in Coded UI Tests, I have recorded a lot of controls that don't have enough defined properties to be found in playback.
For exemple:
public HtmlEdit UIItemEdit
{
get
{
if ((this.mUIItemEdit == null))
{
this.mUIItemEdit = new HtmlEdit(this);
#region Search Criteria
this.mUIItemEdit.SearchProperties[HtmlEdit.PropertyNames.Id] = null;
this.mUIItemEdit.SearchProperties[HtmlEdit.PropertyNames.Name] = null;
this.mUIItemEdit.SearchProperties[HtmlEdit.PropertyNames.LabeledBy] = null;
this.mUIItemEdit.SearchProperties[HtmlEdit.PropertyNames.Type] = "SINGLELINE";
this.mUIItemEdit.FilterProperties[HtmlEdit.PropertyNames.Title] = null;
this.mUIItemEdit.FilterProperties[HtmlEdit.PropertyNames.Class] = null;
this.mUIItemEdit.FilterProperties[HtmlEdit.PropertyNames.ControlDefinition] = "type=\"text\" value=\"\"";
this.mUIItemEdit.FilterProperties[HtmlEdit.PropertyNames.TagInstance] = "5";
this.mUIItemEdit.WindowTitles.Add("http://cms.home.psafe.com/");
#endregion
}
return this.mUIItemEdit;
}
In this post, I learned about SearchProperties, but it doesn't look to be an appropriate solution in this case.
Is there any other way to wrap these controls properly?
You might be able to find it if its containing element can be found. You can use the containing element to scope the search. So, find that element's parent, then find an input type=text within it:
var container = new HtmlControl(bw); //where bw is the browser window
HtmlDiv parentDiv = new HtmlDiv(container);
parentDiv.SearchProperties[HtmlDiv.PropertyNames.Id] = "theIdOfYourDiv";
HtmlEdit edt = new HtmlEdit(parentDiv); //the search scope is narrowed down to the div only. This may be enough to find your control with the search property.
edt.SearchProperties[HtmlEdit.PropertyNames.Type] = "SINGLELINE";
You have two options:
Try crowcoder's solution of searching in the parent. The problem with this solution is when you move a control around you're going to be changing code a lot.
Add an Id property to all your controls in the HTML, this will make your Coded UI more robust and responsive to changes in the UI.

FastColoredTextbox AutoWordSelection?

FastColoredTextbox is an user-control that can be downloaded in this url, it looks like this:
Its an amazing control but only can select one word when doubleclicking on the text, can't hold the mouse to select more words, so it only selects the entire current word over the mouse pointer even if you try to move the mouse cursor to left or right to select more text.
I have not found any information explaining the problem, and all of the official example projects has this problem.
Nobody means how to make an AutoWordSelection equivalent of a default TextBox for a FastcoloredTextbox control, but even the most important thing is:
How to select just more than one word with the mouse?
UPDATE:
#mostruash answer is very instructive but in all this time I could not carry out the modifications by myself.
I need a huge help from a C# programmer to carry out this task, my knowledge of C# is very low and the modifications that I made to the source did not work (don't compiled), I went back to the original user-control source to not end up spoiling more. I hate to say this but this time I need the job done, this source is too much for me.
If I'm requesting for too much then maybe with the necesary extended instructions of a C# developer, explaining how to accomplish this step by step, maybe I could carry it out by myself.
UPDATE
A video that demostrates the problem:
https://www.youtube.com/watch?v=Cs2Sh2tMvII
UPDATE
Another demo, I show what the FastColoredTextBox can't do but I would like to do like every other text-editor can do:
I've checked the source code of the project. Dragging is cancelled if a double click occurs and SelectWord is called.
You could modify the source code to include the feature that you request. (https://github.com/PavelTorgashov/FastColoredTextBox). In that case:
You must trace selections that start with double clicks.
Instead of calling SelectWord function, use the Selection class and draggedRange attribute to mark the selected word in OnMouseMove.
You also must handle deselection of words in OnMouseMove.
You must also select spaces between words in OnMouseMove.
The double click is handled in the code piece below:
if (!isLineSelect)
{
var p = PointToPlace(e.Location);
if (e.Clicks == 2)
{
mouseIsDrag = false; //Here, drag is cancelled.
mouseIsDragDrop = false;
draggedRange = null; //Drag range is nullified
SelectWord(p); //SelectWord is called to mark the word
return;
}
if (Selection.IsEmpty || !Selection.Contains(p) || this[p.iLine].Count <= p.iChar || ReadOnly)
OnMouseClickText(e);
else
{
mouseIsDragDrop = true;
mouseIsDrag = false;
}
}
EDIT:
This may require a lot of work to accomplish. So maybe you should use another tool/library. I have not studied the whole source code so there will probably be additional steps to those provided above.
For example, to trace double clicks you can do the following:
Define a class variable/property in FastColoredTextbox.cs: bool isDoubleClick.
Set it to true in OnMouseDown under if(e.Clicks == 2) condition. Set it to false in all other conditions.
Set it to false in OnMouseClick or OnMouseUp or in other relevant mouse event handlers.
That way you will know if series of mouse events had started with a double click event or not. Then you would act accordingly in OnMouseMove because that is where you (un)mark characters or (un)mark words.
LAST WORDS OF CAUTION:
The author of that project did not include any inline comments or any other means of documentation so you will be studying the code line by line to understand what each function/part does.
Add the following statement between Line 5276 and line 5277 in the class FastColoredTextBox.cs:
SelectWord(p);
mouseIsDrag = true; // here
return;
Note that implementing the ultimate behavior would require a good bunch of coding. Whereas the workaround mentioned above might satisfy your needs.
As #mostruash points out in his answer, that is the place where author cancels the mouse drag. Not sure why he deliberately prevents this feature. Only he knows.
if (e.Clicks == 2)//Line 5270
{
mouseIsDrag = false;
mouseIsDragDrop = false;
draggedRange = null;
SelectWord(p);
return;
}
I didn't read whole code, and I have no reason to do it. I just checked quickly and removed them. And it works as you expect.
if (e.Clicks == 2)//Line 5270
{
//Comment or remove completely.
//mouseIsDrag = false;
//mouseIsDragDrop = false;
//draggedRange = null;
SelectWord(p);
return;
}
Note: Am not sure this breaks something else, I've not tested. At least that works. Test it yourself.
My solution is a bit tweaky, but seems to work at first glance.
You have to make some changes in the Code:
Add mouseIsWholeWordSelection flag and a Range variable which can store the initial selected range after double click (best after line 100, I guess):
private bool mouseIsWholeWordSelection;
private Range mouseIsWholeWordSelectionBaseRange;
Change the selection code for double click event as stated above and extend it a bit (line 5222):
if (e.Clicks == 2)
{
//mouseIsDrag = false;
mouseIsDragDrop = false;
mouseIsWholeWordSelection = true;
//draggedRange = null;
SelectWord(p);
mouseIsWholeWordSelectionBaseRange = Selection.Clone();
return;
}
Add evaluation of dragging event for recreating selection (line 5566):
else if (place != Selection.Start)
{
if (mouseIsWholeWordSelection)
{
Selection.BeginUpdate();
var oldSelection = Selection.Clone();
SelectWord(place);
if (Selection.End >= mouseIsWholeWordSelectionBaseRange.End)
{
Selection.Start = (mouseIsWholeWordSelectionBaseRange.Start > Selection.Start) ? mouseIsWholeWordSelectionBaseRange.Start : Selection.Start;
Selection.End = mouseIsWholeWordSelectionBaseRange.End;
}
else if (Selection.Start < mouseIsWholeWordSelectionBaseRange.End)
{
Selection.Start = new Place(Selection.End.iChar, Selection.End.iLine);
Selection.End = mouseIsWholeWordSelectionBaseRange.Start;
}
Selection.EndUpdate();
DoCaretVisible();
Invalidate();
}
else
{
Place oldEnd = Selection.End;
Selection.BeginUpdate();
if (Selection.ColumnSelectionMode)
{
Selection.Start = place;
Selection.ColumnSelectionMode = true;
}
else
Selection.Start = place;
Selection.End = oldEnd;
Selection.EndUpdate();
DoCaretVisible();
Invalidate();
}
return;
}
Add at every place where isMouseDrag is being set to false:
isMouseWholeWordSelection = false;
And there you go.

Browsable(false) at run time?

I am using a datasource to populate my datagridview with the data. However, im trying to find a way for the user to be able to hide columns that he does not want to see.
I am able to hide and show columns before the program runs using:
[Browsable(false)]
public string URL
{
get
{
return this._URL;
}
set
{
this._URL = value;
this.RaisePropertyChnaged("URL");
}
}
I cannot seem to figure out how to change the [Browsable(false)] at run time.
Any ideas how I could accomplish this?
Basically, I want to bind an "on/off" to a menu.
Apologies if im not using the right terminology when explaining my problem, I am self taught and started a few weeks ago - so still very newbie :)
Edit:
Cant hide the column because when i run my update function all columns appear again. Here is my function for updating:
private void UpdateResults()
{
Invoke(new MethodInvoker(
delegate
{
this.dgvResults.SuspendLayout();
this.dgvResults.DataSource = null;
this.dgvResults.DataSource = this._mySource;
this.dgvResults.ResumeLayout();
this.dgvResults.Refresh();
}
));
}
At run time, you can just specify the column as being invisible:
dgv.Columns["ColumnName"].Visible = false;
The way to do this properly at runtime is to provide a custom ITypedList implementation on the collection, or provide a TypeDescriptionProvider for the type, or (for single-object bindings, not lists), to implement ICustomTypeDescriptor. Additionally, you would need to provide your own filtered PropertyDescriptor implementation. Is it really worth it? In most cases: no. It is much easier to configure the grid properly, showing (or not) the appropriate columns by simply choosing which to add.
Indeed, as others had mention the purpose of BrowsableAttribute is different, but I understand what you want to do:
Let's suppose that we want to create a UserControl than wraps a DataGridView and gives the user the ability to select which columns to display, allowing for complete runtime binding. A simple design would be like this (I'm using a ToolStrip, but you can always use a MenuStrip if that's what you want):
private void BindingSource_ListChanged(object sender, ListChangedEventArgs e) {
this.countLabel.Text = string.Format("Count={0}", this.bindingSource.Count);
this.columnsToolStripButton.DropDownItems.Clear();
this.columnsToolStripButton.DropDownItems.AddRange(
(from c in this.dataGrid.Columns.Cast<DataGridViewColumn>()
select new Func<ToolStripMenuItem, ToolStripMenuItem>(
i => {
i.CheckedChanged += (o1, e2) => this.dataGrid.Columns[i.Text].Visible = i.Checked;
return i;
})(
new ToolStripMenuItem {
Checked = true,
CheckOnClick = true,
Text = c.HeaderText
})).ToArray());
}
In this case, bindingSource is the intermediary DataSource of the dataGrid instance, and I'm responding to changes in bindingSource.ListChanged.

c# How to reach a panel that is beeing created while program is running?

I have this for loop:
int iPanelNumber = 1;
foreach (string[] Persons in alItems)
{
Panel pPanelContainer = new Panel();
pPanelContainer.Width = contentPanel.Width;
pPanelContainer.Height = 50;
pPanelContainer.BackColor = Color.FromArgb(
Convert.ToInt32(aWhiteContentBackgroundColors[0]),
Convert.ToInt32(aWhiteContentBackgroundColors[1]),
Convert.ToInt32(aWhiteContentBackgroundColors[2]));
pPanelContainer.Name = "PanelContainer" + iPanelNumber.ToString();
pPanelContainer.Visible = false;
pPanelContainer.Location = new Point(0, 0);
}
So as you can see, i have given the panels i create the name "PanelContainer1", "PanelContainer2" etc...
But how can i reach these panels?
I certainly could not reach them by writing:
PanelContainer1.visible = true;
Anyone got an idea?
Thanks in advance
Easiest way is probably to add a List<Panel> field to your class and store references to all panels in that list, e.g:
class MyClass
{
private List<Panel> _panels = new List<Panel>();
void MethodWhichCreatesThePanels()
{
//..
foreach (string[] Persons in alItems)
{
Panel pPanelContainer = new Panel();
_panels.Add(pPanelContainer);
...
}
}
Then you can access each panel later using an index:
Panel aPanel = _panels[i];
Martin's answer is pretty much what you are looking for, but it appears you are in confusion over what the .Name property of the panel control does.
What it doesn't do is set the name of the variable.
What it does do is the following (from MSDN: http://msdn.microsoft.com/en-us/library/system.windows.forms.control.name.aspx)
The Name property can be used at run time to evaluate the object by name rather than type and programmatic name. Because the Name property returns a String type, it can be evaluated in case-style logic statements (Select statement in Visual Basic, switch statement in Visual C# and Visual C++).
You can't reference the Panel by its name because there's no field in the form (or local variable) with that name. Those fields are defined in the partial class file for the Form that the Form Designer generates; they aren't (and can't be) created at runtime.
This doesn't mean you can't access it by its name; you just can't access it by using its name as a variable name in your code.
The most obvious way to do this is to add the Panel to its containing control's Controls collection after creating it. Since you've set Visible to false, this won't have any visible effect on your form. So in your code, you'd add something like
contentPanel.Add(p);
You can then reference it by name:
contentPanel.Controls["PanelContainer1"].Visible = true;
If for some reason you don't want to add it to the Controls collection yet (there are plenty of reasons you might not), the next approach is to create an instance of a collection class of some kind and add the Panel to that collection. Since you want to be able to reference it by name, the most obvious choice would be a dictionary, e.g.:
Dictionary<string, Panel> panels = new Dictionary<string, Panel>;
...
panels.Add(p.Name, p);
And again, you can then reference it by name:
panels["PanelContainer1"].Visible = true;
...though in this case, the Panel wouldn't actually become visible, because it's not in the Controls collection of a visible container control.
Just as an aside: if it's within your power to do so, you should put an end to using type prefixes on your variable names. There are still shops that use this convention, but it's been generally abandoned by the community of C# programmers.

Categories

Resources