DrawingView

O DrawingView oferece uma superfície que permite desenhar linhas por meio de interações por toque ou com o mouse. O resultado de um desenho de usuários pode ser salvo como uma imagem. Um caso de uso comum para isso é fornecer uma caixa de assinatura em um aplicativo.

Uso Básico

DrawingView permite definir a cor da linha, a largura da linha e a associação à coleção de linhas.

XAML

Incluindo o namespace XAML

Para usar o kit de ferramentas no XAML, o xmlns a seguir precisa ser adicionado à sua página ou exibição:

xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"

Portanto, o seguinte:

<ContentPage
    x:Class="CommunityToolkit.Maui.Sample.Pages.MyPage"
    xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml">

</ContentPage>

Seria modificado para incluir o xmlns da seguinte maneira:

<ContentPage
    x:Class="CommunityToolkit.Maui.Sample.Pages.MyPage"
    xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit">

</ContentPage>

Usando o DrawingView

<toolkit:DrawingView
            Lines="{Binding MyLines}"
            LineColor="Red"
            LineWidth="5" />

C#

using CommunityToolkit.Maui.Views;

var drawingView = new DrawingView
{
    Lines = new ObservableCollection<IDrawingLine>(),
    LineColor = Colors.Red,
    LineWidth = 5
};

A captura de tela a seguir mostra o DrawingView resultante no Android:

Captura de tela de um DrawingView no Android

Uso de múltiplas linhas

Por padrão DrawingView , dá suporte apenas a uma linha. Para habilitar MultiLine, defina IsMultiLineModeEnabled como true. Verifique se ShouldClearOnFinish é falso.

XAML

<views:DrawingView
            Lines="{Binding MyLines}"
            IsMultiLineModeEnabled="true"
            ShouldClearOnFinish="false" />

C#

using CommunityToolkit.Maui.Views;

var gestureImage = new Image();
var drawingView = new DrawingView
{
    Lines = new ObservableCollection<IDrawingLine>(),
    IsMultiLineModeEnabled = true,
    ShouldClearOnFinish = false,
};

A captura de tela a seguir mostra o DrawingView resultante no Android:

Captura de tela de um DrawingView com várias linhas no Android

Salvando o resultado em uma imagem

O .NET MAUI Community Toolkit oferece várias opções para salvar o desenho resultante em uma imagem, estas opções são as seguintes:

Salvando de DrawingView

O DrawingView fornece o GetImageStream método que gerará uma imagem e retornará o conteúdo em um Stream.

O exemplo a seguir exportará o desenho para uma imagem com uma largura desejada de 400 e uma altura desejada de 300. As dimensões desejadas serão ajustadas para garantir que a proporção do desenho seja mantida.

await drawingView.GetImageStream(desiredWidth: 400, desiredHeight: 300);

Note

Por padrão, o GetImageStream método retornará uma imagem que contém as linhas desenhadas, isso não corresponderá à superfície completa que o usuário vê. Para gerar uma imagem que corresponda diretamente à superfície exibida no aplicativo, o GetImageStream método com o DrawingViewOutputOption parâmetro deve ser usado.

O exemplo a seguir mostra como gerar uma imagem que corresponde diretamente à superfície DrawingView exibida em um aplicativo:

await drawingView.GetImageStream(desiredWidth: 400, desiredHeight: 300, imageOutputOption: DrawingViewOutputOption.FullCanvas);

Salvando a partir de DrawingViewService

Usar os métodos DrawingView pode dificultar a criação de um aplicativo usando o padrão MVVM, para ajudar a lidar com isso, o kit de ferramentas da comunidade .NET MAUI também fornece a classe DrawingViewService que também permitirá a capacidade de gerar um fluxo de imagem.

ImageLineOptions.JustLines

O exemplo a seguir mostra como gerar um fluxo de imagem de uma largura desejada de 1920 e altura de 1080 e um plano de fundo azul. Os desenvolvedores podem usar o ImageLineOptions.JustLines método para fornecer opções adequadas para exportar apenas as linhas desenhadas. Para exportar a tela inteira, consulte ImageLineOptions.FullCanvas

await using var stream = await DrawingViewService.GetImageStream(
    ImageLineOptions.JustLines(Lines, new Size(1920, 1080), Brush.Blue));

ImageLineOptions.FullCanvas

Para gerar uma imagem que corresponda diretamente à superfície DrawingView, o ImageLineOptions.FullCanvas método pode ser usado da seguinte maneira.

await using var stream = await DrawingViewService.GetImageStream(
    ImageLineOptions.FullCanvas(Lines, new Size(1920, 1080), Brush.Blue, new Size(CanvasWidth, CanvasHeight)));

