2012-11-21 9 views
7

मेरे पास एक ContentControl है जिसे मैं किसी ईवेंट में इसकी सामग्री टेम्पलेट को बदलना चाहता हूं। सामग्री टेम्पलेट में नियंत्रण करते समय मैं कुछ मान (टेक्स्टबॉक्स पर टेक्स्ट) जोड़ना चाहता हूं। लेकिन, मैंने पाया है कि नया ContentTemplate लागू किया गया है (नए टेम्पलेट के सभी नियंत्रण लोड करने के मामले में) संपत्ति ContentTemplate बदलने के बाद सीधे नहीं।क्या कोई घटना दिखा रही है कि नया ContentTemplate पूरी तरह से लागू है?

var cp = GetVisualChild<ContentPresenter>(myContentControl); 
var txt = myContentControl.ContentTemplate.FindName("Path_Cover", cp) as TextBox; 
txt.Text = "test"; 

GetVisualChild

private T GetVisualChild<T>(DependencyObject parent) where T : Visual 
    { 
     T child = default(T); 
     int numVisuals = VisualTreeHelper.GetChildrenCount(parent); 
     for (int i = 0; i < numVisuals; i++) 
     { 
      Visual v = (Visual)VisualTreeHelper.GetChild(parent, i); 
      child = v as T; 
      if (child == null) 
      { 
       child = GetVisualChild<T>(v); 
      } 
      if (child != null) 
      { 
       break; 
      } 
     } 
     return child; 
    } 

मैं एक त्रुटि मिल गया है:

myContentControl.ContentTemplate = newContentTemplate; 
// at this line controls of new template are not loaded! 

मुझे लगता है कि लाइन के बाद इस कोड को जोड़ा द्वारा परीक्षण

This operation is valid only on elements that have this template applied.

वहाँ कुछ घटना है यह नया सामग्री टेम्पलेट दिखा रहा है ई पूरी तरह से लागू है?

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

@eran मैं onApplyTemplate

public override void OnApplyTemplate() 
{ 
    var cp = GetVisualChild<ContentPresenter>(Content_Option); 
    var txt = Content_Option.ContentTemplate.FindName("Path_Cover", cp) as TextBox; 
    txt.Text = "test"; 
} 

कोशिश की, लेकिन मिला त्रुटि:

Object reference not set to an instance of an object.

संपादित 2

इस "गंदे" विधि बस ठीक काम करता है:

myContentControl.ContentTemplate = newContentTemplate; 

System.Windows.Threading.DispatcherTimer timer = new System.Windows.Threading.DispatcherTimer(); 
timer.Interval = TimeSpan.FromMilliseconds(0.000001); 
timer.Tick += new EventHandler(delegate(object s, EventArgs a) 
{ 
    timer.Stop(); 
    var cp = GetVisualChild<ContentPresenter>(Content_Option); 
    var txt = Content_Option.ContentTemplate.FindName("Path_Cover", cp) as TextBox; 
    txt.Text = "teSt"; 
}); 
timer.Start(); 

किसी ने मुझसे मदद कर सकते हैं और अधिक "क्लीन" (profesional) जिस तरह से :)

संपादित के साथ एक ही परिणाम प्राप्त करने के 3

मेरे परिदृश्य है, मेरे पास ContentControl के लिए डिस्प्ले के रूप में मेनू और एक ग्रिड (दाईं तरफ) के रूप में एक TreeView (बाईं तरफ) है। ट्री व्यू में कुछ नोड्स हैं। प्रत्येक नोड का अपना डेटा टेम्पलेट होता है। प्रत्येक बार जब TreeView नोड क्लिक किया जाता है, तो DataTemplate ContentControl पर सेट होता है और एक मान (उदा। पथ_Cover.Text) डेटाबेस से सेट होता है। लेआउट अधिक या कम विंडोज एक्सप्लोरर की तरह।

XAML

