Horizontally align rows in multiple tables using web user control - c#

I need to align rows in different tables that are layed out horizontally. I'd prefer to put the html code in a single web user control so I can create as many instances of that control as I want and lay them out horizontally. The problem is, the text in the rows needs to wrap. So some rows may expand vertically and some may not (see the example below). When that happens, the rows in the other tables aren't aligned horizontally. I know I can accomplish all this by using a single table, but that would mean I'd have to duplicate the name, address and phone html code instead of dynamically creating new instances of my user control (in reality there are many more fields than this, but I'm keeping it simple). Is there any way to do this whether with div's, tables or something else?
Here's the problem: Mary Jane's address field expands 2 lines, causing her phone field to not align properly with John's and Bob's.
Name: John Doe Name: Mary Jane Name: Bob Smith
Address: 123 broadway Address: Some really long address Address: Short address
Phone: 123-456 that takes up multiple lines Phone: 111-2222
Phone: 456-789
I'm not restricted in any way how to do this (other than using asp.net), but I'd prefer to use a single web control that I instantiate X times at design time (in this example, it's 3 times). I'm using VS2008, and .Net 3.5

Render your data, then use javascript (jQuery please) client-side to find all your td.address (for example) cells, find the one with the greatest height, and set the height of all others to that. You mention other fields, so the logic might be a little more involved, but the principle stands.
Some quick code:
<script type="text/javascript">
$(function() {
var cells = $('td.address');
var height;
// some code to foreach on all relevant cells to find max size
cells.each(function(index) {
$(this).height(height));
});
});
</script>

Just so everyone knows, this is possible. I finished the solution using jquery and it works pretty well. I assigned each table a specific css class and used that to identify which tables need to be resized (don't want to use an id since each one must be unique). It works in the 4 major browsers. For IE7 make sure to add a space in the empty cells for this to work. Here's the Javascript:
function ResizeTableRows() {
// select tables by css class name using jquery
var tables = $('.myCssClassName');
// all tables should have the save number of rows, so just use the first one
var totalRows = tables[0].rows.length;
for (var rowNumber = 0; rowNumber < totalRows; rowNumber++) {
var maxRowHeight = GetMaxRowHeight(tables, rowNumber);
for (var i = 0; i < tables.length; i++) {
if (maxRowHeight > 0) {
tables[i].rows[rowNumber].height = maxRowHeight;
SetCellHeight(tables[i].rows[rowNumber].cells, maxRowHeight);
}
}
}
}
function GetMaxRowHeight(tables, rowNumber) {
var maxRowHeight = 0;
for (var i = 0; i < tables.length; i++) {
var row1 = tables[i].rows[rowNumber];
var cell1 = row1.cells[0];
var rowHeight = row1.clientHeight;
if (rowHeight <= 0) {
rowHeight = row1.height;
}
if (rowHeight <= 0) {
rowHeight = cell1.clientHeight;
}
if (rowHeight > maxRowHeight) {
maxRowHeight = rowHeight;
}
}
return maxRowHeight;
}
function SetCellHeight(cells, maxRowHeight) {
for (var i = 0; i < cells.length; i++) {
cells[i].height = maxRowHeight;
}
}
Here's the code to start the process. Add it to the main page and not the web control (if you're using .net)
<script type="text/javascript">
// runs automatically after this page has been loaded and rendered
$(document).ready(function() {
ResizeTableRows();
});
</script>

Related

Not able to copy specific pages of word document

