C# Gtk get rendered size - c#

In Gtk# (using Mono), I sometimes need to allocate the default size to a widget (I understand you do that by widget.SetSizeRequest(-1, -1);. Someone please correct me if this is not the proper way). I do this to ensure that the size of a, say, Button widget is just as expected. I don't want it to be too big or too small. Allocating the default size ensures that the button is not too big but not too small either.
So when I allocate the default size to a widget, the widget.GetSizeRequest(out width, out height); returns -1 and -1 for width and height respectively.
Which is expected but not what I need. I need to know the rendered size (width and height separately) as I need to place another widget relative to it. So unless I know about its size, I can't place the other widget in the right position.
Although there are other instances where knowing about the rendered size would be helpful, this is the main reason.
I hope you understand what I mean by rendered size.
Update:
Since the answers seem to suggest using widget.Allocation although I've tried (see my comment), here's what I've tried:
Widget[] widgets = GetWidgets();//I initialize the widgets somewhere and get them as an array
//fix is a Gtk.Fixed container
foreach(Widget widget in widgets)
{
//although this is not the exact way I place the widgets, this is just an example
fix.Put(widget, GetPosition(widget).X, GetPosition(widget).Y);
widget.SetSizeRequest(-1, -1);
Console.WriteLine(w.Allocation.Size);
}
//later on, I need to place a few widgets relative to another
The output of this code is:
{Width=1, Height=1}
{Width=1, Height=1}
{Width=1, Height=1}
{Width=1, Height=1}
{Width=1, Height=1}
{Width=1, Height=1}
However, when I print the Allocation of a, say, Button widget in the Clicked event of the Button, it prints the rendered size as expected. However, in the above scenario, it just prints 1 and 1.
Can anyone identify what I'm doing wrong?

You are looking the Allocation property (get_allocation in gtk).
Example:
protected void OnButton2Clicked (object sender, EventArgs e)
{
var rect = button.Allocation;
PropertyInfo[] properties = rect.GetType().GetProperties();
var sb = new System.Text.StringBuilder();
foreach (PropertyInfo pi in properties)
{
sb.Append(
string.Format("Name: {0} | Value: {1}\n",
pi.Name,
pi.GetValue(rect, null)
)
);
}
Console.WriteLine (sb);
}
Output:
Name: GType | Value: GdkRectangle
Name: Top | Value: 50
Name: Bottom | Value: 82
Name: Right | Value: 69
Name: Left | Value: 30
Name: IsEmpty | Value: False
Name: Size | Value: {Width=40, Height=33}
Name: Location | Value: (30,50)
Update:
A widget that has not been rendered (this includes Visible=false) does not have any container allocation, as, well it does not need any and thus packing will occur correctly for the rest of the visible widgets.
Name: GType | Value: GdkRectangle
Name: Top | Value: -1
Name: Bottom | Value: -1
Name: Right | Value: -1
Name: Left | Value: -1
Name: IsEmpty | Value: False
Name: Size | Value: {Width=1, Height=1}
Name: Location | Value: (-1,-1)
If you really need a true rendered size (with current fonts, border, etc...), you need to render it somewhere. A real hack would be to place it on a double-buffered fixed container and 'render' it but never display it. Get and save the allocation from it (maybe in a dictionary), delete the widget and do it for the next one. Now you have a dictionary of widget sizes that match the current runtime environment..., like I said, a real hack...
Also asking this question a Gtk tagged C language question, or on their mailing list, might get you a better answer, their answer would be in C vs C#, transform it as needed. Make sure you mention that you need to do this in Gtk2 as Gtk3 is really different.

