wpf radgridview How to get vertical offset of GridViewRow? - wpf

I have row index of target GridViewRow, and ScrollChanged handler callback of GridViewScrollViewer inside RadGridView. I want to get vertical offset of target row in viewport, while scrolling, if that row leaves viewport from top, return 0; if leaves from bottom, return ActualHeight of RadGridView; else, get a value same as Top offest of target row and viewport.
Here is function to get vertival offset, it will be incorrect after scrollbar moved:
//const int indicator_size = 3;
//RadGridView grid;
Func rowIndicatorOffset = (rowIndex) =>
{
double offset = (rowIndex + 1.5) * this.grid.RowHeight;
if (offset > this.grid.ActualHeight)
{
return this.grid.ActualHeight - indicator_size;
}
return offset;
};
When change vertical scroll bar offset, I want that indicator(red one in the picture above) keep same Y position with target row:
//System.Windows.Controls.ScrollChangedEventArgs offsetValues (from ScrollChanged event)
double fixRatio = (this.grid.ActualHeight - this.offsetValues.VerticalOffset) / this.grid.ActualHeight;
double offset = (rowIndex + 1.5) * this.grid.RowHeight * fixRatio;
the code above works not well as expected apparently, so what's the answer?

I got it wrong about Viewport, the diagram below may figure it out.
diagram
//const int indicator_size = 3;
//RadGridView grid;
Func<int, double> rowIndicatorOffset = (rowIndex) =>
{
double targetOffset = (rowIndex + 1.5) * this.grid.RowHeight;
double viewportOffset = this.offsetValues.VerticalOffset;
double offset = targetOffset - viewportOffset;
if (offset > this.grid.ActualHeight)
{
return this.grid.ActualHeight - indicator_size;
}
else if (offset < 0)
{
return 0;
}
else
{
return offset;
}
};

Related

Avoid window opening between two screens

I am writing a Photoshop plugin for Windows and want to place the plugin dialog in the center of the main window.
This is my code:
void centre_window(HWND hwnd){
RECT rs, rd;
HWND hw = GetParent(hwnd); // GetDesktopWindow();
if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
MoveWindow(hwnd,(rs.right + rs.left + rd.left - rd.right) / 2,
(rs.bottom + rs.top + rd.top - rd.bottom) / 3,
rd.right - rd.left, rd.bottom - rd.top, TRUE);
}
So far, it works. But there is one flaw: If the main window is spread across two screens, then my window is between both screens.
I looked at other Photoshop plugins and they handle it like this:
Place the window at the main window center
If it would be between two screens, choose one of these two and place it at the border of the screen
How can I do that?
With the helpful hint of #IInspectable , I wrote following code:
void _doMonitorAdjustments(LPRECT rcPlugin) {
RECT rcMonitorWork;
HMONITOR hMonitor;
MONITORINFO grMonitorInfo = { sizeof(grMonitorInfo) };
int nXAdjust = 0, nYAdjust = 0, nPluginWidth, nPluginHeight, nMonitorWorkWidth, nMonitorWorkHeight;
hMonitor = MonitorFromRect(rcPlugin, MONITOR_DEFAULTTONEAREST);
if (hMonitor == NULL) return;
if (!GetMonitorInfoA(hMonitor, &grMonitorInfo)) return;
rcMonitorWork = grMonitorInfo.rcWork;
// Don't let the window exit the left/right borders of the monitor
nPluginWidth = rcPlugin->right - rcPlugin->left;
nMonitorWorkWidth = rcMonitorWork.right - rcMonitorWork.left;
if (nPluginWidth > nMonitorWorkWidth) {
// Window larger than screen width. Decrease the width!
rcPlugin->left = rcMonitorWork.left;
rcPlugin->right = rcMonitorWork.right;
}
else if (rcPlugin->left < rcMonitorWork.left) {
nXAdjust = rcMonitorWork.left - rcPlugin->left;
}
else if (rcPlugin->right > rcMonitorWork.right) {
nXAdjust = rcMonitorWork.right - rcPlugin->right;
}
// Don't let the window exit the top/bottom borders of the monitor
nPluginHeight = rcPlugin->bottom - rcPlugin->top;
nMonitorWorkHeight = rcMonitorWork.bottom - rcMonitorWork.top;
if (nPluginHeight > nMonitorWorkHeight) {
// Window larger than screen height. Decrease the height!
rcPlugin->top = rcMonitorWork.top;
rcPlugin->bottom = rcMonitorWork.bottom;
}
else if (rcPlugin->top < rcMonitorWork.top) {
nYAdjust = rcMonitorWork.top - rcPlugin->top;
}
else if (rcPlugin->bottom > rcMonitorWork.bottom) {
nYAdjust = rcMonitorWork.bottom - rcPlugin->bottom;
}
OffsetRect(rcPlugin, nXAdjust, nYAdjust);
}
/*
* Centers a window to the center of its parent form but avoids
* being spread across two screens.
*/
void centre_window(HWND hwnd) {
RECT rcParent, rcWindowOriginal, rcPlugin;
HWND hParent;
hParent = GetParent(hwnd);
if (hParent == NULL) hParent = GetDesktopWindow();
if (!GetWindowRect(hParent, &rcParent)) return;
if (!GetWindowRect(hwnd, &rcWindowOriginal)) return;
rcPlugin.left =
rcParent.left
+ (rcParent.right - rcParent.left) / 2
- (rcWindowOriginal.right - rcWindowOriginal.left) / 2;
rcPlugin.top =
rcParent.top
+ (rcParent.bottom - rcParent.top) / 2
- (rcWindowOriginal.bottom - rcWindowOriginal.top) / 2;
rcPlugin.right =
rcPlugin.left + rcWindowOriginal.right - rcWindowOriginal.left;
rcPlugin.bottom =
rcPlugin.top + rcWindowOriginal.bottom - rcWindowOriginal.top;
// Avoid that the window is spread across two screens
_doMonitorAdjustments(&rcPlugin);
MoveWindow(hwnd,
rcPlugin.left,
rcPlugin.top,
/*width=*/rcPlugin.right - rcPlugin.left,
/*height=*/rcPlugin.bottom - rcPlugin.top,
TRUE);
}

