2012-11-21 12 views
7

いくつかのイベントでContentTemplateを変更したいContentControlがあります。 ContentTemplateのコントロールがロードされたときに、いくつかの値(TextBoxにテキスト)を追加します。 しかし、新しいContentTemplateが、プロパティContentTemplateを変更した後ではなく、新しいテンプレートのすべてのコントロールをロードするという点では、直接適用されないことがわかりました。新しいContentTemplateが完全に適用されたことを示すイベントはありますか?

私はその行の後に追加このコードによってテスト
myContentControl.ContentTemplate = newContentTemplate; 
// at this line controls of new template are not loaded! 

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

私はエラーを持っている:

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

は、いくつかのイベントがありますその新しいContentTemplatを示すeは完全に適用されますか? @eran

EDIT 1

私は

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

onApplyTemplate

試みたが、エラーが発生しました:

Object reference not set to an instance of an object.

EDIT 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(); 

誰かが(プロフェッショナル)の方法より "クリーン" と同じ結果を達成するために私を助けることができます:)

EDIT 3

マイシナリオは、私はContentControlのための表示としてメニューとグリッド(右側)のTreeView(左側)を持っています。 TreeViewにはいくつかのノードがあります。各ノードには独自のDataTemplateがあります。 TreeViewノードがクリックされるたびに、DataTemplateがContentControlに設定され、データベースから値(例:Path_Cover.Text)が設定されます。 レイアウトは、Windowsエクスプローラとほぼ同じです。

まあ、これは必要なすべてのコードです:

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(); 
} 

の後ろ

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> 

コード私はちょうどタイマー内のコード "移動" に必要です.tickイベントハンドラを、DataTemplate/ContentTemplateが完全に適用された後に起動する新しいイベントに追加します。

+0

OnApplayTemplateの後にOnLoadまたはOnLoadDataが呼び出されます。 check msdn –

+0

@eran Onloadイベントは1回だけ発生します。 ContentTemplateを変更するたびに発生するイベントが必要です。 – 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> 

の背後にあるコード:あなたはViewModelに(のpropperty)にコンテンツを結合して、内のコードを置くことができる

myContentControl.Content = "Test"; 

かそこ。

ContentTemplate内のコントロールにアクセスするには、ContentTemplateが適用されているコントロールから名前を付けてFindNameを実行するだけです。 そのVisualChildのものでcontentpresenterを検索する必要はありません。

私は、あなたがコントロールテンプレートとデータテンプレート(contenttemplate、itemtemplate)を混ぜていると感じています。

  1. OnApplyTemplateは、ControlTemplateが適用される瞬間を表しているため、ContentTemplateやその他のデータテンプレートではありません。
  2. ContentPresentersはContentTemplatesではなくControlTemplatesの典型的な要素です。
+0

申し訳ありませんが、私はwpfにはとても新しいです。私が最初に覚える必要があることがたくさんあります(mvvmのコンセプトを含む)。上のコードを "フォロー"して、私の流れに基づいたまっすぐな答え(コード)を教えてください:P私はtimer.tickイベントハンドラ内のコードをいくつかの新しいイベントに "移動"するだけでいいです。 – Reyn

+0

...はい、時々私はDataTemplate、ContentTemplate、ItemTemplate、およびContentControlについて混乱します。 :P – Reyn

+0

私はあなたが何かシンプルなものを実現したいと感じていますが、複雑な解決策で考えると思います。コンテンツプレゼンターのビジュアルツリーを通過する必要はありません。タイマーは必要ありません。単純にしておけば、データテンプレートで定義した内容の2つのcontentcontrolを使用し、選択したものの可視性を切り替えます。そこから始めて、より良いソリューションを作りましょう。 – 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

WPFフレームワークにこのようなイベントはないと思います。ただし、新しく割り当てられたコンテンツテンプレートが適用された後にコードが実行されることを保証できます。

ContentControlに関連付けられているDispatcherを利用することで、これを達成する方法(および「汚れた」ソリューションの「適切な」バージョン)を得ることができます。このコードは、あなたが達成しようとしているだけで何をします。このコードが実行されると、優先度がDispatcherPriority.Loadedに設定されているので、それはあなたが入れたコードと同じ優先度で実行されること

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"; 
})); 

お知らせFrameworkElement.Loadedイベントハンドラで