Is it possible to rotate FrameworkElement.Cursor?
My application allows user to rotate objects around their center. Once rotated, default resize cursors appear awkward on top of tilted borders.
My first thought was to apply RotateTransform to the Cursor property, but it looks like we can't do that in XAML. Next I tried inheriting from Cursor class, but it looks like MS guys have sealed it.
Another way is to set default cursor to None and use my own image (with transform) and set its position upon MouseMove. I'm not willing to go down that path if there are easier alternatives. Anyone with a good suggestion?
I'm looking for a WPF-only solution if at all possible.
Finally managed it within the bounds of WPF, without using WinForms or PInvokes. Instead of creating custom cursors (*.cur) on the fly or converting Visuals into cursors, I used MouseMove event of the parent control along with a WPF element (Path) as my cursor. Here's the way just in case anyone is interesed:
Set the Cursor of your resize thumb (or whatever you're using as the border of your shape) to None, so that WPF doesn't display default arrow.
Create your own cursor. Could be any FrameworkElement, but I have used Path for its easy manipulation to create any shape you want. Note that most of the properties I have set below are important.
<Path x:Name="PART_EW"
Data="M0,20 L25,0 25,15 75,15 75,0 100,20 75,40 75,25 25,25 25,40z"
Fill="White" Stroke="Black" StrokeThickness="1" Visibility="Collapsed"
Width="50" Height="20" Opacity=".7" Stretch="Fill" Panel.ZIndex="100001"
HorizontalAlignment="Left" VerticalAlignment="Top" IsHitTestVisible="False"
/>
Add the following code in your resize thumb:
protected override void OnMouseEnter(MouseEventArgs e)
{
base.OnMouseEnter(e);
var Pos = e.GetPosition(this);
PART_EW.Margin = new Thickness(
Pos.X - PART_EW.Width / 2,
Pos.Y - PART_EW.Height / 2,
-PART_EW.Width,
-PART_EW.Height);
PART_EW.Visibility = Visibility.Visible;
}
protected override void OnMouseLeave(MouseEventArgs e)
{
base.OnMouseLeave(e);
PART_EW.Visibility = Visibility.Collapsed;
}
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
var Pos = e.GetPosition(designerItem);
PART_EW.Margin = new Thickness(
Pos.X - PART_EW.Width / 2,
Pos.Y - PART_EW.Height / 2,
-PART_EW.Width,
-PART_EW.Height);
}
Note that I've not set RotateTransform of my Path anywhere in the code, since it is already part of the resize thumb and therefore acquires the angle of the parent control automatically.
Hope this helps people down the road.
Related
This question already has answers here:
Make Object Follow Mouse On MouseDown and "Stick" On MouseUp
(3 answers)
Closed 2 years ago.
I am making a Space Invaders clone where you control your ship with your mouse. I found online solutions for dragging and dropping a control, which worked but I couldn't manage to translate it to just moving the control with only your mouse.
private void Window_MouseMove(object sender, MouseEventArgs e)
{
Mothership.Body.Margin = new Thickness(Mouse.GetPosition(gridSpace).X, Mouse.GetPosition(gridSpace).Y, 0, 0);
}
To clarify, I want the ship's(Image control) location to be exactly where to cursor is, without an offset. I don't even know where this offset comes from or how to get it.
Here is a screenshot of my mouse moving, you can see the image itself it very far away from the actual cursor. How do I fix this? If you need more information I will try to provide it. image
I guess that yours image control inside the grid have HorizontalAlignment="Stretch" and VerticalAlignment="Stretch" (The default values), that cause the unwanted offset. So as Clemens suggested you must use a canvas. otherwise if you can't change your parent control set the image HorizontalAlignment="Left" and VerticalAlignment="Top" and your code will be work. But as the Clemens and Ostas suggested moving a object by margin is not a good way and if you must use grid you can use a TranslateTransform (also see this).
So just set RenderTransform property of image control and set it's X and Y properties in Window_MouseMove.
Sample xaml:
<Grid x:Name="gridSpace">
<Image x:Name="Body" Source="*.png" Width="100" Height="100" HorizontalAlignment="Left" VerticalAlignment="Top">
<Image.RenderTransform>
<TranslateTransform x:Name="mTransform"/>
</Image.RenderTransform>
</Image>
</Grid>
Sample code:
private void Window_MouseMove(object sender, MouseEventArgs e)
{
//Body.Margin = new Thickness(Mouse.GetPosition(gridSpace).X, Mouse.GetPosition(gridSpace).Y, 0, 0);
Point pos = e.GetPosition(gridSpace);
mTransform.X = pos.X;
mTransform.Y = pos.Y;
}
I have a circle shaped button and I like to add a pair of Angel Wings to it, but I can't seem to draw it correctly.
I'd like to draw a border with an angel wing on the left and a circle in the middle and an angel wing on the right, in one custom shape..
the Custom shape is the border of the button.
The button is a resizable button, it means it can change Height & Width with the Window, and still maintain the same position.
this button is just an example of what I have made so far.
I've searched for a solution on Google and this site but can't find anything that can help me..
You can use software like Inkscape and with that you can open most image format and from images you can use the trace bitmap option to trace a path. This will create a <path> object in a <canvas>. It can then be used as a background.
things to note :
The quality highly depends on the image source quality and precision. So if your image have lots of color and gradient, huge line thickness and uses anti aliasing you will get very bad result because of a huge amount of vector on the path result. if you use thinner lines and very little color with no gradient you can get amazing conversion result.
You can also hand trace over the image and use that instead of the automatic option. You will get better result like that but it require more work.
With this path created you can create a resource in your application dictionary and use it for icon or background on anything you want. It is scalable in any direction without quality lost as it is now a vector "image".
try looking for image/Photoshop/any other image or drawing method to Xaml converter
you can create an image and convert it to xaml
see http://vectormagic.com
taken form here
Can you try this if it helps
Create Custom control , something like this
<Grid ClipToBounds="True" HorizontalAlignment="Center" VerticalAlignment="Center">
<ed:Arc x:Name="KnobA" ArcThickness="1" ArcThicknessUnit="Percent" EndAngle="360" Height="120" Stretch="None"
Stroke="Black"
StartAngle="0" Width="120" RenderTransformOrigin="0.5,0.5" StrokeThickness="0">
<ed:Arc.Fill>
<ImageBrush ImageSource="/Gazelle;component/Resources/Images/image.png" Stretch="UniformToFill" />
</ed:Arc.Fill>
</ed:Arc>
</Grid>
Use it in your other XAML
<controls:BoundRotaryButton Grid.Column="1" Margin="0,0,35,30"
VerticalAlignment="Bottom" HorizontalAlignment="Right" Opacity="0.2"
Grid.ColumnSpan="2"
BoundRotaryButtonState="{Binding myvm.RotaryPressed, Mode=OneWayToSource,
NotifyOnTargetUpdated=True, UpdateSourceTrigger=PropertyChanged}" />
Create the dependency in code behind of the button control and the handler for mouse clicks.
public static readonly DependencyProperty KnobAPushButtonStateProperty = DependencyProperty.Register("KnobAPushButtonState", typeof (bool), typeof (KnobA));
public bool KnobAPushButtonState
{
get { return (bool) GetValue(KnobAPushButtonStateProperty); }
set { SetValue(KnobAPushButtonStateProperty, value); }
}
private void KnobA_MouseDown(object sender, MouseEventArgs e)
{
Mouse.Capture(this);
KnobAPushButtonState = true;
private void KnobA_MouseUp(object sender, MouseEventArgs e)
{
Mouse.Capture(null);
KnobAPushButtonState = false;
}
In your viewmodel you will know when this dependency property changes the button is pressed or released and invoke the command you need.
I have something like a circular button, which you can rotate and press and so on. But you can use any shape you like with Blend.
I'm trying to draw a tree on a Canvas with a coordinate system. I'm not yet that far that I could draw the tree itself, at the moment I'm trying to get the coordinate system a little bit more flexible, so it gets redrawn when I resize the window and the canvas within it.(That means expanding/reducing the axes of it, not the scale.)
I have already read some topics about canvas and the deleting of its children elements and I found a solution. The problem is that this solution does not work out that fine for me, because it keeps deleting only one line of the coordinate system on every resize event.
All lines that are used to draw the coordinate system got the UID starting with CoordinateSystemIn.
Currently my code for this is:
Canvas = Anzeige;
private void Window_SizeChanged(object sender, SizeChangedEventArgs e)
{
if (MainWindowLoaded)
{
foreach (UIElement Element in Anzeige.Children)
{
if (Element.Uid.StartsWith("CoordinateSystemIn"))
{
Anzeige.Children.Remove(Element);
}
}
Stift.DrawCoordinateSystem(Anzeige.ActualWidth, Anzeige.ActualHeight, 25);
}
}
Is there any other solution which redraws the whole coordinate system at once and not only line for line at each resize event?
Put you Canvas in a ViewBox - the latter will automatically resize the canvas to the appropriate size. Your code could look like the following:
<Window x:Class="WpfAccessControlFromResources.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Viewbox>
<Canvas Width="80"
Height="80">
<Path Stroke="DarkGray">
<Path.Data>
<GeometryGroup>
<EllipseGeometry RadiusX="40" RadiusY="40" Center="40, 40" />
<LineGeometry StartPoint="15, 40" EndPoint="65, 40" />
<LineGeometry StartPoint="40, 15" EndPoint="40, 65" />
</GeometryGroup>
</Path.Data>
</Path>
</Canvas>
</Viewbox>
</Window>
This way you do not have to mess with the coordinates yourself.
Update after Marv's comment:
Actually, I think you should encapsulate your coordinate system in a separate class. This class would derive from System.Windows.Controls.Panel and would override the ArrangeOverride and MeasureOverride methods to position the child elements. Within these former method, you could also add / remove unnecessary PathGeometries that show the coordinate systems. The code could somewhat look like this:
public class CoordinateSystem : Panel
{
// This element will display the actual coordinate system
// It must be added to the collection of InternalChildren
private Path _coordinateSystemPath;
// This method is used to determine how much space the children want to have
protected override Size MeasureOverride(Size availableSize)
{
Size infiniteSize = new Size(double.PositiveInfinity, double.PositiveInfinity);
Size desiredSize = new Size();
// Measure how many space the items you want to display need
foreach (var child in InternalChildren)
{
child.Measure(infiniteSize);
// Check at which position this child wants to be and
// calculate the desired size of the coordinate system
}
return desiredSize;
}
// this method will arrange the path for the coordinate system as well as
// all the children that will be displayed
protected override Size ArrangeOverride(Size finalSize)
{
foreach (var child in InternalChildren)
{
if (child == _coordinateSystemPath)
{
// Update the PathGeometry of this path according to provided finalSize here
continue;
}
child.Arrange(child.DesiredSize);
}
}
}
Please note that this is only a sketch and not the whole class. In this case you could also integrate zoom factor and translation of the point of origin of the coordinate system if you'd want to.
Anyway, while this solution is maybe the cleanest regarding Separation of Concerns, it also requires a good understanding about the WPF layout system and quite some work and testing. You can read more about this topic here: http://msdn.microsoft.com/en-us/library/ms754152(v=vs.110).aspx
Perhaps what you might like to use is the Viewbox class. This will do all of your resizing for you automatically (without a single line of procedural code). Take this example:
<Viewbox>
<Path StrokeThickness="1" Stroke="Black" Data="M0,0 L25,25 M0,25 L25,0"/>
</Viewbox>
Here we have a simple Path 'X' to represent your Canvas content. It's quite small initially, or without the Viewbox, just 25 X 25 pixels. However, once inside a Viewbox, it can automatically fill its provided space. You might also find that the Stretch and StretchDirection properties are of use to you.
Just add your Canvas into a ViewBox and experiment with it to see if it will meet your needs.
I think I have some difficulty to think through. I have a canvas surrounded by a border and I want to draw a rectangle around the mouse. My problem is, the mouse can move and what I want is like a datagrid. In example, in a datagrid, all cell are already visible, (I know I can make them invisible, that's not the point), but all the cell are static, what I'm trying to do is when I mouseover the canvas, the rectangle be drawned at a static position, sorry if I'm unclear. The picture under should be better.
So as you can see in the first picture my mouse is there and I get some value, what I would like is whenever I'm in the place where I get this value I draw a rectangle over it. As you can see in the second picture when I move my mouse a little bit, I'm still in the area where I have the same value so the rectangle would still be there, and on the third picture I've moved a little further and you see the x coordinate changes and the value also do change then the rectangle would have to be drawned again over the other part
My problem is I don't know how to draw a rectangle of 45 by 40 relative to the mouse but relative to the canvas because if the mouse it's at x : 0 Y : 0 then the rectangle would be 45 by 40 but if my mouse is at X : 10 Y : 10 then the rectangle would have to be, if the mouse is the start point of 10 up the mouse down of 30 and then 10 by left and 35 to the right, I've tried to be as clear as possible, if it's still confuse tell me I will do my best.
You can achieve your requirements easily using the TranslateTransform Class and a couple of double properties:
<Rectangle Name="Rectangle" Stroke="Black" StrokeThickness="1" Fill="{x:Null}">
<Rectangle.RenderTransform>
<TranslateTransform X="{Binding XTransform}" Y="{Binding YTransform}" />
</Rectangle.RenderTransform>
</Rectangle>
You can handle the UIElement.PreviewMouseMove Event on the Canvas element and update your two transform properties from the handler... try something like this:
private void CanvasPreviewMouseMove(object sender, MouseEventArgs e)
{
XTransform = e.GetPosition(Rectangle).X;
YTransform = e.GetPosition(Rectangle).Y;
}
The MouseMove event fires whenever the mouse moves over an element. If I understand your question, you want the know where the mouse is as it moves over the Canvas. So add an event handler to the Canvas. Be sure an name the canvas, you will need the name to refer to is in the mouse event handler.
XAML
<Border Margin='20'
BorderBrush='Orange'
BorderThickness='2'>
<Canvas x:Name='DrawingCanvas'
MouseMove='DrawingCanvas_MouseMove'
Background='Transparent'>
<Rectangle x:Name='MouseRectangle'
Width='30'
Height='30'
Stroke='Purple'
StrokeThickness='2' />
</Canvas>
</Border>
The MouseMove event provides the MouseEventArgs argument. Use it to get the mouse location relative to any element in the UI. In your case, that would be the canvas.
Code
Centers the Rectangle over the mouse tip
private void DrawingCanvas_MouseMove(object sender, MouseEventArgs e) {
Canvas.SetTop(MouseRectangle,
e.GetPosition(DrawingCanvas).Y - MouseRectangle.Height/2);
Canvas.SetLeft(MouseRectangle,
e.GetPosition(DrawingCanvas).X - MouseRectangle.Width /2);
}
I am trying to write a small interactive game-like application, where I need to have a Draw method that's gonna draw on screen, but can't figure out how to structure the method for WPF.
If this was Winforms, I could use:
public void Draw (Graphics g)
{
}
But for a WPF Window, what should I have on it in the xaml (currently only have a Grid), and what should this Draw method receive as an argument?
First I want to do it like this to get it working, then I can think about how to make it more WPF, etc. But now I am more interested in getting this to work.
Typically, you "draw" in WPF in a completely different manner.
In Windows Forms/GDI, the graphics API is an immediate mode graphics API. Each time the window is refreshed/invalidated, you explicitly draw the contents using Graphics.
In WPF, however, things work differently. You rarely ever directly draw - instead, it's a retained mode graphics API. You tell WPF where you want the objects, and it takes care of the drawing for you.
The best way to think of it is, in Windows Forms, you'd say "Draw a line from X1 to Y1. Then draw a line from X2 to Y2. Then ...". And you repeat this every time you need to "redraw" since the screen is invalidated.
In WPF, instead, you say "I want a line from X1 to Y1. I want a line from X2 to Y2." WPF then decides when and how to draw it for you.
This is done by placing the shapes on a Canvas, and then letting WPF do all of the hard work.
When there are just too many objects to be drawn very quickly (huge Visual Tree) another option would be to use a WriteableBitmap. Just use the Pixels property to set the pixels and/or use the Render method to draw UIElements.
I preffer to use OnRender method like in this example:
protected override void OnRender(DrawingContext drawingContext)
{
base.OnRender(drawingContext);
drawingContext.DrawRectangle(null, new Pen(Brushes.Black, 2), new Rect(0, 0, ActualWidth, Height));
}
To Implement a Draw loop type behavior in WPF you can use the CompositionTarget.Rendering event. This is raised once per frame when the WPF drawing system is painting frames.
As others have pointed out this is not very WPF friendly but it will work and can be used to get more immediate drawing behavior out of a WPF app.
In most cases you would use a single root canvas and update say the Canvas position of an element on the CompositionTarget.Rendering event.
For example to make a ellipse fly all over the screen do this:
In your XAML (For a Window that is 640 by 480 in size):
<Canvas x:Name="theCanvas">
<Ellipse x:Name="theEllipse" Height="10" Width="10" Fill="Black" />
</Canvas>
In your Code behind for the Window that the above XAML is in (Make sure to add a reference to System.Windows.Media in order to see the CompsitionTarget object :
public static Random rand = new Random();
public View()
{
InitializeComponent();
CompositionTarget.Rendering += CompositionTarget_Rendering;
}
void CompositionTarget_Rendering(object sender, System.EventArgs e)
{
double newLeft = rand.Next(0, 640);
double newTop = rand.Next(0, 480);
theEllipse.SetValue(Canvas.LeftProperty,newLeft);
theEllipse.SetValue(Canvas.TopProperty, newTop);
}
Yow should add a Canvas (or change the Grid for a Canvas) and then draw over it. Here is Microsoft tut on drawing over a canvas
Also, I don't know how related is this other question to yours, but you might want to check it out.