2010-08-22 15 views
24

बदलता है तो यहाँ XAML है कि मेरे पास है:WPF: पुन: लागू DataTemplateSelector जब एक निश्चित मूल्य

public class ListTemplateSelector : DataTemplateSelector { 
public DataTemplate GroupTemplate { get; set; } 
public DataTemplate ItemTemplate { get; set; } 
public override DataTemplate SelectTemplate(object item, DependencyObject container) { 
    GroupList<Person> list = item as GroupList<Person>; 
    if (list != null && !list.IsLeaf) 
     return GroupTemplate; 
    return ItemTemplate; 
} 
} 

GroupTemplate डेटा टेम्पलेट के अंदर ही ListTemplateSelector का संदर्भ:

<ItemsControl ItemsSource="{Binding Path=Groups}" ItemTemplateSelector="{Binding RelativeSource={RelativeSource AncestorType=Window}, Path=ListTemplateSelector}"/> 

यहाँ मेरी ListTemplateSelector वर्ग है , इसलिए मैंने स्थापित किया है जैसे मैंने इसे स्थापित किया है। यह एकमात्र रिकर्सिव हैक मैं एक साथ रख सकता हूं। लेकिन यह मेरी समस्या नहीं है।

मेरी समस्या यह है कि, IsLeaf संपत्ति में परिवर्तन होने पर मैं आइटम टेम्पलेट से GroupTemplate में बदलना चाहता हूं। यह पहली बार खूबसूरती से काम करता है क्योंकि यह पहली बार संपत्ति को पढ़ता है। लेकिन एक बार यह संपत्ति बदल जाती है, तो टेम्पलेट चयनकर्ता को पुनः प्राप्त नहीं किया जाता है। अब, मैं मूल्य से जुड़ने के लिए ट्रिगर्स का उपयोग कर सकता हूं और आइटम टेम्पलेट को उचित रूप से सेट कर सकता हूं, लेकिन मुझे प्रत्येक आइटम के लिए एक अलग टेम्पलेट सेट करने में सक्षम होना चाहिए, क्योंकि वे एक अलग राज्य में हो सकते हैं।

समूह 1::

उदाहरण के लिए, मैं एक समूह के इस तरह सूची है कहना IsLeaf = false, = GroupTemplate

समूह तो टेम्पलेट 2: IsLeaf = सच है, तो टेम्पलेट = ItemTemplate

समूह 3: IsLeaf = false है, तो टेम्पलेट = GroupTemplate

और एक बार समूह 1 के IsLeaf गुण परिवर्तन को सही पर, templat ई को आइटम टेम्पलेट में स्वचालित रूप से बदलने की जरूरत है।

संपादित करें:

यहाँ मेरी अस्थायी समाधान है। ऐसा करने का कोई बेहतर तरीका?

<ItemsControl ItemsSource="{Binding Path=Groups}"> 
<ItemsControl.ItemTemplate> 
    <DataTemplate> 
     <ContentControl Content="{Binding}"> 
      <ContentControl.Style> 
       <Style TargetType="{x:Type ContentControl}"> 
        <Setter Property="ContentTemplate" Value="{DynamicResource ItemTemplate}"/> 
        <Style.Triggers> 
         <DataTrigger Binding="{Binding Path=IsLeaf}" Value="False"> 
          <Setter Property="ContentTemplate" Value="{DynamicResource GroupTemplate}"/> 
         </DataTrigger> 
        </Style.Triggers> 
       </Style> 
      </ContentControl.Style> 
     </ContentControl> 
    </DataTemplate> 
</ItemsControl.ItemTemplate> 
</ItemsControl> 
+2

स्पष्टता के लिए, क्या आपने ट्रिगर के पक्ष में DataTemplateSelector दृष्टिकोण को त्याग दिया था, या आपने ट्रिगर को डेटा टेम्पलेट चयनकर्ता के साथ समाधान में भी काम किया था? – alastairs

+0

@ एलास्टेयर मैं ओपी के लिए बात नहीं कर सकता, लेकिन ट्रिगर्स डेटा टेम्पलेट चयनकर्ता को अनावश्यक बनाते हैं। – piedar

उत्तर

16

अपने संपादित करें के बारे में, एक DataTemplate उत्प्रेरक पर्याप्त बजाय एक शैली का उपयोग कर नहीं होगी? यह है:

