c# - Agregue varias vistas dentro de una vista usando WPF y Caliburn.Micro
mvvm (3)
EDITAR - Nueva (más completa) Respuesta a continuación:
Ok, CM está haciendo muchas cosas por ti, se trata de preparar tus clases y xaml preparado para que CM pueda encontrarlo. Como dije antes, prefiero escribir código explícito, en lugar de confiar en suposiciones implícitas de código en el marco.
Entonces, el Bootstrapper, del proyecto CM predeterminado, está bien.
public class AppBootstrapper : Bootstrapper<MainViewModel>
{
// ... You shouldn''t need to change much, if anything
}
La sección `Bootstrapper ''es muy importante, indica qué ViewModel es la primera, o pantalla principal, cuando se inicia la aplicación.
[Export(Typeof(MainViewModel))]
public class MainViewModel : Screen, IShell
{
[ImportingConstructor]
public MainViewModel(YourFirstViewModel firstViewModel, YourSecondViewModel secondviewModel) // etc, for each child ViewModel
{
}
}
En [ImportingConstructor]
no necesita hacer nada más que especificar que MainViewModel requiere la presencia de otros ViewModels. En mi caso particular, me gusta que MainViewModel sea un contenedor, y solo contenedor, la lógica de eventos se maneja en otro lado. Pero con la misma facilidad podría tener su lógica de manejo aquí, pero eso es un momento de otra discusión.
Ahora, cada modelo de vista secundario también necesita exportarse para que CM sepa dónde encontrarlos.
[Export(Typeof(YourFirstViewModel))]
public class YourFirstViewModel : IShell
{
// VM properties and events here
}
No es necesario especificar un Constructor de importación si solo está utilizando un constructor predeterminado.
Ahora, cada una de sus Vistas para estos se verá algo así como:
<UserControl x:Class="Your.Namespace.MainView"
xmlns:views="clr-namespace:Your.Namespace.Views"
xmlns:cal="http://www.caliburnproject.org"
cal:Bind.Model="Your.Namespace.ViewModels.MainViewModel"
MinWidth="800" MinHeight="600">
<StackPanel x:Name="RootVisual">
<views:YourFirstView />
<views:YourSecondView />
<!-- other controls as needed -->
</StackPanel>
</UserControl>
XAMl o una de las vistas de los niños
<UserControl x:Class="Your.Namespace.Views.YourFirstView"
xmlns:cal="http://www.caliburnproject.org"
cal:Bind.Model="Your.Namespace.ViewModels.YourFirstViewModel"
MinWidth="800" MinHeight="600">
<Grid x:Name="RootVisual">
<!-- A bunch of controls here -->
</Grid>
</UserControl>
¿Qué demonios está pasando realmente aquí?
Bueno, CM ve en el programa de arranque, que MainViewModel es el punto de partida debido a la línea que especifica public class AppBootstrapper : Bootstrapper<MainViewModel>
. MainViewModel
requiere que un YourFirstViewModel
y YourSecondViewModel
(y otros ViewModels) sean necesarios en su constructor, por lo que CM construye cada uno. Todos estos Modelos de Vista terminan en el IoC (haciendo que su vida sea mucho más fácil más tarde, de nuevo, una discusión completamente diferente).
CM maneja la asignación del contexto de datos, en su nombre, a cada una de las vistas porque especifica a qué máquina virtual vincularse con la línea como cal:Bind.Model="Your.Namespace.ViewModels.YourFirstViewModel"
Con un poco de suerte, eso debería comenzar. Consulte también el ejemplo de ejemplo de CM Caliburn.Micro.HelloEventAggregator
ya que hace exactamente lo que está buscando (aunque, se describe como una demo de Event Aggregator, que también es muy útil, pero nuevamente, otra discusión)
(Respuesta original por reverencia, abajo)
Usted necesita hacer ésto:
<UserControl x:Class="Your.Namespace.Here.YourView"
xmlns:cal="http://www.caliburnproject.org"
cal:Bind.Model="Your.Namespace.Here.YourViewModel"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="1024">
<YourControlLayout />
</UserControl>
Observe la línea cal:Bind.Model="Your.Namespace.Here.YourViewModel"
que especifica el modelo de vista exacto para enlazar esta vista.
No olvide exportar su tipo de clase, o cm no puede encontrarlo.
[Export(typeof(YourViewModel))]
public class YourViewModel : IShell
{
...
}
Luego puede anidar sus controles de usuario como mejor le parezca. Es una muy buena forma de utilizar CM, y lo encontrará altamente escalable. La única debilidad es que View y ViewModel deben estar en el mismo proyecto (hasta donde yo sé). Pero la fuerza de este enfoque es que puede separar las clases Ver y Ver modelo en diferentes espacios de nombres (dentro del mismo proyecto) si lo desea, para mantener las cosas organizadas.
Como comentario sobre cm prefiero este método, en realidad, incluso si no tengo que anidar View UserControls y tal. Prefiero declarar explícitamente que VM VM una Vista está obligada (y aún así dejar que CM maneje todo el trabajo pesado en IoC) que dejar que cm "lo resuelva" del código implícito.
Incluso con un buen marco: el código explícito es más fácil de mantener que el código implícito. Especificar el modelo de vista encuadernado tiene la ventaja de indicar claramente cuál es el contexto de datos esperado, por lo que no tendrá que adivinar más tarde.
Estoy tratando de aprender usando Caliburn.Micro con WPF. ¿Cómo puedo agregar varias vistas dentro de una vista?
<Window x:Class="ProjectName.Views.MainView"
...>
<Grid>
<views:MyControlView />
</Grid>
</Window>
Otra vista, con viewmodel: MyControlViewModel
<UserControl x:Class="ProjectName.Views.MyControlView"
...>
<Grid>
...
</Grid>
</UserControl>
Si solo agrego la vista, no detectará que tiene un modelo de vista con el nombre apropiado. ¿Cómo puedo unir esto a esto?
He probado con diferentes bootstrappers y usando algo como cal: Bind.Model = "path / classname / merge of the two". He intentado agregar eso a la vista principal y al control de usuario (MyControlView). Estoy MUY agradecido por cualquier ayuda con respecto a este asunto. Estoy bastante atrapado, y realmente quiero usar Caliburn.Micro :)
Saludos cordiales, pez diamante
Editar: Todavía no puedo hacer que funcione, el problema parece estar en el programa de arranque u otra cosa. Pero solo para aclarar, aquí está mi código que estoy ejecutando para un proyecto de prueba.
MainView xaml:
<Window x:Class="Test.Views.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:cal="clr-namespace:Caliburn.Micro;assembly=Caliburn.Micro"
xmlns:views="clr-namespace:Test.Views"
Title="MainWindow" Height="360" Width="640">
<Grid>
<views:MyControlView />
</Grid>
Código de MainViewModel:
public partial class MainViewModel : PropertyChangedBase
{
}
MyControlView xaml:
<UserControl x:Class="Test.Views.MyControlView"
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:cal="clr-namespace:Caliburn.Micro;assembly=Caliburn.Micro"
cal:Bind.Model="Test.MyControlViewModel"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<TextBlock Text="{Binding MyProp}"/>
</Grid>
Código MyControlView:
public class MyControlViewModel : PropertyChangedBase
{
public string MyProp
{
get { return "Working"; }
}
}
Captura de pantalla del error: http://clip2net.com/s/1gtgt
Yo he tratado
cal:Bind.Model="Test.ViewModels.MyControlViewModel"
también. También probé la referencia de referencia:
xmlns:cal="http://www.caliburnproject.org"
Captura de pantalla de mi proyecto http://clip2net.com/s/1gthM
Debido a que la documentación es principalmente para Silverlight y algunas veces es para Caliburn y no para CM, podría haber implementado el arrancador incorrecto. Para este proyecto de prueba, es así: (con .xaml-change en App.xaml)
public class BootStrapper : Bootstrapper<MainViewModel>
{
}
Por favor, ayúdame aquí! Parece que es algo básico que me falta :)
Un mejor enfoque es usar ContentControl
en su vista principal y darle el mismo nombre que una propiedad pública en su MainViewModel
que es de tipo MyControlViewModel
. P.ej
MainView.xaml
<ContentControl x:Name="MyControlViewModel" />
MainViewModel.cs
// Constructor
public MainViewModel()
{
// It would be better to use dependency injection here
this.MyControlViewModel = new MyControlViewModel();
}
public MyControlViewModel MyControlViewModel
{
get { return this.myControlViewModel; }
set { this.myControlViewModel = value; this.NotifyOfPropertyChanged(...); }
}
en el archivo App.xaml.cs, en el método GetInstance agrega las siguientes líneas
protected override object GetInstance(Type service, string key) { if (service == null && !string.IsNullOrWhiteSpace(key)) { service = Type.GetType(key); key = null; } // the rest of method }