I am trying to cut specific pages of my word document(.docx), say 2, 4. I am using for loop to traverse as per the page gave splitting it based on ,.Below is the code for the same
if (startEnd.Contains(','))
{
arrSpecificPage = startEnd.Split(',');
for (int i = 0; i < arrSpecificPage.Length; i++)
{
range.Start = doc.GoTo(WdGoToItem.wdGoToPage, WdGoToDirection.wdGoToAbsolute, arrSpecificPage[i]).Start;
range.End = doc.GoTo(WdGoToItem.wdGoToPage, WdGoToDirection.wdGoToAbsolute, arrSpecificPage[i]).End;
range.Copy();
newDocument.Range().Paste();
}
newDocument.SaveAs(outputSplitDocpath);
}
but the issue with this code is that its just copying the last page only to the new document i.e 4 in this case. How to add 2 as well? What's wrong in the code?
Since you always specify the entire document "range" as the target, each time you paste the entire content of the document is replaced.
It's correct that you work with a Range object and not with a selection, but it helps if you think about a Range like a selection. If you select everything (Ctrl+A) then paste, what was selected is replaced by what is pasted. Whatever is assigned to a Range will replace the content of the Range.
The way to solve this is to "collapse" the Range - think of it like pressing the Right-arrow or left-arrow key to "collapse" a selection to its start or end point. In the object model, this is the Collapse method that takes a parameter indicating whether to collapse to the start or end point (see the code below).
Note that I've also changed the code to use document.Content instead of Document.Range. Content is a property that returns the entire body of the document; Rangeis a method that expects a start and end point defining a Range. Using the property is the preferred method for the entire document.
if (startEnd.Contains(','))
{
arrSpecificPage = startEnd.Split(',');
for (int i = 0; i < arrSpecificPage.Length; i++)
{
range.Start = doc.GoTo(WdGoToItem.wdGoToPage, WdGoToDirection.wdGoToAbsolute, arrSpecificPage[i]).Start;
range.End = doc.GoTo(WdGoToItem.wdGoToPage, WdGoToDirection.wdGoToAbsolute, arrSpecificPage[i]).End;
range.Copy();
Word.Range targetRange = newDocument.Content
targetRange.Collapse(Word.WdCollapseDirection.wdCollapseEnd);
targetRange.Paste();
}
newDocument.SaveAs(outputSplitDocpath);
}

Using DataGridView in Visual Studio C#

I have a string that contains: "# of rows, # of columns, Row'X'Col'X'=Serial#, ...
How do I create a DataGrid table with the number of rows and columns defined, and then place the serial #s into the grid.
Examples:
2,1,R1C1=111,R2C1=112,
2,2,R1C1=211,R1C2=212,R2C1=213,R2C2=214,
thanks
Below is code that does what you are asking; however I must point out some problems with this approach. First, getting the total rows and cols from the first two elements in order to create your table is risky. If that data is wrong, this code will most likely crash or possibly omit data. Example if the input is: 2,2,RXCX=.., RXCX=.., RXCX=.., RXCX=..,RXCX=, RXCX=… This line will only get the first 4 values.
Worse… this will crash… if the input is 2,2,RXCX=.., RXCX=.. Then it will crash when you try to access the 4th element in the splitArray because there isn’t a 4th element. Either way is not good.
My point is to be safe… it would be a better approach to see how much data is actually there before you create the grid. You could get how many items there are with StringArray.Length minus the first two elements. These elements will define the dimensions and allow you to check their validity. This will make sure your loops won’t go out of bounds because the supplied data was wrong. It seems redundant and error prone to supply the dimension values when you can get that info from the data itself.
I still am not 100% sure what you want to accomplish here. It looks like a search of some form. This is what I am picturing…
Looking at your (previous) screen shots it appears to me that after you type into the Serial # text box and click the “Search Txt Files” button it will search for data that came from the input string i.e. “PLX51…” and then have the grid display the “filtered” results that match (or are LIKE) what’s in the Serial # textbox. If this is true, I would ignore the RXCX vales and put the data in a single column. Then wire up an OnKeyPress event for the text box to filter the grid whenever the user types into the Serial # text box.
Otherwise I am lost as to why you would need to create the data in the fashion described. Just because the input has unnecessary data… doesn’t mean you have to use it. Just a thought.
string inputString = "2,2,R1C1=211,R1C2=212,R2C1=213,R2C2=214";
string[] splitArray = inputString.Split(',');
int totalRows = int.Parse(splitArray[0]);
int totalCols = int.Parse(splitArray[1]);
int itemIndex = 2;
// add the columns
for (int i = 0; i < totalCols; i++)
{
dataGridView1.Columns.Add("Col", "Col");
}
// add the rows
dataGridView1.Rows.Add(totalRows);
for (int i = 0; i < totalRows; i++)
{
for (int j = 0; j < totalCols; j++)
{
dataGridView1.Rows[i].Cells[j].Value = splitArray[itemIndex];
itemIndex++;
}
}

