Refactor almost identical Views and ViewModels to be reusable

ReedReed Member ✭✭
edited January 18 in Xamarin.Forms

Let's say I'm building an e-shop application and I want to display a list of orders in progress and delivered orders in separate pages. Both view models and the views are almost identical, because they work on the same model.

The difference between them is a Label that gives some details about the order status. For each of the view, the Label text is formatted differently and is bound to a different property. Also this Label must be translated into multiple languages.

This is how the list views data template could look for orders in progress. The label is nested into grid.
<DataTemplate> <ViewCell> <Grid> <Grid.RowDefinitions> <RowDefinition Height="*" /> <RowDefinition Height="*" /> <RowDefinition Height="*" /> <RowDefinition Height="*" /> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="*" /> <ColumnDefinition Width="*" /> <ColumnDefinition Width="*" /> <ColumnDefinition Width="*" /> <ColumnDefinition Width="*" /> </Grid.ColumnDefinitions> ... <Label Grid.Row="2" Grid.RowSpan="2" Grid.Column="2"> <Label.FormattedText> <FormattedString> <Span Text={resource:Translate OrderInProgress} /> <Span Text="{Binding First}" /> <Span Text=" / " /> <Span Text="{Binding Second}" /> <Span Text={resource:Translate Third} /> </FormattedString> </Label.FormattedText> </Label> ... </Grid> </ViewCell> </DataTemplate>

For delivered orders, everything is the same, but the Label could be formatted like this.
<Label Grid.Row="2" Grid.RowSpan="2" Grid.Column="2"> <Label.FormattedText> <FormattedString> <Span Text={resource:Translate Delivered} /> <Span Text=" : " /> <Span Text="{Binding Name}" /> </FormattedString> </Label.FormattedText> </Label>

As you can see, the only difference is the FormattedString structure, the properties that it binds to and translations.

Difference in the view models are that for each of the view model, when I click one of the list item, it navigates to a different page (different page for orders in progress and delivered orders view model).

If I keep it this way, the code duplication would be very high. How can I solve this?

Possible solutions for the view model problem is that I've thought of is that OrdersInProgressViewModel and OrdersDeliveredViewModel inherits from OrderViewModel and override the navigation part.

And for the view problem is to switch from FormattedString to Label Text property and use value converter to format the Label Text. This would require me to identify if the BindingContext is OrdersInProgressViewModel or OrdersDeliveredViewModel and format it accordingly in the value converter, but I'm not sure if it's possible to reference translations in the value converter class.



  • ClintStLaurentClintStLaurent USUniversity ✭✭✭✭✭

    ViewModels can be used as BindingContext for 0:n views.
    Just use the same ViewModel for all your Views that require the same data.

    As for the couple labels... Format it at the UI layer based on an enum and maybe a DataTrigger or converter. You could have 'mode' enum for things like "Mechanic" "Customer" "Manager" etc.

    Sounds like a situation were you might want to make a reusable control that you place on a few pages, for the purpose of presenting that data.

  • ReedReed Member ✭✭

    Helpful as always, thanks.

    I have couple of questions to clear things up.

    If I understood correctly, my ViewModel should contain the Mode enum, which will tell the view how to format the label and the enum value should be set when navigating to ViewModel.

    I wish I could use the same ViewModel, but I have to implement one ICommand differently which determines to what view it navigates when list item is clicked. Orders in progress list item and delivered order list item will navigate to separate views. when clicked. That's why I proposed to create OrdersViewModel, which will contain all the common functionality (maybe declare it as an abstract class with an abstract property ICommand SelectedOrder) and then I inherit it in OrdersInProgressViewModel and OrdersDeliveredViewModel and implement the SelectedOrder differently.

    Or maybe I could use the same Mode enum to decide where to navigate in the ViewModel?

Sign In or Register to comment.