Check transformation values before applying the transform

I'm using a render transform to pan an image. I need the panning to stop at a certain point, so I'm checking the bounds of the image. The problem is that the image can be panned slightly beyond the bounds, because I'm checking the bounding box before the transform has been applied. Is there a way of applying the transform to the bounding box, then checking if this is within the valid range?
Thanks for any help
private void RecalculatePositions(MouseEventArgs e)
{
TranslateTransform tt1 = (TranslateTransform)((TransformGroup)this.image1.RenderTransform).Children.First(tr => tr is TranslateTransform);
TranslateTransform tt2 = (TranslateTransform)((TransformGroup)this.image2.RenderTransform).Children.First(tr => tr is TranslateTransform);
Vector vector = this._startPoint - e.GetPosition(this.grid);
double offsetX = this._originPoint.X - vector.X;
double offsetY = this._originPoint.Y - vector.Y;
Rect image1Bounds = this.image1.TransformToAncestor(this.viewBox1).TransformBounds(new Rect(this.image1.RenderSize));
if (vector.X < 0)
{
//Moving image right
if (offsetX <= 0)
{
tt1.X = offsetX;
tt2.X = offsetX;
}
}
else
{
//Moving image left
if (image1Bounds.Right >= 0)
{
tt1.X = offsetX;
tt2.X = offsetX;
}
}
if (vector.Y < 0)
{
//Moving image down
if (offsetY <= 0)
{
tt1.Y = offsetY;
tt2.Y = offsetY;
}
}
else
{
//Moving image up
if (image1Bounds.Bottom >= this.ActualHeight)
{
tt1.Y = offsetY;
tt2.Y = offsetY;
}
}
}

FinalSize in ArrangeOverride is coming wrong

I'm doing a custom panel, based(heriting) the WrapPanel.
I'm doing this because some UserControls I'm using in the WrapPanel don't wrap because they are able to use an "infinite" width, so a WrapPanel with a vertical orientation will never wrap.
I wanted to do one WrapPanel that ignore the width of the elements when having orientation = Vertical, and ignore the height of the elements when having an orientation = Horizontal.
Currently I've the following code:
protected override Size ArrangeOverride(Size finalSize)
{
int numberOfColumns, numberOfRows;
UIElement uiElement = InternalChildren.OfType<UIElement>().First();
if (Orientation == Orientation.Horizontal)
{
double desiredWidth = uiElement.DesiredSize.Width;
numberOfColumns = (int)Math.Round(finalSize.Width / desiredWidth);
numberOfRows = (int)Math.Ceiling(InternalChildren.Count / (double)numberOfColumns);
}
else
{
double desiredHeight = uiElement.DesiredSize.Height;
numberOfRows = (int)Math.Round(finalSize.Height / desiredHeight);
numberOfColumns = (int)Math.Ceiling(InternalChildren.Count / (double)numberOfRows);
}
int row = 0, column = 0;
Size elementSize = new Size(Math.Floor(finalSize.Width / numberOfColumns), Math.Floor(finalSize.Height / numberOfRows));
foreach (UIElement child in InternalChildren)
{
child.Arrange(new Rect(new Point(row * elementSize.Width, column * elementSize.Height), elementSize));
if (Orientation == Orientation.Horizontal)
{
column++;
if (column >= numberOfColumns)
{
column = 0;
row++;
}
}
else
{
row++;
if (row >= numberOfRows)
{
row = 0;
column++;
}
}
}
return finalSize;
}
The issue is that the finalSize that I receives is bigger than the "ActualSize" of the component(Width 2x the actualSize in fact).
What did I miss?