C# Being Able to Search/Manipluate Existing RichTextBoxes That are Differently Named

With some of the knowledge I gained from yesterday and the day before, I have a general idea of what I want to do. I do have a general question about RichTextBoxes. I have a GUI already made of multiple RichTextBoxes aligned in different rows of one another. One row of boxes monitors the load of a product, the other monitors if one of the channels have been tripped and the other two rows displays the current and voltage of the product. The GUI has been premade and each row of product has been named StatusOutxxx, TripStatOutxxx, VoltageOutxxx and CurrentOutxxx (the xxx indicates a number of each RichTextBox, like CurrentOut001, for example).
The example gui is listed below:
From the image, you can see that I have 4 rows of RichTextBoxes. The monitoring backend of the product has already been established and I am trying to take the data and turn the product into a user friendly display to let the person now, if the load on a certain channel is okay or if the voltage of a product is too high/low or if a certain channel has been tripped.
Now, I know how to turn them all on at once via:
foreach(var rtb in this.Controls.OfType<RichTextBox>())
rtb.BackgroundColor = Color.Green;
and I know how to control which ones to turn on via if(rtb.Name.StartsWith("Insert Prefix Here")). But I am asking a bit of a harder question in terms of searching and manipulating an individual RichTextBox.
For example, if there is a load issue on Channel 17 of my product that drops my voltage, I want to be able to search for the StatusOut017, change the backcolor of that to Red and change the value of VoltageOut017 to the voltage display.
I know that the long way of doing it is doing a giant if statement for each load, trip and voltage channel to change it, but I am aiming for reducing the amount of lines of code and attempting to doing a for loop so my code will look something like this (in psuedocode):
for (i = 1; i < 25; i++)
{
if (StatusOut[i] == true)
{
StatusOut[i].BackColor == Color.Green;
}
else
{
StatusOut[i].BackColor == Color.Red;
}
if (TripStatOut[i] == true)
{
TripStatOut[i].BackColor == Color.Green;
}
else
{
TripStatOut[i].BackColor == Color.Red;
}
VoltageOut[i].Text = VoltageReading;
CurrentOut[i].Text = CurrentReading;
}
I am hoping for some help on this one.
I assume your naming convention is Name then 3 digits for the RichTextBox controls. You can use the controls collection to match the corresponding controls for that row. As noted a DataGrid would be better.
private void ChangeStatus()
{
//loop through the RichtextBoxes
foreach (RichTextBox rtb in this.Controls.OfType<RichTextBox>())
{
Color c = Color.Green; //default to good value
//only handle the status Out Control
if (rtb.Name.StartsWith("StatusOut"))
{ ///check the value and set the color if false
if (rtb.Text == "false")
c = Color.Red;
//update the controls
rtb.BackColor = c;
//string controlNum = rtb.Name.Replace("StatusOut", ""); //If it is not 3 just use replace since we know we are on the StatusOut control
string controlNum = rtb.Name.Substring(rtb.Name.Length - 3);
((RichTextBox)Controls["TripStateOut" + controlNum]).BackColor = c;
((RichTextBox)Controls["VoltageOut" + controlNum]).Text = VoltageReading;
((RichTextBox)Controls["CurrentOut" + controlNum]).Text = CurrentReading;
}
}
}

iTextSharp pdfpTable flowing in two columns in same page

