Scale components in FlowLayout as big as possible - winforms

How can I scale PictureBox components to best fit the given space on the screen while keeping their aspect ratio (interdependent of the actual image or its SizeMode) ?
I tested setting the Dock of the FlowLayout and the PictureBox to Fill. I also tested using a Panel as a wrapper and tested different settings for AutoSize and AutoSizeMode.
To give more information about the background: I want to dynamically add and remove images in the viewport of the application, so a TableLayout is in the first step sort of to static. I have to admit I'm was also thinking of calculating the size an position manually - or to dynamically adapt the row and column count of the TableLayout - but it seems to me prone to errors. I thought having a FlowLayout and automatically sized components should be the correct way - but it seems not to work that way. (To speak as web developer, I simply want to "float the images left", "with width and height set to 'auto'" and no scrolling.)
The images should visualize this a bit: the first figure should point out the layout, if there is only one PictureBox - it takes the whole space (or as big as possible with the given aspect ratio). The second shows how I would like the Layout to be, if there are two (three or four) images. The third figure is showing basically a resized window with three (to six) images.
Is there some point I'm missing?

This code snippet do this:
It arranges the visible controls inside a container in respect of the aspect ratio (see R variable in the code), and uses the container margin values to get horizontal and vertical gaps between items. The padding of the container is also handled.
public static void Arrange(Control container)
{
var H = container.DisplayRectangle.Height;
var W = container.DisplayRectangle.Width;
var N = container.Controls.OfType<Control>().Count(c => c.Visible);
var R = 4 / 3d; // item aspect ratio
var margin = container.Margin;
var padding = container.Padding;
var horizontalGap = margin.Left + margin.Right;
var verticalGap = margin.Top + margin.Bottom;
if (N == 0)
return;
var bestSizedItem = (
// Try n rows
Enumerable.Range(1, N).Select(testRowCount =>
{
var testItemHeight = (H - verticalGap * (testRowCount - 1)) / testRowCount;
return new
{
testColCount = (int)Math.Ceiling((double)N / testRowCount),
testRowCount = testRowCount,
testItemHeight = (int)testItemHeight,
testItemWidth = (int)(testItemHeight * R)
};
})
// Try n columns
.Concat(
Enumerable.Range(1, N).Select(testColCount =>
{
var testItemWidth = (W - horizontalGap * (testColCount - 1)) / testColCount;
return new
{
testColCount = testColCount,
testRowCount = (int)Math.Ceiling((double)N / testColCount),
testItemHeight = (int)(testItemWidth / R),
testItemWidth = (int)testItemWidth
};
})))
// Remove when it's too big
.Where(item => item.testItemWidth * item.testColCount + horizontalGap * (item.testColCount - 1) <= W &&
item.testItemHeight * item.testRowCount + verticalGap * (item.testRowCount - 1) <= H)
// Get the biggest area
.OrderBy(item => item.testItemHeight * item.testItemWidth)
.LastOrDefault();
Debug.Assert(bestSizedItem != null);
if (bestSizedItem == null)
return;
int x = container.DisplayRectangle.X;
int y = container.DisplayRectangle.Y;
foreach (var control in container.Controls.OfType<Control>().Where(c => c.Visible))
{
control.SetBounds(x, y,
bestSizedItem.testItemWidth,
bestSizedItem.testItemHeight);
x += bestSizedItem.testItemWidth + horizontalGap;
if (x + bestSizedItem.testItemWidth - horizontalGap > W)
{
x = container.DisplayRectangle.X;
y += bestSizedItem.testItemHeight + verticalGap;
}
}
}
I put this snippet on Gist so you can contribute if you wish.

Related

how to avoid whitespace on right side if columns are less in Ag grid

The answer might be sizeTofit .
But When I have to resize the column then that whitespace is coming on the right side of the grid.
So I need to do the last column of the grid to occupy the grid fully if columns are less (Or if I remove come column)otherwise it will take the column width provided by the property to Ag grid
This is the image I am getting now
like this I want
if size of it is not what you want then you can write a function and do the calculations manually.
set the width of columns and at the end set new column defs to your ag grid;
var tableWidth = sth;
var totalWidth = 0;
for(var i = 0; i<columnDefs.length; i++) {
totalWidth += columnDefs[i].width;
if((i == columnDefs.length - 1) && (totalWidth < tableWidth)) {
columnDefs[i] += tableWidth - totalWidth;
}
}
api.setColumnDefs(columnDefs);

