Skip to content
This repository was archived by the owner on May 1, 2024. It is now read-only.

Drawing view multi line #1475

Merged
merged 19 commits into from
Jul 16, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 76 additions & 33 deletions samples/XCT.Sample.WPF/DrawingViewRenderer.wpf.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Collections.Specialized;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
using System.Windows.Controls;
Expand All @@ -10,6 +11,7 @@
using Xamarin.Forms.Platform.WPF;

[assembly: ExportRenderer(typeof(DrawingView), typeof(DrawingViewRenderer))]

namespace Xamarin.CommunityToolkit.Sample.WPF
{
public class DrawingViewRenderer : ViewRenderer<DrawingView, InkCanvas>
Expand All @@ -19,11 +21,11 @@ public class DrawingViewRenderer : ViewRenderer<DrawingView, InkCanvas>
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName == DrawingView.PointsProperty.PropertyName)
if (e.PropertyName == DrawingView.LinesProperty.PropertyName)
{
canvas!.Strokes.StrokesChanged -= OnStrokesChanged;
canvas.Strokes.Clear();
LoadPoints();
LoadLines();
canvas.Strokes.StrokesChanged += OnStrokesChanged;
}
}
Expand All @@ -35,15 +37,15 @@ protected override void OnElementChanged(ElementChangedEventArgs<DrawingView> e)
{
canvas = new InkCanvas
{
DefaultDrawingAttributes =
Background = Element.BackgroundColor.ToBrush(),
DefaultDrawingAttributes = new()
{
Color = Element.LineColor.ToMediaColor(),
Width = Element.LineWidth,
Height = Element.LineWidth
},
Background = Element.BackgroundColor.ToBrush()
Color = Element.DefaultLineColor.ToMediaColor(),
Width = Element.DefaultLineWidth,
Height = Element.DefaultLineWidth
}
};
Element.Points.CollectionChanged += OnCollectionChanged;
Element.Lines.CollectionChanged += OnCollectionChanged;
SetNativeControl(canvas);

canvas.Strokes.StrokesChanged += OnStrokesChanged;
Expand All @@ -53,55 +55,96 @@ protected override void OnElementChanged(ElementChangedEventArgs<DrawingView> e)
if (e.OldElement != null)
{
canvas!.Strokes.StrokesChanged -= OnStrokesChanged;
Element!.Points.CollectionChanged -= OnCollectionChanged;
Element!.Lines.CollectionChanged -= OnCollectionChanged;
if (Control != null)
Control.PreviewMouseDown -= OnPreviewMouseDown;
}
}

void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs args) => LoadPoints();
void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs args)
{
canvas!.Strokes.StrokesChanged -= OnStrokesChanged;
canvas.Strokes.Clear();
LoadLines();
canvas.Strokes.StrokesChanged += OnStrokesChanged;
}

void OnPreviewMouseDown(object sender, MouseButtonEventArgs e) => Clear();

void OnPreviewMouseDown(object sender, MouseButtonEventArgs e)
void Clear(bool force = false)
{
canvas!.Strokes.Clear();
Element.Points.Clear();
if (!Element.MultiLineMode || force)
{
canvas!.Strokes.Clear();
Element.Lines.Clear();
}
}

void OnStrokesChanged(object sender, StrokeCollectionChangedEventArgs e)
{
Element.Points.CollectionChanged -= OnCollectionChanged;
Element.Lines.CollectionChanged -= OnCollectionChanged;
Copy link
Contributor

@pictos pictos Jul 13, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a good reason to rename this prop? I presume this will be a binary breaking change

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe I didn’t understand your question, but I will try to answer.
DrawingView doesn’t contain collection of lines any more. Instead we added an additional layer - Line. Almost all DrawingView properties are moved to the Line class.
I don’t think it will be correct to leave the name Points. Furthermore it is a Collection of Lines, not Points.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right @pictos and not even ABI, API as well :)

That's why I was wondering what we should do with this one. Because this means in 1.2 we have the implementation that we have right now and in 1.3 we have a whole bunch of breaking changes with the DrawingView already while we actually said that we should deprecate something first and then actually remove it.

I think this is a major improvement though and I think if we communicate this in the release notes very clear then it's not too much of a problem?

