Данные C # не отображаются в выпадающем списке, хотя список не пуст

#c# #wpf #xaml #mvvm

#c# #wpf #xaml #mvvm

Вопрос:

Доброго времени суток, я столкнулся с проблемой. У меня есть выпадающий список для ввода данных из базы данных. Я могу брать данные из базы данных, но по какой-то причине данные не хотят отображаться в выпадающем списке. Что мне нужно сделать, чтобы отобразить данные? Заранее благодарю вас.

BoatView.xaml:

 <UserControl x:Class="OrderBoatNew.WPF.Controls.BoatCard"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:local="clr-namespace:OrderBoatNew.WPF.Controls"
             xmlns:vm="clr-namespace:OrderBoatNew.WPF.ViewModels"
             mc:Ignorable="d"
             d:DesignHeight="400" d:DesignWidth="400">

    <UserControl.DataContext>
        <vm:BoatCardViewModel />
    </UserControl.DataContext>

    <Grid>
        <StackPanel Orientation="Horizontal">

            <Grid Margin="15 15 0 0">

                <Grid.Resources>
                    <Style TargetType="ComboBox">
                        <Setter Property="Margin"
                                Value="0 20 0 0" />
                    </Style>
                </Grid.Resources>

                <Grid.RowDefinitions>
                    <RowDefinition Height="auto" />
                    <RowDefinition Height="auto" />
                    <RowDefinition Height="auto" />
                </Grid.RowDefinitions>

                <TextBlock Text="Model:"
                           Grid.Row="0" />
                <ComboBox Grid.Row="0"
                          Width="150"
                          ItemsSource="{Binding Models}"
                          DisplayMemberPath="Name" />

                <TextBlock Text="Wood:"
                           Grid.Row="1" />
                <ComboBox Grid.Row="1"
                          ItemsSource="{Binding Woods}" />

                <TextBlock Text="Color:"
                           Grid.Row="2" />
                <ComboBox Grid.Row="2"
                          ItemsSource="{Binding Colors}" />
            </Grid>
        </StackPanel>
    </Grid>
</UserControl>
 

Я попытался взять обе таблицы и сразу взять имя из таблицы, но ни один из этих вариантов не работает.

BoatCardViewModel.cs

 using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
using OrderBoatNew.Domain.Models;
using OrderBoatNew.Domain.Services;
using OrderBoatNew.EntityFramework.Services;
using OrderBoatNew.EntityFramework;

namespace OrderBoatNew.WPF.ViewModels
{
    public class BoatViewModel : ViewModelBase
    {
        private readonly IDataService<Model> _modelService;
        private readonly IDataService<Wood> _woodService;
        private readonly IDataService<Color> _colorService;

        private List<Model> _models;

        public List<Model> Models
        {
            get => _models;
            set
            {
                _models = value;
                OnPropertyChanged(nameof(Models));
            }
        }
        
        private List<string> _woods;

        public List<string> Woods
        {
            get => _woods;
            set
            {
                _woods = value;
                OnPropertyChanged(nameof(Woods));
            }
        }

        private List<string> _colors;

        public List<string> Colors
        {
            get => _colors;
            set
            {
                _colors = value;
                OnPropertyChanged(nameof(Colors));
            }
        }

        public BoatViewModel()
        {
            var contextFactory = new OrderBoatNewDbContextFactory();
            _modelService = new GenericDataService<Model>(contextFactory);
            _woodService = new GenericDataService<Wood>(contextFactory);
            _colorService = new GenericDataService<Color>(contextFactory);
        }

        public static BoatViewModel LoadBoatViewModel()
        {
            BoatViewModel boatViewModel = new BoatViewModel();
            boatViewModel.LoadValues();

            return boatViewModel;
        }

        private async void LoadValues()
        {
            await _modelService.GetAll().ContinueWith(task =>
            {
                if (task.Exception == null)
                    Models = task.Result.ToList();
            });

            await _woodService.GetAll().ContinueWith(task =>
            {
                if (task.Exception == null)
                    Woods = task.Result.Select(w => w.Name).ToList();
            });

            await _colorService.GetAll().ContinueWith(task =>
            {
                if (task.Exception == null)
                    Colors = task.Result.Where(c => c.ForAdditionalMoney == false)
                                        .Select(c => c.Name)
                                        .ToList();
            });
        }
    }
}
 

IDataService.cs

 using System.Collections.Generic;
using System.Threading.Tasks;

