How can I change the way InkCanvas draws? - wpf

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.

Related

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!

Changing MediaElement source without Flicker

I have a simple video player that plays a series of videos using the WPF MediaElement. The videos together form one continuous film that move around a still image. At the end of each video the movement freezes on the final frame of the currently playing video. When I press a button the next video plays, which continues the movement around the still image. It's an application I'm going to use to give a speech. Effectively I've got a series of videos for which the last frame of each video is the same as the first frame of the next video.
I'm using a WPF MediaElement and changing the Source property when the user clicks on the mouse.
The problem that I have is that, when I change the Source property, the MediaElement becomes transparent while the next video is loaded. This means there is a flicker between videos. Is there any way of preventing this flicker? What other strategies could I use?
Here's some code:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.x_MediaElement.MouseLeftButtonDown += x_MediaElement_MouseLeftButtonDown;
this.MouseLeftButtonDown += MainWindow_MouseLeftButtonDown;
this.WindowStyle = WindowStyle.None;
this.WindowState = WindowState.Maximized;
}
void MainWindow_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
MoveNext();
}
private void MoveNext()
{
_sourceIndex++;
if (_sourceIndex >= _sources.Length)
_sourceIndex = 0;
Debug.WriteLine(string.Format("Playing {0}", _sources[_sourceIndex]));
this.x_MediaElement.Source = new Uri(_sources[_sourceIndex]);
this.x_MediaElement.Play();
}
private int _sourceIndex = -1;
private string[] _sources = new string[] {
//SOURCE GO HERE
};
void x_MediaElement_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
MoveNext();
e.Handled = true;
}
}
I am gonna be honest with you. MediaElement has more bugs than you can count with fingers. Starting from that the mediaElement blows up after playing 20 videos(no more MediaEnded event, it will crash or something like that). And ofcourse the performance. Its not synchronized with vertical sync. So the video might actually seem laggy.
I advise you to look into DirectShow technology(essentially what WPF is based on, but you can switch renderer which will avoid lag). COnsidering that you will not be developing any professional application, I guess MediaElement will be fine.
However, MediaElement is the simplest option, and if it works for you, then keep working with it. As for your problem, I think there are few possible solutions:
Have two MediaElements and switch between them. If one video ends, start another vid in another MediaElement, as long as you play first frame on second mediaElement, hide the first mediaElement, and vice versa. You can poll for position, and maybe MediaStarted event. This way the flicker will be almost impossible to notice.
If you want fluent video playing without ANY flicker at all, there is GMFPlay. You can check it out. Though it's not MediaElement. But it can play videos simultaneously without any flicker.
Take screenshot of the last frame(you can take screenshots with WPF) and show it as Image while MediaElement is secretly loading.

How to make strokes disappear on an InkCanvas

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 :)

WPF - Expand Window to the Left

I have a WPF window with expandable panel (via Expander). The panel is on the left side of the window, and when expanded the window grows to fit the content.
By default, windows are anchored to the top-left, so my window grows to the right. I'd like the window to grow to the left.
I tried to do the following in the Window.SizeChanged event:
private void onWindowSizeChanged(object sender, SizeChangedEventArgs e)
{
Left -= (e.NewSize.Width - e.PreviousSize.Width)
}
and it works, but the growth is jerky, and I'd like to find a smoother solution.
I managed to overcome this using a simple solution: Hide & Show.
Here's the code:
protected override void OnRenderSizeChanged(SizeChangeInfo sizeInfo)
{
if (!sizeInfo.WidthChanged)
{
base.OnRenderSizeChanged(sizeInfo);
return;
}
Hide();
base.OnRenderSizeChanged(sizeInfo);
Left -= (sizeInfo.NewSize.Width - sizeInfo.PreviousSize.Width);
Show();
}
I replaced the event handler for Window.SizeChanged with this override of FrameworkElement.OnRenderSizeChanged.
I haven't tried to make a Window grow to the left like what you're requesting, but if all else fails, I would consider templating a button to look like the expander button. Then instead of trying to make your Window grow to the left, make a new Window grow to the left of your primary Window using Transforms.
UPDATE
Well, the poor rendering performance could be video card related, layout (overly complex) related, or both. I've got an idea that might do the trick for you. Jeff Prosise blogged about a magnifying glass in Silverlight that uses a WriteableBitmap to achieve the desired effect. I thought, "why not use a WriteableBitmap to create a screenshot of your layout to the right of the Expander, and cover up the other elements with it?". I think that if you do this and hide the underlying elements (so they don't get adjusted), rendering performance will be much improved.
I got Jeff's code to work in WPF with little modification.
http://www.wintellect.com/CS/blogs/jprosise/archive/2009/10/29/more-fun-with-silverlight-3-s-writeablebitmap.aspx
Solution 1
Try to use Window property: SizeToContent="width" this will scale your window to the size of your content and you can scale your content using animation and easing, this will make scaling of the window nice and smooth.
Solution 2
You could create a window which is bigger than it's content and make your background transparent. You still have to add background to some element.
Here is an example of how it may look like:
You may put your expander in a grid (where the column size can change) and then set the ExpandDirection property of your expander to left ?

wpf: capturing mouse does not work

I am developing an kind of outlook calendar application where I need to make the appointment resizable from mouse.
My first try with a thumb did not work properly so I tried another way.
What I did is that:
1) on the botton of the appointmennt panel I added a rectangle to figure out the resize zone (the thumb). The appointment panel is put on a grid panel.
2) I intercept down event on the rectangle and send event to this code:
private Point startPoint;
private void OnResizeElementMouseDown(object sender, MouseButtonEventArgs e)
{
e.Handled = true;
this.MouseMove += new MouseEventHandler(ResizeEndElement_MouseMove);
this.MouseLeftButtonUp += new MouseButtonEventHandler(OnResizeElementMouseUp);
// some code to perform new height computation
Mouse.Capture(this);
}
where this is the appointment panel that own the thumb.
Decreasing height works well.
But increasing is more difficult. If I move the mouse very very slowly it's OK, if I speed it up a little bit it tends to leave out the appointment panel and then all MouseMove event are lost.
I thought Mouse.Capture() was propose to solve this kind of problem, but in fact not.
Does anybody know what is wrong in my code?
You should be using an actual Thumb control. Check out MSDN for help:
How to: Use a Thumb to Enable Dragging
you should use a thumb, but to play with mouse capture, override the protected override void OnLostMouseCapture(MouseEventArgs e) method, then you will know if you have lost the capture.

Resources