What you're looking for is in widget.Allocation, however the size is only set at the Expose Event. That gives you the rendered size of the widget. I'm guessing you're also using a Fixed container though. widget.SetSizeRequest(-1, -1) clears any programmed sizing and lets the Gtk library do what it wants with the size. Unless you really need tight control where each widget is placed, you're best off using containers such as VBox and HBox. One of the advantages of Gtk is that it does a lot of the finer alignment of widgets for you.
EDIT:
You could just draw one widget at a time. Its sort of, kind of "recursion". Once the Gtk run time calls the expose event handler, the size of the widget has already been determined so you can use that to place the next widget. Here is a really simplified example that draws three buttons at a downward diagonal line.
int idx;
string[] userDefinedText;
Fixed fix;
public MainWindow () : base (Gtk.WindowType.Toplevel) {
Build ();
// I'm not really sure how you store the text or just a list of widgets
// but for the sake of simplicity I'm just using a string array
idx = 0;
userDefinedText = new string[3] { "Medium text length", "Long text that goes on and on", "Short" };
fix = new Fixed ();
fix.SetSizeRequest (400, 400);
Add (fix);
fix.Show ();
Button b = new Button ();
b.SetSizeRequest (-1, -1);
b.Label = userDefinedText [idx];
b.ExposeEvent += OnButtonExposeEvent;
fix.Put (b, 5, 5);
b.Show ();
ShowAll ();
}
protected void OnButtonExposeEvent (object sender, ExposeEventArgs args) {
if (idx < (userDefinedText.Length - 1)) {
// There are still widgets to be placed on the screen
++idx;
Button b = sender as Button;
int x = b.Allocation.Right;
int y = b.Allocation.Bottom;
Button b2 = new Button ();
b2.SetSizeRequest (-1, -1);
b2.Label = userDefinedText [idx];
b2.ExposeEvent += OnButtonExposeEvent;
fix.Put (b2, x + 5, y + 5);
b2.Show ();
}
}

One last note, and only if you're using GTK+ 3:
I really don't know what you're trying to get the sizes for, buut if you're implementing your own container and want to see what size the widget wants to be (in a way better than RobertN's "real hack") is to use the preferred size system. GTK+ 3 has methods such as get_preferred_width() and get_preferred_height(), which return both a minimum (the absolute minimum, so just the width of ... for a label, for example) and a natural (the more reasonable size that the widget prefers to be to keep every part of it visible) size that you can use; see which one fits your use case more. There's also equivalents for height-for-width and width-for-height geometry (if you ever decide to do that).
I do not know what these methods are called in C#.

Related

How to calculate multiple times and display each answer in the form

How to calculate multiple times and display each answer in the Windows Form using C#? The Windows Form will only display one answer regardless of how many times I click the "calculate" button. How can I display all the answers, every time I click the button? Sorry if I ask a dummy question.
Below are my coding of calculation: pert = (o + (4 * m) + p) / 6;
System.Windows.Forms.Label ans1 = new System.Windows.Forms.Label();
this.Controls.Add(ans1);
ans1.BackColor = System.Drawing.Color.Transparent;
ans1.Top = 400;
ans1.Left = 300;
ans1.Text = pert.ToString();
You're probably overwriting the existing value each time.
I'd say you need to append the values. Your variable pert isn't defined in your code sample; I assume it is of type DateTime.
In which case, you need to do something like:
// To put the values on the same line with a comma/space separating them
ans1.Text += $"{pert}, ";
// To put the values on different lines
ans1.Text += $"{pert}\n";
Tip: You can also format the date/time if you wish using ToString() format modifiers such as:
pert.ToString("yyyy-MM-dd") -> 2020-11-30
pert.ToString("yy-MM-dd h:mm") -> 20-11-30 10:08
pert.ToString("F") -> Monday, 30 November 2020 10:08:45
More info on formatting here.
UPDATE: It just occurred to me that you might be wanting to add a new Label control with a single value each time. In that case, you need to generate your labels and dispense with appending values to the same label:
this.Controls.Add(new Label()
{
BackColor = System.Drawing.Color.Transparent,
Top = top,
Left = left,
Text = $"{pert}"
});
You would need to modify either (or both) top or left to avoid overlaying controls on top of each other.

WebBrowser get page size