I am using iTextSharp PdfPTtable for creating tables from a database. When the table is lengthy (long but just with 3 columns), I managed to get the table flowing (or continuing) to the next PDF page. But I want them to continue in the right side (or column) of the same page. And after that it has to continue to next page (left column and then the right column and so on...).
Your requirement is (almost) an exact match with one of the examples of my book.
Please take a look at page 3 and higher of column_table.pdf.
The book has the Java version of the example, but there's also a version ported to C#.
Basically, you need to add the PdfPTable to a ColumnText object and go() as long as there is content in the column:
// Column definition
float[][] x = {
new float[] { document.Left, document.Left + 380 },
new float[] { document.Right - 380, document.Right }
};
column.AddElement(yourTable);
int count = 0; // can be 0 or 1 if your page is divided in 2 parts
float height = 0;
int status = 0;
// render the column as long as it has content
while (ColumnText.HasMoreText(status)) {
// add the top-level header to each new page
if (count == 0) {
AddFooterTable(); // for you to implement to add a footer
height = AddHeaderTable(); // for you to implement to add a header
}
// set the dimensions of the current column
column.SetSimpleColumn(
x[count][0], document.Bottom,
x[count][1], document.Top - height - 10
);
// render as much content as possible
status = column.Go();
// go to a new page if you've reached the last column
if (++count > 1) {
count = 0;
document.NewPage();
}
}
document.NewPage();

C# Best way to parse flat file with dynamic number of fields per row

I have a flat file that is pipe delimited and looks something like this as example
ColA|ColB|3*|Note1|Note2|Note3|2**|A1|A2|A3|B1|B2|B3
The first two columns are set and will always be there.
* denotes a count for how many repeating fields there will be following that count so Notes 1 2 3
** denotes a count for how many times a block of fields are repeated and there are always 3 fields in a block.
This is per row, so each row may have a different number of fields.
Hope that makes sense so far.
I'm trying to find the best way to parse this file, any suggestions would be great.
The goal at the end is to map all these fields into a few different files - data transformation. I'm actually doing all this within SSIS but figured the default components won't be good enough so need to write own code.
UPDATE I'm essentially trying to read this like a source file and do some lookups and string manipulation to some of the fields in between and spit out several different files like in any normal file to file transformation SSIS package.
Using the above example, I may want to create a new file that ends up looking like this
"ColA","HardcodedString","Note1CRLFNote2CRLF","ColB"
And then another file
Row1: "ColA","A1","A2","A3"
Row2: "ColA","B1","B2","B3"
So I guess I'm after some ideas on how to parse this as well as storing the data in either Stacks or Lists or?? to play with and spit out later.
One possibility would be to use a stack. First you split the line by the pipes.
var stack = new Stack<string>(line.Split('|'));
Then you pop the first two from the stack to get them out of the way.
stack.Pop();
stack.Pop();
Then you parse the next element: 3* . For that you pop the next 3 items on the stack. With 2** you pop the next 2 x 3 = 6 items from the stack, and so on. You can stop as soon as the stack is empty.
while (stack.Count > 0)
{
// Parse elements like 3*
}
Hope this is clear enough. I find this article very useful when it comes to String.Split().
Something similar to below should work (this is untested)
ColA|ColB|3*|Note1|Note2|Note3|2**|A1|A2|A3|B1|B2|B3
string[] columns = line.Split('|');
List<string> repeatingColumnNames = new List<string();
List<List<string>> repeatingFieldValues = new List<List<string>>();
if(columns.Length > 2)
{
int repeatingFieldCountIndex = columns[2];
int repeatingFieldStartIndex = repeatingFieldCountIndex + 1;
for(int i = 0; i < repeatingFieldCountIndex; i++)
{
repeatingColumnNames.Add(columns[repeatingFieldStartIndex + i]);
}
int repeatingFieldSetCountIndex = columns[2 + repeatingFieldCount + 1];
int repeatingFieldSetStartIndex = repeatingFieldSetCountIndex + 1;
for(int i = 0; i < repeatingFieldSetCount; i++)
{
string[] fieldSet = new string[repeatingFieldCount]();
for(int j = 0; j < repeatingFieldCountIndex; j++)
{
fieldSet[j] = columns[repeatingFieldSetStartIndex + j + (i * repeatingFieldSetCount))];
}
repeatingFieldValues.Add(new List<string>(fieldSet));
}
}
System.IO.File.ReadAllLines("File.txt").Select(line => line.Split(new[] {'|'}))

Categories

Resources