【问题标题】:WPF TabControl with MVVM using Dependency Injection使用依赖注入的带有 MVVM 的 WPF TabControl
【发布时间】:2020-04-25 19:55:24
【问题描述】:

我对 WPF 非常陌生,但对 .NET 和 C# 非常有经验。我正在尝试为我计划构建的网站创建(我想是的)一个相当简单的 CRUD 管理桌面应用程序。

WPF 似乎比我预期的要复杂得多,经过大量谷歌搜索后,我基本上意识到每个人都使用 MVVM 模式 - 很好。现在,凭借我现有的 .NET 经验,我知道我肯定想要使用依赖注入。我发现一切似乎都在 WPF 中的 ViewModel 中完成,包括所有服务和一切——又好了。

现在,谈谈我的问题。我已经设置了一个基本的选项卡控件,并使用Enum.GetValues() 将选项卡值绑定到一个枚举。我希望在选择选项卡时更改视图,并且视图将取决于选择的选项卡。问题是,我似乎无法显示视图 - 它只是显示一个空白屏幕。该视图是我创建并定义为资源的自定义 UserControl,它包含一个网格和一堆按钮和东西。我从下面省略了它,因为它似乎不相关。

我的 MainWindow.xaml 非常简单,看起来像这样:

<Window x:Class="Stc.Admin.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:viewmodels="clr-namespace:Stc.Admin.ViewModels"
        xmlns:views="clr-namespace:Stc.Admin.Views"
        xmlns:local="clr-namespace:Stc.Admin"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">

    <Grid>
        <TabControl ItemsSource="{Binding Tabs}" SelectedItem="{Binding CurrentTab}">
            <TabControl.Resources>
                <DataTemplate DataType="{x:Type viewmodels:GamesViewModel}">
                    <views:Games />
                </DataTemplate>
            </TabControl.Resources>
            <TabControl.ContentTemplate>
                <DataTemplate>
                    <ContentControl Content="{Binding DataContext.CurrentViewModel, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type TabControl}}}" />
                </DataTemplate>
            </TabControl.ContentTemplate>
        </TabControl>
    </Grid>
</Window>

这是我的 MainViewModel.cs

public class MainViewModel
{
    private readonly IViewModelFactory<GamesViewModel> _gamesViewModelFactory;

    private ViewType _currentTab;
    public ViewType CurrentTab
    { 
        get
        {
            return _currentTab;
        }
        set
        {
            _currentTab = value;
            ChangeView(_currentTab);
        }
    }
    public ObservableCollection<ViewType> Tabs { get; }
    public ViewModelBase CurrentViewModel { get; set; }

    public MainViewModel(IViewModelFactory<GamesViewModel> gamesViewModelFactory)
    {
        _gamesViewModelFactory = gamesViewModelFactory;

        Tabs = new ObservableCollection<ViewType>(Enum.GetValues(typeof(ViewType)).Cast<ViewType>().ToArray());
    }

    private void ChangeView(ViewType viewType)
    {
        switch (viewType)
        {
            case ViewType.Games:
                CurrentViewModel = _gamesViewModelFactory.CreateViewModel();
                break;
            case ViewType.Listings:
                break;
            case ViewType.Users:
                break;
            case ViewType.Languages:
                break;
            case ViewType.Currencies:
                break;
            default:
                break;
        }
    }
}

public enum ViewType
{
    Games,
    Listings,
    Users,
    Languages,
    Currencies
}

GamesViewModel 有服务依赖,所以需要使用工厂创建。

以及我在 App.xaml.cs 中的 DI 设置:

public partial class App : Application
{
    protected override void OnStartup(StartupEventArgs e)
    {
        IServiceProvider serviceProvider = this.createServiceProvider();

        Window window = new MainWindow();
        window.DataContext = serviceProvider.GetRequiredService<MainViewModel>();
        window.Show();

        base.OnStartup(e);
    }

    private IServiceProvider createServiceProvider()
    {
        IServiceCollection services = new ServiceCollection();

        services.AddDbContext<StcContext>(options => 
            options.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=Stc;Integrated Security=True"));

        services.AddSingleton<ICrudService<Game>, CrudService<Game>>();

        services.AddSingleton<IViewModelFactory<GamesViewModel>, GamesViewModelFactory>();

        services.AddScoped<MainViewModel>();

        return services.BuildServiceProvider();
    }
}

【问题讨论】:

    标签: c# wpf mvvm


    【解决方案1】:

    我现在已经解决了这个问题。作为 WPF 的新手,我没有意识到在更改 ViewModel 上的属性值后必须使用 INotifyPropertyChanged 来更新 UI。我在我看到的很多文章和教程中都看到了它,但我并没有真正理解它是什么或如何将它应用到我的应用程序中。

    我所做的更改是在我的基础 ViewModel 上实现这个接口,如下所示:

    public class ViewModelBase : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
    

    然后我将我的 MainViewModel 更改为从基类继承,并在我更改了视图/视图模型属性:

    private ViewType _currentTab;
    public ViewType CurrentTab
    { 
        get
        {
            return _currentTab;
        }
        set
        {
            _currentTab = value;
            ChangeView(_currentTab);
            OnPropertyChanged(nameof(CurrentViewModel));
        }
    }
    

    我相信这是在告诉 UI 某些东西发生了变化,它需要重新绘制自己。如果我错了或者过于简单化了,请纠正我。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-11-12
      • 2017-03-15
      • 2023-03-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-08-06
      相关资源
      最近更新 更多