Ionic: Get the currently visible items in ion-content

I have a long, scrollable ion-content area in my app, filled with items using a collection-repeat.
I need to know which items are visible to the user.
I cannot use the $ionicScrollDelegate.getScrollPosition to calculate the answer, because each item has a different height (item height is calculated per item).
Ended up calculating the summed heights myself of the elements, and by querying for the translateY value of the .scroll element, I can find out which item is in the visible part of the scroll.
It's reinventing the wheel, but works.
When i load the items, i call ScrollManager.setItemHeights(heights) (heights is the array of item heights in pixels), and to get the index of the currently visible item: ScrollManager.getVisibleItemIndex()
angular.module("services")
.service('ScrollManager', function() {
var getTranslateY, getVisibleItemIndex, setItemHeights, summedHeights;
summedHeights = null;
setItemHeights = function(heights) {
var height, sum, _i, _len;
summedHeights = [0];
sum = 0;
for (_i = 0, _len = heights.length; _i < _len; _i++) {
height = heights[_i];
sum += height;
summedHeights.push(sum);
}
};
// returns the style translateY of the .scroll element, in pixels
getTranslateY = function() {
return Number(document.querySelector('.scroll').style.transform.match(/,\s*(-?\d+\.?\d*)\s*/)[1]);
};
getVisibleItemIndex = function() {
var i, y;
y = -getTranslateY();
i = 0;
while (summedHeights[i] < y) {
i++;
}
return i;
};
return {
setItemHeights: setItemHeights,
getVisibleItemIndex: getVisibleItemIndex
};
});

WPF Resize UserControl with aspect ratio

I have a UserControl and that UserControl has to be resized with aspect ratio.
That means: width:height = 2:1.
Currently I am using this code:
protected override Size ArrangeOverride(Size arrangeBounds)
{
if (ActualWidth == 0 || ActualHeight == 0) return arrangeBounds;
base.ArrangeOverride(arrangeBounds);
double ratio = 2;
if (Parent != null)
{
var size = new Size(arrangeBounds.Height * ratio, arrangeBounds.Height);
double containerWidth = ((FrameworkElement)Parent).ActualWidth;
if (containerWidth < size.Width)
{
double newHeight = arrangeBounds.Height * (containerWidth / size.Width);
canvas.Width = newHeight * ratio;
canvas.Height = newHeight;
}
else
{
canvas.Width = size.Height * ratio;
canvas.Height = size.Height;
}
}
return arrangeBounds;
}
But it is not really working. That means it works but not every time. If I max. the window it sometimes does not get resized, so its a bit "random" if the control gets resized. So if someone would have a better solution if would be very nice.
The most straightforward solution would be to Bind the Height directly to the Width, through a value converter.
It's a bit late, but I recently came across the same problem and since I did not find a good solution I decided to write my own layout control/decorator and wrote a blog post about it here:
http://coding4life.wordpress.com/2012/10/15/wpf-resize-maintain-aspect-ratio/
Basically my solution was to overwrite both MeasureOverride and ArrangeOverride. So far it works very nicely in all of the common containers and I didn't encounter any problems like you described.
I recommend reading the post, where you find a working decorator control, but the most important methods are these:
protected override Size MeasureOverride(Size constraint)
{
if (Child != null)
{
constraint = SizeToRatio(constraint, false);
Child.Measure(constraint);
if(double.IsInfinity(constraint.Width)
|| double.IsInfinity(constraint.Height))
{
return SizeToRatio(Child.DesiredSize, true);
}
return constraint;
}
// we don't have a child, so we don't need any space
return new Size(0, 0);
}
protected override Size ArrangeOverride(Size arrangeSize)
{
if (Child != null)
{
var newSize = SizeToRatio(arrangeSize, false);
double widthDelta = arrangeSize.Width - newSize.Width;
double heightDelta = arrangeSize.Height - newSize.Height;
double top = 0;
double left = 0;
if (!double.IsNaN(widthDelta)
&& !double.IsInfinity(widthDelta))
{
left = widthDelta/2;
}
if (!double.IsNaN(heightDelta)
&& !double.IsInfinity(heightDelta))
{
top = heightDelta/2;
}
var finalRect = new Rect(new Point(left, top), newSize);
Child.Arrange(finalRect);
}
return arrangeSize;
}
public Size SizeToRatio(Size size, bool expand)
{
double ratio = AspectRatio;
double height = size.Width / ratio;
double width = size.Height * ratio;
if (expand)
{
width = Math.Max(width, size.Width);
height = Math.Max(height, size.Height);
}
else
{
width = Math.Min(width, size.Width);
height = Math.Min(height, size.Height);
}
return new Size(width, height);
}
I hope it helps someone!
Wrap your Control with a ViewBox and set the ViewBox.Stretch to Uniform. You can also restrict on MaxWidth and MaxHeight that way.
More Info on the Stretch-Enumeration # MSDN
How to apply Stretch # MSDN

Resources