Question

I am trying to develop an application which shows WPF InkCanvas drawings on remote host. Basically it synchronizes local InkCanvas with several remote hosts. I have subscribed to StrokesChanged event:

        this.DrawingCanvas.Strokes.StrokesChanged += this.Strokes_StrokesChanged;

And the handler.

    private void Strokes_StrokesChanged(object sender, StrokeCollectionChangedEventArgs e)
    {
        if (e.Added != null && e.Added.Count > 0)
        {
            this.StrokeSynchronizer.SendAddedStroke(e.Added);
        }

        if (e.Removed != null && e.Removed.Count > 0)
        {
            this.StrokeSynchronizer.SendRemovedStroke(e.Removed);
        }
    }

When I am drawing a new curve the event invoked only once. The remote host draws it correctly by calling this.RemoteInkCanvas.Strokes.Add(addedStrokes).

When I am erasing a curve via InkCanvasEditingMode.EraseByStroke the event also invoked once and remote host uses this.RemoteInkCanvas.Strokes.Remove(removedStrokes) successfully.

Here goes the problem!

When the this.DrawingCanvas.EditingMode is InkCanvasEditingMode.EraseByPoint then the event invoked once but with two collections (Added and Removed). This causes remote hosts to get insane. Here is the remote host code which erases strokes:

    private StrokeCollection FindStrokesInLocalCollection(StrokeCollection receivedCollection)
    {
        var localStrokes = new StrokeCollection();
        foreach (var erasedStroke in receivedCollection)
        {
            var erasedPoints = erasedStroke.StylusPoints;
            foreach (var existentStoke in this.RemoteInkCanvas.Strokes)
            {
                var existentPoints = existentStoke.StylusPoints;
                if (erasedPoints.SequenceEqual(existentPoints))
                {
                    localStrokes.Add(existentStoke);
                }
            }
        }

        return localStrokes;
    }

    private void RemoteStrokeRemoved(StrokeCollection strokes)
    {
        try
        {
            // Simple this.RemoteInkCanvas.Strokes.Remove(strokes)
            // does not work, because local and remote strokes are different (though equal) objects.
            // Thus we need to find same strokes in local collection.
            var strokesToRemove = this.FindStrokesInLocalCollection(strokes);

            if (strokesToRemove.Count != strokes.Count)
            {
                Logger.Warn(string.Format(
                    "Whiteboard: Seems like remotely removed strokes were not found in local whiteboard. Remote count {0}, local count {1}.",
                    strokes.Count,
                    strokesToRemove.Count));
            }

            this.RemoteInkCanvas.Strokes.Remove(strokesToRemove);
        }
        catch (Exception ex)
        {
            Logger.Error("Whiteboard: Can not remove some strokes received from remote host.", ex);
        }
    }

Please note, that the exception is always being caught.

The general question here: how to find same strokes/points in the collection in order to remove them accordingly?

Was it helpful?

Solution 2

The problem is in serialization/deserialization mechanism used by StrokesCollection. When a double value is serialized and deserialized the resulting value slightly varies.

The complete code sample and answer can be found here: http://social.msdn.microsoft.com/Forums/en-AU/wpf/thread/9e1f43fa-6266-41b7-a5d0-7603f87ca58f

OTHER TIPS

I am not sure if you need to have it that complicated, but here's a WPF Markup only solution, which should exactly do what you need:

http://msdn.microsoft.com/en-us/library/system.windows.controls.inkcanvas.aspx

See the example after the API definitions. If you remove the three lines in the markup with the LayoutTransform it should be exactly what you need.

If you want to have the StrokesCollection in the CodeBehind/VM then bind it as a DependencyProperty or as a VM Property and you're set.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top