Как передать ссылку на экземпляр класса при создании модели представления - PullRequest
0 голосов
/ 21 февраля 2019

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

У меня есть основной класс Program, и я хочу иметь некоторые другие классы, такие какproduct, testTool и т. Д.

Когда приложение запускается, я загружаю mainWindow, после чего создается mainWindowViewModel и, в свою очередь, создается класс Program.

Когда пользователь нажимает кнопку, я хочу, чтобы mainWindowViewModel отображал userControl1, но я хочу, чтобы userControl1ViewModel мог видеть класс Program и получать доступ к его свойствам и методам.

Я продолжаю читать вещитипа «передать экземпляр класса по ссылке», что нормально, но если userControl1View создает userControl1ViewModel, как я могу передать ссылку на класс 'program', созданный в начале программы?

Ответы [ 2 ]

0 голосов
/ 22 февраля 2019

Это то, для чего предназначено внедрение зависимостей.

Прежде всего, если вы работаете с MVVM, вы должны иметь возможность запускать все приложение без создания каких-либо представлений вообще, то есть только моделей представлений.Если у вас есть MainWindow с ChildView (скажем), то в общем случае вы сопоставляете их с соответствующими моделями представлений:

public MainViewModel : ViewModeBase
{
    public ChildViewModel MyChild {get; } // gets assigned later

Тогда в вашем XAML:

<Window ...

   <local:ChildView DataContext="{Binding MyChild}" />

Иногда вам понадобитсяMyChild для отображения различных представлений, каждое из которых будет иметь свою собственную соответствующую модель представления, и вам может потребоваться изменить ее во время выполнения.В этих случаях MyChild должен иметь тип объекта (или некоторый общий базовый класс), а также должен поддерживать уведомление об изменении свойства:

public class MainViewModel : ViewModelBase
{
    private object _MyChild;
    public object MyChild
    {
        get { return this._MyChild; }
        set
        {
            if (this._MyChild != value)
            {
                this._MyChild = value;
                RaisePropertyChanged(() => this.MyChild);
            }
        }
    }
}

Затем в вашем XAML вместо этого вы создаете ContentControl:

<Window ...

   <ContentControl ="{Binding MyChild}" />

В этом месте вы затем используете DataTemplate в своем разделе ресурсов окна или приложения, чтобы указать, какие представления сопоставляются с какими моделями представлений:

    <DataTemplate DataType="{x:Type vm:FooViewModel}">
        <view:FooView />
    </DataTemplate>

    <DataTemplate DataType="{x:Type vm:BarViewModel}">
        <view:BarView />
    </DataTemplate>

Так что теперь, если вы сделаете что-то подобное вваша MainViewModel ...

this.MyChild = new FooViewModel();

... ContentControl автоматически заполняется элементом управления типа FooView.Кроме того, его DataContext будет автоматически установлен на экземпляр FooViewModel, который вы создали.И затем вы переназначаете его следующим образом:

this.MyChild = new BarViewModel();

... тогда FooView будет заменен BarView.

Так что с DataTemplating все, что вам нужно беспокоиться, этоПередача ссылок на ваши ViewModel друг на друга, и здесь начинается внедрение зависимостей. Это большая тема, я предлагаю вам ознакомиться с ней, но идея в том, что вы создаете все свои модели представлений с помощью инфраструктуры DI (вместооператор new) и пусть он склеит все биты вместе.Например, ваши продукты могут быть частью класса репозитория, который управляет всеми ними, поэтому вы начинаете с объявления интерфейса:

public interface IProductRepository
{
    Products[] AllProducts();
    Product GetProductByName(string name);
    ... etc ...

Затем вы создаете реальный класс, который реализует этот интерфейс, и во время установки выдайте вашей структуре зависимостей правила того, что она должна делать всякий раз, когда что-либо запрашивает IProductRepository (используйте один экземпляр, создайте новый и т. д.).Затем, когда что-либо во всем вашем приложении требуется для доступа к репозиторию продукта, все, что ему нужно сделать, это объявить свойство с тегом [Inject] (это если вы используете Ninject, у каждой библиотеки есть свой способ сделать это):

public class MyClass
{
    [Inject]
    public IProductRepository ProductRepo {get; set;} // <-- gets injected

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

Это очень простой обзоркак DataTemplating и Dependency Injection работают в MVVM, как только вы начнете использовать их, вы удивитесь, как раньше обходились без них.Насколько я могу судить, основная проблема в вашем вопросе заключается в том, что вы пытаетесь заставить ваши модели представлений общаться друг с другом.В общем, это не так, как реализован MVVM.Модели представлений обмениваются данными через сервисы, которые вводятся в них. Общее практическое правило заключается в том, чтобы служить проводником между этими сервисами и интерфейсными элементами графического интерфейса, а не друг с другом.

0 голосов
/ 21 февраля 2019

То, о чем вы говорите, на самом деле не простой процесс, а то, о чем вы говорите, это архитектура, позволяющая получить ссылки, которые вы ожидаете, там, где вы ожидаете их.Это может быть решено довольно многими способами, поэтому ниже я приведу довольно несостоятельный, но чрезвычайно быстрый пример.Архитектурные проблемы не совпадают с // HACK: s

Как правило, вы хотите, чтобы Модели поступали из центрального расположения, например, из базы данных, которая контролирует передачу соответствующего экземпляра.

public abstract class Model
{ 
    // HACK: Don't bother wiring up OnPropertyChanged here, since we don't expect ID to get updated that often, but need a public setter for the Provider
    Guid ID { get; set; }
}

// HACK: While using a generic here makes for readable code, it may become problematic if you want to inherit your models
public class ModelProvider<TModelType> where TModelType : Model, new()
{
    // HACK: Use better dependency injection than this
    private static ModelProvider<TModelType> _instance = new ModelProvider<TModelType>();
    public static ModelProvider<TModelType> Instance => _instance;
    private ModelProvider() { }

    // TODO: Make this into a dictionary of WeakReferences so that you're not holding stale data in memory
    ConcurrentDictionary<Guid, TModelType> LoadedModels = new Dictionary<Guid, TModelType>();

    private TModelType GenerateModel(Guid id) => new TModelType { ID = id };
    private TModelType LoadKnownModel(Guid id)
    {
        throw new NotImplementedException("Implement a data store to get known models");
    }

    public TModelType GetNew() => LoadedModels.AddOrUpdate(Guid.NewGuid(). GenerateModel);
    public TModelType GetById(Guid id) => LoadedModels.GetOrAdd(id, LoadKnownModel);
}

И тогда ваши ViewModels имеют доступ к

ModelProvider<Product>.Instance.GetById(WellKnownGuid);

. Для тестирования WellKnownGuid может быть статическим идентификатором в Программе

.
...