if (e.Added.Count > 0)
{
var points = e.Added.First().StylusPoints.Select(point => new Point(point.X, point.Y));
Element.Points.Clear();
foreach (var point in points)
Element.Points.Add(point);
if (!Element.MultiLineMode)
{
Element.Lines.Clear();
}

if (Element.Points.Count > 0)
var lines = Element.MultiLineMode ? e.Added : new StrokeCollection() {e.Added.First()};

foreach (var line in lines)
{
var points = line.StylusPoints.Select(point => new Point(point.X, point.Y)).ToList();
Element.Lines.Add(new Line()
{
Points = new ObservableCollection<Point>(points),
LineColor = Color.FromRgba(line.DrawingAttributes.Color.R, line.DrawingAttributes.Color.G,
line.DrawingAttributes.Color.B, line.DrawingAttributes.Color.A),
LineWidth = (float) line.DrawingAttributes.Width
});
}

if (Element.Lines.Count > 0)
{
if (Element.DrawingCompletedCommand?.CanExecute(null) ?? false)
Element.DrawingCompletedCommand.Execute(Element.Points);
var lastLine = Element.Lines.Last();
if (Element.DrawingLineCompletedCommand?.CanExecute(lastLine) ?? false)
Element.DrawingLineCompletedCommand.Execute(lastLine);
}

if (Element.ClearOnFinish)
{
canvas!.Strokes.StrokesChanged -= OnStrokesChanged;
canvas.Strokes.Clear();
canvas.Strokes.StrokesChanged += OnStrokesChanged;
Element.Points.Clear();
Element.Lines.CollectionChanged -= OnCollectionChanged;
Clear(true);
canvas!.Strokes.StrokesChanged += OnStrokesChanged;
}
}

Element.Points.CollectionChanged += OnCollectionChanged;
Element.Lines.CollectionChanged += OnCollectionChanged;
}

void LoadPoints()
void LoadLines()
{
var stylusPoints = Element?.Points.Select(point => new StylusPoint(point.X, point.Y)).ToList();
if (stylusPoints is { Count: > 0 })
var lines = Element.MultiLineMode
? Element.Lines
: Element.Lines.Any()
? new ObservableCollection<Line> {Element.Lines.LastOrDefault()}
: new ObservableCollection<Line>();
foreach (var line in lines)
{
var stroke = new Stroke(new StylusPointCollection(stylusPoints), canvas!.DefaultDrawingAttributes);
canvas.Strokes.Add(stroke);
var stylusPoints = line.Points.Select(point => new StylusPoint(point.X, point.Y)).ToList();
if (stylusPoints is {Count: > 0})
{
var stroke = new Stroke(new StylusPointCollection(stylusPoints))
{
DrawingAttributes = new()
{
Color = line.LineColor.ToMediaColor(),
Width = line.LineWidth,
Height = line.LineWidth
}
};
canvas!.Strokes.Add(stroke);
}
}
}
}
Expand Down
49 changes: 21 additions & 28 deletions samples/XCT.Sample/Pages/Views/DrawingViewPage.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,25 @@
xmlns:pages="clr-namespace:Xamarin.CommunityToolkit.Sample.Pages"
xmlns:viewsVodel="clr-namespace:Xamarin.CommunityToolkit.Sample.ViewModels.Views;assembly=Xamarin.CommunityToolkit.Sample">

<pages:BasePage.BindingContext>
<viewsVodel:DrawingViewViewModel/>
</pages:BasePage.BindingContext>

<ScrollView>
<StackLayout>
<Switch x:Name="ClearOnFinish" />
<StackLayout Orientation="Horizontal" Margin="5">
<Label Text="Clear on Finish" Margin="0,0,5,0" />
<Switch x:Name="ClearOnFinish" />
</StackLayout>
<StackLayout Orientation="Horizontal" Margin="5">
<Label Text="Multi-Line Mode" Margin="0,0,5,0" />
<Switch x:Name="MultiLineMode" />
</StackLayout>
<Button
BackgroundColor="White"
Clicked="LoadPointsButtonClicked"
Text="Load points"
Clicked="AddNewLine"
Text="Add new line"
TextColor="Black" />
<Button
BackgroundColor="White"
Command="{Binding SetPointsCommand}"
Text="Load points ViewModel"
Clicked="LoadPointsButtonClicked"
Text="Load points"
TextColor="Black" />
<Label
x:Name="HiddenLabel"
Expand All @@ -33,11 +36,6 @@
Clicked="DisplayHiddenLabelButtonClicked"
Text="Display hidden label"
TextColor="Black" />
<Button
BackgroundColor="White"
Command="{Binding GetPointsCommand}"
Text="Get points ViewModel"
TextColor="Black" />
<Button
BackgroundColor="White"
Clicked="GetCurrentDrawingViewImageClicked"
Expand All @@ -53,21 +51,16 @@
HeightRequest="100"
WidthRequest="100" />

