2017-04-02 40 views
1

我想重現WPF的MVVM教程,但將其應用於UWP。但我已經完成了本教程中的所有內容,我相信本教程中顯示的代碼完全相同。MVVM分層導航教程StackOverFlow異常

但是當我運行代碼時,我一直得到一個StackOverflowException,這是因爲MainPageView不停地初始化,直到拋出異常。

事情是我有點在MVVM知道,我想掌握它,所以有人請解釋我爲什麼我得到這個?

我將離開我的每個類和視圖的代碼。

這是我MainPageView.Xaml:

<Page 
x:Class="MVVMHierarchiesDemo.MainPageView" 
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
xmlns:local="using:MVVMHierarchiesDemo" 
xmlns:views="using:MVVMHierarchiesDemo.Views" 
xmlns:viewmodel="using:MVVMHierarchiesDemo.ViewModel" 
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
mc:Ignorable="d"> 

<!--Anytime the current view model is set to an instance of a CustomerListViewModel, 
it will render out a CustomerListView with the ViewModel is hooked up. It’s an order ViewModel, 
it'll render out OrderView and so on. 

We now need a ViewModel that has a CurrentViewModel property and some logic and commanding 
to be able to switch the current reference of ViewModel inside the property.--> 
<Page.DataContext> 
    <local:MainPageView/> 
</Page.DataContext> 

<Page.Resources> 
    <DataTemplate x:Key="CustomerTemplate" x:DataType="viewmodel:CustomerListViewModel"> 
     <views:CustomerListView/> 
    </DataTemplate> 

    <DataTemplate x:Key="OrderTemplate" x:DataType="viewmodel:OrderViewModel"> 
     <views:OrderView/> 
    </DataTemplate> 
</Page.Resources> 

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> 
    <Grid.RowDefinitions> 
     <RowDefinition Height="Auto"/> 
     <RowDefinition Height="*"/> 
    </Grid.RowDefinitions> 

    <Grid.ColumnDefinitions> 
     <ColumnDefinition Width="*"/> 
     <ColumnDefinition Width="*"/> 
     <ColumnDefinition Width="*"/> 
    </Grid.ColumnDefinitions> 

    <Grid x:Name="NavBar" 
      Grid.Row="0"> 
     <Button Content="Customers" 
       Command="{Binding NavCommand}" 
       CommandParameter="customers" 
       Grid.Column="0" 
       Grid.Row="0"/> 

     <Button Content="Orders" 
       Command="{Binding NavCommand}" 
       CommandParameter="orders" 
       Grid.Column="2" 
       Grid.Row="0"/> 
    </Grid> 

    <Grid x:Name="MainContent" 
      Grid.Row="1"> 
     <ContentControl Content="{Binding CurrentViewModel}"/> 
    </Grid>  
</Grid> 
</Page> 

這是我的代碼隱藏MainPageView.xaml.cs - 這裏就是StackoverflowException在它不斷調用它的構造函數拋出。

using Windows.UI.Xaml.Controls; 

// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x409 

namespace MVVMHierarchiesDemo 
{ 
    /// <summary> 
    /// An empty page that can be used on its own or navigated to within a Frame. 
    /// </summary> 
    public sealed partial class MainPageView : Page 
    { 
     public MainPageView() 
     { 
      this.InitializeComponent(); 
     }   
    } 
} 

這是我BindableBase.cs作爲教程顯示:

using System.ComponentModel; 
using System.Runtime.CompilerServices; 

namespace MVVMHierarchiesDemo 
{ 
    /*The main idea behind this class is to encapsulate the INotifyPropertyChanged implementation 
    * and provide helper methods to the derived class so that they can easily trigger the appropriate notifications. 
    * Following is the implementation of BindableBase class.*/ 
    public class BindableBase : INotifyPropertyChanged 
    { 
     public event PropertyChangedEventHandler PropertyChanged = delegate { }; 

     protected virtual void OnPropertyChanged(string propertyName) 
     { 
      PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 
     } 

     protected virtual void SetProperty<T>(ref T member, T val, [CallerMemberName]string propertyName = null) 
     { 
      if (object.Equals(member, val)) 
       return; 

      member = val; 
      OnPropertyChanged(propertyName); 
     } 
    } 
} 

這是MyCommand.cs或更好地稱爲中繼命令模式:

using System; 
using System.Windows.Input; 

namespace MVVMHierarchiesDemo 
{ 
    /* Now it's time to actually start doing some view switching using our CurrentViewModel property. 
    * We just need some way to drive the setting of this property. And we're going to make it so that 
    * the end user can command going to the customer list or to the order view. First add a new class 
    * in your project which will implement the ICommand interface. Following is the implementation of 
    * ICommand interface.*/ 
    public class MyCommand<T> : ICommand 
    { 
     Action<T> _TargetExecuteMethod; 
     Func<T, bool> _TargetCanExecuteMethod; 