Scroll to selection in Angular ui-grid (not ng-grid)

For my Angular JS grid work, I'm using ui-grid rather than ng-grid as ui-grid is meant to be the new version which is purer Angular.
I've got a grid that I'm populating with a http response, and I'm able to select a row (based on finding the record matching a $scope variable value) using the api.selection.selectRow method call.
What I need to do next is scroll the grid to that record.
There's an existing stack overflow question along the same lines that is for ng-grid and the answer to that refers to undocumented features which are not present in ui-grid so I can't use that approach.
The closest I've got is finding $scope.gridApi.grid to get a reference to the actual grid itself but looking through the properties and methods in the Chrome debugger doesn't show anything that sounds like it could work.
You can use the cellNav plugin. You should already have a reference to your row entity from the selection. The documentation is here.
gridApi.cellNav.scrollTo(grid, $scope, rowEntity, null);
I managed to hack together something that works pretty well but it's a bit dodgy and could probably be cleaner with a bit more Angular/jquery understanding.
I used the browser dom explorer to find that the scrollbars have a css class that we can detect to find them and then set the scroll properties on them to have the grid scroll (the grid and scrollbars are separate divs but their properties are bound so changing one updates the other).
It doesn't completely work for scrolling to the last row of the grid. This could be a timing issue, I've noticed when using breakpoints that the grid comes on screen a little larger and then shrinks down to it's final size. This could be messing with the scrolling values.
The first loop finds the height of the grid by adding up the rows, and the y position of the row for my data object (project), then we find the scrollbar and set it's scrollTop, trying to centre the row on screen without going out of bounds.
var grid = $scope.projectsGridApi.grid;
// var row = grid.rowHashMap.get(project.$$hashKey);
var found = false;
var y = 0;
var totalY = 0;
var rowHeight = 0;
for (var rowIdx in grid.rows)
{
var row = grid.rows[rowIdx];
if (row.entity.$$hashKey == project.$$hashKey)
{
found = true;
rowHeight = row.height;
}
if (!found)
{
y += row.height;
}
totalY += row.height;
}
// now find the scroll bar div and set it's scroll-top
// (todo: checking if we're at the end of the list - setting scrollTop > max means it doesn't work properly
var grid = $scope.projectsGridApi.grid;
// annoyingly this is nastily coded to find the scrollbar and isn't completely right
// I think the grid is a little taller when this is called, then shrinks
// which affects what the maximum is (so we might not always be able to put the selected item on screen if it is the last one).
var holderDiv = $('#projectsGridHolder');
if (holderDiv)
{
var scrollBarDivs = holderDiv.find('.ui-grid-native-scrollbar');
if (scrollBarDivs)
{
for (var scrollBarDivIdx in scrollBarDivs)
{
var scrollBarDiv = scrollBarDivs[scrollBarDivIdx];
var scrollBarDivClass = scrollBarDiv.className;
if (scrollBarDivClass)
{
if (scrollBarDivClass.indexOf('vertical') != -1)
{
var scrollHeight = scrollBarDiv.scrollHeight;
var clientHeight = scrollBarDiv.clientHeight;
if (rowHeight > 0)
{
y -= (clientHeight - rowHeight) / 2; // center on screen be scrolling slightly higher up
}
if (y < 0) y = 0;
else if (y > totalY - clientHeight) y = totalY - clientHeight;
scrollBarDiv.scrollTop = y;
}
}
}
}
}

Windows Phone 7 overflow clipping

