2015-12-23 2 views
3

Je travaille sur une application Universal Windows Platform (UWP) et j'ai rencontré un problème complexe (et un peu ennuyeux). Lorsque je navigue de la MainPage à EventDetailPage, un System.ArgumentException. Il indique que l'objet lié aux donnéesUWP C#/XAML: System.ArgumentException (hors de la plage attendue)

"La valeur ne correspond pas à la plage attendue".

La valeur est un objet ScoutingEvent transmis de MainPageViewModel à EventDetailPageViewModel. Bien que l'objet transmis soit parfois nul (vous ne savez pas pourquoi), les données par défaut sont également affectées aux valeurs par défaut pour garantir que ce n'est pas NULL lorsque EventDetailPage tente de lier à la valeur Bind to.

De mon point de vue, tout semble correct. Y a-t-il quelque chose qui me manque ici?

MainPage.xaml

<Page x:Class="ScoutsLog.Views.MainPage" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:Behaviors="using:Template10.Behaviors" 
    xmlns:Core="using:Microsoft.Xaml.Interactions.Core" 
    xmlns:Interactivity="using:Microsoft.Xaml.Interactivity" 
    xmlns:controls="using:Template10.Controls" 
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
    xmlns:local="using:ScoutsLog.Views" 
    xmlns:data="using:ScoutsLog.Models" 
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
    xmlns:vm="using:ScoutsLog.ViewModels" mc:Ignorable="d"> 

    <Page.DataContext> 
     <vm:MainPageViewModel /> 
    </Page.DataContext> 

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> 

     <!-- #region default visual states --> 

     <VisualStateManager.VisualStateGroups> 
      <VisualStateGroup x:Name="AdaptiveVisualStateGroup"> 
       <VisualState x:Name="VisualStateNarrow"> 
        <VisualState.StateTriggers> 
         <AdaptiveTrigger MinWindowWidth="{StaticResource NarrowMinWidth}" /> 
        </VisualState.StateTriggers> 
        <VisualState.Setters> 
         <!-- TODO --> 
        </VisualState.Setters> 
       </VisualState> 
       <VisualState x:Name="VisualStateNormal"> 
        <VisualState.StateTriggers> 
         <AdaptiveTrigger MinWindowWidth="{StaticResource NormalMinWidth}" /> 
        </VisualState.StateTriggers> 
        <VisualState.Setters> 
         <!-- TODO --> 
        </VisualState.Setters> 
       </VisualState> 
       <VisualState x:Name="VisualStateWide"> 
        <VisualState.StateTriggers> 
         <AdaptiveTrigger MinWindowWidth="{StaticResource WideMinWidth}" /> 
        </VisualState.StateTriggers> 
        <VisualState.Setters> 
         <!-- TODO --> 
        </VisualState.Setters> 
       </VisualState> 
      </VisualStateGroup> 
     </VisualStateManager.VisualStateGroups> 

     <!-- #endregion --> 

     <Grid.RowDefinitions> 
      <RowDefinition Height="Auto" /> 
      <RowDefinition /> 
     </Grid.RowDefinitions> 

     <!-- page header --> 

     <controls:PageHeader BackButtonVisibility="Collapsed" Content="Main Page" Frame="{x:Bind Frame}"> 
      <Interactivity:Interaction.Behaviors> 
       <Behaviors:EllipsisBehavior Visibility="Auto" /> 
      </Interactivity:Interaction.Behaviors> 
      <controls:PageHeader.SecondaryCommands> 
       <AppBarButton Click="{x:Bind ViewModel.GotoPrivacy}" Label="Privacy" /> 
       <AppBarButton Click="{x:Bind ViewModel.GotoAbout}" Label="About" /> 
      </controls:PageHeader.SecondaryCommands> 
     </controls:PageHeader> 

     <!-- page content --> 

     <!--<StackPanel Grid.Row="1" VerticalAlignment="Top" 
        Orientation="Horizontal" Padding="12,8,0,0"> 

      <controls:Resizer> 
       <TextBox Width="200" MinWidth="200" 
         MinHeight="60" Margin="0" 
         Header="Parameter to pass" 
         Text="{Binding Value, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" 
         TextWrapping="Wrap"> 
        <Interactivity:Interaction.Behaviors> 
         <Behaviors:TextBoxEnterKeyBehavior> 
          <Core:CallMethodAction MethodName="GotoDetailsPage" TargetObject="{Binding}" /> 
         </Behaviors:TextBoxEnterKeyBehavior> 
         <Core:EventTriggerBehavior> 
          <Behaviors:FocusAction/> 
         </Core:EventTriggerBehavior> 
        </Interactivity:Interaction.Behaviors> 
       </TextBox> 
      </controls:Resizer> 

      <Button Margin="12,0" VerticalAlignment="Bottom" 
        Click="{x:Bind ViewModel.GotoDetailsPage}" Content="Submit" /> 

     </StackPanel>--> 

     <ListBox Grid.Row="1" VerticalAlignment="Top" Padding="12,8,0,0" Name="EventsOverviewListBox" 
       ItemsSource="{x:Bind ViewModel.Events}" SelectionChanged="{x:Bind ViewModel.GotoEventDetailsPage}" 
       SelectedIndex="{Binding EventsIndex, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"> 
      <ListBox.ItemTemplate> 
       <DataTemplate x:DataType="data:ScoutingEvent"> 
        <TextBlock Text="{x:Bind EventName}"/> 
       </DataTemplate> 
      </ListBox.ItemTemplate> 
     </ListBox> 
    </Grid> 
