André Alves de Lima

Talking about Software Development and more…

Utilizando tipos complexos em ConverterParameters e CommandParameters

Olá caro(a) leitor(a)!

No artigo de hoje abordarei a utilização de tipos complexos em ConverterParameters e CommandParameters. Apesar de utilizar um aplicativo para a Windows Store no exemplo deste artigo, as mesmas regras valem para todas as outras engines que utilizam o XAML como linguagem para a user interface.

Para entendermos o funcionamento de ConverterParameters e CommandParameters, precisamos entender primeiramente os conceitos de Converters e Commands. Quanto ao assunto de Converters, sugiro que vocês deem uma olhada neste outro artigo que escrevi sobre esse tema. Já quanto ao assunto de Commands, assistam a este vídeo do Bruno Sonnino sobre esse tema.

Após compreenderem esses dois assuntos, vocês provavelmente também entenderam para quê servem os ConverterParameters e CommandParameters, certo? Caso ainda não tenham entendido, como o próprio nome diz, eles servem para fornecermos parâmetros para um Converter ou Command. O problema é que, caso nós queiramos passar um parâmetro no nosso XAML, por default, ele será uma string. E isso nem sempre é o ideal para todas as situações (aliás, na maioria das situações vocês provavelmente gostariam de passar algum parâmetro que não seja uma string). Então, como podemos passar um objeto que não seja uma string em um ConverterParameter ou CommandParameter? É o que veremos no exemplo a seguir.

Parte 1: ConverterParameters

Para exercitar a utilização de tipos complexos em ConverterParameters, vamos criar, então, um projeto do tipo Windows Store Blank App. Na MainPage desse projeto, vamos adicionar um StackPanel e, dentro desse StackPanel, vamos adicionar um ToggleSwitch e dois TextBlocks. Veja como ficaria o XAML abaixo:

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <StackPanel Margin="100">
            <ToggleSwitch x:Name="TextBlockToogle"
                          IsOn="True"/>
            <TextBlock Text="Texto a ser mostrado"
                       Style="{StaticResource HeaderTextBlockStyle}" />
            <TextBlock Text="Texto a ser escondido"
                       Style="{StaticResource HeaderTextBlockStyle}" />
        </StackPanel>
    </Grid>

Interface parte 1: ToggleButton e TextBlocks

Pelo layout acima, já fica claro qual é o plano para esses controles. Quando o ToggleSwitch estiver ligado, o TextBlock com “Texto a ser mostrado” deve ser exibido e o TextBlock com “Texto a ser escondido” deve ser escondido. E o contrário deve acontecer quando o ToggleSwitch estiver desligado. Para fazermos isso, vamos adicionar um binding na propriedade Visibility dos TextBlocks, linkando-os com a propriedade IsOn do ToggleSwitch. Porém, a propriedade Visibility dos TextBlocks é do tipo Windows.UI.Xaml.Visibility, já a propriedade IsOn do ToggleSwitch é do tipo bool. Portanto, precisamos de um Converter que faça a conversão de Visibility para bool. Ao invés de desenvolvermos o nosso próprio Converter, vamos utilizar o BooleanToVisiblityConverter da biblioteca WinRTConverters, disponível no NuGet:

Manage NuGet Packages

Instalando a biblioteca WinRTConverters

Uma vez adicionada a referência à biblioteca WinRTConverters, vamos voltar à nossa MainPage, declarar o namespace “xmlns:converters” e adicionar uma instância do BooleanToVisibilityConverter como um StaticResource:

<Page x:Class="SeuExemplo.MainPage"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:local="using:SeuExemplo"
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
      xmlns:converters="using:WinRTConverters.Visibility"
      mc:Ignorable="d">
    <Page.Resources>
        <converters:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
    </Page.Resources>
    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <StackPanel Margin="100">
            <ToggleSwitch x:Name="TextBlockToogle"
                          IsOn="True" />
            <TextBlock Text="Texto a ser mostrado"
                       Style="{StaticResource HeaderTextBlockStyle}" />
            <TextBlock Text="Texto a ser escondido"
                       Style="{StaticResource HeaderTextBlockStyle}" />
        </StackPanel>
    </Grid>
</Page>

Feito isso, vamos adicionar o binding no primeiro TextBlock, que é simplesmente um binding entre a propriedade Visibility do TextBlock e a propriedade IsOn do ToggleSwitch, só que utilizando o Converter:

            <TextBlock Text="Texto a ser mostrado"
                       Style="{StaticResource HeaderTextBlockStyle}"
                       Visibility="{Binding ElementName=TextBlockToogle, Path=IsOn, Converter={StaticResource BooleanToVisibilityConverter}}" />

Execute o exemplo neste momento e veja que ao ligar e desligar o ToggleSwitch, o primeiro TextBlock é exibido ou escondido. Porém, o segundo TextBlock está sempre visível, porque não adicionamos nenhum binding nele ainda. O binding que temos que adicionar nesse segundo TextBlock também vai utilizar o mesmo Converter, porém, precisamos passar como ConverterParameter o valor booleano “True“. A primeira ideia que viria em mente para efetuar essa tarefa seria algo como:

            <TextBlock Text="Texto a ser escondido"
                       Style="{StaticResource HeaderTextBlockStyle}"
                       Visibility="{Binding ElementName=TextBlockToogle, Path=IsOn, Converter={StaticResource BooleanToVisibilityConverter}, ConverterParameter=True}" />

