Unity & WPF - внедрение DataContext через внедрение свойства в дочерний элемент управления - PullRequest
4 голосов
/ 09 февраля 2010

Я следовал примеру MVVM Джейсона Доллингера из Lab49 , чтобы изучить основы использования Unity с приложением MVFM WPF. Я построил простой пример, следуя его базовой архитектуре, используя внедрение свойств и атрибут Dependency для внедрения моделей представления в представления. В моем примере есть главное окно с дочерним пользовательским элементом управления, созданным в XAML-окне. Дочерний элемент управления (и главное окно тоже) имеет свойство для назначения модели представления:

[Dependency]
public IChildViewModel VM
{
    set { this.DataContext = value;}
}

Я подключаю все в app.xaml.cs:

protected override void OnStartup(StartupEventArgs e)
{
    base.OnStartup(e);

    IUnityContainer container = new UnityContainer();

    container.RegisterType<IWindowViewModel, Window1ViewModel>();
    container.RegisterType<IChildViewModel, UserControl1ViewModel>();

    Window1 window = container.Resolve<Window1>();
    window.Show();
}

В главное окно вводится его модель представления, а дочерний элемент управления - нет. Есть ли прямой способ получить разрешение для распространения на дочерние элементы управления? Какие архитектурные изменения мне нужно сделать, чтобы сделать это? На данный момент я не привязан к Unity, поэтому могу перейти на другой контейнер, если поддерживается этот тип поведения.

Ответы [ 4 ]

4 голосов
/ 10 февраля 2010

Ответ зависит от того, владеет ли главное окно дочерними окнами как составное представление, или оно создает новые представления на лету (для модальных или немодальных дочерних окон).

В первом случае основной ViewModel должен напрямую владеть дочерними ViewModels, что означает, что вы можете реализовать дочерние ViewModels как свойства только для чтения в основной ViewModel и использовать привязку данных для привязки дочерних представлений к соответствующим свойствам.

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

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


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

public interface IWindow
{
    void Close();

    IWindow CreateChild(object viewModel);

    void Show();

    bool? ShowDialog();
}

Это позволяет ViewModel создавать новые окна и показывать их (например, в виде диалогов). Простая реализация выглядит так:

public class WindowAdapter : IWindow
{
    private readonly Window window;

    public WindowAdapter(Window window)
    {
        if (window == null)
        {
            throw new ArgumentNullException("window");
        }

        this.window = window;
    }

    #region IWindow Members

    public void Close()
    {
        this.window.Close();
    }

    public IWindow CreateChild(object viewModel)
    {
        var cw = new ContentWindow();
        cw.Owner = this.window;
        cw.WindowStartupLocation = WindowStartupLocation.CenterOwner;
        cw.DataContext = viewModel;
        return cw;
    }

    public void Show()
    {
        this.window.Show();
    }

    public bool? ShowDialog()
    {
        return this.window.ShowDialog();
    }

    #endregion
}
2 голосов
/ 17 марта 2011

Есть два способа сделать это. Следующий фрагмент кода должен прояснить это.

        //creating Container
        IUnityContainer _container = new UnityContainer();

        //Data Source
        TasksListViewModel _tasksSource = new TasksListViewModel(); //My Data Source
        _container.RegisterInstance<TasksListViewModel>(_tasksSource); //Registering it                      

        //Resolve Window
        Window1 window = _container.Resolve<Window1>();

        //Answer to your question: Inject ViewModel into the View (User control)
        //Two ways:
        //1. Using Build (assuming View IS already added to the main window with the name "myView")
            _container.BuildUp(typeof(TasksListView), window.FindName("myView"));
        //---- OR ----
        //2. Adding the view to the grid (assuming View IS NOT already added to the main window)
            //Resolve View
            //TasksListView view = _container.Resolve<TasksListView>();
            //Make sure you have grid (content control) named LayoutRoot
            //window.LayoutRoot.Children.Add(view); //Add it to the Main Window's grid (LayoutRoot)
        window.Show();
1 голос
/ 09 июня 2010

Я также боролся с концепцией внедрения DataContext в мои представления (UserControls).
Идея показа дочерних моделей представления через модель представления главного окна имеет ограниченную привлекательность?

Следующая идея работает, но вы получаете отрицательный отзыв от Visual Studio IDE.

Мой App.xaml.cs выглядит так:

protected override void OnStartup(StartupEventArgs e)
{
    base.OnStartup(e);

    UnityContainer unityContainer = new UnityContainer();
    this.Properties["UnityContainer"] = unityContainer; 
    unityContainer.LoadConfiguration();
    unityContainer.Resolve<MainWindow>().Show();
}

public static IUnityContainer UnityContainer 
{ 
     get
    { 
        return (IUnityContainer)App.Current.Properties["UnityContainer"]; 
    } 
}

Я зарегистрировал свои контейнеры в App.config, но это только личный выбор.

В моем пользовательском коде управления есть следующее:

protected override void OnInitialized(EventArgs e)
{
    base.OnInitialized(e);
    this.DataContext = App.UnityContainer.Resolve<MyViewModel>();
}

В приведенном выше примере MyViewModel не зарегистрирован и не имеет интерфейса.

Как я уже говорил, вышеописанное работает для меня, но IDE жалуется на невозможность создать экземпляр пользовательского элемента управления. Однако, если вы запустите приложение, оно будет отлично работать.

0 голосов
/ 10 февраля 2010

Если я объединю свои дочерние модели представлений, которые будут принадлежать и представлены в качестве свойств в модели представления окна, а затем установлю для DataContext пользовательских элементов управления в XAML соответствующее свойство, тогда я смогу удалить свойство, приписываемое зависимостью, из дочернего код в целом. Это делает для своего рода неуклюжую модель представления окна, все же. и я не совсем доволен этим. Я основал этот подход с PL's ответа на этот связанный вопрос .

...