Events

Xamarin Forms, the MVVMLight Toolkit and I: EventToCommandBehavior

Xamarin Forms, the MVVMLight Toolkit and I: EventToCommandBehavior

Often, we want to/need to know when views throw certain events. However, due to using the MVVM pattern, our application logic is separated from the view. There are several ways to get those events into our ViewModel while keeping it separated from the views. One of those is using an interface, which I showed you already in my blog post about navigation in Xamarin.Forms with MVVMLight.

Another way is the good old EventToCommand approach. Some of you might have used this approach already in WPF and other .NET applications. Xamarin.Forms has them too, this post will show you how to implement it.

Xamarin.Forms Behaviors

In Windows applications like WPF or UWP, we normally use the Interactivity namespace to use behaviors. Xamarin.Forms however has its own implementation, so we need to use the Behavior and Behavior<T> classes. All controls that derive from View are providing this BindableProperty, so we can use Behaviors in a lot of scenarios. Until the new XAML Standard is finally defined, we have to deal with this.

EventToCommandBehavior

Xamarin provides a nearly ready-to-use EventToCommandBehavior implementation and an quite detailed explanation (which is why I won’t go into details on that). The implementation has two part – the BehaviorBase<T>implementation and the EventToCommandBehavior implementation itself.

While we are able to use the BehaviorBase<T> implementation as is, we have to do some minor changes to the EventToCommandBehavior to enable a few more usage scenarios.

The first change we need to make is to derive Xamarin’s EventToCommandBehavior sample from VisualElement instead of View. This way, we can also use the behavior on controls that do not derive from View, especially in Pages. Pages do not derive from View, but they do from VisualElement (like Viewdoes, too). You need to change the Type also on the parameter of the OnAttachedTo and OnDetachingFrom methods in this case (which are the other two changes we need to do).

The rest of the implementation is basically the same like in the Xamarin sample and works quite well.

To show you a simple sample in Action, we are using the Appearing and Disappearing events to attach them via the behavior into our ModalPageViewModelon the ModalPage we integrated before. This way, you won’t need the IViewEventBrokerService I showed you in my post on navigation and modal pages. It is up to you to choose the way you want to go along, both ways are fully respecting the MVVM pattern.

Implementation

The implementation has two parts. As we want to handle the events in a Command, the first step to take is to implement two Commands in the corresponding ViewModel. I am using a base implementation (in my apps and also in this sample), so I am going to implement the Commands there. This way, every derived ViewModel can bind to this Command. Additionally, I am using a Execute...Command method and a CanExecute boolean method, which can both be overriden in derived ViewModels to implement the code to execute. Let’s have a look at the code:

public RelayCommand ViewAppearingCommand => _viewAppearingCommand ?? (_viewAppearingCommand = new RelayCommand(ExecuteViewAppearingCommand, CanExecuteViewAppearingCommand));

public virtual void ExecuteViewAppearingCommand()
{

}

public virtual bool CanExecuteViewAppearingCommand()
{
    return true;
}

public RelayCommand ViewDisappearingCommand => _viewDisappearingCommand ?? (_viewDisappearingCommand = new RelayCommand(ExecuteViewDisappearingCommand, CanExecuteViewDisappearingCommand));

public virtual void ExecuteViewDisappearingCommand()
{

}

public virtual bool CanExecuteViewDisappearingCommand()
{
    return true;
}

The second part is the XAML part, which includes the Binding to the Command properties we just created. The implementation is as easy as these four lines for both events:

    <baseCtrl:XfNavContentPage.Behaviors>
        <behaviors:EventToCommandBehavior EventName="Appearing" Command="{Binding ViewAppearingCommand}"></behaviors:EventToCommandBehavior>
        <behaviors:EventToCommandBehavior EventName="Disappearing" Command="{Binding ViewDisappearingCommand}"></behaviors:EventToCommandBehavior>
    </baseCtrl:XfNavContentPage.Behaviors>

That’s it, if you want to attach the behavior only for individual Pages. If you have a base page implementation like I do however, you can automatically attach the event already there to have it attached to all pages:

private void XfNavContentPage_BindingContextChanged(object sender, EventArgs e)
{
    if (this.BindingContext is XfNavViewModelBase)
    {
        this.Behaviors.Add(new EventToCommandBehavior()
        {
            EventName = "Appearing",
            Command = ((XfNavViewModelBase)this.BindingContext).ViewAppearingCommand
        });
 
        this.Behaviors.Add(new EventToCommandBehavior()
        {
            EventName = "Disappearing",
            Command = ((XfNavViewModelBase)this.BindingContext).ViewDisappearingCommand
        });
    }
}

I am attaching the behaviors only if the BindingContextdoes derive from my XfNavViewModelBase. The Command can be set directly in this case, without the need to use the SetBinding method.

These few lines are connecting the Event to the Command, the only thing we need to do is to override the base implementations of the “Execute…Command” methods:

public override async void ExecuteViewAppearingCommand()
{
    base.ExecuteViewAppearingCommand();
    await _dialogService.ShowMessageAsync(this.CorrespondingViewKey, $"from overriden {nameof(ExecuteViewAppearingCommand)}");
}
 
public override async void ExecuteViewDisappearingCommand()
{
    base.ExecuteViewDisappearingCommand();
    await _dialogService.ShowMessageAsync(this.CorrespondingViewKey, $"from overriden {nameof(ExecuteViewDisappearingCommand)}");
}

The above overrides are using the IDialogService you will find in the sample application to show a simple message from which overriden Execute...Command method they are created from.

Converting EventArgs to specific types

Xamarin.Forms has only a few events that have usefull EventArgs. At the time of writing this post, I tried to find valid scenarios where we want to get some things of the events to attach also an IValueConverterimplementation to get this data out of them. Fact is, the only one I ever used is the one from the Xamarin sample – which is a converter that gets the selected Item for a ListView. Because Xamarin.Forms Views already provide most of the properties I ever needed, I was able to solve everything else via Binding. To make this post complete, you can have a look into Xamarin’s sample implementation here.

Conclusion

Hooking into events on the view side of our applications can be done in several ways. It is up to you to choose the route you want to go. With this post, I showed you a second way to achieve this.

If you have some more valid scenarios for using the EventToCommandBehaviorwith a Converter that cannot be solved via Binding directly, I would love to hear them. Feel free to leave a comment here or via social networks. Of course, I updated the sample on Github with the code from this post.

As always, I hope this post is helpful for some of you. Until the next post, happy coding!

Posted by msicc in Android, Dev Stories, iOS, UWP, Xamarin, 3 comments

How to build a custom Application Bar for your Windows Phone app (the easy way)

customappbarexpanded

In one of my recent projects, I was forced to use icons for the ApplicationBarButtons that didn’t fit into the circled template of the standard Windows Phone application bar.

The icons have special circles themselves, and I am not allowed to change anything on that icons (you’re right, it is for the corporate app I am working on). That’s why I needed to find another solution – and I started to write my own “ApplicationBar”.

As I am using Telerik’s Windows Phone Controls, I knew that the RadImageButton have exactly the same behavior than the buttons in the standard ApplicationBar. That point was already save, the only thing I needed to change was the ButtonShape of the RadImageButton from Rectangle to Ellipse – done.

This is the UserControl I created to achieve my goal:

<Border x:Name="customAppBarBorder"  Height="72" VerticalAlignment="Bottom">
    <Grid >
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="80"/>
            <ColumnDefinition Width="80"/>
            <ColumnDefinition Width="80"/>
            <ColumnDefinition Width="80"/>
            <ColumnDefinition Width="80"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"></RowDefinition>
            <RowDefinition Height="Auto"></RowDefinition>
        </Grid.RowDefinitions>

        <telerikPrimitives:RadImageButton x:Name="CustomAppBarRadImageButton1" Grid.Row="0" Grid.Column="1" HorizontalAlignment="Center" ButtonShape="Ellipse" RestStateImageSource="null" ></telerikPrimitives:RadImageButton>
        <telerikPrimitives:RadImageButton x:Name="CustomAppBarRadImageButton2" Grid.Row="0" Grid.Column="2" HorizontalAlignment="Center" RestStateImageSource="null" ButtonShape="Ellipse" ></telerikPrimitives:RadImageButton>
        <telerikPrimitives:RadImageButton x:Name="CustomAppBarRadImageButton3" Grid.Row="0" Grid.Column="3" HorizontalAlignment="Center" ButtonShape="Ellipse" RestStateImageSource="null"></telerikPrimitives:RadImageButton>
        <telerikPrimitives:RadImageButton x:Name="CustomAppBarRadImageButton4" Grid.Row="0" Grid.Column="4" HorizontalAlignment="Center" ButtonShape="Ellipse" RestStateImageSource="null"></telerikPrimitives:RadImageButton>

        <Image x:Name="overflowDots" Grid.Row="0" Grid.Column="5" Width="72" Source="/Assets/AppBar/overflowdots.png" VerticalAlignment="Top" HorizontalAlignment="Right" Tap="overflowDots_Tap"></Image>

        <TextBlock x:Name="CustomAppBarButtonItem1Text" Grid.Row="1" Grid.Column="1" Width="72" FontSize="14" VerticalAlignment="Center" HorizontalAlignment="Center" TextAlignment="Center" Margin="0,-4,0,0" />
        <TextBlock x:Name="CustomAppBarButtonItem2Text" Grid.Row="1" Grid.Column="2" Width="72" FontSize="14" VerticalAlignment="Center" HorizontalAlignment="Center" TextAlignment="Center" Margin="0,-4,0,0" />
        <TextBlock x:Name="CustomAppBarButtonItem3Text" Grid.Row="1" Grid.Column="3" Width="72" FontSize="14" VerticalAlignment="Center" HorizontalAlignment="Center" TextAlignment="Center" Margin="0,-4,0,0" />
        <TextBlock x:Name="CustomAppBarButtonItem4Text" Grid.Row="1" Grid.Column="4" Width="72" FontSize="14" VerticalAlignment="Center" HorizontalAlignment="Center" TextAlignment="Center" Margin="0,-4,0,0" />
    </Grid>