</Page> 

MainPageViewModel.cs

using ScoutsLog.Models; 
using ScoutsLog.Utils; 
using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Threading.Tasks; 
using Windows.Storage; 
using Windows.UI.Core; 
using Windows.UI.Xaml.Navigation; 

namespace ScoutsLog.ViewModels 
{ 
    public class MainPageViewModel : Mvvm.ViewModelBase 
    { 
     Windows.Storage.StorageFolder localFolder = Windows.Storage.ApplicationData.Current.LocalFolder; 
     public List<ScoutingEvent> Events; 

     public MainPageViewModel() 
     { 
      //if (Windows.ApplicationModel.DesignMode.DesignModeEnabled) 
      // Value = "Designtime value"; 

      // Register a handler for BackRequested events and set the 
      // visibility of the Back button 
      SystemNavigationManager.GetForCurrentView().BackRequested += OnBackRequested; 

      ReadDataFromStorage(); 

      //if (Events == null || Events.Count <= 0) 
      //{ 
       Events = EventsManager.GetEvents(); 
      //} 

     } 

     private async void ReadDataFromStorage() 
     { 
      try 
      { 
       StorageFile sampleFile = await localFolder.GetFileAsync("dataFile.txt"); 
       String listAsXml = await FileIO.ReadTextAsync(sampleFile); 
       Events = XmlHandler.DeserializeXmlToList(listAsXml); 
      } 
      catch(Exception e) 
      { 
       System.Diagnostics.Debug.WriteLine(e); 
      } 
     } 

     private async void OnBackRequested(object sender, BackRequestedEventArgs e) 
     { 
      StorageFile sampleFile = await localFolder.CreateFileAsync("dataFile.txt", 
      CreationCollisionOption.ReplaceExisting); 
      await FileIO.WriteTextAsync(sampleFile, XmlHandler.SerializeListToXml(Events)); 

     } 

     ScoutingEvent _Value = new ScoutingEvent(); 
     public ScoutingEvent Value { get { return _Value; } set { Set(ref _Value, value); } } 

     int _EventsIndex = 0; 
     public int EventsIndex { get { return _EventsIndex; } set { Set(ref _EventsIndex, value); } } 

     public override void OnNavigatedTo(object parameter, NavigationMode mode, IDictionary<string, object> state) 
     { 
      if (state.ContainsKey(nameof(Value))) 
       Value = (ScoutingEvent)state[nameof(Value)]; 
      state.Clear(); 
     } 