I have a grid within a grid and i want the content of the second to move about without encroaching on the first grid.
Much like the panorama view but can move left or right as well as up and down.
I can get this working but unfortunately when you move down the top overflows into the outer grid overlapping any controls within it.
Is there a way to hide the overflow almost like CSS overflow:hidden?
Any help would be really appropriated.
Thank you
Andrew
Possible solution:
var gridWidth = (this.tilesize * (this.gridSize - 1)) / 2;
var top = -(((-offsetY + tileY) * this.tilesize) - gridWidth);
var left = -(((-offsetX + tileX) * this.tilesize) - gridWidth);
this.Container.Margin = new Thickness(left, top, 0, 0);
var clipSection = new RectangleGeometry();
clipSection.Rect = new Rect(-1 * left, -1 * top, 480, 400);
this.Container.Clip = clipSection;
this.Container.Dispatcher.BeginInvoke(new ThreadStart(delegate
{
this.Container.Clip = clipSection;
}));
You could do this by putting something in the cells of the "outer" grid and seeing a higher ZIndex than the elements you are moving around. The elements with the higher ZIndex appear above the lower ones.

WPF FlowDocument: force calculation of height etc. "off screen"

My target: a DocumentPaginator which takes a FlowDocument with a table, which splits the table to fit the pagesize and repeat the header/footer (special tagged TableRowGroups) on every page.
For splitting the table I have to know the heights of its rows.
While building the FlowDocument-table by code, the height/width of the TableRows are 0 (of course). If I assign this document to a FlowDocumentScrollViewer (PageSize is set), the heights etc. are calculated. Is this possible without using an UI-bound object? Instantiating a FlowDocumentScrollViewer which is not bound to a window doesn't force the pagination/calculation of the heights.
This is how I determine the height of a TableRow (which works perfectly for documents shown by a FlowDocumentScrollViewer):
FlowDocument doc = BuildNewDocument();
// what is the scrollviewer doing with the FlowDocument?
FlowDocumentScrollViewer dv = new FlowDocumentScrollViewer();
dv.Document = doc;
dv.Arrange(new Rect(0, 0, 0, 0));
TableRowGroup dataRows = null;
foreach (Block b in doc.Blocks)
{
if (b is Table)
{
Table t = b as Table;
foreach (TableRowGroup g in t.RowGroups)
{
if ((g.Tag is String) && ((String)g.Tag == "dataRows"))
{
dataRows = g;
break;
}
}
}
if (dataRows != null)
break;
}
if (dataRows != null)
{
foreach (TableRow r in dataRows.Rows)
{
double maxCellHeight = 0.0;
foreach (TableCell c in r.Cells)
{
Rect start = c.ElementStart.GetCharacterRect(LogicalDirection.Forward);
Rect end = c.ElementEnd.GetNextInsertionPosition(LogicalDirection.Backward).GetCharacterRect(LogicalDirection.Forward);
double cellHeight = end.Bottom - start.Top;
if (cellHeight > maxCellHeight)
maxCellHeight = cellHeight;
}
System.Diagnostics.Trace.WriteLine("row " + dataRows.Rows.IndexOf(r) + " = " + maxCellHeight);
}
}
Edit:
I added the FlowDocumentScrollViewer to my example. The call of "Arrange" forces the FlowDocument to calculate its heights etc. I would like to know, what the FlowDocumentScrollViewer is doing with the FlowDocument, so I can do it without the UIElement. Is it possible?
My guess would be no, you can't do it without a UIElement.
FlowDocument, by itself, doesn't actually render anything. Looking at the type in relector it looks like it is just a data type. Its about like having a string and wanting to know its size when rendered ... can't really do it without doing some kind of measure pass.
I don't know for sure, but you might get better performance in the arrange pass by passing in Double.PositiveInfinity for the size rather than 0. At least then it won't have to worry about measuring 'n' line breaks.

Silverlight specific Image shifting to the right