<UserControl.Resources> 

     <DataTemplate x:Key="General"> 
     <StackPanel> 
      <DockPanel> 
       <TextBlock Text="Cover"/> 
       <TextBox Name="Path_Cover"/> 
      </DockPanel> 
      <DockPanel> 
       <TextBlock Text="Slide"/> 
       <TextBox Name="Path_Slide"/> 
      </DockPanel> 
     </StackPanel> 
     </DataTemplate> 

     <DataTemplate x:Key="Appearance"> 
     <StackPanel> 
      <DockPanel> 
       <TextBlock Text="Cover"/> 
       <TextBox Name="Path_Cover"/> 
      </DockPanel> 
      <DockPanel> 
       <Button Content="Get Theme"/> 
       <TextBox Name="Txt_Theme"/> 
      </DockPanel> 
     </StackPanel> 
     </DataTemplate> 

    <UserControl.REsources> 

<Grid> 
    <ContentControl Name="myContentControl"/> 
</Grid> 

पीछे

private void TreeMenu_SelectionChanged(object sender, SelectionChangedEventArgs e) 
{ 
    myContentControl.ContentTemplate =(DataTemplate)this.Resources[Tree_Menu.SelectedItem.ToString()]; 

    System.Windows.Threading.DispatcherTimer timer = new System.Windows.Threading.DispatcherTimer(); 
    timer.Interval = TimeSpan.FromMilliseconds(0.000001); 
    timer.Tick += new EventHandler(delegate(object s, EventArgs a) 
    { 
     timer.Stop(); 
     switch (Tree_Menu.SelectedItem.ToString()) 
     { 
     case "General": 
       var cp = GetVisualChild<ContentPresenter>(Content_Option); 
       var txt = Content_Option.ContentTemplate.FindName("Path_Cover", cp) as TextBox; 
       txt.Text = "test"; 

       txt = Content_Option.ContentTemplate.FindName("Path_Slide", cp) as TextBox; 
       txt.Text = "test"; 
       break; 

     case "Appearance": 
       var cp = GetVisualChild<ContentPresenter>(Content_Option); 
       var txt = Content_Option.ContentTemplate.FindName("Txt_Theme", cp) as TextBox; 
       txt.Text = "test"; 
       break; 
     } 
    }); 
    timer.Start(); 
} 

कोड मैं कर रहा हूँ बस "चाल" टाइमर के अंदर कोड की जरूरत है:

खैर, यह सभी आवश्यक कोड है कुछ घटनाओं के लिए टिक इवेंट हैंडलर जो डेटा टेम्पलेट/कंटेंट टेम्पलेट के बाद पूरी तरह से लागू होता है।

+0

ऑनलोड या ऑनलोड लोड को ऑनएपप्ले टेम्पलेट के बाद कॉल किया गया है msdn –

+0

@eran ऑनलोड ईवेंट केवल एक बार आग लगती है। मैं ऐसी घटना चाहता हूं जो हर बार सामग्री टेम्पलेट को बदलता है। – Reyn

उत्तर

0

आम तौर पर मुझे इस तरह की किसी भी घटना को नहीं पता है।लेकिन अपने परिदृश्य के लिए एक विशिष्ट WPF तरीका यह है:

<ContentControl Name=myContentControl> 
<ContentControl.ContentTemplate> 
    <DataTemplate> 
     <StackPanel> 
      ...other controls here 
      <TextBox Text={Binding Mode=TwoWay}/> 
      ... more controls here 
     </StackPanel> 
    </DataTemplate> 
</ContentControl.ContentTemplate> 

पीछे कोड:

myContentControl.Content = "Test"; 

या आप के लिए एक ViewModel (के propperty) सामग्री बाँध और में कोड डाल सकते हैं क्या आप वहां मौजूद हैं।

यदि आप contenttemplate के अंदर नियंत्रण प्राप्त करना चाहते हैं, तो आप इसे केवल एक नाम दें और उस नियंत्रण से FindName निष्पादित करें जिस पर सामग्री टेम्पलेट लागू किया गया है। उस VisualChild सामान के साथ सामग्री प्रस्तुतकर्ता की खोज करने की कोई आवश्यकता नहीं है।

मुझे एहसास है कि आप नियंत्रण टेम्पलेट्स और डेटामैप्लेट्स (contenttemplate, itemtemplate) को मिश्रित कर रहे हैं।

  1. OnApplyTemplate नियंत्रण टेम्पलेट लागू होने के पल को संदर्भित करता है, इसलिए ContentTemplate या किसी अन्य डेटामैप्लेट नहीं।
  2. सामग्री प्रस्तुतकर्ता नियंत्रण टेम्पलेट्स का एक विशिष्ट घटक हैं और सामग्री टेम्पलेट्स नहीं हैं।
