DrawingView

O DrawingView fornece uma superfície que permite desenhar linhas por toque ou interação com o rato. O resultado do desenho de um utilizador pode ser guardado como imagem. Um caso de uso comum para isto é fornecer uma caixa de assinatura numa aplicação.

Utilização básica

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

XAML

Incluindo o namespace XAML

Para usar o kit de ferramentas em XAML, a seguinte xmlns precisa ser adicionada à sua página ou vista.

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

Por conseguinte, 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 forma:

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

Utilizar a vista de desenho

<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 ecrã seguinte mostra o resultado do DrawingView no Android:

Captura de ecrã de um DrawingView no Android

Utilização de MultiLine

Por defeito DrawingView , suporta apenas 1 linha. Para ativar MultiLine definir IsMultiLineModeEnabled como verdadeiro. Certifique-se de que ShouldClearOnFinish está definido como 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 ecrã seguinte mostra o resultado do DrawingView no Android:

Captura de ecrã de um DrawingView com multi-linha no Android

Guardar o resultado numa imagem

O .NET MAUI Community Toolkit oferece várias opções para guardar o desenho resultante numa imagem, sendo estas as seguintes:

Guardar a partir de DrawingView

O DrawingView fornece o método GetImageStream, que gera uma imagem e devolve o conteúdo num Stream.

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

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

Observação

Por defeito, o GetImageStream método devolverá uma imagem que contém as linhas desenhadas, mas esta não corresponderá à superfície completa que o utilizador vê. Para gerar uma imagem que corresponda diretamente à superfície apresentada na aplicação, é necessário utilizar o método GetImageStream com o parâmetro DrawingViewOutputOption.

O exemplo seguinte mostra como gerar uma imagem que corresponda diretamente à superfície DrawingView apresentada numa aplicação:

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

A guardar a partir de DrawingViewService

A utilização dos métodos DrawingView pode dificultar a construção de uma aplicação usando o padrão MVVM; para ajudar a lidar com isso, o .NET MAUI Community Toolkit também disponibiliza a classe DrawingViewService que permite gerar um fluxo de imagens.

ImageLineOptions.JustLines

O exemplo seguinte mostra como gerar um fluxo de imagem com largura desejada de 1920, altura de 1080 e fundo azul. Os programadores podem usar o ImageLineOptions.JustLines método para fornecer opções adequadas para exportar apenas as linhas desenhadas. Para exportar toda a tela, veja 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 do DrawingView, o ImageLineOptions.FullCanvas método pode ser usado da seguinte forma.

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

Para efeitos deste exemplo, as propriedades CanvasWidth e CanvasHeight foram associadas por enlace de dados às propriedades Width e Height do DrawingView, respetivamente. Para a solução completa, consulte o .NET MAUI Community Toolkit Sample Application.

Lidar com o evento quando a linha de desenho está concluída

DrawingView permite subscrever 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);
};

Utilização dentro de um ScrollView

Ao utilizar o DrawingView dentro de um ScrollView, a interação por toque com o ScrollView pode, por vezes, ser intercetada no iOS. Isto pode ser evitado definindo a ShouldDelayContentTouches propriedade como false no iOS, conforme o seguinte exemplo:

Resolvi este problema adicionando 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 mais informações, consulte os detalhes do conteúdo do ScrollView.

Utilização avançada

Para obter todos os benefícios, o DrawingView disponibiliza os métodos para obter o fluxo de imagens das linhas de desenho.

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

Propriedade Tipo Descrição
Linhas ObservableCollection<IDrawingLine> A coleção desses IDrawingLine encontra-se atualmente no DrawingView
IsMultiLineModeEnabled bool Ativa ou desativa o modo multilinha. Quando ativado, podem ser desenhadas várias linhas no DrawingView se o toque/clique for libertado entre cada linha. Nota: quando ClearOnFinish também está ativado, as linhas são limpas após o toque ou clique ser libertado. Além disso, DrawingLineCompletedCommand será acionado após cada linha desenhada.
ShouldClearOnFinish bool Indica se o DrawingView é apagado após libertar o toque/clique e é desenhada uma linha. Nota: quando IsMultiLineModeEnabled também está ativado, isto pode causar comportamentos inesperados.
ComandoInícioDoDesenhoDeLinha ICommand Este comando é invocado sempre que o desenho de uma linha no DrawingView tem começado.
ComandoCanceladoDeDesenhoDeLinha ICommand Este comando é invocado sempre que o desenho de uma linha no DrawingView é cancelado.
DesenhoLinhaCompletadaComando ICommand Este comando é invocado sempre que o desenho de uma linha no DrawingView estiver concluído. . Tenha em atenção que isto é acionado depois de o toque ou clique ser libertado. Quando MultiLineMode está ativado, este comando é executado várias vezes.
ComandoPontoDesenhado ICommand Este comando é invocado sempre que o desenho de um ponto em o DrawingView foi concluído.
AoIniciarLinhaDeDesenho EventHandler<DrawingLineStartedEventArgs> DrawingView O evento ocorre quando se inicia o desenho da linha.
OnDrawingLineCancelled EventHandler<EventArgs> DrawingView O evento ocorre quando o desenho de uma linha é cancelado.
OnDrawingLineConcluído EventHandler<DrawingLineCompletedEventArgs> DrawingView o evento ocorre quando o desenho de linha estiver concluído.
OnPointDrawn EventHandler<PointDrawnEventArgs> DrawingView o evento ocorre quando o ponto é desenhado.
LineColor Color A cor que é utilizada por predefinição para traçar uma linha no DrawingView.
Largura da linha float A largura utilizada por defeito para traçar uma linha no DrawingView.

Linha de desenho

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

Propriedades

Propriedade Tipo Descrição Valor padrão
LineColor Color A cor usada para traçar a linha no DrawingView. Colors.Black
Largura de Linha float A largura que é usada para desenhar a linha no DrawingView. 5
Pontos ObservableCollection<PointF> A coleção de PointF que constitui a linha. new()
Granularidade int A granularidade desta linha. Valor mínimo é 5. Quanto maior o valor, mais suave a linha, mais lento é o programa. 5
DeveriaSuavizarTrajetoAoDesenhar bool Ativa ou desativa se esta linha for suavizada (anti-alias) quando desenhada. false

IDrawingLine Personalizada

Existem 2 passos para substituir o padrão DrawingLine pela implementação personalizada:

  1. Crie uma classe personalizada que implemente IDrawingLine:
    public class MyDrawingLine : IDrawingLine
    {
        public ObservableCollection<PointF> Points { get; } = new();
        ...
    }
    
  2. Crie uma classe personalizada que implemente 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

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

Argumentos do Evento de Conclusão do Desenho de Linha

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

Propriedades

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

PointDrawnEventArgs

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

Propriedades

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

Methods

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

Exemplos

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

API

Você pode encontrar o código-fonte para DrawingView no repositório GitHub do .NET MAUI Community Toolkit.