2013-01-07 6 views
7

Sto tentando di formare un xaml per un modulo dati a livello di programmazione utilizzando una stringa. Posso far apparire la casella combinata. ma quando tento di usare il codice specificando "MouseLeftButtonUp" o il gestore di eventi "Loaded" nella stringa; la pagina diventerà bianca (nessun errore evidente) dopo averla inserita. Si prega di consultare il codice pertinente di seguito.Gestori di eventi quando si utilizza una stringa come modello di dati per il modulo dati in Silverlight

 StringBuilder editTemplate = new StringBuilder(""); 
    editTemplate.Append("<DataTemplate "); 
    editTemplate.Append("xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation' "); 
    editTemplate.Append("xmlns:toolkit='http://schemas.microsoft.com/winfx/2006/xaml/presentation/toolkit' "); 
    editTemplate.Append("xmlns:navigation='clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation' "); 
    editTemplate.Append("xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml' >"); 
    editTemplate.Append("<StackPanel>"); 
    editTemplate.Append(@" <toolkit:DataField Label='" + GetFieldWithoutNumber(theInfo, theDataContext) + "'>"); 
    /* Won't Work */ editTemplate.Append(@" <ComboBox MouseLeftButtonUp='ComboBox_MouseLeftButtonUp' />"); 
    /* Will Work */ editTemplate.Append(@" <ComboBox />"); 
    editTemplate.Append(@" </toolkit:DataField>"); 
    editTemplate.Append("</StackPanel></DataTemplate>"); 
    dynamicDataForm.EditTemplate = XamlReader.Load(editTemplate.ToString()) as DataTemplate; 
+1

Questo non mi sorprende, ciò che si dovrebbe fare è collegare il gestore eventi a livello di codice dopo aver creato il datatemplate. – slugster

+0

@slugster Si prega di avvisare ... come si fa? Dagli un nome? – Kulingar

risposta

1

I gestori di eventi collegati in XAML devono essere dichiarati nel code-behind collegato al file XAML. Nel caso di un ResourceDictionary o di qualcosa caricato da XamlReader.Load non può esserci code-behind, quindi i gestori di eventi non possono essere impostati in XAML. Il modo più semplice per aggirare questa limitazione potrebbe essere quella di non costruire il modello da stringhe e proprio dichiararla nella sezione Risorse del file XAML che si può poi fare come:

Resources["MyTemplate"] as DataTemplate

per ottenere il modello e assegnalo in codice come fai qui, o usa semplicemente StaticResource in XAML. Finché rimane nello stesso file XAML collegato a questo codice, i gestori di eventi che hai in esso dovrebbero funzionare correttamente. Anche la parte dinamica delle stringhe deve essere modificata per utilizzare i binding.

Se si desidera attenersi al metodo XamlReader, è necessario risolvere 2 problemi.

  1. Individuare l'istanza ComboBox all'interno del template reso
  2. Attendere che il modello è reso a cercare la ComboBox

Per trovare la casella combinata è necessario prima dare un x: attributo Name in il testo del modello (puoi semplicemente sostituire il codice evento attualmente lì). Quindi devi essere in grado di localizzare un oggetto nell'albero visivo per nome. Questo è abbastanza semplice e puoi trovare uno example here per farlo.

Per chiamare questo codice al momento giusto è necessario eseguire l'override di OnApplyTemplate, che purtroppo non funzionerà se si utilizza qualcosa come un UserControl o si utilizza un altro trucco per impedirne l'esecuzione finché tutti i controlli non vengono visualizzati . Ecco un esempio completo che potrebbe andare in un costruttore e utilizza il metodo find legato dall'alto:

DataTemplate template = Resources["MyTemplate"] as DataTemplate; 
dynamicDataForm.ContentTemplate = template; 

Dispatcher.BeginInvoke(() => 
{ 
    ComboBox button = FindVisualChildByName<ComboBox>(this, "MyControl"); 
    if (button != null) 
     button.MouseLeftButtonUp += (s, _) => MessageBox.Show("Click"); 
}); 

Nel tuo caso sembra che il modello potrebbe essere necessario attendere per passare a uno stato di modifica prima che si rende in tal caso, dovresti tenere a bada il collegamento dell'evento e trovare qualche altro evento sul tuo dataform che si verifica quando tale stato viene modificato.