     public MyCommand(Action<T> targetExecuteMethod) 
     { 
      _TargetExecuteMethod = targetExecuteMethod; 
     } 

     public MyCommand(Action<T> targetExecuteMethod, Func<T,bool> targetCanExecuteMethod) 
     { 
      _TargetExecuteMethod = targetExecuteMethod; 
      _TargetCanExecuteMethod = targetCanExecuteMethod; 
     } 

     public event EventHandler CanExecuteChanged = delegate { }; 

     public void RaiseCanExecuteChanged() 
     { 
      CanExecuteChanged?.Invoke(this, EventArgs.Empty); 
     } 

     bool ICommand.CanExecute(object parameter) 
     { 
      if (_TargetCanExecuteMethod != null) 
      { 
       T tparam = (T)parameter; 
       return _TargetCanExecuteMethod(tparam); 
      } 

      if (_TargetExecuteMethod != null) 
       return true; 

      return false; 
     } 

     void ICommand.Execute(object parameter) 
     { 
      if(_TargetExecuteMethod!=null) 
      { 
       T tparam = (T)parameter; 
       _TargetExecuteMethod(tparam); 
      } 
     } 
    } 
} 

這是我OrdersView.xaml用戶控件:

<UserControl 
    x:Class="MVVMHierarchiesDemo.Views.OrderView" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:local="using:MVVMHierarchiesDemo.Views" 
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
    mc:Ignorable="d" 
    d:DesignHeight="300" 
    d:DesignWidth="400"> 

    <Grid> 
     <TextBlock Text="Order View"/> 
    </Grid> 
</UserControl> 

這是我的用戶控制CustomerListView.xaml:

<UserControl 
    x:Class="MVVMHierarchiesDemo.Views.CustomerListView" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:local="using:MVVMHierarchiesDemo.Views" 
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
    mc:Ignorable="d" 
    d:DesignHeight="300" 
    d:DesignWidth="400"> 

    <Grid> 
     <TextBlock Text="Customer List View"/> 
    </Grid> 
</UserControl> 

這是我OrderViewModel:

namespace MVVMHierarchiesDemo.ViewModel 
{ 
    /*Derive all of your ViewModels from BindableBase class.*/ 
    public class OrderViewModel : BindableBase 
    { 
    } 
} 

這是我CustomerViewModel:

namespace MVVMHierarchiesDemo.ViewModel 
{ 
    /*Derive all of your ViewModels from BindableBase class.*/ 
    public class CustomerListViewModel : BindableBase 
    { 
    } 
} 

最後,這是我MainPageViewModel:

namespace MVVMHierarchiesDemo.ViewModel 
{ 
    /*Derive all of your ViewModels from BindableBase class.*/ 
    public class MainPageViewModel : BindableBase 
    { 
     public MainPageViewModel() 
     { 
      NavCommand = new MyCommand<string>(OnNavigation); 
     } 

     private CustomerListViewModel _customerListViewModel = new CustomerListViewModel(); 

     private OrderViewModel _orderViewModel = new OrderViewModel(); 

     private BindableBase _currentViewModel; 

     public BindableBase CurrentViewModel 
     { 
      get 
      { 
       return _currentViewModel; 
      } 

      set 
      { 
       SetProperty(ref _currentViewModel, value); 
      } 
     } 

     public MyCommand<string> NavCommand { get; private set; } 

     private void OnNavigation(string destination) 
     { 
      switch (destination) 
      { 
       case "orders": 
        { 
         CurrentViewModel = _orderViewModel; 
         break; 
        } 
       case "customers": 
       default: 
        CurrentViewModel = _customerListViewModel; 
        break; 
      } 
     } 
    } 
} 

最後我認爲MainPageView是造成無限循環的原因,但我不明白爲什麼?

如果有人能夠如此友好地告訴我我在做什麼錯誤的UWP?

另外我可以使用MVVM Light或MVVMCross我對這些解決方案不感興趣,我想手動學習MVVM,後來我可能會檢查這些框架。

+0

**錯字警報:**它是一個**層次結構** - 不是「heirachy」,因爲你一直在使用...... –

回答

3

這是因爲在你MainPageView.xaml你有這樣的:

<Page.DataContext> 
    <local:MainPageView/> 
</Page.DataContext> 

所以每次MainPageview創建一個嵌套MainPageViewDataContext。這些創建直到你吹堆棧。

我想你打算在這裏放一個MainPageViewModel

+0

該死的,你是對的!馬安,我怎麼也看不見它。我很抱歉不喜歡複製粘貼代碼,我更喜歡自己重新輸入。我想我太累了,我沒有看到那個明顯的錯誤。我試圖把MainPageViewModel。 感謝您借給我您的鷹眼! –