namespace OrderBoatNew.Domain.Services
{
    public interface IDataService<T>
    {
        Task<IEnumerable<T>> GetAll();
        Task<T> Get(int id);
        Task<T> Create(T entity);
        Task<T> Update(int id, T entity);
        Task<bool> Delete(int id);
    }
}
 

GenericDataService.cs

 using System.Collections.Generic;
using System.Threading.Tasks;
using OrderBoatNew.Domain.Services;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking;
using OrderBoatNew.Domain.Models;

namespace OrderBoatNew.EntityFramework.Services
{
    public class GenericDataService<T> : IDataService<T> where T : DomainObject
    {
        private readonly OrderBoatNewDbContextFactory _contextFactory;

        public GenericDataService(OrderBoatNewDbContextFactory contextFactory) { _contextFactory = contextFactory; }

        public async Task<IEnumerable<T>> GetAll()
        {
            using (OrderBoatNewDbContext context = _contextFactory.CreateDbContext())
            {
                IEnumerable<T> entities = await context.Set<T>().ToListAsync();

                return entities;
            }
        }

        public async Task<T> Get(int id)
        {
            using (OrderBoatNewDbContext context = _contextFactory.CreateDbContext())
            {
                T entity = await context.Set<T>().FirstOrDefaultAsync((e) => e.ID == id);

                return entity;
            }
        }

        public async Task<T> Create(T entity)
        {
            using (OrderBoatNewDbContext context = _contextFactory.CreateDbContext())
            {
                EntityEntry<T> createdResult = await context.Set<T>().AddAsync(entity);
                await context.SaveChangesAsync();

                return createdResult.Entity;
            }
        }

        public async Task<T> Update(int id, T entity)
        {
            using (OrderBoatNewDbContext context = _contextFactory.CreateDbContext())
            {
                entity.ID = id;

                context.Set<T>().Update(entity);
                await context.SaveChangesAsync();

                return entity;
            }
        }

        public async Task<bool> Delete(int id)
        {
            using (OrderBoatNewDbContext context = _contextFactory.CreateDbContext())
            {
                T entity = await context.Set<T>().FirstOrDefaultAsync((e) => e.ID == id);
                context.Set<T>().Remove(entity);
                await context.SaveChangesAsync();

                return true;
            }
        }
    }
}
 

UpdateViewModelCommand.cs

 #nullable enable

using System;
using System.Windows.Input;
using OrderBoatNew.Domain.Models;
using OrderBoatNew.EntityFramework.Services;
using OrderBoatNew.WPF.Services;
using OrderBoatNew.WPF.State.Navigators;
using OrderBoatNew.WPF.ViewModels;

namespace OrderBoatNew.WPF.Commands
{
    public class UpdateViewModelCommand : ICommand
    {
        private readonly INavigator _navigator;

        public UpdateViewModelCommand(INavigator navigator)
        {
            _navigator = navigator;
        }

        public bool CanExecute(object? parameter)
        {
            return true;
        }

        public void Execute(object? parameter)
        {
            if (parameter is ViewType viewType)
            {
                _navigator.CurrentViewModel = viewType switch
                {
                    ViewType.Boats => BoatViewModel.LoadBoatViewModel(),
                    ViewType.BoatsAccessory => new BoatAccessoryViewModel(),
                    _ => throw new ArgumentOutOfRangeException()
                };
            }
        }

        public event EventHandler? CanExecuteChanged;
    }
}
 

Комментарии:

1. Попробуйте быть более конкретным в отношении проблемы.

Ответ №1:

Прежде всего, вы назначаете экземпляр a BoatCardViewModel как DataContext .

 <UserControl.DataContext>
   <vm:BoatCardViewModel />
</UserControl.DataContext>
 

Вы никогда не должны этого делать, это нарушает наследование контекста данных. Вы должны либо привязать свойство контекста данных, в котором фактически используется пользовательский элемент управления, если это необходимо, например:

 <BoatCard DataContext="{Binding ...}" />
 

Теперь проблема в том, что модель представления контекста данных создается с использованием конструктора без параметров, который инициализирует службы, но никогда не вызывает их для получения данных.

Другая проблема заключается в том, что LoadBoatViewModel метод либо вообще не вызывается, либо он бесполезен, поскольку вызов возвращает новый экземпляр a BoatCardViewModel , который никогда не устанавливается в вашем пользовательском элементе управления. Таким образом, вы потенциально используете два разных экземпляра модели представления, не замечая этого.