I'm trying to reuse a class that I implemented a long time ago in Windows Forms in a new WPF project. This class overrides the OnRender method that uses an object Graphics that draws various objects on the form. Now that I'm using my class in WPF, I have substituted the Graphics object with the DrawingContext object, but some methods of the Graphics class aren't implemented. In particular the methods FillPie, FillEllipse, DrawBeziers are the ones that I need.
Does a class exist that implements these methods? How can I implement these methods?
This is a simple example of how something like this could work in practice.
<Window x:Class="WpfApplication4.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow"
Width="500"
Height="500">
<StackPanel>
<Slider Name="PositionSlider"
Margin="10"
Maximum="50"
Minimum="1"
Value="10" />
<Canvas Width="300" Height="300">
<Path Fill="Gold"
Stroke="Black"
StrokeThickness="1">
<Path.Data>
<EllipseGeometry Center="150,150"
RadiusX="{Binding ElementName=PositionSlider,
Path=Value}"
RadiusY="{Binding ElementName=PositionSlider,
Path=Value}" />
</Path.Data>
</Path>
</Canvas>
</StackPanel>
</Window>
The above WPF shows a simple slider and a circle. The radius of the circle is databound to the value of the slider. As you move the slider the circle because bigger or smaller. In your old world when the slider ( ie. your zoom state changed) you will get a repaint in which you will draw a bigger smaller circle. But in the WPF world this is taken care of by dependency properties and databinding.
The above example is very elementary , you can do more complicated things by doing multibinding bindings with value converters and objects implementing dependency properties and INotifyPropertyChanged all of which is too much to explain in one answer.
Related
I am developing a simple graph viewer. I got it rendered nicely using simple Ellipses and Lines on a canvas.
Now, I want to have some dots navigating the graph. You could imagine it as cars traversing the streets of a city.
To have something that could run the animation logic at a certain rate I setup a DispatcherTimer and did all the animation logic there. It works, but it is really choppy. To be true, I am not surprised. I understand that this way of rendering onto a canvas and animate using the event system is not something you could expect to run as smooth as a 2D game engine for example.
So, my question is, being a newbie to WPF, is there something I could do to improve the performance/accuracy? I have seen there exist a Skia package available for c# (SkiaSharp) that I think could improve the rendering performance but what about running the logic at an "accurate" (no realtime os here) frame rate?
Performance was not one of WPFs primary design goals. If you need high-speed custom animations that can't be implemented with the WPF animation system then you're better off deriving from FrameworkElement directly and overriding OnRender:
public class CustomControl : FrameworkElement
{
public CustomControl()
{
// leave this line in to force a render of the control for each frame
CompositionTarget.Rendering += (s, e) => InvalidateVisual();
}
protected override void OnRender(DrawingContext drawingContext)
{
var angle = Math.PI * DateTime.Now.Ticks / 10000000;
var xoffset = 100 + (int)(50 * Math.Sin(angle));
var yoffset = 100 + (int)(50 * Math.Cos(angle));
drawingContext.DrawEllipse(Brushes.Red, null, new Point(xoffset, yoffset), 50, 50);
}
}
It is necessary to use Animatios to display data in the required manner. WPF Animations are meant to be used for smooth rendering and demonstrate better performance when dispatcher.
In the simplest case you will need to start a Double animation to update a single property of your view model. Then recalculate properties (size, positions, etc) of different objects based on this value.
Another possible solution is to define a custom animation class. This solution is demonstrated in WPF Tutorial - Part 2 : Writing a custom animation class. This solution provides more flexibility, but generally you will get a similar value (you can consider it to be "time ellapsed") that can be used to calculate objects settings.
There's a special mechanism in WPF just for your case - Animations. I'd recommend you to read this article: How to: Animate an Object Along a Path.
Below is an example of traversing a simple graph. Also note that in the example I use one predefined path (for simplicity). In practice you can create such paths and animations dynamically from code-behind.
<Canvas>
<Canvas.Resources>
<PathGeometry x:Key="AnimationPath" Figures="M 100,100 L 200,150 L 230,250 L 150,300 L 100,100"/>
</Canvas.Resources>
<Line X1="115" X2="215" Y1="115" Y2="165" Stroke="Green" StrokeThickness="5"/>
<Line X1="215" X2="245" Y1="165" Y2="265" Stroke="Green" StrokeThickness="5"/>
<Line X1="245" X2="165" Y1="265" Y2="315" Stroke="Green" StrokeThickness="5"/>
<Line X1="165" X2="115" Y1="315" Y2="115" Stroke="Green" StrokeThickness="5"/>
<Ellipse Width="30" Height="30" Canvas.Left="100" Canvas.Top="100" Fill="Red"/>
<Ellipse Width="30" Height="30" Canvas.Left="200" Canvas.Top="150" Fill="Red"/>
<Ellipse Width="30" Height="30" Canvas.Left="230" Canvas.Top="250" Fill="Red"/>
<Ellipse Width="30" Height="30" Canvas.Left="150" Canvas.Top="300" Fill="Red"/>
<Ellipse x:Name="traveller" Width="30" Height="30" Fill="Blue">
<Ellipse.RenderTransform>
<TranslateTransform x:Name="transform"/>
</Ellipse.RenderTransform>
</Ellipse>
<Canvas.Triggers>
<EventTrigger RoutedEvent="Loaded">
<BeginStoryboard>
<Storyboard RepeatBehavior="Forever">
<DoubleAnimationUsingPath PathGeometry="{StaticResource AnimationPath}"
Storyboard.TargetName="transform"
Storyboard.TargetProperty="X"
Source="X"
Duration="0:0:10"/>
<DoubleAnimationUsingPath PathGeometry="{StaticResource AnimationPath}"
Storyboard.TargetName="transform"
Storyboard.TargetProperty="Y"
Source="Y"
Duration="0:0:10"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Canvas.Triggers>
</Canvas>
When I run this code in WPF it gives me 1/4 of a circle. When removing the ClipToBounds tag, i get my whole circle.
1. Why is it clipping before rendering?
2. How to i fix that, while keeping clipping?
<Canvas ClipToBounds="True">
<Ellipse Canvas.Left="-10"
Canvas.Top="-10"
Width="20"
Height="20"
Fill="LightSeaGreen"/>
<Canvas.RenderTransform>
<TransformGroup>
<ScaleTransform ScaleX="4.8"
ScaleY="4.8"
CenterX="0"
CenterY="0"/>
<TranslateTransform X="48"
Y="48"/>
</TransformGroup>
</Canvas.RenderTransform>
</Canvas>
Why is it clipping before rendering?
It's not.
As you can see from here:
Your Ellipse is rendered perfectly. The ClipToBounds="True" is what ruins is as you can see from your Canvas:
How to i fix that, while keeping clipping?
This is quite a broad question. Your problem comes from the fact you are putting your Ellipse outside the Canvas (Canvas.Left="-10" Canvas.Top="-10") and then you clip it. Explain what is your goal and I can try to help you out.
This behaviour is by design. The authors of the textbook (Computer Graphics: Principles and Practice - Third Edition) have confusingly introduced ClipToBounds in a way that makes it seem like it is a part of the examples that follow in the book. In fact, they are not using ClipToBounds="True". You can verify that by downloading their lab package from http://sklardevelopment.com/graftext/ChapWPF2D/ .
To illustrate, here is the actual source code for one of their examples:
<Canvas
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Name="ClockCanvas" >
<Ellipse
Width="20.0" Height="20.0"
Canvas.Left="-10.0" Canvas.Top="-10.0"
Fill="lightgray"/>
</Canvas>
Note that there is no ClipToBounds="True" on the canvas.
I'm new to WPF and would like to know what to use to get a shape like the image below:
I also wonder if it is possible that this design may follow the form's dimensions, ie, if the form is resized, the design is too.
In advance, thank you!
Windows 8.1, Visual Studio 2013, C#, WPF Application
To draw the shape you can use a Path. A path can draw any type of shape specified by its Data property. The Fill and Stroke defines the colors of the shape.
To "follow the form's dimensions" you can set the Stretch property to "Uniform". The shape will then scale accordingly to the window.
Here is an example:
<Window x:Class="WpfApplication57.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="600" Width="800">
<Grid>
<Path Data="F1 M 0 0 800 0 800 30 200 30 150 60 0 60 0 0"
Fill="Yellow"
Stretch="Uniform"
Stroke="Black"
StrokeThickness="4"
VerticalAlignment="Top" />
</Grid>
</Window>
Happy coding :-)
Best regards,
Casper Korshøj
You can use a Path to get that shape and you can even use Blend to draw any shape, if you want just shapes.
<Path Data="M200.125,197.375 L200.125,190.25 277.375,190.25 277.375,191.5 277.375,192.375 220,192.375 217.25,195.125 214.5625,197.8125 z" HorizontalAlignment="Left" Height="8.562" Margin="200.125,190.25,0,0" Stretch="Fill" Stroke="Black" VerticalAlignment="Top" Width="78.25" Fill="#FFF0F00C"/>
That should give you the Shape you wanted and in the comment mention above use that link
http://msdn.microsoft.com/en-us/library/ms747393%28v=vs.110%29.aspx
I would like to paint Images to the XAML page in Windows Store App.
The main goal is that:
Adding Images (e.g. flower leaf) to a circle on the center like that:
I have a simple solution to that, but its is very redundant.
<Image Height="200" Width="200" Source="{Binding ActualImage.NormalUri}">
<Image.RenderTransform>
<RotateTransform Angle="12"></RotateTransform>
</Image.RenderTransform>
</Image>
... // And 28 other like these
<Image Height="200" Width="200" Source="{Binding ActualImage.NormalUri}">
<Image.RenderTransform>
<RotateTransform Angle="360"></RotateTransform>
</Image.RenderTransform>
</Image>
How can I do that with a binding of a Image collection? What XAML control should I use?
Use a custom class which inherits from ItemsControl. You can then override the necessary functions, such as ones to determine the angle to rotate between each item. I think it's likely you'll want to use this PrepareContainerForItemOverride for this.
One thing to note that you will have to do is to define a new ItemsPanel. The default is a StackPanel, which will not work. You'll likely want to use something like a Canvas, which allows you to explicitly position items in it.
The Solution of Nate Diamond is much more nicer and better, but I solved it from code-behind for earn the easier way:
foreach (Petal petalObject in MainPageViewModel.Petals)
{
var petalImage = new Image
{
Height = petalObject.Height,
Width = petalObject.Width,
RenderTransform = new RotateTransform() {Angle = petalObject.Angle},
Source = new BitmapImage(new Uri(petalObject.NormalUri)),
};
PetalsGrid.Children.Add(petalImage);
}
I'm trying to implement zoom-functionality in a RadSlideView ItemTemplate. I'm doing this by using a ViewportControl with a Canvas and then applying a RenderTransform (ScaleTransform) to a StackPanel in the Canvas. Similar to the SDK-sample found here.
The problem I have is that the ScaleTransform seems to be affecting the swipe-gesture used to change item in the SlideView/Panorama/Pivot control. E.g. if the ScaleTransform is set to 0.1 it seems like I only need to swipe 1/10th of the length to change item compared to using a ScaleTransform of 1.0.
I found that if I set IsHitTestVisible to false on the ItemTemplate the swiping works like I want. But this is not a solution since I sometimes need to be able to pan the content vertically while still being able to change item by swiping horizontally.
So my question is how can I solve this?
For reference the XAML looks like this:
<Controls:RadSlideView Name="SlideView" ItemsSource="{Binding Pages}" IsLoopingEnabled="False" SelectionChanged="RadSlideView_SelectionChanged" CacheMode="BitmapCache" ManipulationStarted="SlideView_ManipulationStarted" ManipulationCompleted="SlideView_ManipulationCompleted" ManipulationDelta="SlideView_ManipulationDelta">
<Controls:RadSlideView.ItemTemplate>
<DataTemplate>
<ViewportControl x:Name="SlideViewViewport" ViewportChanged="SlideViewViewport_ViewportChanged" Loaded="SlideViewViewport_Loaded">
<Canvas>
<StackPanel>
<Image Source="{Binding Image}" Stretch="Fill" Width="{Binding ElementName=SlideView, Path=DataContext.PageWidth}" Height="{Binding ElementName=SlideView, Path=DataContext.PageHeight}" CacheMode="BitmapCache"/>
<StackPanel.RenderTransform>
<ScaleTransform x:Name="xform"/>
</StackPanel.RenderTransform>
</StackPanel>
</Canvas>
</ViewportControl>
</DataTemplate>
</Controls:RadSlideView.ItemTemplate>
I have also looked at Teleriks RadPanAndZoom-control to avoid implementing my own zoom-functionality, but since I sometimes need to place two pictures side by side and zoom them as if they were one I don't think I can use it.
The problem is that ScaleTransformation scales your picture, but doesn't change it's height and width. Only if Height And Width are overflowing scrollviewer you can scroll the content