3

我正在开发一个通用 Windows 平台 (UWP) 应用程序,并且遇到了一个令人困惑的问题(并且有点烦人的问题)。当我从 MainPage 导航到 EventDetailPage 时,一个System.ArgumentException. 表示数据绑定对象

“值不在预期范围内”。

值是从 MainPageViewModel 传递到 EventDetailPageViewModel 的 ScoutingEvent 对象。尽管传递的对象有时为空(不知道为什么),但也为 Value 分配了默认数据,以确保在 EventDetailPage 尝试绑定到 Bind to value 时它不为空。

从我的角度来看,一切看起来都是正确的。我在这里缺少什么吗?

主页.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);
        }
    }
}

更新 1:添加 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;
    }
}
}
4

1 回答 1

2

问题出在EventDetailPage您尝试将ItemsSource您的 分配GridView给单个ScoutingEvent而不是集合的地方。

    <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>

由于它是一个单独的项目,您可以使用ContentPresenterwith ContentTemplate,或者只是保持简单,只保留模板的内部控件。GridView通过以下 XAML替换上述内容:

<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>
于 2015-12-25T20:00:14.990 回答