Porém, ao testar esse código vocês verão que ele não funcionará como esperado. Isso acontece porque, ao fazermos dessa forma, o valor “True” estará sendo passado como parâmetro do Converter na forma de string, e não na forma de um valor bool (que é o que o Converter está esperando). Nesse caso, temos que declarar o binding de uma forma ligeiramente diferente. Vejam só como fica:

            <TextBlock Text="Texto a ser escondido"
                       Style="{StaticResource HeaderTextBlockStyle}">
                <TextBlock.Visibility>
                    <Binding ElementName="TextBlockToogle"
                             Path="IsOn"
                             Converter="{StaticResource BooleanToVisibilityConverter}">
                        <Binding.ConverterParameter>
                            <x:Boolean>True</x:Boolean>
                        </Binding.ConverterParameter>
                    </Binding>
                </TextBlock.Visibility>
            </TextBlock>

Como vocês podem notar, o binding deve ser declarado dentro da tag Visibility, e o ConverterParameter deve ser declarado separadamente, dentro da tag Binding.ConverterParameter. Dessa forma tudo funciona como esperado.

Parte 2: CommandParameters

Na segunda parte deste exemplo, vamos abordar a utilização de tipos complexos em CommandParameters. A ideia é criarmos um comando bem simples que exibirá uma MessageBox, porém, esse comando receberá como parâmetro o título e o conteúdo a serem exibidos na MessageBox. Vamos começar criando uma classe simples chamada MostrarMensagemParameters, com duas propriedades string: Titulo e Mensagem. Adicione essa classe ao projeto:

    public class MostrarMensagemParameters
    {
        public string Titulo { get; set; }
        public string Mensagem { get; set; }
    }

Em seguida, vamos criar o nosso Command que fará a exibição da mensagem. Vamos chamar esse comando de MostrarMensagemCommand:

    public class MostrarMensagemCommand : System.Windows.Input.ICommand
    {
        public event EventHandler CanExecuteChanged;

        public bool CanExecute(object parameter)
        {
            return true;
        }

        public async void Execute(object parameter)
        {
            MostrarMensagemParameters mostrarMensagemParameters = parameter as MostrarMensagemParameters;

            if (mostrarMensagemParameters != null)
                await new Windows.UI.Popups.MessageDialog(mostrarMensagemParameters.Mensagem, mostrarMensagemParameters.Titulo).ShowAsync();
        }
    }

O próximo passo é voltar à MainPage e adicionarmos uma instância de MostrarMensagemCommand como StaticResource na nossa página:

    <Page.Resources>
        <converters:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
        <local:MostrarMensagemCommand x:Key="MostrarMensagemCommand" />
    </Page.Resources>

E então podemos adicionar um Button no nosso StackPanel, configurar a tag Command e passar o CommandParameter dentro da tag Button.CommandParameter:

            <Button Content="Mostrar Mensagem estática"
                    Margin="0,100,0,0"
                    Command="{StaticResource MostrarMensagemCommand}">
                <Button.CommandParameter>
                    <local:MostrarMensagemParameters Titulo="Título da mensagem"
                                                     Mensagem="Conteúdo da mensagem" />
                </Button.CommandParameter>
            </Button>

Bacana, não? Mas, e se quisermos fazer um binding nas propriedades Titulo e Mensagem? Podemos fazer, mas, primeiramente precisamos converter essas propriedades na classe MostrarMensagemParameters para Dependency Properties, do contrário não é possível fazer bindings nessas propriedades. Para entenderem mais sobre Dependency Properties, vejam este outro vídeo do Bruno Sonnino. Olhem só como ficaria a classe MostrarMensagemParameters depois de converter as propriedades em Dependency Properties:

    public class MostrarMensagemParameters : Windows.UI.Xaml.DependencyObject
    {
        public static readonly Windows.UI.Xaml.DependencyProperty TituloProperty =
            Windows.UI.Xaml.DependencyProperty.Register(
            "Titulo", typeof(string), typeof(MostrarMensagemParameters), null);

        public string Titulo
        {
            get { return (string)this.GetValue(TituloProperty); }
            set { this.SetValue(TituloProperty, value); }
        }

        public static readonly Windows.UI.Xaml.DependencyProperty MensagemProperty =
            Windows.UI.Xaml.DependencyProperty.Register(
            "Mensagem", typeof(string), typeof(MostrarMensagemParameters), null);

        public string Mensagem
        {
            get { return (string)this.GetValue(MensagemProperty); }
            set { this.SetValue(MensagemProperty, value); }
        }
    }

Agora podemos voltar ao XAML da nossa MainPage para adicionarmos dois TextBoxes e um outro Button para fazermos uso de binding no CommandParameter:

            <TextBox x:Name="TituloMensagemTextBox"
                     Width="300"
                     HorizontalAlignment="Left"
                     Margin="0,100,0,0"/>
            <TextBox x:Name="ConteudoMensagemTextBox"
                     Width="300"
                     HorizontalAlignment="Left" 
                     Margin="0,5,0,0"/>
            <Button Content="Mostrar Mensagem dinâmica"
                    Command="{StaticResource MostrarMensagemCommand}">
                <Button.CommandParameter>
                    <local:MostrarMensagemParameters Titulo="{Binding ElementName=TituloMensagemTextBox, Path=Text}"
                                                     Mensagem="{Binding ElementName=ConteudoMensagemTextBox, Path=Text}" />
                </Button.CommandParameter>
            </Button>

E com isso vocês aprenderam como fica a sintaxe para a utilização de tipos complexos em ConverterParameters e CommandParameters. Espero que vocês tenham gostado!

Como sempre, este exemplo está também disponível no meu repositório do GitHub e na MSDN Code Gallery (postarei quando ele voltar a funcionar, já que a funcionalidade de copy-and-paste não está funcionando no momento).

Até a próxima semana!

André Lima

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *