2011-10-24 11 views
12

Sto cercando di imparare a utilizzare Caliburn.Micro con WPF. Come posso aggiungere più viste all'interno di una vista?Aggiungere più viste in una vista utilizzando WPF e Caliburn.Micro

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

altro punto di vista, con ViewModel: MyControlViewModel

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

se ho appena aggiungo la vista, non rileverà che ha un ViewModel con il nome appropriato. Come posso legarlo a questo?

Ho provato con diversi bootstrapper e utilizzando qualcosa come cal: Bind.Model = "percorso/nome/unione dei due". Ho provato ad aggiungerlo alla vista principale e all'usercontrol (MyControlView). Sono molto grato per qualsiasi aiuto in merito. Sono praticamente bloccati, e ho molta voglia di utilizzare Caliburn.Micro :)

migliori saluti, diamondfish

Edit: io ancora non riesco a farlo funzionare, il problema sembra essere in bootstrapper o qualcos'altro. Ma per chiarire, ecco il mio codice che sto correndo per un testproject.

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> 

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

codice

MyControlView:

public class MyControlViewModel : PropertyChangedBase 
{ 
    public string MyProp 
    { 
     get { return "Working"; } 
    } 
} 

Schermata dell'errore: http://clip2net.com/s/1gtgt

ho cercato

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

pure. Abbiamo anche provato il cal-di riferimento:

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

screenshot del mio progetto http://clip2net.com/s/1gthM

Dato che la documentazione è in gran parte per Silverlight e, a volte è per Caliburn e non CM, avrei attuato il programma di avvio automatico sbagliata. Per questo test-progetto, è proprio così: (con il XAML-cambio di App.xaml)

public class BootStrapper : Bootstrapper<MainViewModel> 
{ 
} 

Please help me qui! E sembra che sia un po 'di cose di base che mi manca :)

+0

- post modificato per includere il tag MVVM, benvenuto in SO! – EtherDragon

+0

Controlla l'anser: ho aggiunto una sezione sull'esportazione del tipo. Questo è un requisito importante per c.m trovare il ViewModel correlato alla vista. – EtherDragon

risposta

16

EDIT - Nuovo (più completo) risposta qui sotto:

Ok, CM sta facendo un sacco di roba per te, tutto su come ottenere le vostre classi e xaml preparato per CM per essere in grado di trovarlo. Come detto sopra, preferisco essere un codice di scrittura esplicito, piuttosto che fare affidamento su ipotesi implicite del codice da parte del framework.

Quindi, il Bootstrapper, dal progetto C.M predefinito va bene.

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

