MVVM no Windows Phone 7 – Parte 1 (Commands)
O padrão MVVM (Model-View-ViewModel) é uma especialização do padrão Presenter porém utilizando capacidades específicas de Silverlight e WPF como data binding, command e behaviors. O MVVM é semelhante a muitos outros padrões que separam a camada de apresentação da camada de lógica, sendo assim toda a lógica de negócio pode ser programada para qualquer tipo de interface XAML. Utilizando MVVM o código da sua view irá ficar totalmente limpo e muito mais fácil de dar manutenção. Sem utilizar MVVM o simples clique de um botão é definido da seguinte forma:
<Button Click="Button_Click" />
private void Button_Click(object sender, RoutedEventArgs e)
{
}
Mas qual o problema desta abordagem? Bom dou um bom exemplo: Você está desenvolvendo um sitema que tem um cliente Desktop WPF e um cliente móvel em WP7. Se os códigos estiverem todos nas views, terá que recodificar tudo em cada cliente. Com a utilização dos viewModels, você irá codificar esta ação uma única vez tanto para WPF quando para WP7. Deixando a teoria de lado, vamos por a mão na massa. Requisitos:
- Prism v4 para Windows Phone 7
Crie um novo projeto utilizando o template Windows Phone Application, e coloque o nome de CommandWP7. Com nosso projeto criado adicione um botão à MainPage.xaml ![]()
<!--ContentPanel - place additional content here--> <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0"> <Button Content="Botão com Command" HorizontalAlignment="Center" VerticalAlignment="Center"/> </Grid>
No WP7 por enquanto não temos uma propriedade Command e CommandParameter como no silverlight e wpf, então teremos que utilizar um Behavior para nosso command funcionar. Adicione a referência ao assembly System.Windows.Interactivity ao seu projeto e coloque no seu xaml o uso deste assembly
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
e modificaremos o código do nosso button para
<Button Content="Botão com Command" HorizontalAlignment="Center" VerticalAlignment="Center"> <i:Interaction.Behaviors> </i:Interaction.Behaviors> </Button>
Agora teremos que criar o nosso ButtonCommand que será uma implementação de um beehavior que irá fazer o binding do nosso botão com o comando no ViewModel, adicione a referência ao assembly Microsoft.Practices.Prism e Microsoft.Practices.Prism.Interactivity, crie uma nova classe e dê o nome de ButtonCommand.
public class ButtonCommand : Behavior<Button>
{
public static readonly DependencyProperty CommandParameterBindingProperty =
DependencyProperty.Register("CommandParameterBinding", typeof(Binding), typeof(ButtonCommand), new PropertyMetadata(HandleBindingChanged));
public static readonly DependencyProperty CommandBindingProperty =
DependencyProperty.Register("CommandBinding", typeof(Binding), typeof(ButtonCommand), new PropertyMetadata(HandleBindingChanged));
private readonly BindingListener commandBindinglistener;
private readonly BindingListener parameterBindinglistener;
private ButtonClickCommandBinding binding;
public ButtonCommand()
{
this.commandBindinglistener = new BindingListener(this.HandleCommandBindingValueChanged);
this.parameterBindinglistener = new BindingListener(this.HandleCommandParameterBindingValueChanged);
}
public Binding CommandBinding
{
get { return (Binding)GetValue(CommandBindingProperty); }
set { SetValue(CommandBindingProperty, value); }
}
public Binding CommandParameterBinding
{
get { return (Binding)GetValue(CommandParameterBindingProperty); }
set { SetValue(CommandParameterBindingProperty, value); }
}
protected ICommand Command { get; set; }
protected object CommandParameter { get; set; }
protected override void OnAttached()
{
this.commandBindinglistener.Element = this.AssociatedObject;
this.parameterBindinglistener.Element = this.AssociatedObject;
this.CreateBinding();
base.OnAttached();
}
protected override void OnDetaching()
{
this.commandBindinglistener.Element = null;
this.parameterBindinglistener.Element = null;
base.OnDetaching();
}
protected void OnBindingChanged(DependencyPropertyChangedEventArgs e)
{
if (e.Property == CommandBindingProperty)
{
this.commandBindinglistener.Binding = (Binding)e.NewValue;
}
if (e.Property == CommandParameterBindingProperty)
{
this.parameterBindinglistener.Binding = (Binding)e.NewValue;
}
this.CreateBinding();
}
private static void HandleBindingChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
((ButtonCommand)sender).OnBindingChanged(e);
}
private void HandleCommandBindingValueChanged(object sender, BindingChangedEventArgs e)
{
this.CreateBinding();
}
private void HandleCommandParameterBindingValueChanged(object sender, BindingChangedEventArgs e)
{
this.CreateBinding();
}
private void CreateBinding()
{
if (this.commandBindinglistener.Value != null)
{
if (this.binding != null)
{
this.binding.Detach();
}
this.binding = new ButtonClickCommandBinding(
this.AssociatedObject,
(ICommand)this.commandBindinglistener.Value,
() => this.parameterBindinglistener.Value);
}
}
public class ButtonClickCommandBinding
{
private readonly ICommand command;
private readonly Button button;
private readonly Func<object> parameterGetter;
public ButtonClickCommandBinding(Button button, ICommand command, Func<object> parameterGetter)
{
this.command = command;
this.button = button;
this.parameterGetter = parameterGetter;
this.command.CanExecuteChanged += this.CommandCanExecuteChanged;
this.button.Click += this.IconButtonClicked;
this.button.IsEnabled = this.command.CanExecute(this.parameterGetter());
}
public void Detach()
{
this.button.Click -= this.IconButtonClicked;
this.command.CanExecuteChanged -= this.CommandCanExecuteChanged;
}
private void IconButtonClicked(object s, EventArgs e)
{
this.command.Execute(this.parameterGetter());
}
private void CommandCanExecuteChanged(object s, EventArgs ea)
{
this.button.IsEnabled = this.command.CanExecute(this.parameterGetter());
}
}
}
Faça o build do projeto e volte ao nosso botão. Teremos que adicionar o namespace do projeto em nosso xaml
xmlns:local="clr-namespace:CommandWp7"
e então adicionar o nosso behavior de command ao nosso botão fazendo o binding para o comando no viewmodel.
<Button Content="Botão com Command" HorizontalAlignment="Center" VerticalAlignment="Center">
<i:Interaction.Behaviors>
<local:ButtonCommand CommandBinding="{Binding ViewCommand}"/>
</i:Interaction.Behaviors>
</Button>
Para criar o ViewModel, crie uma nova classe e coloque o nome de MainPageViewModel
public class MainPageViewModel
{
public DelegateCommand ViewCommand { get; set; }
public MainPageViewModel()
{
this.ViewCommand = new DelegateCommand(ViewCommandInvoke);
}
public void ViewCommandInvoke()
{
MessageBox.Show("Comando executado com sucesso");
}
}
e por último no MainPage.xaml.cs setamos o DataContext da view para o viewModel
public partial class MainPage : PhoneApplicationPage
{
// Constructor
public MainPage()
{
InitializeComponent();
this.DataContext = new MainPageViewModel();
}
}
agora é só executar.
O codigo fonte pode ser baixado aqui.