When working with WebBrowser on IE 9 and above the property
webBrowser.Document.Body.ScrollRectangle.Size
returns always 250 x 250 as body dimensions.
Therefore I can't come across a way to check the current page size.
How can I check a html page actual size using IE 9 and above?
I've found a solution that seemed to work all the time until now.
I hope it'll help people with the same problem as I do, as you might have figured out in IE 9 or above, the Body element contains the default size for the ScrollRectangle property, however, as I found out, some other elements do contain the ScrollRectangle property with different sizes.
Most certainly that the HTML element contains a correct ScrollRectangle property, but some other elements might contain a ScrollRectangle greater or smaller in size, and sometimes it fits better.
So I've come to a conclusion that checking all the elements for the ScrollRectangle property is the smartest thing to do, here's the script :
int
CurrentWidth
, CurrentHeight
, CurrentMinWidth = WebBrowserOuterPanel.Width
, CurrentMaxWidth = 0
, CurrentMinHeight = WebBrowserOuterPanel.Height
, CurrentMaxHeight = 0;
foreach(HtmlElement webBrowserElement in webBrowser.Document.All)
{
if ((CurrentWidth = Math.Max(webBrowserElement.ClientRectangle.Width, webBrowserElement.ScrollRectangle.Width)) > CurrentMaxWidth)
CurrentMaxWidth = CurrentWidth;
if ((CurrentHeight = Math.Max(webBrowserElement.ClientRectangle.Height, webBrowserElement.ScrollRectangle.Height)) > CurrentMaxHeight)
CurrentMaxHeight = CurrentHeight;
}
webBrowser.Size = new Size (CurrentMaxWidth > CurrentMinWidth ? CurrentMaxWidth : CurrentMinWidth, CurrentMaxHeight > CurrentMinHeight ? CurrentMaxHeight : CurrentMinHeight);
Another way but might be not correct, in idea for implementing
HtmlElement webBrowserElement = webBrowser.Document.Body.FirstChild;
CurrentMaxWidth = Math.Max(webBrowserElement.ClientRectangle.Width, webBrowserElement.ScrollRectangle.Width);
CurrentMaxHeight = Math.Max(webBrowserElement.ClientRectangle.Height, webBrowserElement.ScrollRectangle.Height);
This should get you the window size
webBrowser.Document.Window.Size

Check if int is 10, 100, 1000,

I have a part in my application which needs to do do something (=> add padding 0 in front of other numbers) when a specified number gets an additional digit, meaning it gets 10, 100, 1000 or so on...
At the moment I use the following logic for that:
public static bool IsNewDigit(this int number)
{
var numberString = number.ToString();
return numberString.StartsWith("1")
&& numberString.Substring(1).All(c => c == '0');
}
The I can do:
if (number.IsNewDigit()) { /* add padding 0 to other numbers */ }
This seems like a "hack" to me using the string conversion.
Is there something something better (maybe even "built-in") to do this?
UPDATE:
One example where I need this:
I have an item with the following (simplified) structure:
public class Item
{
public int Id { get; set; }
public int ParentId { get; set; }
public int Position { get; set; }
public string HierarchicPosition { get; set; }
}
HierarchicPosition is the own position (with the padding) and the parents HierarchicPositon. E.g. an item, which is the 3rd child of 12 from an item at position 2 has 2.03 as its HierarchicPosition. This can as well be something more complicated like 011.7.003.2.02.
This value is then used for sorting the items very easily in a "tree-view" like structure.
Now I have an IQueryable<Item> and want to add one item as the last child of another item. To avoid needing to recreate all HierarchicalPosition I would like to detect (with the logic in question) if the new position adds a new digit:
Item newItem = GetNewItem();
IQueryable<Item> items = db.Items;
var maxPosition = items.Where(i => i.ParentId == newItem.ParentId)
.Max(i => i.Position);
newItem.Position = maxPosition + 1;
if (newItem.Position.IsNewDigit())
UpdateAllPositions(items.Where(i => i.ParentId == newItem.ParentId));
else
newItem.HierarchicPosition = GetHierarchicPosition(newItem);
UPDATE #2:
I query this position string from the DB like:
var items = db.Items.Where(...)
.OrderBy(i => i.HierarchicPosition)
.Skip(pageSize * pageNumber).Take(pageSize);
Because of this I can not use an IComperator (or something else wich sorts "via code").
This will return items with HierarchicPosition like (pageSize = 10):
03.04
03.05
04
04.01
04.01.01
04.01.02
04.02
04.02.01
04.03
05
UPDATE #3:
I like the alternative solution with the double values, but I have some "more complicated cases" like the following I am not shure I can solve with that:
I am building (on part of many) an image gallery, which has Categories and Images. There a category can have a parent and multiple children and each image belongs to a category (I called them Holder and Asstes in my logic - so each image has a holder and each category can have multiple assets). These images are sorted first be the categories position and then by its own position. This I do by combining the HierarchicPosition like HolderHierarchicPosition#ItemHierarchicPosition. So in a category which has 02.04 as its position and 120 images the 3rd image would get 02.04#003.
I have even some cases with "three levels" (or maybe more in the future) like 03.1#02#04.
Can I adapt the "double solution" to suport such scenarios?
P.S.: I am also open to other solution for my base problem.
You could check if base-10 logarithm of the number is an integer. (10 -> 1, 100 -> 2, 1000 -> 3, ...)
This could also simplify your algorithm a bit in general. Instead of adding one 0 of padding every time you find something bigger, simply keep track of the maximum number you see, then take length = floor(log10(number))+1 and make sure everything is padded to length. This part does not suffer from the floating point arithmetic issues like the comparison to integer does.
From What you describe, it looks like your HierarchicPosition position should maintain an order of items and you run into the problem, that when you have the ids 1..9 and add a 10, you'll get the order 1,10,2,3,4,5,6... somewhere and therefore want to pad-left to 01,02,03...,10 - correct?
If I'm right, please have a look at this first: https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem
Because what you try to do is a workarround to solve the problem in a certain way. - But there might be more efficent ways to actually really solve it. (therefore you should have better asked about your actual problem rather than the solution you try to implement)
See here for a solution, using a custom IComparator to sort strings (that are actually numbers) in a native way: http://www.codeproject.com/Articles/11016/Numeric-String-Sort-in-C
Update regarding your update:
With providing a sorting "String" like you do, you could insert a element "somewhere" without having ALL subsequent items reindexed, as it would be for a integer value. (This seems to be the purpose)
Instead of building up a complex "String", you could use a Double-Value to achieve the very same result real quick:
If you insert an item somewhere between 2 existing items, all you have to do is : this.sortingValue = (prior.sortingValue + next.sortingValue) / 2 and handle the case when you are inserting at the end of the list.
Let's assume you add Elements in the following order:
1 First Element // pick a double value for sorting - 100.00 for example. -> 100.00
2 Next Element // this is the list end - lets just add another 100.00 -> 200.00
1.1 Child // this should go "in between": (100+200)/2 = 150.00
1.2 Another // between 1.1 and 2 : (150+200)/2 = 175
When you now simple sort depending on that double field, the order would be:
100.00 -> 1
150.00 -> 1.1
175.00 -> 1.2
200.00 -> 2
Wanna Add 1.1.1? Great: positon = (150.00 + 175.00)/2;;
you could simple multiply all by 10, whenever your NEW value hits x.5* to ensure you are not running out of decimal places (but you dont have to - having .5 .25 .125 ... does not hurt the sorting):
So, after adding the 1.1.1 which would be 162,5, multiply all by 10:
1000.00 -> 1
1500.00 -> 1.1
1625.00 -> 1.1.1
1750.00 -> 1.2
2000.00 -> 2
So, whenever you move an item arround, you only need to recalculate the position of n by looking at n-1 and n+1
Depending on the expected childs per entry, you could start with "1000.00", "10.000" or whatever matches best.
What I didn't take into account: When you want to move "2" to the top, you would need to recalculate all childs of "2" to have a value somewhere between the sorting value of "2" and the now "next" item... Could serve some headache :)
The solution with "double" values has some limitations, but will work for smaller sets of groups. However you are talking about "Groups, subgroups, and pictures with counts of 100" - so another solution would be preferable:
First, you should refactor your database: Currently you are trying to "squeeze" a Tree into a list (datatables are basically lists)
To really reflect the complex layout of a tree with an infinite depth, you should use 2 tables and implement the composite pattern.
Then you can use a recursive approach to get a category, its subcategory, [...] and finally the elements of that category.
With that, you only need to provide a position of each leaf within it's current node.
Rearanging leafs will not affect any leaf of another node or any node.
Rearanging nodes will not affect any subnode or leaf of that node.
You could check sum of square of all digits for the input, 10,100,1000 has something in common that, if you do the sum of square of all digits, it should converge to one;
10
1^2 + 0^2 = 1
100
1^2 + 0^2 + 0^2 = 1
so on so forth.