+0

मुझे खेद है, लेकिन मैं wpf के लिए बहुत नया हूँ। मुझे इतना सीखना है कि मुझे पहले सीखना होगा (एमवीवीएम अवधारणा सहित)। क्या आप उपरोक्त मेरे कोड का पालन कर सकते हैं और मुझे अपने प्रवाह के आधार पर सीधे जवाब (कोड) दे सकते हैं: पी मुझे बस कुछ नए कार्यक्रम में टाइमर.tick इवेंट हैंडलर के अंदर कोड को "स्थानांतरित करने" की आवश्यकता है। – Reyn

+0

... और हाँ, कभी-कभी मैं डेटा टेम्पलेट, कंटेंट टेम्पलेट, आइटम टेम्पलेट और कंटेंट कंट्रोल के बारे में उलझन में हूं। : पी – Reyn

+0

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

1

मुझे पता है कि यह एक पुराना सवाल है, लेकिन मैं इसका उत्तर ढूंढ रहा हूं और इसका आविष्कार कर रहा हूं, सोचा कि यह साझा करने के लिए यह एक अच्छी जगह होगी।

मैं बस बनाया है अपने ही ContentPresenter वर्ग मानक ContentPresenter से विस्तार:

public class ContentPresenter : System.Windows.Controls.ContentPresenter { 

    #region RE: ContentChanged 
    public static RoutedEvent ContentChangedEvent = EventManager.RegisterRoutedEvent("ContentChanged", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(ContentPresenter)); 
    public event RoutedEventHandler ContentChanged { 
     add { AddHandler(ContentChangedEvent, value); } 
     remove { RemoveHandler(ContentChangedEvent, value); } 
    } 
    public static void AddContentChangedHandler(UIElement el, RoutedEventHandler handler) { 
     el.AddHandler(ContentChangedEvent, handler); 
    } 
    public static void RemoveContentChangedHandler(UIElement el, RoutedEventHandler handler) { 
     el.RemoveHandler(ContentChangedEvent, handler); 
    } 
    #endregion 

    protected override void OnVisualChildrenChanged(System.Windows.DependencyObject visualAdded, System.Windows.DependencyObject visualRemoved) { 
     base.OnVisualChildrenChanged(visualAdded, visualRemoved); 
     RaiseEvent(new RoutedEventArgs(ContentChangedEvent, this)); 
    } 
} 

मुझे आशा है कि यह आप में से जो वहाँ बाहर ContentPresenter के डिजाइन में इस स्पष्ट निरीक्षण करने के लिए एक सरल उपाय की तलाश में मदद मिल सकती है।

+0

धन्यवाद, पूरी तरह से काम करता है! – BendEg

0

मुझे नहीं लगता कि डब्ल्यूपीएफ ढांचे में ऐसी घटना है। हालांकि आप आश्वस्त कर सकते हैं कि नए कोड किए गए सामग्री टेम्पलेट के लागू होने के बाद आपका कोड चलाया जा सकता है।

इसे पूरा करने का तरीका (और आपके "गंदे" समाधान का "उचित" संस्करण) ContentControl से जुड़े Dispatcher का उपयोग करना है। इस कोड को आप सिर्फ क्या प्राप्त करना चाहते हैं क्या करेंगे:

myContentControl.ContentTemplate = newContentTemplate; 
myContentControl.Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new Action(() => 
{ 
    var cp = GetVisualChild<ContentPresenter>(myContentControl); 
    var txt = myContentControl.ContentTemplate.FindName("Path_Cover", cp) as TextBox; 
    txt.Text = "test"; 
})); 

सूचना है कि प्राथमिकता है जिसके साथ इस कोड को निष्पादित किया जाएगा DispatcherPriority.Loaded के लिए सेट है, तो यह कोड डाल के रूप में ही प्राथमिकता के साथ निष्पादित किया जाएगा FrameworkElement.Loaded इवेंट हैंडलर में।

संबंधित मुद्दे