I have a lot of control values in my C# app. I'm wondering if there is a possibility to store and read the entire available control values at once, instead of the code below.
Please note, the code below is only for 1 groupBox. I've 8 of them, thus this will generate a lot of code to implement it (8 times the code below).
groupBox are copies of each other
the code below works
I store the data using serializing the configData class (works).
private void uiStateWriteToData()
{
configData.datSensor4mA1 = (int)uiSensor4mA1.Value;
configData.datSensor20mA1 = (int)uiSensor20mA1.Value;
configData.datSensorPidP1 = (int)uiSensorPidP1.Value;
configData.datSensorPidI1 = (int)uiSensorPidI1.Value;
configData.datSensorPidD1 = (int)uiSensorPidD1.Value;
configData.datSensorPidS1 = (int)uiSensorPidS1.Value;
configData.datSensor1InToOutput1 = uiSensor1Out1.Enabled;
configData.datSensor1InToOutput2 = uiSensor1Out2.Enabled;
configData.datSensor1InToOutput3 = uiSensor1Out3.Enabled;
}
private void uiStateUpdateFromData()
{
uiSensor4mA1.Value = configData.datSensor4mA1;
uiSensor20mA1.Value = configData.datSensor20mA1;
uiSensorPidP1.Value = configData.datSensorPidP1;
uiSensorPidI1.Value = configData.datSensorPidI1;
uiSensorPidD1.Value = configData.datSensorPidD1;
uiSensorPidS1.Value = configData.datSensorPidS1;
uiSensor1Out1.Enabled = configData.datSensor1InToOutput1;
uiSensor1Out2.Enabled = configData.datSensor1InToOutput2;
uiSensor1Out3.Enabled = configData.datSensor1InToOutput3;
}
I don't know if there are any frameworks that can do that, but maybe you could come up with your own utility that writes control values into xml file based on the control hierarchy, types and names.
Here is how it may look (rather in pseudo-code):
private static void WriteControlValuesToXml(XmlDocument document, XmlNode parentNode, Control currentControl)
{
// if current control is container, writing its children values
if (currentControl.Controls.Count > 0)
{
var addedNode = AddNode(parentNode, currentControl.Name);
foreach (Control childControl in currentControl.Controls)
WriteControlValuesToXml(document, addedNode, childControl);
}
else
{
// if current control is not container, writing control values
if (currentControl is CheckBox)
{
var checkBox = (CheckBox)control;
AddNode(parentNode, currentControl.Name, "CheckBox", checkBox.Checked);
}
if (currentControl is TextBox)
{
var textBox = (TextBox)control;
AddNode(parentNode, currentControl.Name, "TextBox", textBox.Text);
}
// ... other known controls
}
}
And following the same logic, you can implement reading the same control values from file.
Related
I am very new to c# and visual studio.
I am using c# with Visual studio. I want to create a method that lops through a number of textboxes and labels and set their visible control to "True."
This is the code I have come up with so far, but it does not work.
public static void showFields(params string[] values)
{
foreach (var value in values)
{
value.Visible = true;
}
}
Any help would be greatly appreciated.
You are on the right path, just need to replace string with Control, by the way, string does not have the Visible property.
public static void showFields(params Control[] values)
{
foreach (var value in values)
{
value.Visible = true;
}
}
Code should be similar to this. You may have nested controls. In this case, you create a recursive method
private void MakeThemVisible(Control topControl)
{
foreach (var c in topControl.Controls)
{
if (c is TextBox txt && <your second criteria>) // <-- pattern matching
{
// <---- txt specific code here -------
txt.Visible = true;
continue;
}
else if (c is Label lbl && <your second criteria>) // <-- pattern matching
{
// <---- lbl specific code here -------
lbl.Visible = true;
continue;
}
MakeThemVisible(c);
}
}
Your form is also control
If you already have a list of needed controls in the form of array - Control[] values, you can use LINQ
values.ToList().ForEach(c => {c.Visible = true;});
I'm working on an inventory program and have finished the main functionality as a command line console app. I am now working on a version for winforms. I want to enable it to dynamically generate a Groupbox that holds some textboxes. I'd rather not design 50+ lines of multiple textboxes. Keep in mind I'm rather new to programming, having started with C# a year ago. I know next to nothing on Winforms.
I've tried to use dynamic item = new Groupbox();as a similar method allowed generation of objects at runtime. In the command line app, the way it works is that based on information given, a certain amount of objects are passed into the list _AllItems. I was thinking of generating the Groupboxes by using:
private void InitializeGroupBox()
{
foreach (Product product in Product._AllItems)
{
dynamic Item = new GroupBox();
}
}
But I have the feeling I'm nowhere near the correct method. Thanks to anybody who helps.
You will need to learn a bit more, but here is what I usually do to achieve what you asked.
internal class DynamicForm : Form
{
private FlowLayoutPanel mFlowLayoutPanel;
public DynamicForm()
{
mFlowLayoutPanel = new FlowLayoutPanel();
mFlowLayoutPanel.Dock = DockStyle.Fill;
// Add to this Form
this.Controls.Add(mFlowLayoutPanel);
InitializeGroupBox();
}
private void InitializeGroupBox()
{
mFlowLayoutPanel.SuspendLayout(); // Performance
for (int i = 1; i <= 20; i++) {
var groupBox = new GroupBox();
groupBox.Text = "GroupBox #" + i;
groupBox.Size = new Size(200, 50);
var textBox = new TextBox();
textBox.Dock = DockStyle.Fill;
// Add the TextBox to GroupBox
groupBox.Controls.Add(textBox);
// Add to this Form
mFlowLayoutPanel.Controls.Add(groupBox);
}
mFlowLayoutPanel.ResumeLayout(); // after suspend, resume!
}
}
I'm building out an application to query SQL Server Report Server, return a list of controls for the user to select from, then initiate the report and save the result to a folder of the users choosing.
I began the journey by replicating parts of Ch.15 of Paul Turley's "SQL Server 2016 Reporting Services and Mobile Reports" which gives some basic code examples that extract a simple report to a folder location. This launching point was intended for very basic reports with a single parameter. My organization has nearly a thousand reports of various complexity that this application will have to work with. I have a method:
private REService.ParameterValue[] GetReportExecutionParameters()
where REService is the Web Reference to ReportExecution2005.asmx.
I build a List of controls:
var controlList = new List<Control>();
and for each control in my Wpf Stackpanel, I add to the list according to the control type:
foreach (var control in StackPanelParameters.Children.OfType<Object>())
with a series of if statements:
if (control is DatePicker )
{
DatePicker dp = (DatePicker)control;
controlList.Add(dp);
continue;
}
then, within the same method, I create a List of values:
var parameterValues = new List<REService.ParameterValue>();
and attempt to populate the parameterValues with another foreach/if block.
the method in it's entirety.
private REService.ParameterValue[] GetReportExecutionParameters()
{
var controlList = new List<Control>();
//same issue using UIElement....
//https://stackoverflow.com/questions/14688136/loop-through-stackpanel-children-genericaly
//***This one helped:
//https://stackoverflow.com/questions/22298431/traversing-elements-of-stackpanel/22298680#22298680
//create a list<> of controls extracted from the StackPanel, add them to the "controlList"
foreach (var control in StackPanelParameters.Children.OfType<Object>())
{
if (control is DatePicker )
{
DatePicker dp = (DatePicker)control;
controlList.Add(dp);
continue;
}
if (control is TextBox)
{
TextBox t = (TextBox)control;
controlList.Add(t);
continue;
}
if (control is CheckBox)
{
CheckBox cb = (CheckBox)control;
controlList.Add(cb);
continue;
}
if (control is ComboBox)
{
ComboBox cbx = (ComboBox)control;
controlList.Add(cbx);
continue;
}
if (control is Label)
{
Label l = (Label)control;
controlList.Add(l);
continue;
}
else
{
//the generic case
Control x = (Control)control;
controlList.Add(x);
continue;
}
} //Ok, now I have a populated controlList
// now I'm assigning the control value to the report parameter
// per "https://odetocode.com/articles/123.aspx" a different approach?
var parameterValues = new List<REService.ParameterValue>();
foreach(var control in controlList)
{
if (control is DatePicker)
{
parameterValues.Add(new REService.ParameterValue
{
Name = control.Name,
Value = ((DatePicker)control).ToString()
});
continue;
}
else if (control is TextBox)
{
parameterValues.Add(new REService.ParameterValue
{
Name = control.Name,
Value = ((TextBox)control).Text
});
continue;
}
else if (control is CheckBox)
{
parameterValues.Add(new REService.ParameterValue
{
Name = control.Name,
Value = ((CheckBox)control).IsChecked.ToString()
});
continue;
}
else if (control is ComboBox)
{
parameterValues.Add(new REService.ParameterValue
{
Name = control.Name,
Value = ((ComboBox)control).SelectedItem.ToString()
});
continue;
}
else if (control is Label)
{
parameterValues.Add(new REService.ParameterValue
{
//Name = control.ToString(),
//Name = control.Name, //this one is null
//Name = ((System.Windows.Forms.Label)control).label
//the control doesn't have a Label property, where did I lose you, little Label?
Name = "",
Value = ((Label)control).Content.ToString()
});
continue;
}
}
return parameterValues.ToArray();
}
The error message is generated at the point of generating the report using the collected information from both the user input to the dynamically generated control parameters and the report definition returned from ReportServer using ReportService2010.asmx and ReportExecution2005.asmx.
I get an error, the expectation is for the Name property in the ParameterValue to be populated and I believe the Label value is what it's looking for. While the Label property exists at he control array level, when I "foreach" it, the Label property is no longer available.
Error:
Message "The required field Name is missing from the input structure. --->
I can't access a CDN to host the screen shots (at work) I have but the Locals shows the columns: Name, Value, Type. When I look at the 'control' variable in the (var control in controlList) under 'Value', I see {System.Windows.Controls.Label: ProcessDate}
When I expand it to look for the Label property, it's not there. I need the Label and its value.
I get that my problem is a fundamental misunderstanding of how C# works... please instruct me on the error of my ways.
if(pictureBox1.Tag.ToString() == "accept")
{
char room1 = Convert.ToChar(lbl_roomid1.Text);
row1[13] = (byte)room1;
DateTime sdt1 = DateTime.Parse(txt_sdate1.Text);
//some code
}
if (pictureBox2.Tag.ToString() == "accept")
{
char room2 = Convert.ToChar(lbl_roomid2.Text);
row1[13] = (byte)room2;
DateTime sdt1 = DateTime.Parse(txt_sdate2.Text);
//some code
}
/* What if i wanted to type this code inside a loop?? i need to do the same for 10 pictureboxes */
Something like this:
public void DoMagic(Label label, TextBox textBox)
{
//...
}
And this:
foreach (Control currentControl in this.Controls)
{
if (currentControl is PictureBox)
{
if (((PictureBox)currentControl).Tag.ToString().Equals("accept"))
{
string controlId = currentControl.Name.Remove(0, 11);
string labelName = string.Concat("lbl_roomid", controlId);
string txtName = string.Concat("txt_sdate", controlId);
this.DoMagic(this.Controls.Find(labelName, true)[0] as Label, this.Controls.Find(txtName, true)[0] as TextBox);
}
}
}
Regards
You could make an array of the elements.
The following is a little dirty, but...
class PBRoomDate {
// Helper data holder class. Could probably be an anonymous class in C# 4.0
public PictureBox PB;
public Label RoomLabel;
public TextBox DateText;
PBRoomDate(PictureBox PB, Label RoomLabel, TextBox DateText) {
this.PB = PB; this.RoomLabel = RoomLabel; this.DateText = DateText;
}
}
// [...]
var pbRoomDates = new PBRoomDate[]{
new PBRoomDate(pictureBox1, lbl_roomid1, txt_sdate1),
new PBRoomDate(pictureBox2, lbl_roomid2, txt_sdate2),
new PBRoomDate(pictureBox3, lbl_roomid3, txt_sdate3),
// etc.
};
foreach(var pbRoomDate in pbRoomDates) {
if(pbRoomDate.PB.Tag.ToString() == "accept") {
row1[13] = (byte)Convert.ToChar(pbRoomDate.RoomLabel.Text);
DateTime dt = DateTime.Parse(pbRoomDate.DateText.Text);
}
}
The cleaner solution would be to use a custom UserControl to contain the three elements per "picturebox" and lay those out. That would also be easier to maintain and more extendable should the need arise.
Loops to access form elements in c#.
I usually use something like this.
Let say i have 10 picturebox i name it picturebox1 - picturebox10.
Then
Do a loop for the number to loop to 10 i use a variable no
inside it put this code
String Picturebox = "picturebox" + no.ToString();
Control[] oControl = Controls.Find(Picturebox, true);
foreach(Control foundControl in oControl)
{
PictureBox foundControlA = foundControl as PictureBox;
foundControlA.(methods or property here) = (what you want to put to foundControlA)
}
If you have more controls accompanied by each control you can use the same method. The hint here is that i use the same naming convention on my elements or object in my winforms and loop the variable no according to what you want.
I am using BuildManager Class to Load a dynamically generated ASPX File, please note that it does not have a corresponding .cs file.
Using Following code I am able to load the aspx file, I am even able to loop through the control collection of the dynamically created aspx file, but when I am assigning values to controls they are not showing it up. for example if I am binding the value "Dummy" to TextBox control of the aspx page, the textbox remains empty.
Here's the code that I am using
protected void Page_Load(object sender, EventArgs e)
{
LoadPage("~/Demo.aspx");
}
public static void LoadPage(string pagePath)
{
// get the compiled type of referenced path
Type type = BuildManager.GetCompiledType(pagePath);
// if type is null, could not determine page type
if (type == null)
throw new ApplicationException("Page " + pagePath + " not found");
// cast page object (could also cast an interface instance as well)
// in this example, ASP220Page is a custom base page
System.Web.UI.Page pageView = (System.Web.UI.Page)Activator.CreateInstance(type);
// call page title
pageView.Title = "Dynamically loaded page...";
// call custom property of ASP220Page
//pageView.InternalControls.Add(
// new LiteralControl("Served dynamically..."));
// process the request with updated object
((IHttpHandler)pageView).ProcessRequest(HttpContext.Current);
LoadDataInDynamicPage(pageView);
}
private static void LoadDataInDynamicPage(Page prvPage)
{
foreach (Control ctrl in prvPage.Controls)
{
//Find Form Control
if (ctrl.ID != null)
{
if (ctrl.ID.Equals("form1"))
{
AllFormsClass cls = new AllFormsClass();
DataSet ds = cls.GetConditionalData("1");
foreach (Control ctr in ctrl.Controls)
{
if (ctr is TextBox)
{
if (ctr.ID.Contains("_M"))
{
TextBox drpControl = (TextBox)ctr;
drpControl.Text = ds.Tables[0].Rows[0][ctr.ID].ToString();
}
else if (ctr.ID.Contains("_O"))
{
TextBox drpControl = (TextBox)ctr;
drpControl.Text = ds.Tables[1].Rows[0][ctr.ID].ToString();
}
}
}
}
}
}
}
I saw that you got part of your code from How To Dynamically Load A Page For Processing. Read the comments too as this one by Mike.
Invert this:
((IHttpHandler)pageView).ProcessRequest(HttpContext.Current);
LoadDataInDynamicPage(pageView);
To this:
LoadDataInDynamicPage(pageView);
((IHttpHandler)pageView).ProcessRequest(HttpContext.Current);
In this case changing the order of the calls does change the end result I think. The inverse of Commutativity property. :)