2010-04-02 9 views
7

Zrobiłem przykładowy demo VS 2010 RC przykładowy projekt, ponieważ w moim projekcie produkcyjnym mam ten sam błąd przy użyciu MVVM.ContentTemplateSelector jest wywoływany tylko jeden raz pokazując zawsze ten sam datatemplate

W moim projekcie próbki demo używam tylko kodu źródłowego bez 3rd zależności partii, dzięki czemu można pobrać projekt demo tutaj i uruchomić go dla siebie: http://www.sendspace.com/file/mwx7wv

Teraz do problemu: Kiedy klikam dziewczęta/chłopcy przycisk powinien przełączać datatemplate, nie?

Co mam nie tak?

OK Oferuję tutaj fragment kodu zbyt:

Code-Behind MainWindow.cs:

namespace ContentTemplateSelectorDemo 
{ 
    /// <summary> 
    /// Interaction logic for MainWindow.xaml 
    /// </summary> 
    public partial class MainWindow : Window 
    { 
     Person person; 

     public MainWindow() 
     { 
      InitializeComponent(); 

      person = new Person(){ Gender = "xxx"}; 
      person.IsBoy = true; 


      ContentGrid.DataContext = person; 
     } 

     private void btnBoys_Click(object sender, RoutedEventArgs e) 
     { 
      person.IsBoy = true; 
      person.IsGirl = false; 
      this.ContentGrid.DataContext = person; 
     } 

     private void btnGirls_Click(object sender, RoutedEventArgs e) 
     { 
      person.IsGirl = true; 
      person.IsBoy = false; 
      this.ContentGrid.DataContext = person; 

     }   
    } 
} 

XAML MainWindow.xaml: Klasa

<Window x:Class="ContentTemplateSelectorDemo.MainWindow" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:local="clr-namespace:ContentTemplateSelectorDemo" 
     Title="MainWindow" Height="350" Width="525"> 

    <Window.Resources> 

     <DataTemplate x:Key="girlsViewTemplate"> 
      <local:UserControl1 /> 
     </DataTemplate> 

     <DataTemplate x:Key="boysViewTemplate" > 
      <local:UserControl2 /> 
     </DataTemplate> 

     <local:PersonDataTemplateSelector x:Key="PersonSelector" /> 

    </Window.Resources> 

    <Grid x:Name="ContentGrid" > 
     <StackPanel> 
      <Button Name="btnGirls" Click="btnGirls_Click">Switch Girls</Button> 
      <Button Name="btnBoys" Click="btnBoys_Click">Switch Boys</Button> 
     <ContentControl Content="{Binding}" ContentTemplateSelector="{StaticResource ResourceKey=PersonSelector}" /> 
     </StackPanel> 
    </Grid> 
</Window> 

DataTemplateSelector :

public class PersonDataTemplateSelector : DataTemplateSelector 
{ 
    public override DataTemplate SelectTemplate(object item,DependencyObject container) 
    { 
     if (item is Person) 
     { 
      Person person = item as Person; 

      Window window = Application.Current.MainWindow; 

      if (System.ComponentModel.DesignerProperties.GetIsInDesignMode(window)) 
       return null; 

      if (person.IsBoy)    
       return window.FindResource("boysViewTemplate") as DataTemplate; 
      if (person.IsGirl)    
       return window.FindResource("girlsViewTemplate") as DataTemplate; 

     } 
     return null; 
    } 
} 

:)

+1

Lepsze fragmenty kodu pocztowego zamiast oferując pobrania , ludzie najprawdopodobniej nie będą pobierać tha t. –

+1

ok edytowałem mój wpis init! – msfanboy

+1

ok po niektórych badań: http://joshsmithonwpf.wordpress.com/2007/03/18/updating-the-ui-when-binding-directly-to-business-objects-that-are-modified/ tak jak Josh powiedział w swoim komentarzu do kodu: "... Jest to konieczne, ponieważ system wiążący WPF zignoruje powiadomienie PropertyChanged, jeśli właściwość zwróci tę samą referencję obiektu, co wcześniej ..." Nie mogę zwróć ten sam obiekt, zamiast tego muszę odtworzyć obiekt Person w module obsługi zdarzeń przycisków, takim jak: ... = new Person() {IsBoy = true, IsGirl = false} etc ... to działa. dziękuję josh! – msfanboy

Odpowiedz

5