Synchronising TableLayoutPanels

I have several TableLayoutPanels, each of which displays a category of name/value information in two columns - one with informational labels, and one with data labels.
In each one, I've set the first column to autosize and right-aligned all the labels, which works just fine. However, it works separately to each TableLayoutPanel (obviously), and looks something like this:
TableLayoutPanel 1:
+--------+--------+
| Name 1 | Data 1 |
+--------+--------+
| Name 2 | Data 2 |
+--------+--------+
| Name 3 | Data 3 |
+--------+--------+
TableLayoutPanel 2:
+------------------+--------+
| Long Name 1 | Data 1 |
+------------------+--------+
| Very Long Name 2 | Data 2 |
+------------------+--------+
| Long Name 3 | Data 3 |
+------------------+--------+
I'm looking for a way to consider all of the name labels when autosizing all of the first columns, so it looks like this:
TableLayoutPanel 1:
+------------------+--------+
| Name 1 | Data 1 |
+------------------+--------+
| Name 2 | Data 2 |
+------------------+--------+
| Name 3 | Data 3 |
+------------------+--------+
TableLayoutPanel 2:
+------------------+--------+
| Long Name 1 | Data 1 |
+------------------+--------+
| Very Long Name 2 | Data 2 |
+------------------+--------+
| Long Name 3 | Data 3 |
+------------------+--------+
I can't put all the data into one table, because each table represents a different category of information, and is inside a custom control with a collection of collapsible panels (so you can show or hide each category separately).
I've been trying to achieve this by overriding the container controls OnLayout(), setting all of the TableLayoutPanels' first columns to autosize, getting all of their widths, finding the maximum, and then settings all of their first columns to a fixed size of the greatest width. This works, but looks horrible every time layout occurs as all the columns jump to autosize and then back to fixed size.
I'm assuming I'm going to have to hook ControlAdded and ControlRemoved for each table, and then SizeChanged for each child control, to know when the size of any child control changed, and then manually set the column width somehow, but I'm not sure how to reliably get the correct widths.
I tried a variation of the first method - using GetPreferredSize() on all the controls in the first columns, to attempt to find the largest width, and then setting all first columns to a fixed size, but it seemed to return widths that were slightly to small. Should I be applying some extra spacing?
Does anyone know any way of asking the TableLayoutPanel to perform autosize calculations without it actually applying them visually? Or perhaps, lying to the tables to 'pretend' that there is a control of a certain width, just so it takes it into account? I can't add actual controls, since it will then want to allocate more cells for them. I tried looking at the source with ILSpy, but well, it isn't pretty. Seems most of the work is done by TableLayout class, which is, of course, internal, and I couldn't follow what it was doing.
Thanks for any ideas...
You can use the Graphics.Measurestring to determine the length in pixels without actually drawing it. There are some slight imperfections with it, so you may think about adding or removing some padding. After a test or two, you can get pretty close. That's as proper of a way as I know of, and it doesn't involve the text being in a label.
Also, trying to find a way to get the TableLayoutPanel to calculate sizes without displaying it visually just sounds like you're trying to hack it into doing something it wasn't designed to.
It turned out the width returned by GetPreferredSize() was useful, it was just 'too late'; I was working out the correct size and returning it within code that was called from the TableLayoutPanels' OnLayout() method, and setting the column width there has no effect until the next layout.
I had a solution that used a separate Component implementing IExtenderProvider which could be used to join tables together, but due to the issue above it always lagged behind control changes. Even hooking SizeChanged on all the child controls, the TableLayoutPanel gets the event first, and starts layout.
So I couldn't see any other way but to override the layout process itself. I tried creating a LayoutEngine that performed the necessary calculations, resized the columns, then delegated the actual layout work to the old layout engine, but unfortunately Control.LayoutEngine is read-only, and TableLayoutPanel doesn't even use a backing field, it just returns another object directly, so I couldn't even get around it by using reflection to assign the private backing field.
In the end I had to resort to subclassing the control, to override OnLayout(). Here is the result:
public class SynchronizedTableLayoutPanel : TableLayoutPanel
{
/// <summary>
/// Specifies a key used to group <see cref="SynchronizedTableLayoutPanel"/>s together.
/// </summary>
public String SynchronizationKey
{
get { return _SynchronizationKey; }
set
{
if (!String.IsNullOrEmpty(_SynchronizationKey))
RemoveSyncTarget(this);
_SynchronizationKey = value;
if (!String.IsNullOrEmpty(value))
AddSyncTarget(this);
}
} private String _SynchronizationKey;
#region OnLayout(), Recalculate()
protected override void OnLayout(LayoutEventArgs levent)
{
if (ColumnCount > 0 && !String.IsNullOrEmpty(SynchronizationKey))
{
Recalculate();
ColumnStyles[0] = new ColumnStyle(SizeType.Absolute, GetMaxWidth(SynchronizationKey));
}
base.OnLayout(levent);
}
public void Recalculate()
{
var LargestWidth = Enumerable.Range(0, RowCount)
.Select(i => GetControlFromPosition(0, i))
.Where(c => c != null)
.Select(c => (Int32?)((c.AutoSize ? c.GetPreferredSize(new Size(Width, 0)).Width : c.Width)+ c.Margin.Horizontal))
.Max();
SetMaxWidth(this, LargestWidth.GetValueOrDefault(0));
}
#endregion
#region (Static) Data, cctor, AddSyncTarget(), RemoveSyncTarget(), SetMaxWidth(), GetMaxWidth()
private static readonly Dictionary<SynchronizedTableLayoutPanel, Int32> Data;
static SynchronizedTableLayoutPanel()
{
Data = new Dictionary<SynchronizedTableLayoutPanel, Int32>();
}
private static void AddSyncTarget(SynchronizedTableLayoutPanel table)
{
Data.Add(table, 0);
}
private static void RemoveSyncTarget(SynchronizedTableLayoutPanel table)
{
Data.Remove(table);
}
private static void SetMaxWidth(SynchronizedTableLayoutPanel table, Int32 width)
{
Data[table] = width;
foreach (var pair in Data.ToArray())
if (pair.Key.SynchronizationKey == table.SynchronizationKey && pair.Value != width)
pair.Key.PerformLayout();
}
private static Int32 GetMaxWidth(String key)
{
var MaxWidth = Data
.Where(p => p.Key.SynchronizationKey == key)
.Max(p => (Int32?) p.Value);
return MaxWidth.GetValueOrDefault(0);
}
#endregion
}
This version only cares about the first column, but it could be adapted to synchronise other columns, or rows.
This approach does not flicker or cause jumps with sizing:
public partial class Form1 : Form
{
private readonly Timer _timer = new Timer();
public Form1()
{
InitializeComponent();
_timer.Interval = 500;
_timer.Tick += (o, ea) => UpdateWithRandomSizes();
_timer.Start();
}
private void UpdateWithRandomSizes()
{
var rand = new Random();
label1.Text = new string('A', rand.Next(10));
label2.Text = new string('B', rand.Next(10));
label3.Text = new string('C', rand.Next(10));
label4.Text = new string('D', rand.Next(10));
tableLayoutPanel1.ColumnStyles[0].SizeType = SizeType.AutoSize;
tableLayoutPanel2.ColumnStyles[0].SizeType = SizeType.AutoSize;
var width1 = tableLayoutPanel1.GetColumnWidths()[0];
var width2 = tableLayoutPanel2.GetColumnWidths()[0];
var max = Math.Max(width1, width2);
tableLayoutPanel1.ColumnStyles[0].Width = max;
tableLayoutPanel1.ColumnStyles[0].SizeType = SizeType.Absolute;
tableLayoutPanel2.ColumnStyles[0].Width = max;
tableLayoutPanel2.ColumnStyles[0].SizeType = SizeType.Absolute;
}
}

Horizontally align rows in multiple tables using web user control

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>

Categories

Resources