2011-10-24 20 views
12

Estoy tratando de aprender usando Caliburn.Micro con WPF. ¿Cómo puedo agregar varias vistas dentro de una vista?Agregar varias vistas dentro de una vista usando WPF y Caliburn.Micro

<Window x:Class="ProjectName.Views.MainView" 
     ...> 
<Grid> 
     <views:MyControlView /> 
</Grid> 
</Window> 

Otro punto de vista, con el modelo de vista: MyControlViewModel

<UserControl x:Class="ProjectName.Views.MyControlView" 
     ...> 
<Grid> 
    ... 
</Grid> 
</UserControl> 

Si Puedo añadir 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 = "ruta/nombre de clase/fusión de los dos". 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 :)

Best Regards, diamondfish

Editar: Todavía no puedo conseguir que funcione, el problema parece estar en el bootstrapper o algo más. 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 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

he tratado

cal:Bind.Model="Test.ViewModels.MyControlViewModel" 

también. También probamos la cal-referencia:

xmlns:cal="http://www.caliburnproject.org" 

Captura de pantalla de mi proyecto http://clip2net.com/s/1gthM

Dado que la documentación es sobre todo para Silverlight y, a veces es para Caliburn y no CM, podría haber implementado el programa previo equivocado. Para esta prueba en proyectos, es como esta: (con el .xaml-cambio en App.xaml)

public class BootStrapper : Bootstrapper<MainViewModel> 
{ 
} 

por favor me ayude aquí! Parece que es un poco de materia básica que me falta :)

+0

- publicación editada para incluir la etiqueta MVVM, bienvenido a SO! – EtherDragon

+0

Verificar el anser - He añadido una sección sobre la exportación del tipo. Este es un requisito importante para c.m para encontrar el ViewModel relacionado con la Vista. – EtherDragon

Respuesta

16

EDITAR - Nueva (más completa) respuesta a continuación:

Ok, CM está haciendo un montón de cosas para usted, se trata de conseguir sus clases y xaml preparado para CM para poder encontrarlo. Como dije antes, prefiero escribir código explícito, en lugar de confiar en suposiciones implícitas de código en el marco.

Por lo tanto, el Bootstrapper, del proyecto C.M predeterminado está bien.

public class AppBootstrapper : Bootstrapper<MainViewModel> 
{ 
    // ... You shouldn't need to change much, if anything 
} 

La sección `Bootstrapper' es muy importante, es que indica qué modelo de vista es su primera o principal de la pantalla, cuando la aplicación se inicia.

[Export(Typeof(MainViewModel))] 
public class MainViewModel : Screen, IShell 
{ 
    [ImportingConstructor] 
    public MainViewModel(YourFirstViewModel firstViewModel, YourSecondViewModel secondviewModel) // etc, for each child ViewModel 
    { 
    } 
} 

En el [ImportingConstructor] que no es necesario hacer nada más que especificar que el MainViewModel requiere la presencia de los 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 igual 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, por lo que C.M sabe dónde encontrarlos.

[Export(Typeof(YourFirstViewModel))] 
public class YourFirstViewModel : IShell 
{ 
    // VM properties and events here 
} 

hay necesidad de especificar un constructor Importación de si usted está usando un constructor por defecto.

Ahora, cada uno de sus Vistas de éstos quedará del siguiente modo:

<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 uno de los niños-vistas

<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é diablos está sucediendo realmente aquí?

Bueno, C.M ve en el bootstrapper, que MainViewModel es el punto de partida debido a la línea que especifica public class AppBootstrapper : Bootstrapper<MainViewModel>. MainViewModel requiere que se necesiten YourFirstViewModel y YourSecondViewModel (y otros modelos de vista) en su constructor, por lo que C.M 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).

C.M maneja la asignación de la DataContext, en su nombre, a cada uno de los puntos de vista, porque se especifica qué VM para unirse a la línea como cal:Bind.Model="Your.Namespace.ViewModels.YourFirstViewModel"

Con un poco de suerte, que debe empezar. Consulte también el proyecto de ejemplo de CM Caliburn.Micro.HelloEventAggregator ya que hace exactamente lo que está buscando (aunque se describe como una demo del Agregador de eventos, que también es muy útil, pero nuevamente, otra discusión)

(Original Answer for reverence, más adelante)

Es necesario hacer esto:

<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> 

Aviso cal:Bind.Model="Your.Namespace.Here.YourViewModel" la línea que especifica la vista exacta Modelo para unir esta vista para.

No olvide exportar su tipo de clase, o c.m 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 C.M, 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 en c.m prefiero este método, en realidad, incluso si no tengo que anidar View UserControls y tal. Prefiero declarar explícitamente que la máquina virtual a la que está destinada una Vista (y aún dejar que C.M maneje todo el trabajo pesado en IoC) antes que dejar que c.m "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 adelante.

+0

Gracias por declarar cómo debería ser. Aunque no puedo hacer que funcione. Por favor revise mi publicación original y verifique la parte actualizada. Me encantaría arreglar esto :) – diamondfish

+1

Estás en el camino correcto, solo nos falta algo tonto. Se agregó información sobre Exportar. – EtherDragon

+0

Gah, simplemente no funcionará: S Intenté agregar una clase MefBootstrapper e implementé la interfaz de IShell, pero aún nada. Si tiene tiempo para consultar mi proyecto, siéntase libre de hacerlo :) http://johanbjarnle.se/temp/CaliburnTest.rar – diamondfish

16

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 del 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(...); } 
} 
+0

Tengo esto para trabajar. Pero no parece utilizar C.M tanto? Aunque es una buena manera de hacerlo, ¡muchas gracias! – diamondfish

+0

También descubrí que no puedo ver la interfaz de MyControl en MainView mientras edito en VS cuando uso ContentControl. ¿Hay una manera de hacer eso? – diamondfish

+2

Está utilizando CM, que es lo que corresponde al nombre de ContentControl con el nombre de su propiedad de modelo de vista, ubicando la vista, inyectando la vista en ContentControl y vinculando los controles de esa vista a las propiedades del modelo de vista. Este sería el enfoque recomendado para ver la composición con Caliburn.Micro. – devdigital

1

en el archivo App.xaml.cs, en el método GetInstance añadir 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 
} 
+0

Se corrigió mi error de nulo valor en el uso de Ninject en mi bootstrapper. Muchas gracias :) – Dave

Cuestiones relacionadas