     public override async Task OnNavigatedFromAsync(IDictionary<string, object> state, bool suspending) 
     { 
      if (suspending) 
       state[nameof(Value)] = Value; 
      await Task.Yield(); 
     } 

     public void GotoEventDetailsPage() 
     { 
      Value = Events.ElementAt(EventsIndex); 
      NavigationService.Navigate(typeof(Views.EventDetailPage), Value); 
     } 

     public void GotoPrivacy() 
     { 
      NavigationService.Navigate(typeof(Views.SettingsPage), 1); 
     } 

     public void GotoAbout() 
     { 
      NavigationService.Navigate(typeof(Views.SettingsPage), 2); 
     } 

    } 
} 

EventDetailPage.xaml

<Page 
x:Class="ScoutsLog.Views.EventDetailPage" 
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
xmlns:Behaviors="using:Template10.Behaviors" 
xmlns:Core="using:Microsoft.Xaml.Interactions.Core" 
xmlns:Interactivity="using:Microsoft.Xaml.Interactivity" 
xmlns:controls="using:Template10.Controls" 
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
xmlns:local="using:ScoutsLog.Views" 
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
xmlns:vm="using:ScoutsLog.ViewModels" x:Name="ThisPage" 
xmlns:data="using:ScoutsLog.Models" 
mc:Ignorable="d"> 

    <Page.DataContext> 
     <vm:EventDetailsPageViewModel /> 
    </Page.DataContext> 

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> 

     <!-- adaptive states --> 

     <VisualStateManager.VisualStateGroups> 
      <VisualStateGroup x:Name="AdaptiveVisualStateGroup"> 
       <VisualState x:Name="VisualStateNarrow"> 
        <VisualState.StateTriggers> 
         <AdaptiveTrigger MinWindowWidth="{StaticResource NarrowMinWidth}" /> 
        </VisualState.StateTriggers> 
        <VisualState.Setters> 
         <!-- TODO --> 
        </VisualState.Setters> 
       </VisualState> 
       <VisualState x:Name="VisualStateNormal"> 
        <VisualState.StateTriggers> 
         <AdaptiveTrigger MinWindowWidth="{StaticResource NormalMinWidth}" /> 
        </VisualState.StateTriggers> 
        <VisualState.Setters> 
         <!-- TODO --> 
        </VisualState.Setters> 
       </VisualState> 
       <VisualState x:Name="VisualStateWide"> 
        <VisualState.StateTriggers> 
         <AdaptiveTrigger MinWindowWidth="{StaticResource WideMinWidth}" /> 
        </VisualState.StateTriggers> 
        <VisualState.Setters> 
         <!-- TODO --> 
        </VisualState.Setters> 
       </VisualState> 
      </VisualStateGroup> 
     </VisualStateManager.VisualStateGroups> 

     <Grid.RowDefinitions> 
      <RowDefinition Height="Auto" /> 
      <RowDefinition /> 
     </Grid.RowDefinitions> 

     <!-- header --> 
     <controls:PageHeader Frame="{x:Bind Frame}" Text="Detail Page"> 
      <Interactivity:Interaction.Behaviors> 
       <Behaviors:EllipsisBehavior Visibility="Auto" /> 
      </Interactivity:Interaction.Behaviors> 
     </controls:PageHeader> 

     <GridView Grid.Row="1" VerticalAlignment="Top" Padding="12,8,0,0" Name="EventsOverviewGridView" ItemsSource="{x:Bind ViewModel.Value}"> 
      <GridView.ItemTemplate> 
       <DataTemplate x:DataType="data:ScoutingEvent"> 
        <Grid> 
         <StackPanel Grid.Row="0"> 
          <TextBlock Text="{x:Bind EventName}" /> 
          <ListBox ItemsSource="{x:Bind Companies}"> 
           <ListBox.ItemTemplate> 
            <DataTemplate x:DataType="data:Company"> 
             <TextBlock Text="{x:Bind CompanyName}"/> 
            </DataTemplate> 
           </ListBox.ItemTemplate> 
          </ListBox> 
         </StackPanel> 
        </Grid> 
       </DataTemplate> 
      </GridView.ItemTemplate> 
     </GridView> 

     <!-- #endregion --> 

    </Grid> 