<ItemsControl ItemsSource="{Binding Path=Groups}"> 
    <ItemsControl.ItemTemplate> 
     <DataTemplate> 
      <ContentControl x:Name="cc" Content="{Binding}" ContentTemplate="{DynamicResource ItemTemplate}"/> 

      <DataTemplate.Triggers> 
       <DataTrigger Binding="{Binding Path=IsLeaf}" Value="False"> 
        <Setter TargetName="cc" Property="ContentTemplate" Value="{DynamicResource GroupTemplate}"/> 
       </DataTrigger> 
      </DataTemplate.Triggers> 

     </DataTemplate> 
    </ItemsControl.ItemTemplate> 
</ItemsControl> 
+0

हाँ यह बेहतर होगा। मैं DataTemplate ट्रिगर्स के बारे में भूल गया। मैं इसे अपने समाधान के रूप में उपयोग करूंगा, इसलिए धन्यवाद! – Nick

+9

डेटा टेम्पलेट चयनकर्ताओं को सुधारने की आवश्यकता है ताकि वे इस तरह के परिदृश्य की अनुमति दें क्योंकि यह समाधान, हालांकि आवश्यक है, एक बहुत ही सुस्त वाक्यविन्यास है। उम्मीद है कि डब्ल्यूपीएफ टीम इस – Xcalibur

21

मुझे यह कामकाज मिला जो मेरे लिए आसान लगता है। टेम्पलेट चयनकर्ता के भीतर से उस संपत्ति को सुनें जो आपकी देखभाल करता है और फिर रीफ्रेश को मजबूर करने के लिए टेम्पलेट चयनकर्ता को फिर से लागू करता है।

public class DataSourceTemplateSelector : DataTemplateSelector 
{ 

    public DataTemplate IA { get; set; } 
    public DataTemplate Dispatcher { get; set; } 
    public DataTemplate Sql { get; set; } 

    public override DataTemplate SelectTemplate(object item, System.Windows.DependencyObject container) 
    { 
     var ds = item as DataLocationViewModel; 
     if (ds == null) 
     { 
      return base.SelectTemplate(item, container); 
     } 
     PropertyChangedEventHandler lambda = null; 
     lambda = (o, args) => 
      { 
       if (args.PropertyName == "SelectedDataSourceType") 
       { 
        ds.PropertyChanged -= lambda; 
        var cp = (ContentPresenter)container; 
        cp.ContentTemplateSelector = null; 
        cp.ContentTemplateSelector = this;       
       } 
      }; 
     ds.PropertyChanged += lambda; 

     switch (ds.SelectedDataSourceType.Value) 
     { 
      case DataSourceType.Dispatcher: 
       return Dispatcher; 
      case DataSourceType.IA: 
       return IA; 
      case DataSourceType.Sql: 
       return Sql; 
      default: 
       throw new NotImplementedException(ds.SelectedDataSourceType.Value.ToString()); 
     } 
    } 


} 
+0

को संबोधित करेगी यह पूरी तरह से काम करता है! डब्ल्यूपीएफ में इस लापता फीचर के लिए सभी कामकाजों में से सर्वश्रेष्ठ! – Vaccano

+1

इस कोड के साथ सावधानी बरतें - इसे अपने स्वयं के टेम्पलेट स्विचिंग स्थिति के समाधान के रूप में कार्यान्वित करने के बाद जांच और प्रदर्शन में गिरावट को ध्यान में रखते हुए डेटा टेम्पलेट के आकार के कारण भारी मेमोरी रिसाव उजागर हुआ - डेटाट्रिगर्स विधि का उपयोग करने के लिए बेहतर विचार बिल्कुल रिसाव नहीं लग रहा है। – toadflakz

+0

लंबे समय तक, लेकिन मुझे इस वर्कअराउंड को यूनिवर्सल ऐप में कार्यान्वित करना पड़ा क्योंकि WinRT में स्टाइल नहीं है। ट्रिगर ... –

2

अपने मूल समाधान और "टेम्पलेट चयनकर्ता फिर से लागू नहीं होता है" समस्या के लिए वापस लौटने वाले: जहां संक्षिप्तता खातिर अपने ItemsControl संदर्भित है आपको लगता है कि

