Xamarin TabbedPage绑定自定义类启动触发TargetInvocationException求助
Hey folks, I'm hitting a TargetInvocationException right when my Xamarin.Forms app starts up, and it happens when I try to bind custom ViewModels to the two child ContentPages in my TabbedPage. Let me share my code and see if anyone can spot what's going wrong.
My TabbedPage XAML
<TabbedPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="DownToledoAPP.Views.InfoPage"> <ContentPage Icon="tablon.png" BackgroundColor="#3A6DC8" x:Name="tablonpage"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="*"/> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> <ListView x:Name="tablon" Grid.Row="0" SelectedItem="{Binding Noticiaelegida}" ItemsSource="{Binding Noticias}" > <ListView.ItemTemplate> <DataTemplate> <Grid> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> <Label Grid.Row="0" Text="{Binding titulo}" FontSize="Large"/> <Label Grid.Row="1" Text="{Binding descripcion}" FontSize="Medium"/> <Grid Grid.Row="2"> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto"/> <ColumnDefinition Width="Auto"/> </Grid.ColumnDefinitions> <Switch Grid.Column="0" Grid.Row="0" IsToggled="{Binding enprogreso}"/> <Label Grid.Row="0" Grid.Column="1" Text="En progreso"/> <Label Grid.Column="0" Grid.Row="1" Text="{Binding responsable}"/> </Grid> </Grid> </DataTemplate> </ListView.ItemTemplate> </ListView> <Button x:Name="newnoticia" BackgroundColor="#FFCA02" Grid.Row="1" FontFamily="Cooper Black" TextColor="White" HorizontalOptions="Center" Text="Abrir nueva actividad"/> </Grid> </ContentPage> <ContentPage Icon="actividades.png" BackgroundColor="#3A6DC8" x:Name="actividadespage"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="*"/> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> <ListView x:Name="actividades" Grid.Row="0" ItemsSource="{Binding Actividades}" SelectedItem="{Binding Actividadelegida}"> <ListView.ItemTemplate> <DataTemplate> <Grid> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> <Label Grid.Row="0" FontSize="Large" Text="{Binding nombre}"/> <Label Grid.Row="1" Text="{Binding descripcion}"/> <Label Grid.Row="2" FontSize="Small" Text="{Binding horario}"/> <Grid Grid.Row="3"> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto"/> <ColumnDefinition Width="Auto"/> </Grid.ColumnDefinitions> <Label Grid.Column="0" Text="{Binding responsable}"/> <Label Grid.Column="1" Text="{Binding localizacion}"/> </Grid> </Grid> </DataTemplate> </ListView.ItemTemplate> </ListView> <Button x:Name="newactividad" BackgroundColor="#FFCA02" Grid.Row="1" FontFamily="Cooper Black" TextColor="White" HorizontalOptions="Center" Text="Abrir nueva noticia"/> </Grid> </ContentPage> </TabbedPage>
Code-behind for InfoPage
public partial class InfoPage : TabbedPage { private CWS cws; private DatosUsuario actualusuario; public InfoPage (string text) { InitializeComponent (); cws = new CWS(); actualusuario = new DatosUsuario(); actualusuario=cws.DescargarInfousuario(text); var alltablon= cargartablon(); var actividades = cargaractividades(); image.Source = actualusuario.foto; name.Text = actualusuario.nombre + " " + actualusuario.apellido1 + " " + actualusuario.apellido2 + " Alias:" + actualusuario.nombreuser; logout.Clicked += salir; userinfo.Clicked += change; newnoticia.Clicked += crearnoticia; newactividad.Clicked += crearactividad; tablonpage.BindingContext = new ViewModel.InfoNoticiasModel(alltablon, actualusuario.idusuario, this); actividadespage.BindingContext = new ViewModel.InfoViewModel(actividades, actualusuario.idusuario, this); } private async void crearactividad(object sender, EventArgs e) { await Navigation.PushAsync(new DetailPage(null, null, "Actividad", actualusuario.idusuario)); } private async void crearnoticia(object sender, EventArgs e) { await Navigation.PushAsync(new DetailPage(null, null, "Noticia", actualusuario.idusuario)); } private List<ActividadesMVVM> cargaractividades() { var noticias = cws.DescargarActividades(actualusuario.idusuario.ToString(),actualusuario.idrol.ToString()); var listamvvm = new List<ActividadesMVVM>(); for (int i = 0; i < noticias.Count; i++) { var actividad = new ActividadesMVVM(); var creador = cws.DescargarNombre(noticias[i].idresponsable.ToString()); actividad.descripcion = noticias[i].descripcion; actividad.horario = noticias[i].horario; actividad... } }
Let's Troubleshoot This
First off, TargetInvocationException is almost always a wrapper for an inner exception that's the real culprit. So your first step should be to dig into that inner exception to get the actual error message.
Step 1: Capture the Inner Exception
Wrap your constructor code in a try-catch block to log or inspect the inner exception:
public InfoPage(string text) { try { InitializeComponent(); // Rest of your constructor code here } catch (TargetInvocationException ex) { var realException = ex.InnerException; Console.WriteLine($"Root cause: {realException.Message}\nStack trace: {realException.StackTrace}"); throw; // Re-throw to keep debugging flow } }
Step 2: Stop Doing Network Calls in the Constructor
Your code is making synchronous network requests (cws.DescargarInfousuario, cws.DescargarActividades) directly in the constructor. This blocks the UI thread and is a common source of exceptions in Xamarin. Move this logic to an async method like OnAppearing:
public InfoPage(string text) { InitializeComponent(); // Only set up event handlers here logout.Clicked += salir; userinfo.Clicked += change; newnoticia.Clicked += crearnoticia; newactividad.Clicked += crearactividad; } protected override async void OnAppearing() { base.OnAppearing(); try { cws = new CWS(); // Wrap sync calls in Task.Run to avoid blocking UI, or better yet, use async versions of your CWS methods actualusuario = await Task.Run(() => cws.DescargarInfousuario(text)); var alltablon = await Task.Run(() => cargartablon()); var actividades = await Task.Run(() => cargaractividades()); // Update UI and set BindingContexts once data is loaded image.Source = actualusuario.foto; name.Text = $"{actualusuario.nombre} {actualusuario.apellido1} {actualusuario.apellido2} Alias:{actualusuario.nombreuser}"; tablonpage.BindingContext = new ViewModel.InfoNoticiasModel(alltablon, actualusuario.idusuario, this); actividadespage.BindingContext = new ViewModel.InfoViewModel(actividades, actualusuario.idusuario, this); } catch (Exception ex) { Console.WriteLine($"Failed to load data: {ex.Message}"); // Show a user-friendly error message here } }
Step 3: Validate Your ViewModels
Make sure your InfoNoticiasModel and InfoViewModel are properly implemented:
- They should implement
INotifyPropertyChangedso bindings work correctly. - All properties referenced in your XAML (like
Noticias,Actividades,Noticiaelegida) must exist and match the exact casing. - Add null checks in your ViewModel constructors—if
alltablonoractividadesis null, handle it gracefully instead of letting it throw.
Step 4: Fix Incomplete Data Loading
Your cargaractividades method cuts off mid-implementation. Make sure you're fully initializing each ActividadesMVVM object and adding it to the list. Also, add null checks for the data returned by cws.DescargarActividades:
private List<ActividadesMVVM> cargaractividades() { var noticias = cws.DescargarActividades(actualusuario.idusuario.ToString(), actualusuario.idrol.ToString()); var listamvvm = new List<ActividadesMVVM>(); // Guard against null data if (noticias == null) return listamvvm; for (int i = 0; i < noticias.Count; i++) { var actividad = new ActividadesMVVM(); var creador = cws.DescargarNombre(noticias[i].idresponsable.ToString()); actividad.descripcion = noticias[i].descripcion; actividad.horario = noticias[i].horario; actividad.nombre = noticias[i].nombre; // Make sure you set all required properties actividad.responsable = creador; actividad.localizacion = noticias[i].localizacion; listamvvm.Add(actividad); } return listamvvm; }
Step 5: Ensure UI Thread Safety
When setting BindingContexts, make sure you're doing it on the UI thread. Wrap those lines in Device.BeginInvokeOnMainThread if you're running code in a background task:
Device.BeginInvokeOnMainThread(() => { tablonpage.BindingContext = new ViewModel.InfoNoticiasModel(alltablon, actualusuario.idusuario, this); actividadespage.BindingContext = new ViewModel.InfoViewModel(actividades, actualusuario.idusuario, this); });
内容的提问来源于stack exchange,提问作者Krever