</Border>

It was a bit tricky to get all the size to get a similar appearance, but it does look like the original one.

The RadImageButton Control has also a Text that we can enter, but the text was to small or to close to the button, no matter what I did. That’s why there is another row in my Grid with the corresponding text.

You may have recognized the image with the Source “overflowdots.png” above. These are located in the Windows Phone icon folder (in Microsoft SDKs under program files). We need this icon to generate the transition the standard ApplicationBar has. It is done with two simple StoryBoards:

<UserControl.Resources>
    <Storyboard x:Name="FadeCustomAppBarButtonTextIn">
        <DoubleAnimation Storyboard.TargetName="customAppBarBorder"
                         Storyboard.TargetProperty="Height"
                         From="72" To="102" Duration="0:0:0.2"/>
    </Storyboard>

    <Storyboard x:Name="FadeCustomAppBarButtonTextOut">
        <DoubleAnimation Storyboard.TargetName="customAppBarBorder"
                         Storyboard.TargetProperty="Height"
                         From="102" To="72" Duration="0:0:0.2"/>
    </Storyboard>
</UserControl.Resources>

All we need now is a proper EventHandler – the TapEvent of the image inside the control is perfect for that:

private void overflowDots_Tap(object sender, System.Windows.Input.GestureEventArgs e)
{
    if (customAppBarBorder.ActualHeight == 72)
    {
        FadeCustomAppBarButtonTextIn.Begin();
    }
    else if (customAppBarBorder.ActualHeight == 102)
    {
        FadeCustomAppBarButtonTextOut.Begin();
    }
}

I am using a Border for the animation because with a Grid it was not as fluent as I wanted. That’s the whole code of the control I created. Let’s have a look at the implementation:

First thing is an additional row in our LayoutRoot Grid,  where we can add our custom app bar control to (set the Height to “Auto”). Add this code to add the app bar:

//declare the control:

public CustomAppBarWP8 customappbar;

//add your data to the app bar:

customappbar = new CustomAppBarWP8();

            customappbar.CustomAppBarBackground = new SolidColorBrush(Colors.Green);

            customappbar.CustomAppBarButtonItem1Text.Text = "test 1";
            customappbar.CustomAppBarButtonItem2Text.Text = "test 2";
            customappbar.CustomAppBarButtonItem3Text.Text = "test 3";
            customappbar.CustomAppBarButtonItem4Text.Text = "test 4";

            customappbar.CustomAppBarRadImageButton1.RestStateImageSource = new BitmapImage(new Uri("Assets/AppBar/microphone.png", UriKind.RelativeOrAbsolute));
            customappbar.CustomAppBarRadImageButton2.RestStateImageSource = new BitmapImage(new Uri("Assets/AppBar/save.png", UriKind.RelativeOrAbsolute));
            customappbar.CustomAppBarRadImageButton3.RestStateImageSource = new BitmapImage(new Uri("Assets/AppBar/delete.png", UriKind.RelativeOrAbsolute));
            customappbar.CustomAppBarRadImageButton4.RestStateImageSource = new BitmapImage(new Uri("Assets/AppBar/questionmark.png", UriKind.RelativeOrAbsolute));

//registering the tap events:

            customappbar.CustomAppBarRadImageButton1.Tap += CustomAppBarRadImageButton1_Tap;
            customappbar.CustomAppBarRadImageButton2.Tap += CustomAppBarRadImageButton2_Tap;
            customappbar.CustomAppBarRadImageButton3.Tap += CustomAppBarRadImageButton3_Tap;
            customappbar.CustomAppBarRadImageButton4.Tap += CustomAppBarRadImageButton4_Tap;

//adding the app bar to the dedicated Grid:
            AppBarGrid.Children.Add(customappbar);

The standard Application Bar does fading out the text on a button tap, so we need to add this line in every tap event. Otherwise, it would remain open all the time.

if (customappbar.ActualHeight == 102)
{
    customappbar.FadeCustomAppBarButtonTextOut.Begin();
}

To get the same result on tapping outside our custom app bar, add the same code to your main Grid’s MouseLeftButtonDown event. This way, you have the same behavior like in the original control.

Additional note: I needed to find a quick way to achieve this, that’s why I may have not been using best practices. I also used the RadImageButton Control to speed things up. I will refine this control when I have more time available for it, as well as add a version without Telerik controls and adding the menu items.

If you have any idea on how to improve this, feel free to left a comment below.

Anyways, you can download the source of the code above here: https://github.com/MSiccDev/CustomAppBar_WP8

As always, I hope this will be helpful for some of you.

Posted by msicc in Dev Stories, wpdev, 0 comments