Para os fins deste exemplo, as propriedades CanvasWidth e CanvasHeight foram vinculadas por associação de dados às propriedades Width e Height de DrawingView, respectivamente. Para obter a solução completa, consulte o aplicativo de exemplo .NET MAUI Community Toolkit.

Manipular evento quando a linha de desenho for concluída

DrawingView permite assinar eventos como OnDrawingLineCompleted. O comando DrawingLineCompletedCommand correspondente também está disponível.

XAML

<views:DrawingView
            Lines="{Binding MyLines}"
            DrawingLineCompletedCommand="{Binding DrawingLineCompletedCommand}"
            OnDrawingLineCompleted="OnDrawingLineCompletedEvent" />

C#

using CommunityToolkit.Maui.Views;

var gestureImage = new Image();
var drawingView = new DrawingView
{
    Lines = new ObservableCollection<IDrawingLine>(),
    DrawingLineCompletedCommand = new Command<IDrawingLine>(async (line) =>
    {
        var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));

        var stream = await line.GetImageStream(gestureImage.Width, gestureImage.Height, Colors.Gray.AsPaint(), cts.Token);
        gestureImage.Source = ImageSource.FromStream(() => stream);
    })
};
drawingView.OnDrawingLineCompleted += async (s, e) =>
{
    var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));

    var stream = await e.LastDrawingLine.GetImageStream(gestureImage.Width, gestureImage.Height, Colors.Gray.AsPaint(), cts.Token);
    gestureImage.Source = ImageSource.FromStream(() => stream);
};

Use dentro de um ScrollView

Ao usar o DrawingView dentro de uma ScrollView, a interação de toque com o ScrollView às vezes pode ser interceptada no iOS. Isso pode ser evitado definindo a propriedade ShouldDelayContentTouches como false no iOS, conforme o exemplo a seguir:

Resolvi esse problema ao adicionar o ios:ScrollView.ShouldDelayContentTouches="false" ao ScrollView que contém o DrawingView:

<ContentPage
    xmlns:ios="clr-namespace:Microsoft.Maui.Controls.PlatformConfiguration.iOSSpecific;assembly=Microsoft.Maui.Controls">

    <ScrollView ios:ScrollView.ShouldDelayContentTouches="false">

        <DrawingView />

    </ScrollView>

</ContentPage>

Para obter mais informações, consulte os toques de conteúdo de ScrollView.

Uso avançado

Para obter todos os benefícios, o DrawingView fornece os métodos para obter o fluxo da imagem das linhas desenhadas.

XAML

<toolkit:DrawingView
            x:Name="DrawingViewControl"
            Lines="{Binding MyLines}"
            IsMultiLineModeEnabled="true"
            ShouldClearOnFinish="true"
            DrawingLineCompletedCommand="{Binding DrawingLineCompletedCommand}"
            OnDrawingLineCompleted="OnDrawingLineCompletedEvent"
            LineColor="Red"
            LineWidth="5"
            HorizontalOptions="Fill"
            VerticalOptions="Fill">
            <toolkit:DrawingView.Background>
                    <LinearGradientBrush StartPoint="0,0"
                                         EndPoint="0,1">
                        <GradientStop Color="Blue"
                                      Offset="0"/>
                        <GradientStop Color="Yellow"
                                      Offset="1"/>
                    </LinearGradientBrush>
            </toolkit:DrawingView.Background>
</toolkit:DrawingView>

C#

using CommunityToolkit.Maui.Views;

var gestureImage = new Image();
var drawingView = new DrawingView
{
    Lines = new ObservableCollection<IDrawingLine>(),
    IsMultiLineModeEnabled = true,
    ShouldClearOnFinish = false,
    DrawingLineCompletedCommand = new Command<IDrawingLine>(async (line) =>
    {
        var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));

        var stream = await line.GetImageStream(gestureImage.Width, gestureImage.Height, Colors.Gray.AsPaint(), cts.Token);
        gestureImage.Source = ImageSource.FromStream(() => stream);
    }),
    LineColor = Colors.Red,
    LineWidth = 5,
    Background = Brush.Red
};
drawingView.OnDrawingLineCompleted += async (s, e) =>
{
    var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));

    var stream = await e.LastDrawingLine.GetImageStream(gestureImage.Width, gestureImage.Height, Colors.Gray.AsPaint(), cts.Token);
    gestureImage.Source = ImageSource.FromStream(() => stream);
};

// get stream from lines collection
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
var lines = new List<IDrawingLine>();
var stream1 = await DrawingView.GetImageStream(
                lines,
                new Size(gestureImage.Width, gestureImage.Height),
                Colors.Black.
                cts.Token);

// get steam from the current DrawingView
var stream2 = await drawingView.GetImageStream(gestureImage.Width, gestureImage.Height, cts.Token);

Propriedades

Property Tipo Descrição
Linhas ObservableCollection<IDrawingLine> Coleção de IDrawingLine que estão atualmente no DrawingView
IsMultiLineModeEnabled bool Ativa ou desativa o modo multilinha. Quando ativado, várias linhas podem ser desenhadas no DrawingView enquanto o toque/clique é liberado entre uma linha e outra. Observação: quando ClearOnFinish também estiver habilitado, as linhas serão limpas após soltar o toque/clique. Além disso, DrawingLineCompletedCommand será acionado após cada linha desenhada.
ShouldClearOnFinish bool Indica se a opção DrawingView está desmarcada depois de liberar o toque/clique e uma linha é desenhada. Observação: quando IsMultiLineModeEnabled também está habilitado, isso pode causar um comportamento inesperado.
DrawingLineStartedCommand ICommand Esse comando é invocado sempre que o desenho de uma linha na DrawingView é iniciado.
DrawingLineCancelledCommand ICommand Esse comando é invocado sempre que o desenho de uma linha no DrawingView for cancelado.
DrawingLineCompletedCommand ICommand Esse comando é invocado sempre que o desenho de uma linha na DrawingView estiver concluído. . Note que isso é acionado depois que o toque ou clique é liberado. Quando MultiLineMode habilitado, esse comando é acionado várias vezes.
PointDrawnCommand ICommand Esse comando é invocado sempre que o desenho de um ponto no DrawingView tiver sido concluído.
OnDrawingLineStarted EventHandler<DrawingLineStartedEventArgs> O evento DrawingView ocorre quando o desenho de linha é iniciado.
OnDrawingLineCancelled EventHandler<EventArgs> DrawingView ocorre quando o desenho de uma linha é cancelado.
OnDrawingLineCompleted EventHandler<DrawingLineCompletedEventArgs> DrawingView ocorre quando a linha de desenho é concluída.
OnPointDrawn EventHandler<PointDrawnEventArgs> DrawingView evento ocorre quando um ponto é desenhado.
Linecolor Color A cor usada por padrão para desenhar uma linha no DrawingView.
Largura da linha float A largura que é usada por padrão para desenhar uma linha no DrawingView.

DrawingLine

O DrawingLine contém a lista de pontos e permite configurar o estilo de cada linha individualmente.

Propriedades

Property Tipo Descrição Valor padrão
Linecolor Color A cor usada para desenhar a linha no DrawingView. Colors.Black
Largura da linha float A largura usada para desenhar a linha no DrawingView. 5
Points ObservableCollection<PointF> A coleção de PointF que compõe a linha. new()
Granularidade int A granularidade dessa linha. O valor mínimo é 5. Quanto maior o valor, mais suave a linha, mais lento será o programa. 5
ShouldSmoothPathWhenDrawn bool Habilita ou desabilita se essa linha é suavizada (anti-aliased) quando desenhada. false

IDrawingLine personalizada

Há 2 etapas para substituir o padrão DrawingLine pela implementação personalizada:

  1. Crie uma classe personalizada que implementa IDrawingLine:
    public class MyDrawingLine : IDrawingLine
    {
        public ObservableCollection<PointF> Points { get; } = new();
        ...
    }
    
  2. Crie uma classe personalizada que implementa IDrawingLineAdapter.
    public class MyDrawingLineAdapter : IDrawingLineAdapter
    {
        public IDrawingLine(MauiDrawingLine mauiDrawingLine)
        {
            return new MyDrawingLine
            {
                Points = mauiDrawingLine.Points,
                ...
            }
        }
    }
    
  3. Definir IDrawingLineAdapter personalizado em IDrawingViewHandler:
    var myDrawingLineAdapter = new MyDrawingLineAdapter();
    drawingViewHandler.SetDrawingLineAdapter(myDrawingLineAdapter);
    

DrawingLineStartedEventArgs

Argumento de evento que contém o último ponto de desenho.

Propriedades

Property Tipo Descrição
Point PointF Último ponto de desenho.

DrawingLineCompletedEventArgs

Argumento de evento que contém a última linha de desenho.

Propriedades

Property Tipo Descrição
LastDrawingLine IDrawingLine Última linha de desenho.

PointDrawnEventArgs

Argumento de evento que contém o último ponto de desenho.

Propriedades

Property Tipo Descrição
Point PointF Último ponto de desenho.

Methods

Método Descrição
GetImageStream Recupera um(a) Stream contendo uma imagem dos(as) Lines atualmente desenhados no DrawingView.
GetImageStream (estático) Recupera uma Stream que contém uma imagem da coleção de IDrawingLine fornecida como parâmetro.

Exemplos

Você pode encontrar um exemplo desse recurso em ação no aplicativo de exemplo .NET MAUI Community Toolkit.

API

O código-fonte do DrawingView pode ser encontrado no repositório GitHub do .NET MAUI Community Toolkit.