WPF TabControl с MVVM с использованием внедрения зависимостей - PullRequest
0 голосов
/ 25 апреля 2020

Я очень новичок в WPF, но с большим опытом. NET и C#. Я пытаюсь создать (как я думаю, было бы) довольно простое приложение рабочего стола администратора CRUD для веб-сайта, который я планирую создать.

Кажется, что WPF намного сложнее, чем я ожидал, и после многих Поиск в Google Я в основном понял, что каждый использует шаблон MVVM - отлично. Теперь, с моим существующим опытом. NET, я знаю, что определенно хочу использовать внедрение зависимостей. Я обнаружил, что, похоже, все сделано в ViewModel в WPF, включая все сервисы и все - опять хорошо.

Теперь по моей проблеме. Я установил базовый элемент управления c tab и привязываю значения табуляции к перечислению, используя Enum.GetValues(). Я хочу, чтобы вид менялся при выборе вкладки, и вид будет зависеть от того, какая вкладка выбрана. Проблема в том, что я не могу показать вид - он просто показывает пустой экран. Представление - это пользовательский элемент управления UserControl, который я создал и определил как ресурс, и он содержит сетку, набор кнопок и прочее. Я пропустил это снизу, поскольку это не выглядит актуальным.

My 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 имеет служебные зависимости, поэтому его необходимо создать с использованием фабрики.

И мои настройки DI в App .xaml.cs :

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();
    }
}

1 Ответ

0 голосов
/ 26 апреля 2020

Я уже разобрал эту проблему. Будучи новичком в WPF, я не осознавал, что должен использовать INotifyPropertyChanged для обновления пользовательского интерфейса после изменения значения свойства в моей ViewModel. Я видел, что это использовалось во многих статьях и учебных пособиях, которые я видел, но на самом деле не понимал, что это было или как применить его к моему приложению.

Я сделал изменения, чтобы реализовать этот интерфейс в моей базовой ViewModel, например, так:

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

Затем я изменяю свой MainViewModel на наследование от базового класса и изменяю установщик свойства CurrentTab на вызов OnPropertyChanged (с имя свойства) после того, как я изменил свойство view / viewmodel:

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

Я считаю, что это говорит пользовательскому интерфейсу, что что-то изменилось, и ему нужно перерисовать себя. Поправь меня, если я ошибаюсь или это упрощение.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...