CollectionViewSource.GetDefaultView(YourItemsControl.ItemsSource).Refresh(); 

की तरह आपके विचार ताज़ा कर सकते हैं जैसा कि इसके नाम ("YourItemsControl") द्वारा अपने XAML करने के लिए कहा:

<ItemsControl x:Name="YourItemsControl" ItemsSource="{Binding Path=Groups}" 
ItemTemplateSelector="{Binding RelativeSource={RelativeSource AncestorType=Window}, Path=ListTemplateSelector}"/> 

समस्या सिर्फ यह कैसे ताज़ा शिक्षा के लिए अपनी परियोजना में सही जगह का चयन हो सकता है। यह एक कोड कोड के पीछे जा सकता है, या, यदि आपका इस्लाफ एक डीपी है, तो सही जगह निर्भरता-संपत्ति-बदली कॉलबैक होगी।

0

मैं इसे बाध्यकारी प्रॉक्सी के साथ करता हूं।

यह एक सामान्य बाध्यकारी प्रॉक्सी तरह काम करता है (लेकिन 2 प्रॉप्स साथ - DataOut को DataIn से प्रतियां डेटा), लेकिन DataOut सेट DataIn मूल्य को बातिल और वापस करने के लिए जब भी उत्प्रेरक मूल्य परिवर्तन:

public class BindingProxyForTemplateSelector : Freezable 
{ 
    #region Overrides of Freezable 

    protected override Freezable CreateInstanceCore() 
    { 
     return new BindingProxyForTemplateSelector(); 
    } 

    #endregion 

    public object DataIn 
    { 
     get { return (object)GetValue(DataInProperty); } 
     set { SetValue(DataInProperty, value); } 
    } 

    public object DataOut 
    { 
     get { return (object) GetValue(DataOutProperty); } 
     set { SetValue(DataOutProperty, value); } 
    } 

    public object Trigger 
    { 
     get { return (object) GetValue(TriggerProperty); } 
     set { SetValue(TriggerProperty, value); } 
    } 


    public static readonly DependencyProperty TriggerProperty = DependencyProperty.Register(nameof(Trigger), typeof(object), typeof(BindingProxyForTemplateSelector), new PropertyMetadata(default(object), OnTriggerValueChanged)); 

    public static readonly DependencyProperty DataInProperty = DependencyProperty.Register(nameof(DataIn), typeof(object), typeof(BindingProxyForTemplateSelector), new UIPropertyMetadata(null, OnDataChanged)); 

    public static readonly DependencyProperty DataOutProperty = DependencyProperty.Register(nameof(DataOut), typeof(object), typeof(BindingProxyForTemplateSelector), new PropertyMetadata(default(object))); 



    private static void OnTriggerValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
    { 
     // this does the whole trick 

     var sender = d as BindingProxyForTemplateSelector; 
     if (sender == null) 
      return; 

     sender.DataOut = null; // set to null and then back triggers the TemplateSelector to search for a new template 
     sender.DataOut = sender.DataIn; 
    } 



    private static void OnDataChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
    { 
     var sender = d as BindingProxyForTemplateSelector; 
     if (sender == null) 
      return; 

     sender.DataOut = e.NewValue; 
    } 

} 

इस तरह यह प्रयोग करें:

<Grid> 
    <Grid.Resources> 
     <local:BindingProxyForTemplateSelector DataIn="{Binding}" Trigger="{Binding Item.SomeBool}" x:Key="BindingProxy"/> 
    </Grid.Resources> 
    <ContentControl Content="{Binding Source={StaticResource BindingProxy}, Path=DataOut.Item}" ContentTemplateSelector="{StaticResource TemplateSelector}"/> 
</Grid> 

तो आप अपने DataContext करने के लिए सीधे बाँध नहीं है, लेकिन BindingProxy के DataOut है, जो मूल DataContext दर्पण है, लेकिन एक छोटा सा फर्क के साथ करने के लिए: जब ट्रिगर परिवर्तन (इस उदाहरण एक में 'आइटम' के अंदर बूल वैल्यू), टेम्पलेट सिलेक्टर retriggered हो जाता है।

इसके लिए आपको अपना टेम्पलेट चयनकर्ता नहीं बदलना है।

अधिक ट्रिगर्स जोड़ना भी संभव है, बस एक ट्रिगर 2 जोड़ें।