</Page> 

EventDetailPageViewModel.cs

using ScoutsLog.Models; 
using System.Linq; 
using System.Collections.Generic; 
using System.Threading.Tasks; 
using Windows.UI.Xaml.Navigation; 

namespace ScoutsLog.ViewModels 
{ 
    public class EventDetailsPageViewModel : Mvvm.ViewModelBase 
    { 
     public EventDetailsPageViewModel() 
     { 

     } 

     ScoutingEvent _Value = new ScoutingEvent { EventName = string.Empty, EventDate = string.Empty, Companies = new List<Company>()}; 
     public ScoutingEvent Value { get { return _Value; } set { Set(ref _Value, value); } } 

     public override void OnNavigatedTo(object parameter, NavigationMode mode, IDictionary<string, object> state) 
     { 
      if (state.ContainsKey(nameof(Value))) 
       Value = (ScoutingEvent)state[nameof(Value)]; 
      state.Clear(); 
     } 

     public override async Task OnNavigatedFromAsync(IDictionary<string, object> state, bool suspending) 
     { 
      if (suspending) 
       state[nameof(Value)] = Value; 
      await Task.Yield(); 
     } 

     public void GotoDetailsPage() 
     { 
      NavigationService.Navigate(typeof(Views.EventDetailPage), Value); 
     } 

     public void GotoPrivacy() 
     { 
      NavigationService.Navigate(typeof(Views.SettingsPage), 1); 
     } 

     public void GotoAbout() 
     { 
      NavigationService.Navigate(typeof(Views.SettingsPage), 2); 
     } 
    } 
} 

Mise à jour 1: Ajout ScoutingEvent.cs

using System; 
using System.Collections.Generic; 
using System.ComponentModel; 

namespace ScoutsLog.Models 
{ 
    public class ScoutingEvent : INotifyPropertyChanged 
    { 
     private string eventName; 
     public string EventName 
     { 
      get 
      { 
       return eventName; 
      } 
       set 
      { 
       if (eventName != value) 
       { 
        eventName = value; 
        OnNotifyPropertyChanged("EventName"); 
       } 
      } 
     } 

    private string eventDate; 
    public string EventDate 
    { 
     get 
     { 
      return eventDate; 
     } 
     set 
     { 
      if (eventDate != value) 
      { 
       eventDate = value; 
       OnNotifyPropertyChanged("EventDate"); 
      } 
     } 
    } 

    public List<Company> Companies { get; set; } 

    public event PropertyChangedEventHandler PropertyChanged; 

    private void OnNotifyPropertyChanged(string propertyName) 
    { 
     if (PropertyChanged != null) 
     { 
      PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); 
     } 
    } 
} 

public class Company 
{ 
    public string CompanyName { get; set; } 
    public string Category { get; set; } 
    public Boolean isRelevent { get; set; } 
    public String DateVisited { get; set; } 
    public string Notes { get; set; } 
} 

public class EventsManager 
{ 
    public static List<ScoutingEvent> GetEvents() 
    { 
     var events = new List<ScoutingEvent>(); 
     var companies = GetCompanies(); 

     events.Add(new ScoutingEvent { EventName = "CES", EventDate = "1/6/2016 - 1/9/2016", Companies = companies}); 
     events.Add(new ScoutingEvent { EventName = "MWC", EventDate = "3/3/2016 - 3/10/2016", Companies = companies }); 

     return events; 
    } 

    public static List<Company> GetCompanies() 
    { 
     var companies = new List<Company>(); 
     companies.Add(new Company { CompanyName = "Spire", Category = "Fittness", DateVisited = "1/6/2016", isRelevent = true, Notes = "Follow-up after event." }); 
     companies.Add(new Company { CompanyName = "Samsung", Category = "Fittness", DateVisited = "1/8/2016", isRelevent = false, Notes = "Not working on relevent technologies." }); 
     companies.Add(new Company { CompanyName = "Fitbit", Category = "Fittness", DateVisited = "1/7/2016", isRelevent = true, Notes = "Pass along to remote team." }); 
     companies.Add(new Company { CompanyName = "Home.io", Category = "Fittness", DateVisited = "1/9/2016", isRelevent = true, Notes = "SHare with Paul/Martta." }); 
     return companies; 
    } 
} 
} 
+0

