How to make strokes disappear on an InkCanvas - wpf

I am completely new to this forum, and still a beginner on WPF.
I am working on a project that requires the strokes on an inkcanvas to be animated. One of the animations required is "disappearing". I want to make the selected strokes gradually disappear with the click of a button, but appear at the end of the animation.
Since there is no opacity property for stroke, I tried using the ColorAnimation class along with storyboards. I have failed to make this code work, as I cannot target the strokes either using themselves or using their names, since they don't have any.
Right now I am thinking of implementing this system by gradually changing the color of the strokes to the color of the background, and at the end, resetting it back to its initial value. This is a costly loop, but I have no other ideas.
I would appreciate it if there are any other solutions you might share with me.
Thanks in advance.
Edit: I have not answered the comments, as I was dealing with other parts of the same project.
I have tried using the Alpha values that are stored in the DrawingAttributes, but I cannot change the value as it is not a variable. The same goes with RGB values. I have no idea on how to make the strokes disappear in a loop. I have already implemented most of the project, so I just need something to slowly make them disappear. Below you can find an example where I change the stroke itself to animate it.
private int dropOffset = 1;
private void DropAnimation()
{
m = new Matrix();
m.Translate(0, dropOffset);
animStrokes.Transform(m, false);
YChange += dropOffset;
dropOffset += 2;
}
And in another class, I have
public void AnimateStrokes(Dispatcher canvasDispatch)
{
Stopwatch initial = Stopwatch.StartNew();
while (initial.ElapsedMilliseconds < 2000)
{
foreach (Animation ai in AnimationList)
{
ai.animateSelected();
}
canvasDispatch.Invoke(new Action(() => { }), DispatcherPriority.Render);
Thread.Sleep(50);
}
foreach (Animation a in AnimationList)
{
a.undoAnimation();
}
canvasDispatch.Invoke(new Action(() => { }), DispatcherPriority.Render);
}
I know that it's not healthy to pass dispatcher like this, but it suffices for now.
Thanks again in advance.

InkCanvas1.DefaultDrawingAttributes.Color = Color.FromArgb(100, 0, 255, 255);
Might be a bit late but help for others none the less! The 100 is the alpha value which basically acts like an opacity value!! Mess with that and you will be able to change how transparent your strokes are :)

Related

WPF Animation (changing multiple properties of single element at the same time)