I am generating a set images to form a human body so that I can use for a physics engine.
The images generated are in a specific user control in where I set the dimentions and co-ordinates of each image. That usercontrol is then loaded in another user control but for some reason when the images are loaded, one specific image which I named (rightBicep) is shifting to the right. Here is a screenshot :
alt text http://img193.imageshack.us/img193/592/imageshift.jpg
I illustrated the positions of the images with dotted lines, the green dotted line is refering to where the image should be located, and the red dotted line is where the image is being shown.
The weird thing is the image beneath it (called rightForearm) take's it's LeftPosition from it, and when during debugging they have the exact same leftProperty value. Here's the syntax :
public void generateRightBicep(string imageUrl)
{
rightBicep = new Image();
rightBicep.Name = CharacterName + "rightbicep";
Uri imageUri = new Uri(imageUrl, UriKind.Relative);
LayoutRoot.Children.Add(rightBicep);
rightBicep.Source = new BitmapImage(imageUri);
rightBicep.ImageOpened += new EventHandler<RoutedEventArgs>(bodyPart_ImageOpened);
}
public void rightBicepLoaded()
{
var bi = waitTillImageLoad(rightBicep.Name);
rightBicep.Height = elbowToArmpit + (2 * palm);
rightBicep.Width = ratio(bi.PixelHeight, bi.PixelHeight, rightBicep.Height); // to be determined
Vector2 topVector;
topVector.X = (float)(Convert.ToDouble(torso.GetValue(Canvas.LeftProperty)) - palm);
topVector.Y = (float)(Convert.ToDouble(neck.GetValue(Canvas.TopProperty)) + neck.Height);
if (!faceRight)
{
perspectiveVectorHeight(ref topVector, ref rightBicep, torso.Width);
rightBicep.Width = ratio(bi.PixelHeight, bi.PixelHeight, rightBicep.Height);
}
rightBicep.SetValue(Canvas.LeftProperty, Convert.ToDouble(topVector.X));
rightBicep.SetValue(Canvas.TopProperty, Convert.ToDouble(topVector.Y));
rightBicep.SetValue(Canvas.ZIndexProperty, rightBicepZindex);
generateRightShoulder();
}
public void generateRightForearm(string imageUrl)
{
rightForearm = new Image();
rightForearm.Name = CharacterName + "rightforearm";
Uri imageUri = new Uri(imageUrl, UriKind.Relative);
LayoutRoot.Children.Add(rightForearm);
rightForearm.Source = new BitmapImage(imageUri);
rightForearm.ImageOpened += new EventHandler<RoutedEventArgs>(bodyPart_ImageOpened);
}
public void rightForearmLoaded()
{
var bi = waitTillImageLoad(rightForearm.Name);
rightForearm.Height = (elbowToHandTip - handLength) + palm;
rightForearm.Width = ratio(bi.PixelHeight, bi.PixelWidth, rightForearm.Height);
Vector2 topVector;
if (faceRight)
{
topVector.X = (float)(Convert.ToDouble(rightBicep.GetValue(Canvas.LeftProperty)));
topVector.Y = (float)(Convert.ToDouble(rightBicep.GetValue(Canvas.TopProperty)) + rightBicep.Height - palm);
}
else
{
topVector.X = (float)(Convert.ToDouble(leftBicep.GetValue(Canvas.LeftProperty)));
topVector.Y = (float)(Convert.ToDouble(leftBicep.GetValue(Canvas.TopProperty)) + leftBicep.Height - palm);
perspectiveVectorHeight(ref topVector, ref rightForearm, torso.Width);
rightForearm.Width = ratio(bi.PixelHeight, bi.PixelWidth, rightForearm.Height);
}
rightForearm.SetValue(Canvas.LeftProperty, Convert.ToDouble(topVector.X));
rightForearm.SetValue(Canvas.TopProperty, Convert.ToDouble(topVector.Y));
rightForearm.SetValue(Canvas.ZIndexProperty, rightForearmZIndex);
generateRightElbow();
}
Now all the values I am adding together are a group of doubles I preset, and the property faceRight is to dertmine if the human body is facing right or left to determine where the positions of the body parts (since if the right hand looks on the left hand side when the human body turns the other way).
If you notice the rightforearm is taking the leftproperty of the rightbicep, so technically it should display direcrly underneath which it isn't. I also debugged the user control and both have the left property of -3.
PS. I call the methods rightbicepLoaded and rightforearmLoaded when an event is called when all the imageOpened events all have been triggered.
Any ideas on why this is happening?
Found out why , in my method ratio it should take hieght and width, and I put and i put 2 hieghts instead

Resources