Lubię rozwiązanie Neila (znajdujący się na Josh's post poprzez the link you provided):

<DataTemplate DataType="{x:Type local:MyType}"> 
     <ContentPresenter Content="{Binding}" Name="cp" /> 
     <DataTemplate.Triggers> 
      <DataTrigger Binding="{Binding Path=IsRunning}" Value="True"> 
       <Setter TargetName="cp" Property="ContentTemplate" Value="{StaticResource StopTemplate}" /> 
      </DataTrigger> 
      <DataTrigger Binding="{Binding Path=IsRunning}" Value="False"> 
       <Setter TargetName="cp" Property="ContentTemplate" Value="{StaticResource StartTemplate}" /> 
      </DataTrigger> 
     </DataTemplate.Triggers> 
    </DataTemplate> 

Edit: nie mogłem rzeczywiście dostać powyższy kod do pracy, ale to działa w stylu:


 <DataTrigger Binding="{Binding Path=SourceSystem.SourceSystemName}" 
        Value="mysite.com"> 
    <Setter Property="ContentControl.ContentTemplate" 
        Value="{StaticResource mysiteToolbar}" /> 

 <DataTrigger Binding="{Binding Path=SourceSystem.SourceSystemName}" 
        Value="mysite2.com"> 
    <Setter Property="ContentControl.ContentTemplate" 
        Value="{StaticResource mysiteToolbar2}" /> 

</Style.Triggers>    

+0

http://stackoverflow.com/questions/5771362/wpf-how-to-set-the-data-template-trigger- for-content-control – invalidusername

3

Uwaga: Myślę, że ta metoda jest dość niezgrabna, ale może działać w przypadku niektórych scenariuszy. Preferuję metodę użycia spustu (z Neil), którą zamieściłem jako osobną odpowiedź.


Innym możliwym sposobem jest związać Content z ContentTemplateSelector do nieruchomości, który określa szablon, który powinien być wybrany. Na przykład tutaj mam dwa różne paski narzędzi wybrane na podstawie wartości SourceSystem. Ustawiam Content jako własność systemu źródłowego.

<ContentControl ContentTemplateSelector="{StaticResource toolbarTemplateSelector}" 
       DataContext="{Binding}" Content="{Binding SourceSystem}" /> 

Selektor szablonów po prostu sprawdza system źródłowy i zwraca niezbędny szablon.

Jeśli szablon wymaga dostępu do datacontext formantu, wystarczy użyć wiązania elementu, aby go ustawić.

<UserControl.Resources> 
    <DataTemplate x:Key="toolbar1"> 
     <views:OrdersToolbar1View Margin="0,5,0,0" 
       DataContext="{Binding ElementName=control,Path=DataContext}"/> 
    </DataTemplate> 
    <DataTemplate x:Key="toolbar2"> 
     <views:OrdersToolbar2View Margin="0,5,0,0" 
       DataContext="{Binding ElementName=control,Path=DataContext}"/> 
    </DataTemplate> 
</UserControl.Resources> 
-2

Zastosowanie tej metody do Selector zwyczaj utworu:

private void ReloadContent() 
{ 
    MainContentControl.ContentTemplate = MainContentControl.ContentTemplateSelector.SelectTemplate(null, MainContentControl); 
} 

w XAML:

<ContentControl Content="{Binding}" x:Name="MainContentControl"> 
    <ContentControl.ContentTemplateSelector > 
      <templateSelectors:MainViewContentControlTemplateSelector> 
       <templateSelectors:MainViewContentControlTemplateSelector.BoysTemplate> 
        <DataTemplate> 
         <local:UserControl1 /> 
        </DataTemplate> 
        </templateSelectors:MainViewContentControlTemplateSelector.BoysTemplate> 
       <templateSelectors:MainViewContentControlTemplateSelector.GirlsTemplate> 
        <DataTemplate> 
         <local:UserControl2 /> 
        </DataTemplate> 
        </templateSelectors:MainViewContentControlTemplateSelector.GirlsTemplate> 
    </ContentControl> 

I Selector:

public class MainViewContentControlTemplateSelector : DataTemplateSelector 
{ 
    public DataTemplate BoysTemplate{ get; set; } 
    public DataTemplate GirlsTemplate{ get; set; } 


    public override DataTemplate SelectTemplate(object item, DependencyObject container) 
    { 
     var contentControl = container.GetVisualParent<ContentControl>(); 
     if (contentControl == null) 
     { 
      return BoysTemplate; 
     } 

     if (//Condition) 
     { 
      return GirlsTemplate; 

     } 

     return BoysTemplate; 
    } 
+1

Chociaż nieco poprawne, nie pozwala na * wykonywanie powiązań treści do ** pełnego obiektu *** jak w '' while pozwalając selektorowi _template ** obserwować różne ** rzeczy niż content_, jak '.IsBoy' vs' .IsGirl 'jak w pytaniu. – quetzalcoatl