I need to animate multiple properties of one ui element at the same time.
For example, decreasing width and height of windows synchronously.
Any idea?
DoubleAnimation widthAnimation = new DoubleAnimation
{
To = 0,
Duration = TimeSpan.FromSeconds(5)
};
DoubleAnimation heightAnimation = new DoubleAnimation
{
To = 0,
Duration = TimeSpan.FromSeconds(5)
};
Storyboard.SetTargetProperty(widthAnimation, new PropertyPath(Window.WidthProperty));
Storyboard.SetTarget(widthAnimation, this);
Storyboard.SetTargetProperty(heightAnimation, new PropertyPath(Window.HeightProperty));
Storyboard.SetTarget(heightAnimation, this);
Storyboard s = new Storyboard();
s.Completed += FadeOut_Completed;
s.Children.Add(widthAnimation);
s.Children.Add(heightAnimation);
this.BeginStoryboard(s, HandoffBehavior.SnapshotAndReplace, true);
It will do animations step by step; height will change after width changes are complete! :|
After looking at your code I understand that you are trying to animate the width and height of Window simultenousely
But I would regret to tell you that since window is not an actual wpf component but a platform component. however content of window is completely controllable as expected via your code, but window is not. any such changes are routed through Pinvoke. and the issue you are facing is a known issue and the work around are bit complex
one solution is here, this uses pinvoke to animate the window's height and width
Animating a WPF window width and height
here is a bug for similar issue created at microsoft, result is (Closed, as Won't Fix)
https://connect.microsoft.com/VisualStudio/feedback/details/715415/window-width-height-animation-in-wpf-got-broken-on-net-framework-4-0
Extra
below is a sample which is not actually solving your problem but will help you to reduce the number of lines you need to perform such animations for other elements. It is a rewrite of your code in less lines
DoubleAnimation anim = new DoubleAnimation
{
To = 0,
Duration = TimeSpan.FromSeconds(5)
};
border.BeginAnimation(Border.HeightProperty, anim);
border.BeginAnimation(Border.WidthProperty, anim);
try this code with any element except window, I used a border with some color filled
apologies for overlooking the Window in your code at first sight

Printing in Silverlight cuts off some portion

How do I provide a print option on my silverlight application. I am able to print using the PrintDocument class but it cuts of the edges.
Is there a way to shrink the view to fit in a print paper. Thanks for any help.
I am using the below code:
PrintDocument document = new PrintDocument();
document.PrintPage += (s, args) =>
{
args.PageVisual = this.LayoutRoot;
};
document.Print("Silverlight Print Application Demo");
There are two conceptually different tasks here:
Printing an element as rendered in the ui at some point and
Render an element exclusively for printing.
Since you're complaining about the cutting off of edges, I assume you want to do the latter. For that, I wouldn't add the current control's layout root, which I suspect you're doing. Add something that is not yet added to the visual tree, or remove it from its parent before printing.
If you have to remove it first for some reason (perhaps because the user needs to interact with it first), then store the element in a variable printedPage that you can re-add to the visual tree on the PrintDocument.EndPrint event.
For the main event handler use something like this:
void HandlePrintPage(object sender, PrintPageEventArgs e)
{
var panel = new Grid();
// get an element to print that is not attached to the visual tree
printedPage = GetDetachedPageToPrint();
panel.Children.Add(printedPage.ReportPage);
// we wrap it in a Viewbox to make it full-page
var viewbox = new Viewbox() { Child = panel };
// we need to have a measure pass
viewbox.Measure(e.PrintableArea);
e.PageVisual = viewbox;
// for more pages, we would need to be more careful
//with parent detachments
e.HasMorePages = false;
}
I had the same problem a couple of months ago and tried several solutions.
I`ve already answered this question in another post. Please check out this:
https://stackoverflow.com/a/20896774/1141477
It converts the framework element into images and fit it inside a rectangle, which is from the same size as the printableArea. It worked well to me.
Hope it helps!

How to smooth WPF animation?

I am struggling in smoothing WPF animation
Actually my animation code is as follows:
private void AnimateX ( FrameworkElement element, double XMoveStart, double XMoveEnd, int secondX)
{
SineEase eEase = new SineEase();
eEase.EasingMode = EasingMode.EaseInOut;
Storyboard sb = new Storyboard();
DoubleAnimation daX = new DoubleAnimation(XMoveStart, XMoveEnd, new Duration(new TimeSpan(0, 0, 0, secondX, 0)));
daX.EasingFunction = eEase;
Storyboard.SetTargetProperty(daX, new PropertyPath("(Canvas.Left)"));
sb.Children.Add(daX);
element.BeginStoryboard(sb);
}
The above code is a method to move an object horizontally with sine ease. When only one object is moving, it is OK. However, whenever two or more objects move together (call AnimateX method on another object when the previous animation has not yet completed), the animation starts to become jittery. By jittery I mean, the objects are kind of shaking during the course of animation.
I faced the same problem many times. I found out that depending on the objects you add to your canvas, WPF will often have to regenerate representations of these objects on every frame (which I believe might be your case, depending on the type of UI elements you are manipulating). You can solve the jitter issue by telling WPF to cache a representation of your canvas in a bitmap. This is done very simply as follows, in your Xaml definition of the canvas:
<Canvas ...Your canvas properties...>
<Canvas.CacheMode>
<BitmapCache />
</Canvas.CacheMode>
...Your objects...
</Canvas>`
This reduces the load on your WPF application, as it simply stores the representation of your objects as a bitmap image, and as a consequence your application does not have to redraw them on every frame. This solution only works if your animation is applied externally to the canvas, and that there is no on-going local animations applying to the individual objects drawn in your canvas. You'll want to create separates canvases with their own caching if other animations in your code move the two objects with respect to each other.
Note that some UI elements will not be eased by this strategy. However, I've seen this strategy work efficiently for many elements, including TextBoxes and the likes, as well as geometric shapes. In any case, it's always worth the try.
Secondly, if caching local representations does not suffice, then you might want to have a look at the performance of your code and see if any process could be responsible for blocking the UI momentarily. There is no uniform solution regarding this aspect and it depends on what else is putting strain on your application UI. Cleaning the code and using asynchronous processes where relevant could help.
Finally, if, after all these checks the overall demand on your application remains too high, you can somewhat remove some strain on the application by reducing its general frame rate, the default being 60. You can try 30 or 40 and see if this improves the jittering by including the following code in your initialization:
Timeline.DesiredFrameRateProperty.OverrideMetadata(typeof(Timeline), new FrameworkPropertyMetadata { DefaultValue = 40 });
Just a guess, but what happens if you directly animate the property, withoud using a Storyboard?
private void AnimateX(FrameworkElement element, double xMoveStart, double xMoveEnd, double durationSeconds)
{
DoubleAnimation animation = new DoubleAnimation
{
From = xMoveStart,
To = xMoveEnd,
Duration = TimeSpan.FromSeconds(durationSeconds),
EasingFunction = new SineEase { EasingMode = EasingMode.EaseInOut }
};
element.BeginAnimation(Canvas.LeftProperty, animation);
}

How can I change the way InkCanvas draws?

I've searched for examples for this, but the ones I've ran across seem to focus on simpler stuff like setting the InkCanvas DefaultDrawingAttributes such as Width, Height, Color etc. Doesn't seem like there's a lot of material for this.
For example, if I hold down the mouse button I can see it drawing lines. What if I want to draw ellipses instead of lines, or draw ellipses around sampled points between the start and end of the line?
I know I can get new points with the StrokeCollected event, but beyond that I have no idea where to go. This guy seemed like he got msdn's code working, but I couldn't do it. I only know how to build the interface using XAML, and there doesn't seem to be a sample either.
edit
Created a StrokeCollection class variable called thisIsNotNice, initialized in the constructor and did this:
private void InkCanvas_StrokeCollected(object sender, InkCanvasStrokeCollectedEventArgs e)
{
myInkCanvas.Strokes = thisIsNotNice;
foreach (StylusPoint p in e.Stroke.StylusPoints)
{
StylusPointCollection spc = new StylusPointCollection();
spc.Add(p);
Stroke s = new Stroke(spc);
s.DrawingAttributes.Height = 3;
s.DrawingAttributes.Width = 3;
thisIsNotNice.Add(s);
}
e.Handled = true;
}
But it doesn't work as it should. The ellipses are drawn, but the lines drawn by the mouse are still there. Also, for some reason, the first time it works as it should, drawing just the ellipses, but afterward it draws both the ellipses and the lines. But, if I do this instead:
private void InkCanvas_StrokeCollected(object sender, InkCanvasStrokeCollectedEventArgs e)
{
myInkCanvas.Strokes = new System.Windows.Ink.StrokeCollection();
e.Handled = true;
}
The lines aren't kept on the screen. So, I don't understand why they aren't being erased in the above code.
If I do this:
private void InkCanvas_StrokeCollected(object sender, InkCanvasStrokeCollectedEventArgs e)
{
foreach (Stroke s in myInkCanvas.Strokes)
System.Diagnostics.Trace.WriteLine(s);
e.Handled = true;
}
I can also see that the canvas contains the line strokes.
While erasing the strokes after they have been added to the collection is far from ideal, it at least does what I want. I could set up the line color to be the same of the background, but then I wouldn't be able to retrieve just the ellipses. I could copy them to a separate collection too, but that's just awful.
It sounds like you want to customize the way strokes appear on your inkCanvas. There are two separate things to consider here:
1) The way they look as the ink flows off the pen, before it is lifted (the DynamicRenderer, who runs on another thread to ensure that ink is always fast, is responsible for this. It sounds like you're happy with your solution to this already.
2) The way the eventual stroke sitting on the canvas looks. To customize this you might try subclassing Stroke, overriding:
protected override void DrawCore(DrawingContext drawingContext, DrawingAttributes drawingAttributes);
Each time you get a strokeCollected (and here's the same horrible thing you were worried about but there you go), you remove the incoming stroke from the canvas and replace it with your custom implementation, stealing the stroke data from the incoming one.
Your implementation of DrawCore would look something like (pseudocode):
foreach(sp in this.StylusPoints)
drawingContext.DrawEllipse(RADIUS, sp.X, sp.Y)
And so as not to get the lines that normally happen you would not call base.DrawCore(context,attributes) at any point.

In WPF, after DoubleAnimation UIElement property cannot be changed?

OK, I'm making a GUI for my MP3 player using WPF and I have a border that enlarges its width property for every second of the played track, thus making a "Progress Bar" for the currently played song. I named the border ProgressBarBorder. After the whole playlist is complete, I wanted to use a DoubleAnimation to fade out the border. Now, if I start the player again, the border reacts as it's supposed to (meaning the width starts from 0 and progresses to the end of the song), but the opacity property for some strange reason stays 0.0 (that is the value that DoubleAnimation sets). I have explicitly coded
ProgressBarBorder.Opacity = 1.0;
in the method that starts the playback. Nevertheless, it stays invisible. Now, if I don't use DoubleAnimation and just write
ProgressBarBorder.Opacity = 0.0;
when the playlist is complete, it does go back to 1.0 when I start the player again. This is the reason why I am positive that the animation is the one causing the problem. Also, isn't the property supposed to go back to it's original state after the animation is finished? If yes, my border should become visible automatically after the animation is complete.
Here's my partially pseudo-code:
if (TrackIsComplete)
{
DoubleAnimation Fading = new DoubleAnimation();
Fading.From = 1.0;
Fading.To = 0.0;
Fading.Duration = TimeSpan.FromSeconds(3);
ProgressBarBorder.BeginAnimation(Border.OpacityProperty, Fading);
}
and
private void PlayTrack()
{
ProgressBarBorder.Opacity = 1.0;
Play();
....
}
Could anyone help please? Thanks.
The animation is holding on to its target value. To free the dependency property of any animations, do a BeginAnimation with a null value:
private void PlayTrack()
{
ProgressBarBorder.BeginAnimation(Border.OpacityProperty, null);
ProgressBarBorder.Opacity = 1.0;
....
}
When an Animation ends, it continues holding the value. This is what is causing the behavior you noticed, where setting the property does not appear to update it. Here's some info on how to set a property after an animation has been applied to it.
Play around with the FillBevior of your animation timeline. This might help:
http://msdn.microsoft.com/en-us/library/system.windows.media.animation.fillbehavior.aspx

Resources