La sezione `avvio automatico' è molto importante, è indica che ViewModel è la tua prima o principale dello schermo, quando l'applicazione si avvia.

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

Nella [ImportingConstructor] non c'è bisogno di fare qualcosa di diverso da specificare che il MainViewModel richiede la presenza degli altri ViewModel. Nel mio caso particolare, mi piace che il mio MainViewModel sia un contenitore, e solo contenitore, la logica degli eventi è gestita altrove. Ma si potrebbe facilmente avere qui la tua logica di Handle, ma questa è un'altra discussione.

Ora ogni modello di vista figlio deve anche esportarsi in modo che C.M sappia dove trovarli.

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

Non è necessario specificare un Costruttore di importazione se si utilizza solo un costruttore predefinito.

Ora, ognuno dei vostri punti di vista per questi avrà un aspetto simile:

<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 di uno dei bambini-vista

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

Che diamine è effettivamente succedendo qui?

Bene, C.M vede nel bootstrapper che MainViewModel è il punto di partenza a causa della linea che specifica public class AppBootstrapper : Bootstrapper<MainViewModel>. MainViewModel richiede che nel suo costruttore sia richiesto un YourFirstViewModel e YourSecondViewModel (e altri ViewModels), quindi C.M ne costruisce ciascuno. Tutti questi ViewModels finiscono nell'IoC (rendendoti la vita molto più semplice in seguito, ancora una volta, un'intera discussione).

C.M gestisce l'assegnazione del DataContext, a vostro nome, a ciascuno dei punti di vista a causa di specificare quali VM per l'associazione a con la linea come cal:Bind.Model="Your.Namespace.ViewModels.YourFirstViewModel"

Con po 'di fortuna, che dovrebbe iniziare. Fai riferimento anche al progetto di esempio CM Caliburn.Micro.HelloEventAggregator poiché fa esattamente quello che stai cercando (anche se è descritto come una demo di Aggregatore di eventi, che è anche molto utile - ma ancora un'altra discussione)

(Risposta originale per riverenza, sotto)

Hai bisogno di fare questo:

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

Avviso linea cal:Bind.Model="Your.Namespace.Here.YourViewModel" che specifica la vista esatto modello di legare questo View to.

Non dimenticare di esportare il tipo di classe, oppure c.m non riesce a trovarlo.

[Export(typeof(YourViewModel))] 
public class YourViewModel : IShell 
{ 
    ... 
} 

Quindi è possibile annidare i controlli utente come si ritiene opportuno. È un ottimo modo per utilizzare C.M e lo troverai estremamente scalabile. L'unica debolezza è che View e ViewModel devono essere nello stesso progetto (per quanto posso dire). Ma la forza di questo approccio è che puoi separare le classi Visualizza e Visualizza modello in diversi Namespace (all'interno dello stesso progetto), se lo desideri, per mantenere le cose organizzate.

Come commento su CD, preferisco questo metodo, in realtà, anche se non devo nidificare View UserControls e così via. Preferirei dichiarare esplicitamente la strega VM a cui la Vista è vincolata (e che comunque permetta a C.M di gestire tutto il pesante sollevamento di IoC) piuttosto che lasciare che c.m "lo elabori" dal codice implicito.

Anche con un buon framework: il codice esplicito è più gestibile rispetto al codice implicito. La specifica del modello di vista associato ha il vantaggio di dichiarare chiaramente quale dovrebbe essere il contesto dei dati, quindi non sarà necessario indovinare in seguito.

+0

Grazie per aver detto come dovrebbe essere. Anche se non riesco a farlo funzionare. Si prega di guardare oltre il mio post originale e controllare la parte aggiornata. Mi piacerebbe avere questo risolto :) – diamondfish

+1

Sei sulla strada giusta - ci manca solo qualcosa di stupido. Aggiunte informazioni sull'esportazione. – EtherDragon

+0

Gah, semplicemente non funzionerà: S Ho provato ad aggiungere una classe MefBootstrapper e ho implementato l'interfaccia IShell, ma ancora nulla. Se avete il tempo di controllare il mio progetto, sentitevi liberi di farlo :) http://johanbjarnle.se/temp/CaliburnTest.rar – diamondfish

16

Un approccio migliore è utilizzare ContentControl nella vista principale e assegnargli lo stesso nome di una proprietà pubblica su MainViewModel che è di tipo MyControlViewModel. Per esempio.

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

Ho capito che funziona. Ma non sembra utilizzare C.M tanto? Anche se un buon modo di farlo, grazie mille! – diamondfish

+0

Ho anche scoperto che non riesco a vedere l'interfaccia di MyControl in MainView durante la modifica in VS quando utilizzo ContentControl. C'è un modo per farlo? – diamondfish

+2

Si sta utilizzando CM, che corrisponde al nome di ContentControl con il nome della proprietà del modello di visualizzazione, individuando la vista, iniettando la vista in ContentControl e vincolando i controlli della vista alle proprietà del modello di visualizzazione. Questo sarebbe l'approccio consigliato per visualizzare la composizione con Caliburn.Micro. – devdigital

1

nel file di App.xaml.cs, nel metodo GetInstance aggiungere le seguenti righe

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

risolto il mio valore non può essere errore nullo su utilizzando Ninject nel mio bootstrapper. Molte grazie :) – Dave

Problemi correlati