统一与WPF - 通过属性注入将 DataContext 注入子控件

发布于 2024-08-20 11:17:52 字数 902 浏览 3 评论 0原文

我按照 Lab49 中的 Jason Dollinger 的 MVVM 示例学习了将 Unity 与 MVVM 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 结合,所以如果支持这种行为,我可以更改为另一个容器。

I followed Jason Dollinger's MVVM sample from Lab49 to learn the basics of using Unity with an MVVM WPF application. I constructed a simple sample following his basic architecture, using property injection and the Dependency attribute to inject viewmodels into the views. My sample has a main window with a child user control created in the window's XAML. The child control (and the main window, too) has a property for assigning the viewmodel:

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

I wire everything up in 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();
}

The main window is getting its viewmodel injected, but the child control is not. Is there any direct way of getting the resolution to propagate down into child controls? What kind of architectural changes would I need to make to do so? I'm not wedded to Unity at this point, so I can change to another container if this kind of behavior is supported.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

朮生 2024-08-27 11:17:52

答案取决于主窗口是否“拥有”子窗口作为复合视图,或者它动态创建新视图(对于模态或非模态子窗口)。

在第一种情况下,主 ViewModel 必须直接拥有子 ViewModel,这意味着您可以将子 ViewModel 实现为主 ViewModel 上的只读属性,并使用数据绑定将子 View 绑定到适当的属性。

您是想让主 ViewModel 直接控制子级的创建,还是使用构造函数注入将它们注入到其中,取决于您需要的可变性程度。

与往常一样,如果您需要在任意时间创建子视图的新实例,注入的抽象工厂是一个更好的模型。


举个例子,我经常定义这个接口并将其注入到需要它的 ViewModel 中:

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
}

The answer depends on whether the main windows 'owns' the child windows as a Composite View, or it creates new Views on the fly (for modal or non-modal child windows).

In the first case, the main ViewModel must own the child ViewModels directly, which means that you can implement the child ViewModels as read-only properties on the main ViewModel and use databinding to bind the child Views to the appropriate properties.

Whether you want to let the main ViewModel control the creation of the children directly or have them injected into it using Constructor Injection depends on the degree of variability you need.

As always, if you need to create new instances of child Views at arbitrary times, an injected Abstract Factory is a better model.


As an example, I often define and inject this interface into those of my ViewModels that need it:

public interface IWindow
{
    void Close();

    IWindow CreateChild(object viewModel);

    void Show();

    bool? ShowDialog();
}

This allows the ViewModel to create new windows and show them (e.g. as dialogs). A simple implementation looks like this:

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
}
滥情稳全场 2024-08-27 11:17:52

我也一直在努力解决将 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 抱怨无法创建用户控件的实例。但是,如果您启动该应用程序,它就会完美运行。

I have also struggled with the concept on injecting DataContext into my views (UserControls).
The idea of exposing child viewmodels via the main window viewmodel has a limited amount of appeal?

The following idea works but you do get negative feedback from the Visual studio IDE.

My App.xaml.cs look like this:

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"]; 
    } 
}

I have registered my containers in App.config, but that’s just personal choice.

In my user control code behind I have the following:

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

In the above instance MyViewModel is not registered and does not have an interface.

As I said before the above works for me but the IDE complains about not being able to create an instance of the user control. However if you start the application it works perfectly.

佼人 2024-08-27 11:17:52

有两种方法可以做到这一点。下面的代码片段应该可以澄清这一点。

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

There are two ways to do this. Following code snippet should clarify it.

        //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();
潦草背影 2024-08-27 11:17:52

如果我将子视图模型合并为窗口视图模型上的属性并公开,然后将 XAML 中用户控件的 DataContext 设置为适当的属性,那么我可以从子代码隐藏中删除依赖属性属性共。不过,它造成了一种笨重的窗口视图模型。我对此并不完全满意。我基于 PL's 回答 此相关问题

If I consolodate my child view models to be owned and exposed as properties on the window's viewmodel, and then set the DataContext of the user controls in XAML to the appropriate property, then I can remove the Dependency-attributed property from the child code-behind altogether. It makes for a kind of clunky window viewmodel, though. and I'm not entirely happy with it. I based this approach from PL's answer to this related question.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文