1

Una soluzione è quella di gestire l'evento BeginningEdit del DataForm e l'uso che per sottoscrivere il vostro gestore di eventi per eventi del ComboBox MouseLeftButtonUp.

Per fare ciò, aggiungere al codice, dietro un campo privato denominato isEventWiredUp. Utilizzeremo questo campo per tenere traccia della nostra iscrizione all'evento e impedire che l'evento venga sottoscritto più volte.

Successivamente, aggiungere un attributo x:Name="..." al numero ComboBox. Usiamo questo nome per ottenere alla casella combinata.

Una volta fatto, aggiungere i seguenti due metodi, che dovrebbe fare ciò che si desidera. Sostituire yourComboBoxName con la x:Name hai dato alla tua casella combinata:

private void dynamicDataForm_BeginningEdit(object sender, CancelEventArgs e) 
    { 
     Dispatcher.BeginInvoke(OnBeginEdit); 
    } 

    private void OnBeginEdit() 
    { 
     if (!isEventWiredUp) 
     { 
      var combobox = dynamicDataForm.FindNameInContent("yourComboBoxName") as ComboBox; 
      if (combobox != null) 
      { 
       combobox.MouseLeftButtonUp += combobox_MouseLeftButtonUp; 
       isEventWiredUp = true; 
      } 
     } 
    } 

Iscriviti al primo di questi due metodi per eventi del DataForm BeginningEdit.

Devo ammettere che non sono riuscito a far scattare l'evento MouseLeftButtonUp sul ComboBox. Non sono sicuro del motivo per cui ciò accada, ma sembra essere un problema generale con il ComboBox rispetto a qualcosa che accade a causa del modo in cui stai costruendo XAML. Tuttavia, sono riuscito a ottenere un gestore di eventi per l'evento SelectionChanged di ComboBox.

Ho anche provato a sostituire la linea Dispatcher.BeginInvoke con una chiamata diretta al metodo OnBeginEdit, ma ho trovato che questo approccio non ha funzionato. Gli eventi non erano completamente cablati correttamente; di nuovo, non sono sicuro del perché.

+0

Risposte in cui gli utenti di 13k rep dicono che non sono sicuri del perché qualcosa stia accadendo mi rende felice perché giustifica la mia sanità mentale. ;) Grazie per il tuo contributo, sto osservando i tuoi suggerimenti e quelli di Luke. Ti farò sapere a breve. :) Ma il tuo sembra essere più facile. – Kulingar

0

Piuttosto che cercare di hookup direttamente l'evento, è possibile utilizzare l'interattività per fasciare i vostri eventi

esempio

... 
editTemplate.Append("xmlns:i='clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity' "); 
... 
editTemplate.Append(@" 
<ComboBox> 
<i:Interaction.Triggers> 
    <i:EventTrigger EventName='MouseLeftButtonUp'> 

     <i:InvokeCommandAction Command='{Binding DataContext.YourCommand,                     
      RelativeSource={RelativeSource AncestorType=XXX}}' 
      CommandParameter='{Binding}'/> 

    </i:EventTrigger> 
</i:Interaction.Triggers> 
</ComboBox>"); 

potrebbe essere necessario utilizzare un legame ancestore per ottenere il contesto su cui è definito il gestore. Io uso un'implementazione personalizzata di InvokeCommandAction; fondamentalmente una copia di System.Windows.Interactivity.InvokeCommandAction ma estesa in modo che passi gli argomenti dell'evento al comando, potresti voler fare lo stesso.

0

XamlReader.Load non è autorizzato ad allegare eventHandlers in esso. quindi usa questa tecnica per allegare dinamicamente l'eventHandler ad esso.

1- Scrivere la stringa Xaml senza eventHandlers -Ma scrivere la proprietà Name di tali controlli.

2- Caricare la stringa con XamlReader.Load(str);

3- Poi caricare il contenuto di DataTemplate da esso. utilizzando Grid template = ((Grid)(dt.LoadContent()));

Nota: qui il Grid è il controllo genitore in DataTemplate.

4- Trovare il controllo per nome a cui si desidera collegare il gestore eventi. Button img = (Button)template.FindName("MyButtonInDataTemplate");

Spero che sia d'aiuto.

Problemi correlati