I consider this a pretty simple request, but I can't seem to find a conclusive answer in my searches. How can I determine the bounds of a particular visual element in my window, relative to some other parent element?
I've tried using LayoutInformation.GetLayoutSlot but this just seems to return a Rect at 0,0 and doesn't reflect the actual location of the element.
What I'm trying to do is take a "screenshot" of a window using RenderTargetBitmap and then crop it to a particular element, but I can't get the element's bounds to know what to crop the bitmap to!
It is quite simple:
public static Rect BoundsRelativeTo(this FrameworkElement element,
Visual relativeTo)
{
return
element.TransformToVisual(relativeTo)
.TransformBounds(LayoutInformation.GetLayoutSlot(element));
}
In fact it may be overkill to put it in a separate method.
The LayoutSlot option didn't work for me at all.
This ended up giving me a child position relative to a specified parent/ancestor control:
public static Rect BoundsRelativeTo(this FrameworkElement child, Visual parent)
{
GeneralTransform gt = child.TransformToAncestor(parent);
return gt.TransformBounds(new Rect(0, 0, child.ActualWidth, child.ActualHeight));
}
Taking into account a few suggestions i found here this solved the problem for me.
item.TransformToVisual( relativeToElement )
.TransformBounds( new Rect( item.RenderSize ) );
Nevermind, I finally managed to figure it out using a combination of LayoutInformation.GetLayoutSlot() (although I probably could have used either ActualWidth/ActualHeight or RenderSize) and UIElement.TranslatePoint().
Seems a rather complicated solution when it could be as simple as this:
myElement.GetBounds( relativeElement );
Oh well. Maybe time for an extension method. :)
Related
I have a set of Coordinates that is drawn on my override method OnRender using DrawGeometry. One of the Polygons im trying to draw is 121000 points which is a lot. This slows down my map control.
Also when this OnRender happens the points are already in memory I'm just passing the points to DrawGeomerty
here is an example what happens OnRender
MapProjection pa = new MapProjection();
if (this.mapCommunication.MapLayers == null)
{
return;
}
foreach (KeyValuePair<Guid, MapLayerHelper> coordinatePointsLayer in this.mapCommunication.MapLayers)
{
if (!coordinatePointsLayer.Value.IsVisible)
{
continue;
}
if (coordinatePointsLayer.Value.State != LayerEnum.Visible)
{
continue;
}
foreach (CoordinateHelper coordinatePoints in coordinatePointsLayer.Value.Coordinates)
{
foreach (StreamGeometry item in coordinatePoints.GeomertyPoints)
{
drawingContext.DrawGeometry(null, penDrawing, item);
}
}
}
My Question is what direction should i take from here should i optimize, or should i try and incorporate DirectX would this help or what approach should i take?
Thanks for help i pretty new to render this much data.
What i eventually figured out was help me out if I'm wrong :) WPF is already build upon Direct X so would not help me much if i tried accessing through something like SharpDX, because i was going for dubble buffering and that is already implemented.
So what i implemented was instead of reading the point in the OnRender is used a Canvas with a fixed size and added System.Windows.Shapes.Path adding my Geometry that way.
This caused WPF to better manage dubble buffering and the Points rendered much faster.
A simple code for drawing a line.
using (Graphics g = this.CreateGraphics())
{
g.DrawLine(Pens.Black, new Point(50, 50), new Point(100, 100));
}
This will draw a typical line. However i want to this line to be selectable so that user can manipulate it further(streching, resizing etc) at run time. Initially i attempted to use controls that can be manipulated at runtime with the line as background however that could not work due to overlapping controls issue.
My question is how can i select this line at runtime ?
You need to write it yourself.
Write a class that wraps Line
Add all needed additional behavior to the class (what happens when the line is selected, what happens when the line is stretched, deleted, changed color ...)
Write a class that manages either object was was picked by mouse or not (RayTracer)
etc...
Or simply use: piccolo2d framework
Structured 2D Graphics Framework
I think what you're looking to do is make the Graphics object to be selectable?
If so, you could put your logic into the MouseOver and MouseButton events.
Check this out, it may give you some insight.
Selectable Graphics Object
You must create it yourself. Declare interfaces that your graphics objects implement. Suggestion:
public interface IObject
{
bool HitTest(Point mouseLocation);
void Paint(Graphics g);
List<IAdorner> Adorners { get; }
}
public interface IAdorner
{
bool HitTest(Point mouseLocation);
void Paint(Graphics g);
void StartMoving(Point mouseLocation);
void Move(Point mouseLocation);
}
The adorners are the selectable end points of a line object for instance.
Your main paint routine will look something like this:
private void drawingSurface_Paintobject sender, PaintEventArgs e)
{
foreach (IObject o in _objects) {
o.Paint(e.Graphics);
if (o == _selectedObject) {
foreach (IAdorner a in o.Adorners) {
a.Paint(e.Graphics);
}
}
}
}
And of cause you need all the mouse event handling.
These interfaces are abstract enough in order to allow the implementation of any shapes. For instance they don't include any coordinates, as different types of objects need different numbers and kinds of coordinates and parameters.
Maybe It's a little too late, since you already accepted an answer, but you should really consider WPF for this.
https://stackoverflow.com/a/15469477/643085
That's an exact sample of what you're after, in WPF + MVVM. With a real whole LOT of advantages over any winforms approach (such as NO flicker due to hardware acceleration).
Please at least give it a try. Full CSProj project Source available.
You're REALLY reiventing the wheel implementing all this yourself in GDI. And in the end it will be unusable due to flickering and stuff like that.
In my program the source rectangle for drawing can either be a regular rectangle, an empty rectangle, or a rectangle with an X or Y of -1. If the rectangle is normal (example being (0, 0, 64, 64)) then it just draws that from the texture. If it is Rectangle.Empty it draws nothing and just continues with the loop. If the source rectangle has an X or Y of -1 then it is determined to be a collision tile.
The problem with this is that it -1 is not intuitive. It is confusing and a bad solution. Also if there come to be more tile types it will start getting ridiculous like -2 meaning a slow tile or -3 meaning a water tile.
Another problem is that since I did not know there were going to be collision tiles early on and regular XNA rectangles were fine, the entire system (thankfully only around 1,000 of lines of code at the moment) uses XNA rectangles. I figure I'm going to have to make a separate class at this point and update everything but I'm not sure.
What would be a good solution to this? I have not really dabbled in extension methods at all. Could they be applied to the Rectangle class and be given methods like IsCollisionTile() or IsBlankTile()? Initially I was hoping I could derive from the Rectangle class to make a Tile class but unfortunately the class is sealed. I suppose another simple solution could be just making a global constants class with -1 being CollisionTile, 0 being BlankTile, et cetera. This would at least make it slightly more understandable but this still looks ugly to me:
if (tiles[y, x].X == Constants.BlankTile)
continue;
if (tiles[y, x].X == Constants.CollisionTile)
{
Utility.DrawRectangle(spriteBatch, new Rectangle(x * TileSize, y * TileSize, TileSize, TileSize), collisionTileColor);
continue;
}
spriteBatch.Draw(tileset, new Rectangle(x * TileSize, y * TileSize, TileSize, TileSize), tiles[y, x], Color.White);
If only there was a property I could use like Tag with controls. I'd really like to not abandon using the Rectangle class because it is so embedded in to the system and the program is purely functional, just not aesthetic in this regard. Ideally, I'd prefer a solution that just allows the Rectangle class to be extended to somehow be able to communicate with its clients what kind of tile it is supposed to be.
Well then, that took a lot more typing than I had originally hoped for. Sorry for the long read x_x
I would recommend setting global constants. The problem with extension methods in this case arises because Rectangle is a struct, a value type. That means that your extension method is working with a copy of the rectangle, not the original.
If the class can't be inherited from (which would usually be the appropriate solution here. Shame on you Microsoft!) then extension methods could defiantly work as you described.
Problem is - IMO it's less in style with C# and OOP in general to use methods that function like getters. That's what getters are for.
Because of that, I think the global constants option is more in line with the general style, but that's just my opinion.
From a totally programmatic POV - both methods are valid where the global constant class might be slightly faster (though I'm not sure of this)
At the beginning, you should consider, when do you use your Methods
IsCollisionTile() and IsBlankTile()
You have two choices:
*) You wanna use it globally, then you should better write a Utility-Class to have your Methods right there where you need them:
public static class CollisionHelper
{
public static Boolean IsCollisionTile(ITile tileToCheck)
{
...
}
}
*) Second, if you wanna use it only in connection with your tiles, you should definitly write an extension method, e.g. to accept every ITile-Object. Extensions methods are a great way to widely EXTEND the capabilities of classes. A sample could be:
public class RectangleTile : ITile
{
public static Boolean IsCollisionTile(this ITile tileToCheck)
{
...
}
}
I hope you have now an idea about Extension-Methods and how you could use them to solve your problem very easily ;)
I've made a test WindowForm application that takes a snapshot of the window by it's boundaries. But I can't seem to give GetBounds() what it needs. He wants graphics but it already contains the bounds, I just want his point and size:
private void CaptureBtn_Click(object sender, EventArgs e)
{
Region region = GetRegionByHWnd(GetForegroundWindow());
Rectangle rectangle = new Rectangle(
region.GetBounds().Location,
region.GetBounds().Size);
CaptureImage(rectangle.Location, Point.Empty, rectangle.Size);
}
Did that problem occur to anyone before, or knows how to fix it?
For getBound some windows for you can
use ClientRectangle property , This
property returns bound form and this
dll (ScreenCaptureLib.dll) helps you
for getImage in the desktop.
http://www.codeproject.com/script/Articles/ViewDownloads.aspx?aid=19415
First, Region implements IDisposable, so you should be wrapping it in a using statement or calling Dispose when you done with it.
Other than that we will need to see your code for GetRegionByHWnd and CaptureImage. Do you really even need that code at all? Why won't someWindow.Bounds work for you?
Perhaps that helps: RECTANGLE
I don't know your methods like captureimage. But Perhaps you need the relative position to the control?
Point to Client / Point to Screen
If not, please tell the data you expect to get and what you receive.
Also take a lookt to the RECT MSDN type instead of the Rectangle type.
Some good articles are here:
http://www.codeguru.com/csharp/csharp/cs_graphics/screencaptures/article.php/c6139
http://cid-32fd2eb6551ddb56.office.live.com/self.aspx/.Public/CaptureWindow.rar?sa=170500830
http://winapi.freetechsecrets.com/win32/WIN32Capturing_an_Image.htm
Regards
I am animating two RenderTransforms of elements in a programmatic way in C# by doing the following:
Storyboard.SetTargetProperty(shiftAnimation, new PropertyPath("RenderTransform.X"));
The problem is, now where I use two transforms, I needed to add a TransformGroup. But I cannot figure out how the path is accessed with it.
TransformGroup.RenderTransform.X or anything similar does not work.
One way would be:
new PropertyPath("RenderTransform.Children[0].X");
and
new PropertyPath("RenderTransform.Children[1].ScaleX");
Though this is kinda static with the hard coded index