Xamarin Forms with Prism - Problem with using WebService - forms

Can anyone help me? I created simply project Xamarin Forms with Prism i VS2017 on Android (screen). I used Prism Template Pack. I would like connect project with my WebService. here is a link to screen of all project
I have two projects PrismCoursApp and PrismCoursApp.Droid. First project contains SecondPageViewModel.cs where I try use connected WebService (wsMES) but I can't add namespace with PrismCoursApp.Droid.
The namespace of project PrismCourseApp.Android is PrismCourseApp.Droid and
PrismCourseApp.Android depends on PrismCourseApp.
I could add reference to Web service only in PrismCoursApp.Android project but I would like to use it in SecondPageViewModel.cs in PrismCourseApp.
Can someone tell me what I'm doing wrong?
Thanks
SecondPageViewModel.cs
using Prism.Commands;
using Prism.Mvvm;
using Prism.Navigation;
using System;
using System.Collections.Generic;
using System.Linq;
using PrismCourseApp.Models;
using System.Collections.ObjectModel;
namespace PrismCourseApp.ViewModels
{
public class SecondPageViewModel : BindableBase, INavigationAware
{
//zmienna do WebService
//wsMES.WSwitoMES ws = new wsMES.WSwitoMES();
private string _title;
public string Title
{
get { return _title; }
set { SetProperty(ref _title, value); }
}
private string _UserCode;
public string UserCode
{
get { return _UserCode; }
set { SetProperty(ref _UserCode, value); }
}
private string _LokalizCode;
public string LokalizCode
{
get { return _LokalizCode; }
set { SetProperty(ref _LokalizCode, value); }
}
public SecondPageViewModel()
{
UserCode = AppStateTest.User;
LokalizCode = AppStateTest.CurrentCode;
Title = "Użytkownik/Lokalizacja";
}
public void OnNavigatedFrom(INavigationParameters parameters)
{
}
public void OnNavigatedTo(INavigationParameters parameters)
{
if (parameters.ContainsKey("par1"))
{
string par1 = (string)parameters["par1"];
string par2 = (string)parameters["par2"];
}
}
public void OnNavigatingTo(INavigationParameters parameters)
{
}
}
}
SecondPage.axml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:prism="clr-namespace:Prism.Mvvm;assembly=Prism.Forms"
prism:ViewModelLocator.AutowireViewModel="True"
x:Class="PrismCourseApp.Views.SecondPage"
BackgroundColor="White"
Title="{Binding Title}"
xmlns:b="clr-namespace:Prism.Behaviors;assembly=Prism.Forms"
xmlns:c="clr-namespace:PrismCourseApp.Converters;assembly=PrismCourseApp">
<ContentPage.Resources>
<ResourceDictionary>
<!--<c:ItemTappedEventArgsConverter x:Key="itemTappedEventArgsConverter" />-->
</ResourceDictionary>
</ContentPage.Resources>
<StackLayout
Spacing="20">
<Label
Text="Zalogowany użytkownik:"
TextColor="Gray"/>
<Label
Text="{Binding UserCode}"
FontSize="Large"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand" />
<Label
Text="Lokalizacja:"
TextColor="Gray"/>
<Label
Text="{Binding LokalizCode}"
FontSize="Large"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand" />
<ListView
x:Name="lstView">
<!--ItemsSource="{Binding MyDatas}">-->
<!--<ListView.Behaviors>
<b:EventToCommandBehavior EventName="ItemTapped"
Command="{Binding ItemTappedCommand}"
EventArgsConverter="{StaticResource itemTappedEventArgsConverter}" />
</ListView.Behaviors>-->
<ListView.ItemTemplate>
<DataTemplate>
<TextCell Text="{Binding name}" Detail="{Binding comment}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage>
SecondPage.axml.cs
using Xamarin.Forms;
using PrismCourseApp.Models;
using System.Collections.ObjectModel;
namespace PrismCourseApp.Views
{
public partial class SecondPage : ContentPage
{
//Elementy do ListView (klasa MyDate w PrismCourseApp)
private ObservableCollection<MyDate> MyDatas { get; set; }
public SecondPage()
{
InitializeComponent();
MyDatas = new ObservableCollection<MyDate>();
lstView.ItemsSource = MyDatas;
for (int i = 0; i < 30; i++)
{
MyDatas.Add(new MyDate
{
name = "Pozycja " + (i+1).ToString(),
comment = "Miejsce na szczegóły " + (i+1).ToString()
});
}
}
}
}
MainActivity.cs in Android Project
using Android.App;
using Android.Content.PM;
using Android.OS;
using Prism;
using Prism.Ioc;
namespace PrismCourseApp.Droid
{
[Activity(Label = "PrismCourseApp", Icon = "#drawable/ic_launcher", Theme = "#style/MainTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
protected override void OnCreate(Bundle bundle)
{
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;
base.OnCreate(bundle);
global::Xamarin.Forms.Forms.Init(this, bundle);
LoadApplication(new App(new AndroidInitializer()));
}
}
public class AndroidInitializer : IPlatformInitializer
{
public void RegisterTypes(IContainerRegistry container)
{
// Register any platform specific implementations
}
}
}

Summarising the discussion in the comments above:
One should not make a web service dependent upon something specific to one of many client platforms. It's preferable to put the service's interface between the server and the part of the client that's shared between the different client implementations.
Say you have a mobile app for Android and IOS. Then you'll have two projects MyApp.Droid and MyApp.IOS for the respective client-specific implementations. Also, there's a project that both of them reference, and that (hopefully) contains most of your app's client-side logic: MyApp.Logic.
Now for the server: you have the MyApp.Server project that implements the service. If you need to define interfaces to communicate between the app and the service (WCF comes to mind), you define a project referenced by both the client-side logic (MyApp.Logic) and the server implementation (MyApp.Server): MyApp.Interface.
MyApp.Droid & MyApp.IOS -ref-> MyApp.Logic -ref-> MyApp.Interface <-ref- MyApp.Server

Related

Collectionview SelectionChangeCommand doesn´t work with CommunityToolkit.Mvvm .NET MAUI

I have a simple project using Community Toolkit Mvvm tool and a collectionview. The issue is that the SelectionChangeCommand of the CollectionView doesn´t fired when I select an element of the collection. I created this project because on another more complex project the error is that it can't find the binding command on the viewmodel. I know that the connection between the view and viewmodel is working because the collectionview is filling with elements on the viewmodel and also I am able to change the visibility of the border through the binded property.
Sample project: https://github.com/luis95gr/MvvmError.App
Code
LoginPage (first page with collectionview)
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MvvmError.Views.LoginPage"
xmlns:models="clr-namespace:MvvmError.Models"
Title="LoginPage">
<RefreshView VerticalOptions="FillAndExpand">
<Border BackgroundColor="Red" IsVisible="{Binding Visible}" Stroke="Red" HorizontalOptions="FillAndExpand" Padding="0,0" Margin="0,0,0,0" VerticalOptions="FillAndExpand">
<Border.StrokeShape>
<RoundRectangle CornerRadius="40,0,0,0" />
</Border.StrokeShape>
<CollectionView Background="Transparent" ItemsSource="{Binding Messages}" SelectionMode="Single" SelectedItem="{Binding MessageSelected}" SelectionChangedCommand="{Binding MessageSelectedCommand}">
<CollectionView.ItemTemplate>
<DataTemplate x:DataType="models:MessagesMessages">
<Grid RowDefinitions="100*" ColumnDefinitions="100*">
<Label Grid.Row="0" Text="{Binding Asunto}" />
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</Border>
</RefreshView>
LoginPageViewModel
public partial class LoginPageViewModel : ObservableObject
{
[ObservableProperty]
private MessagesMessages messageSelected;
[ObservableProperty]
private bool visible;
public ObservableCollection<MessagesMessages> Messages { get; } = new();
public LoginPageViewModel()
{
Visible = true;
GetMessages();
}
[RelayCommand]
async void MessageSelectedCommand()
{
var snackbar = Snackbar.Make("Hola");
await snackbar.Show();
}
private void GetMessages()
{
Messages.Add(new MessagesMessages
{
Asunto = "Hola"
});
Messages.Add(new MessagesMessages
{
Asunto = "Hola1"
});
Messages.Add(new MessagesMessages
{
Asunto = "Hola2"
});
Messages.Add(new MessagesMessages
{
Asunto = "Hola3"
});
}
}
LoginPage.xaml.cs
public partial class LoginPage : ContentPage
{
public LoginPage(LoginPageViewModel loginPageViewModel)
{
InitializeComponent();
BindingContext= loginPageViewModel;
}
}
MauiProgram.cs
builder.Services.AddTransient<LoginPage>();
builder.Services.AddSingleton<ViewModels.LoginPageViewModel>();
you are not following the naming convention
The name of the generated command will be created based on the method
name. The generator will use the method name and append "Command" at
the end, and it will strip the "On" prefix, if present. Additionally,
for asynchronous methods, the "Async" suffix is also stripped before
"Command" is appeneded.
to generate a command named MessageSelectedCommand your method should look like
[RelayCommand]
async void MessageSelected()

CommunityToolkit.Maui.Core.Views.MauiPopup throws System.ObjectDisposedException on Close()

The following code results in a System.ObjectDisposedException on the line Close(result); in BarcodeScannerPopup.xaml.cs when a barcode is detected by the CameraBarcodeReaderView.
The detected barcode is correctly displayed in the label in BarcodePage.
Any idea why this exception is thrown?
BarcodePage.xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage
x:Class="MyApp.BarcodePage"
x:DataType="vm:BarcodeViewModel"
xmlns:vm="clr-namespace:MyApp.ViewModels"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns="http://schemas.microsoft.com/dotnet/2021/maui">
<VerticalStackLayout
Margin="20">
<Label
Text="{Binding Barcode}"/>
<Button
Command="{Binding ScanBarcodeClickCommand}"
Text="Scan barcode"/>
</VerticalStackLayout>
</ContentPage>
BarcodePage.xaml.cs
using MyApp.ViewModels;
namespace MyApp.Views;
public partial class BarcodePage : ContentPage
{
public BarcodePage(BarcodeViewModel viewModel)
{
InitializeComponent();
BindingContext = viewModel;
}
}
BarcodeScannerPopup.xaml
<?xml version="1.0" encoding="utf-8" ?>
<toolkit:Popup
x:Class="MyApp.BarcodeScannerPopup"
xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:zxing="clr-namespace:ZXing.Net.Maui.Controls;assembly=ZXing.Net.MAUI"
xmlns="http://schemas.microsoft.com/dotnet/2021/maui">
<VerticalStackLayout
Margin="20">
<Label
Text="Add a barcode by scanning it."
VerticalOptions="Center"
HorizontalOptions="Center"/>
<zxing:CameraBarcodeReaderView
x:Name="barcodeReader"
BarcodesDetected="BarcodesDetected"
HeightRequest="300"
IsDetecting="True"
Margin="5"
WidthRequest="300"/>
</VerticalStackLayout>
</toolkit:Popup>
BarcodeScannerPopup.xaml.cs
using CommunityToolkit.Maui.Views;
using ZXing.Net.Maui;
namespace MyApp.Views;
public partial class BarcodeScannerPopup : Popup
{
public BarcodeScannerPopup()
{
InitializeComponent();
barcodeReader.Options = new BarcodeReaderOptions
{
AutoRotate = true,
Multiple = false
};
}
private void BarcodesDetected(object sender, BarcodeDetectionEventArgs e)
{
var result = e.Results[0].Value;
Close(result);
}
}
BarcodeViewModel.cs
using CommunityToolkit.Maui.Views;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using MyApp.Views;
namespace MyApp.ViewModels;
public partial class BarcodeViewModel : ObservableObject
{
[ObservableProperty]
private string _barcode;
private BarcodeScannerPopup _popup = new BarcodeScannerPopup();
[RelayCommand]
public async Task ScanBarcodeClick()
{
Barcode = (string)await Application.Current.MainPage.ShowPopupAsync(_popup);
}
}
#ToolmakerSteve was right. The CameraBarcodeReaderView continues sending data which results in the System.ObjectDisposedException. setting the IsDetecting property to false prevents this. As this was a PoC I didn't add a proper null check on e, so I've added as a good coding practise in this example.
BarcodeScannerPopup.xaml.cs
using CommunityToolkit.Maui.Views;
using ZXing.Net.Maui;
namespace MyApp.Views;
public partial class BarcodeScannerPopup : Popup
{
public BarcodeScannerPopup()
{
InitializeComponent();
barcodeReader.Options = new BarcodeReaderOptions
{
AutoRotate = true,
Multiple = false
};
}
private void BarcodesDetected(object sender, BarcodeDetectionEventArgs e)
{
var result = e?.Results?.Any() == true
? e.Results[0].Value
: string.Empty;
IsDetecting = false;
Close(result);
}
}

MAUI: Listview cannot be refreshed automatically

There are 2 elements binding data in my test app, one is Button, another is Listview. But Listview cannnot be refreshed automatically. But if I disable ButtonText = $"It took {totalSecs} seconds" in ViewModel, Listview can be refreshed automatically. Any ideas? TIA.
Here is the Xaml file.
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:test"
x:Class="test.MainPage">
<ContentPage.BindingContext>
<local:PurchasesInvoiceMasterViewModel />
</ContentPage.BindingContext>
<ScrollView>
<VerticalStackLayout
Spacing="25"
Padding="30,0"
VerticalOptions="Start">
<Button
x:Name="PurchasesInvoiceList"
Text="{Binding ButtonText}"
Command="{Binding AddListCommand}"
SemanticProperties.Hint="Get Purchases Invoice List"
HorizontalOptions="Center" />
<ListView x:Name="PurchasesInvoiceMasterObjectView" ItemsSource="{Binding PurchasesInvoiceMasterObject}" HeightRequest="500">
<ListView.ItemTemplate>
<DataTemplate>
<TextCell Text="{Binding purchases_invoice_id}"
Detail="{Binding purchases_invoice_date }" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</VerticalStackLayout>
</ScrollView>
Here is ViewModel.
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
using System.Xml.Linq;
namespace test
{
class PurchasesInvoiceMasterViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public ICommand AddListCommand { get; set; }
private string _buttonText = "Purchases Invoice List";
public string ButtonText
{
get => _buttonText;
set
{
if (_buttonText != value)
{
_buttonText = value;
OnPropertyChanged("");
}
}
}
private ObservableCollection<PurchasesInvoiceMaster> _purchasesInvoiceMasterObject = new();
public ObservableCollection<PurchasesInvoiceMaster> PurchasesInvoiceMasterObject
{
get => _purchasesInvoiceMasterObject;
set
{
if (_purchasesInvoiceMasterObject != value)
{
_purchasesInvoiceMasterObject = value;
OnPropertyChanged("");
}
}
}
public PurchasesInvoiceMasterViewModel()
{
AddListCommand = new Command(() =>
{
double statrtSecs = DateTime.Now.TimeOfDay.TotalSeconds;
string purchasesInvoiceMaster = "test.PurchasesInvoiceMaster.xml";
using (Stream streamPurchasesInvoiceMaster = this.GetType().Assembly.
GetManifestResourceStream(purchasesInvoiceMaster))
{
using (var readerPurchasesInvoiceMaster = new System.IO.StreamReader(streamPurchasesInvoiceMaster))
{
var xmlpurchasesInvoiceMaster = XDocument.Load(readerPurchasesInvoiceMaster);
foreach (XElement xmmd in xmlpurchasesInvoiceMaster.Descendants("curtemp01"))
{
_purchasesInvoiceMasterObject.Add(new PurchasesInvoiceMaster
{
purchases_invoice_id = xmmd.Element("purchases_invoice_id").Value,
purchases_invoice_date = xmmd.Element("purchases_invoice_date").Value,
});
}
}
}
double endSecs = DateTime.Now.TimeOfDay.TotalSeconds;
float totalSecs = (float)(endSecs - statrtSecs);
ButtonText = $"It took {totalSecs} seconds";
});
}
public void OnPropertyChanged([CallerMemberName] string name = "") =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}

Xamarin forms TabbedPage View Model called multiple time

I have implemented Tabbedpage using ViewModel but my ViewModel constructor call 4 times because I create 4 tabs, I also used prism for ViewModel binding.
Below is a design file
<?xml version="1.0" encoding="UTF-8"?>
<TabbedPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:prism="clr-namespace:Prism.Mvvm;assembly=Prism.Forms"
xmlns:material="clr-namespace:XF.Material.Forms.UI;assembly=XF.Material"
xmlns:ffimageloading="clr-namespace:FFImageLoading.Forms;assembly=FFImageLoading.Forms"
xmlns:ffTransformations="clr-namespace:FFImageLoading.Transformations;assembly=FFImageLoading.Transformations"
prism:ViewModelLocator.AutowireViewModel="True"
xmlns:ios="clr-namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assembly=Xamarin.Forms.Core"
xmlns:extended="clr-namespace:Xamarin.Forms.Extended;assembly=Xamarin.Forms.Extended.InfiniteScrolling"
xmlns:customcontrols="clr-namespace:QuranicQuizzes.CustomControls"
xmlns:local="clr-namespace:QuranicQuizzes.Views" NavigationPage.HasNavigationBar="True"
x:Class="QuranicQuizzes.Views.DashboardPage">
<NavigationPage.TitleView>
<StackLayout Orientation="Horizontal" HorizontalOptions="FillAndExpand">
<Label Text="Dashboard" TextColor="White" HorizontalTextAlignment="Center" HorizontalOptions="CenterAndExpand" VerticalTextAlignment="Center" FontFamily="{StaticResource QuranFontBold}" FontSize="Medium" />
<StackLayout Orientation="Horizontal">
<material:MaterialMenuButton x:Name="Menus" ButtonType="Text" Image="list" TintColor="White" BackgroundColor="Transparent" CornerRadius="24" Choices="{Binding Actions}" MenuSelected="MaterialMenuButton_MenuSelected" />
</StackLayout>
</StackLayout>
</NavigationPage.TitleView>
<local:HomeTabPage/>
<local:QuizzesTabPage/>
<local:LiveGameTabPage/>
<local:AssignmentTabPage/>
</TabbedPage>
Below is my code
public partial class DashboardPage : TabbedPage
{
private DashboardPageViewModel vm;
public DashboardPage()
{
try
{
InitializeComponent();
vm = BindingContext as DashboardPageViewModel;
}
catch (Exception ex)
{
}
}
}
Below is my ViewModel
public class DashboardPageViewModel : ViewModelBase
{
INavigationService _navigationService;
IClientAPI _clientAPI;
Dashboards dashboard;
public DashboardPageViewModel(INavigationService navigationService, IClientAPI clientAPI) : base(navigationService)
{
_navigationService = navigationService;
_clientAPI = clientAPI;
if (CrossConnectivity.Current.IsConnected)
{
var StartDate = DateTime.Now.AddDays(-7).ToString("yyyy-MM-dd");
var Enddate = DateTime.Now.ToString("yyyy-MM-dd");
if (dashboard == null)
{
dashboard = new Dashboards();
getDashboardData(StartDate, Enddate);
}
}
}
}
I see what you're trying to do. You want to initialise your vm instance so that you can access you vm from your view.
Instead of doing this:
vm = BindingContext as DashboardPageViewModel;
what we can do is change the type of the existing BindingContext property by doing this:
public partial class DashboardPage
{
new DashboardPageViewModel BindingContext
{
get => (DashboardPageViewModel) base.BindingContext;
set => base.BindingContext = value;
}
public DashboardPage()
{
InitializeComponent();
}
}
now you can just access BindingContext.DoSomething because its type is now DashboardPageViewModel.
Now that's sorted out, your viewmodel should not be being called 4 times! Something is wrong here. Here is a checklist of things to do that may be causing the constructor being called 4 times as not a lot more info was provided.
Try removing <NavigationPage.TitleView> segment.
Make sure you are navigating to DashboardPage.
Make sure that each individual TabbedPage has it's own viewmodel.
Try removing prism:ViewModelLocator.AutowireViewModel="True"and manually adding the viewmodel to the TabbedPage.
Finally constructors should be able to run very fast and should only be used for assigning variables or instantiation or very quick operations. What you could maybe do is separate the code in your VM:
public class DashboardPageViewModel : ViewModelBase
{
IClientAPI _clientAPI;
Dashboards dashboard;
public DashboardPageViewModel(INavigationService navigationService, IClientAPI clientAPI) : base(navigationService)
{
_clientAPI = clientAPI;
}
public void Init()
{
if (CrossConnectivity.Current.IsConnected)
{
var StartDate = DateTime.Now.AddDays(-7).ToString("yyyy-MM-dd");
var Enddate = DateTime.Now.ToString("yyyy-MM-dd");
if (dashboard == null)
{
dashboard = new Dashboards();
getDashboardData(StartDate, Enddate);
}
}
}
}
and then in your view you could add this method:
protected override void OnBindingContextChanged()
{
base.OnBindingContextChanged();
if(BindingContext == null)
{
return;
}
BindingContext.Init();
}
I hope this really helps you.
NB: All this code was written on the fly and never compiled, there may be some errors.

UWP - How to specify the order of updates when use x:Bind?

I'm developing a UWP app and I'm facing a problem. The app uses the MVVM pattern with Template10. I have created a similar solution that recreates the problem that I'm facing. In that solution, a list of orders are displayed, the user chooses an order and then click the "Edit" button. Then a second page is displayed and pre-loaded with the previous selected order, in this second page the user can edit the order. The problem is in the second page, the data bound to comboboxes doesn't show. Maybe the problem is related to this question. In my case, the SelectedValue is set before the ItemsSource. After debugging, I have reached these lines of code in OrderEditionPage.g.cs:
private void Update_ViewModel(global::ComboApp.ViewModels.OrderEditionPageViewModel obj, int phase)
{
this.bindingsTracking.UpdateChildListeners_ViewModel(obj);
if (obj != null)
{
if ((phase & (NOT_PHASED | DATA_CHANGED | (1 << 0))) != 0)
{
this.Update_ViewModel_SelectedOrder(obj.SelectedOrder, phase);
}
if ((phase & (NOT_PHASED | (1 << 0))) != 0)
{
this.Update_ViewModel_BusinessAssociates(obj.BusinessAssociates, phase);
this.Update_ViewModel_TransactionTypes(obj.TransactionTypes, phase);
this.Update_ViewModel_OrderTypes(obj.OrderTypes, phase);
this.Update_ViewModel_ShowSelectedOrder(obj.ShowSelectedOrder, phase);
}
}
}
If I could achieve this line of code be executed at last, my problem would be solved: this.Update_ViewModel_SelectedOrder(obj.SelectedOrder, phase);
How could I achieve this? How does Visual Studio determine the order of this lines?
OrderEditionPage.xaml
<Page
x:Class="ComboApp.Views.OrderEditionPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:myconverters="using:ComboApp.Converters"
xmlns:t10converters="using:Template10.Converters"
mc:Ignorable="d">
<Page.Resources>
<t10converters:ChangeTypeConverter x:Key="TypeConverter" />
<myconverters:DateTimeConverter x:Key="DateTimeConverter" />
</Page.Resources>
<ScrollViewer VerticalScrollBarVisibility="Auto">
<StackPanel
Padding="15, 5"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<TextBox
Header="Order #"
Margin="5"
Width="150"
HorizontalAlignment="Left"
Text="{x:Bind ViewModel.SelectedOrder.ExternalId, Mode=TwoWay}" />
<ComboBox
Header="Business Associate"
Margin="5"
MinWidth="300"
SelectedValuePath="BusinessAssociateId"
DisplayMemberPath="Name1"
ItemsSource="{x:Bind ViewModel.BusinessAssociates}"
SelectedValue="{x:Bind ViewModel.SelectedOrder.BusinessAssociateId, Mode=TwoWay, Converter={StaticResource TypeConverter}}" />
<DatePicker
Header="Delivery Date"
Margin="5"
MinWidth="0"
Width="200"
Date="{x:Bind ViewModel.SelectedOrder.DeliveryDate, Mode=TwoWay, Converter={StaticResource DateTimeConverter}}" />
<ComboBox
Header="Transaction"
MinWidth="200"
Margin="5"
SelectedValuePath="Value"
DisplayMemberPath="Display"
ItemsSource="{x:Bind ViewModel.TransactionTypes}"
SelectedValue="{x:Bind ViewModel.SelectedOrder.TransactionType, Mode=TwoWay}" />
<TextBox
Header="Priority"
Margin="5"
MaxWidth="150"
HorizontalAlignment="Left"
Text="{x:Bind ViewModel.SelectedOrder.Priority}" />
<ComboBox
Header="Type"
Margin="5"
MinWidth="200"
SelectedValuePath="Value"
DisplayMemberPath="Display"
ItemsSource="{x:Bind ViewModel.OrderTypes}"
SelectedValue="{x:Bind ViewModel.SelectedOrder.OrderType, Mode=TwoWay}" />
<TextBox
Header="Information"
Margin="5"
Height="100"
AcceptsReturn="True"
TextWrapping="Wrap"
ScrollViewer.VerticalScrollBarVisibility="Auto"
Text="{x:Bind ViewModel.SelectedOrder.Information, Mode=TwoWay}" />
<Button
Margin="5"
Content="Show"
Width="100"
HorizontalAlignment="Right"
Command="{x:Bind ViewModel.ShowSelectedOrder}" />
</StackPanel>
</ScrollViewer>
</Page>
OrderEditionPage.xaml.cs
using ComboApp.ViewModels;
using Windows.UI.Xaml.Controls;
namespace ComboApp.Views
{
public sealed partial class OrderEditionPage : Page
{
public OrderEditionPageViewModel ViewModel => DataContext as OrderEditionPageViewModel;
public OrderEditionPage()
{
this.InitializeComponent();
}
}
}
OrderEditionPageViewModel.cs
using ComboApp.Models;
using ComboApp.Services;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Threading.Tasks;
using Template10.Mvvm;
using Template10.Utils;
using Windows.UI.Xaml.Navigation;
namespace ComboApp.ViewModels
{
public class OrderEditionPageViewModel
: ViewModelBase
{
private IBusinessAssociateService businessAssociateService;
private Order selectedOrder;
public Order SelectedOrder
{
get { return selectedOrder; }
set { Set(ref selectedOrder, value); }
}
public ObservableCollection<object> TransactionTypes { get; set; } = new ObservableCollection<object>();
public ObservableCollection<object> OrderTypes { get; set; } = new ObservableCollection<object>();
public ObservableCollection<BusinessAssociate> BusinessAssociates { get; set; } = new ObservableCollection<BusinessAssociate>();
public OrderEditionPageViewModel(IBusinessAssociateService businessAssociateService)
{
this.businessAssociateService = businessAssociateService;
TransactionTypes.Add(new { Value = "I", Display = "Incoming" });
TransactionTypes.Add(new { Value = "O", Display = "Outgoing" });
TransactionTypes.Add(new { Value = "T", Display = "Transfer" });
OrderTypes.Add(new { Value = "M", Display = "Manual" });
OrderTypes.Add(new { Value = "A", Display = "Automatic" });
OrderTypes.Add(new { Value = "S", Display = "Semi-automatic" });
}
public override async Task OnNavigatedToAsync(object parameter, NavigationMode mode, IDictionary<string, object> state)
{
// Loading buiness associates
var response = await businessAssociateService.GetNextPageAsync();
if (response.IsSuccessful)
{
BusinessAssociates.AddRange(response.Result.Items);
}
SelectedOrder = (Order)parameter;
await base.OnNavigatedToAsync(parameter, mode, state);
}
private DelegateCommand showSelectedOrder;
public DelegateCommand ShowSelectedOrder => showSelectedOrder ?? (showSelectedOrder = new DelegateCommand(async () =>
{
await Views.MessageBox.ShowAsync(JsonConvert.SerializeObject(SelectedOrder, Formatting.Indented));
}));
}
}
It is a known issue of x:Bind when the SelectedValue of a ComboBox is sometimes set before its ItemsSource, you can read more about it here.
As a workaround you can use Bindings instead of x:Bind, but make sure that ItemsSource binding is placed before SelectedValue binding in XAML.
Alternatively you can try calling Bindings.Update() in the Page_Loaded event of your second page.