<Label Text="Default DrawingView"/>
<views:DrawingView BackgroundColor="LightGray" HeightRequest="200" />
<Label Text="DrawingView (Code behind)"/>
<views:DrawingView x:Name="DrawingViewControl"
BackgroundColor="LightGray"
HeightRequest="200"
DefaultLineColor="Red"
DefaultLineWidth="3"
Lines="{Binding Lines, Mode=TwoWay}"
ClearOnFinish="{Binding Source={x:Reference ClearOnFinish}, Path=IsToggled}"
MultiLineMode="{Binding Source={x:Reference MultiLineMode}, Path=IsToggled}"/>

<Label Text="Advanced DrawingView"/>
<views:DrawingView
x:Name="DrawingViewControl"
Points="{Binding MacroPoints, Mode=TwoWay}"
EnableSmoothedPath="false"
Granularity="5"
BackgroundColor="DarkGray"
ClearOnFinish="{Binding Source={x:Reference ClearOnFinish}, Path=IsToggled}"
HorizontalOptions="FillAndExpand"
LineColor="Red"
LineWidth="10"
HeightRequest="200" />
<Editor x:Name="Logs" HeightRequest="50" />
</StackLayout>
</ScrollView>
Expand Down
60 changes: 48 additions & 12 deletions samples/XCT.Sample/Pages/Views/DrawingViewPage.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,32 @@
using Xamarin.Forms;
using Xamarin.CommunityToolkit.UI.Views;
using System.Collections.ObjectModel;
using System.Linq;

namespace Xamarin.CommunityToolkit.Sample.Pages.Views
{
public partial class DrawingViewPage : BasePage
{
static Random random = new Random();

public DrawingViewPage()
{
InitializeComponent();
DrawingViewControl.Points = GeneratePoints(200);
DrawingViewControl.DrawingCompletedCommand = new Command<ObservableCollection<Point>>(points =>
DrawingViewControl.Lines = GenerateLines(5);
DrawingViewControl.DrawingLineCompletedCommand = new Command<Line>(line =>
{
Logs.Text += "GestureCompletedCommand executed" + Environment.NewLine;
DrawImage(points);
var stream = Line.GetImageStream(line.Points.ToList(), new Size(GestureImage.Width, GestureImage.Height), 10, Color.White, Color.Black);
GestureImage.Source = ImageSource.FromStream(() => stream);
});

BindingContext = this;
}

void LoadPointsButtonClicked(object sender, EventArgs e) => DrawingViewControl.Points = GeneratePoints(50);
void LoadPointsButtonClicked(object sender, EventArgs e) => DrawingViewControl.Lines = GenerateLines(50);

void DisplayHiddenLabelButtonClicked(object sender, EventArgs e) => HiddenLabel.IsVisible = !HiddenLabel.IsVisible;
void DisplayHiddenLabelButtonClicked(object sender, EventArgs e) =>
HiddenLabel.IsVisible = !HiddenLabel.IsVisible;

void GetCurrentDrawingViewImageClicked(object sender, EventArgs e)
{
Expand All @@ -31,26 +38,55 @@ void GetCurrentDrawingViewImageClicked(object sender, EventArgs e)

void GetImageClicked(object sender, EventArgs e)
{
var points = GeneratePoints(100);
DrawImage(points);
var lines = GenerateLines(10);
DrawImage(lines.ToList());
}

ObservableCollection<Line> GenerateLines(int count)
{
var lines = new ObservableCollection<Line>();
for (var i = 0; i < count; i++)
{
lines.Add(new Line()
{
Points = GeneratePoints(10),
LineColor = Color.FromRgb(random.Next(255), random.Next(255), random.Next(255)),
LineWidth = 10,
EnableSmoothedPath = false,
Granularity = 5
});
}

return lines;
}

static ObservableCollection<Point> GeneratePoints(int count)
ObservableCollection<Point> GeneratePoints(int count)
{
var points = new ObservableCollection<Point>();
for (var i = 0; i < count; i++)
{
points.Add(new Point(i, i));
points.Add(new Point(random.Next(1, 100), random.Next(1, 100)));
}

return points;
}

void DrawImage(IEnumerable<Point> points)
void DrawImage(List<Line> lines)
{
var stream = DrawingView.GetImageStream(points, new Size(GestureImage.Width, GestureImage.Height), 10,
Color.White, Color.Black);
var stream = DrawingView.GetImageStream(lines, new Size(GestureImage.Width, GestureImage.Height), Color.Gray);
GestureImage.Source = ImageSource.FromStream(() => stream);
}

private void AddNewLine(object sender, EventArgs e)
{
DrawingViewControl.Lines.Add(new Line()
{
Points = GeneratePoints(10),
LineColor = Color.FromRgb(random.Next(255), random.Next(255), random.Next(255)),
LineWidth = 10,
EnableSmoothedPath = true,
Granularity = 5
});
}
}
}
Loading