'= Valeur Events.ElementAt (EventsIndex);' - Ceci est probablement là que les choses vont mal. Ma conjecture est que 'EventsIndex' est plus grand que le nombre d'éléments dans la liste. Rappelez-vous que les index sont ** basés sur zéro **. –

+0

Désolé, j'ai oublié de mentionner que lorsque je trace la valeur transmise, elle représente correctement l'objet référencé à partir de EventsIndex, du côté de MainPageViewModel. Toutefois, le code rompt avec l'objet _Value initial du côté EventDetailPageViewModel, avant qu'il ne soit défini sur la valeur transmise. – TheMoonbeam

+0

N'oubliez pas de marquer la bonne réponse. –

Répondre

2

Le problème est dans votre EventDetailPage où vous essayez d'affecter la ItemsSource de votre GridView à un seul ScoutingEvent au lieu d'une collection.

<GridView Grid.Row="1" VerticalAlignment="Top" Padding="12,8,0,0" 
       Name="EventsOverviewGridView" ItemsSource="{x:Bind ViewModel.Value}"> 
     <GridView.ItemTemplate> 
      <DataTemplate x:DataType="data:ScoutingEvent"> 
       <Grid> 
        <StackPanel Grid.Row="0"> 
         <TextBlock Text="{x:Bind EventName}" /> 
         <ListBox ItemsSource="{x:Bind Companies}"> 
          <ListBox.ItemTemplate> 
           <DataTemplate x:DataType="data:Company"> 
            <TextBlock Text="{x:Bind CompanyName}"/> 
           </DataTemplate> 
          </ListBox.ItemTemplate> 
         </ListBox> 
        </StackPanel> 
       </Grid> 
      </DataTemplate> 
     </GridView.ItemTemplate> 
    </GridView> 

Comme il est un seul élément, vous pouvez utiliser un ContentPresenter avec ContentTemplate, ou tout simplement garder facile et ne garder que les contrôles internes de votre modèle. Remplacer le GridView ci-dessus par XAML suivant:

<StackPanel Grid.Row="1" VerticalAlignment="Top" Padding="12,8,0,0"> 
    <TextBlock Text="{x:Bind ViewModel.Value.EventName}" /> 
    <ListBox ItemsSource="{x:Bind ViewModel.Value.Companies}"> 
     <ListBox.ItemTemplate> 
      <DataTemplate x:DataType="viewModels:Company"> 
       <TextBlock Text="{x:Bind CompanyName}"/> 
      </DataTemplate> 
     </ListBox.ItemTemplate> 
    </ListBox> 
</StackPanel> 
+0

Je fais face à la tête quand je lis ceci. Je ne peux pas croire que j'ai oublié ça. 'ItemSource' et' GridView' n'ont pas de sens pour un seul objet 'ScoutingEvent'. Mais, cela aurait du sens pour la 'List ' stockée dans 'Value'. Cela étant dit, cela a résolu le problème. Cependant, seul le jeu de données par défaut 'Value 'dans' EventDetailsPageViewModel' est affiché. 'ScoutingEvent _Value = new ScoutingEvent {EventName = chaîne.Empty, EventDate = chaîne.Empty, Companies = nouvelle Liste ()};'. L'objet 'Value' passé de' GotoEventDetailsPage' semble être ignoré.Je ne sais pas pourquoi, cependant. – TheMoonbeam

+0

Lorsque j'ai essayé votre code localement, le dictionnaire d'état était vide. – Bart

+0

Intéressant. De mon côté, ce n'est pas vide, et les données correctes sont passées de 'MainPageViewModel' à' EventDetailsViewModel'. J'ai juste mis à jour mon post avec la classe 'SoutingEvent', qui inclut la classe' EventsManager', qui crée des données fictives à tester. – TheMoonbeam