【问题标题】:Xamarin forms ViewModels InheritanceXamarin 形成 ViewModels 继承
【发布时间】:2020-10-26 06:33:43
【问题描述】:

我正在为我的宠物项目使用 MVVM 开发一个关于视频游戏的 Xamarin 表单应用程序。我是 Xamarin 表单的新手,我需要您的建议。

我有几个 ViewModel 中的代码相同。我决定创建一个基础 ViewModel 并从中继承其他的。

我有带有 PropertyChanged 事件的 ViewModelBase:

 public class ViewModelBase : INotifyPropertyChanged
 {
    private string _title;
    public string Title
    {
        get => _title;
        set => Set(ref _title, value);
    }

  
    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void Set<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
    {
        field = value;
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
 
}

我还有一个继承其他人的基本 GamesViewModel,有很多代码,这就是为什么我只显示我正确继承了所有内容:

public class GamesViewModel : ViewModelBase

以下是派生的 ViewModel:

public class NewGamesViewModel : GamesViewModel

public class SearchViewModel : GamesViewModel

问题是我在基本 GamesViewModel 中有 SearchGame 属性:

  private string _searchGame;
    public string SearchGame
    {
        get => _searchGame;
        set => Set(ref _searchGame, value);
    }

程序运行时,我将值放在 SearchGame 属性中,在 GamesViewModel 中我可以看到分配的值,但在派生的 ViewModels 中值为 null:

例如,在继承自 GamesViewModel 的 SearchViewModel 中进行调试时,我检查了该值,它为空。

var test = SearchGame; - value is null here

我没有在项目中创建 GamesViewModel 的任何对象。

在 BindingContext 的页面代码隐藏文件中,我这样做:

public partial class SearchGamePage : ContentPage
{
    public SearchGamePage()
    {
        InitializeComponent();
        BindingContext = new SearchViewModel();
    }
}

我试图尽可能多地解释。也许在 Xamarin 表单中,使用 ViewModels 的继承工作并不明显。

提前感谢您的帮助!

祝你有美好的一天!

【问题讨论】:

  • 你有没有在setter上下断点,看看有没有其他代码设置成null
  • @mjwills 我编辑了代码,我使用 var test = SearchGame;是的,我在设置器上放了一个断点,我说在基本 GamesViewModel 中分配的值没有问题,但在派生的视图模型中,值为 null :(
  • 我们需要minimal reproducible example。我会说你正在写入的对象和正在读取的对象有 90% 的可能性是不同的对象。
  • @mjwills 是的,我同意它们是不同的对象,但我没有创建 GamesViewModel 的任何对象。也许我错过了什么
  • 分享minimal reproducible example,我们来看看。还要在SearchViewModel的构造函数中添加一个断点,这样你就可以看到它们什么时候被构造了。

标签: c# xamarin mvvm xamarin.forms viewmodel


【解决方案1】:

首先,我做了一个ViewModelBase,如下代码。

    public class ViewModelBase : INotifyPropertyChanged
    {

        private string _title;
        public string Title
        {
            get => _title;
            set => SetProperty(ref _title, value);
        }

        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs((propertyName)));
        }

        protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
        {
            if (EqualityComparer<T>.Default.Equals(storage, value))
            {
                return false;
            }
            storage = value;
            OnPropertyChanged(propertyName);

            return true;
        }
    }
}

我在SearchViewModel 中为SearchGame 设置了一个文本。

    public class SearchViewModel : GamesViewModel
    {
        public SearchViewModel()
        {
            SearchGame = "test";
        } 
        
    }

这是关于GamesViewModel的代码。

    public class GamesViewModel:ViewModelBase
    {
        private string _searchGame;
        public string SearchGame
        {
            get => _searchGame;
            set => SetProperty(ref _searchGame, value);
        }
    }

然后我创建一个Label 来绑定SearchGame 属性,如xaml。

   <Label Text="{Binding SearchGame}" 
           HorizontalOptions="Center"
           VerticalOptions="CenterAndExpand" />

这是布局背景代码。

  public partial class MainPage : ContentPage
    {
        public MainPage()
        {
            InitializeComponent();
            this.BindingContext = new SearchViewModel();
            
        }
    }

这是运行截图。

【讨论】:

    【解决方案2】:

    我向 GamesViewModel 发送了一条消息,其中包含如下搜索游戏值:

      MessagingCenter.Send(this, "search_game", titleViewModel.SearchGame);
      var detailPage = (Application.Current.MainPage as MasterDetailPage)?.Detail;
      await detailPage.Navigation?.PushAsync(new SearchGamePage());
    

    我在 GamesViewModel 构造函数中订阅此消息并分配给 SearchGame 属性消息值,如下所示:

     MessagingCenter.Subscribe<CustomTitleView, string>(this, "search_game", (sender, message) =>
       {
                SearchGame = message;
        });
    

    但正如您在上面的代码中看到的,我创建了 SearchGamePage 实例。 SearchGamePage 构造函数调用并在其中创建新的 SearchViewModel 实例:

    public partial class SearchGamePage : ContentPage
    {
        public SearchGamePage()
        {
            InitializeComponent();
            BindingContext = new SearchViewModel();
        }
    }
    

    这就是 GamesViewModel 构造函数再次调用并在 SearchGame 属性中分配 null 的原因。

    我在 App.xaml.cs 文件中使用 DependencyService.Register 解决了这个问题:

     public App()
        {
            InitializeComponent();
    
            DependencyService.Register<MockDataStore>();
            DependencyService.Register<IGameApiClient, GameApiClient>();
            DependencyService.Register<IFavoriteGameService, FavoriteGameService>();
            DependencyService.Register<GamesViewModel>();
            DependencyService.Register<SearchViewModel>();
            DependencyService.Register<NewGamesViewModel>();
            DependencyService.Register<TitleViewModel>();
            MainPage = new MainPage();
        }
    

    最后,在 SearchGamePage 构造函数中,我像这样分配给 BindingContext SearchViewModel:

     public SearchGamePage()
     {
            InitializeComponent();
            BindingContext = DependencyService.Get<SearchViewModel>();
     }
    

    现在,当我使用 messenger 为 GamesViewModel 中的 SearchGame 属性分配值时,一切都很好,我可以在 SearchViewModel 中的 SearchGame 属性中看到相同的值,因为没有重新创建此 ViewModel。

    我了解我在提问时没有提供完整信息。但我希望如果其他人面临同样的问题,这个答案会很有用。

    【讨论】:

      猜你喜欢
      • 2021-07-11
      • 1970-01-01
      • 2023-03-04
      • 1970-01-01
      • 1970-01-01
      • 2014-06-14
      • 2012-02-13
      • 1970-01-01
      • 2023-03-07
      相关资源
      最近更新 更多