2011-04-15 52 views
2

我有一個用戶控件(不是一個看不見的自定義控件),這取決於一些自定義的狀態屬性,在各種ContentTemplates交換,所有定義爲相關XAML文件中的資源。在代碼隱藏中,我需要在交換的ContentTemplates中找到其中一個元素。在WPF中,如何在通過觸發器切換的模板中查找元素?

現在在一個看不見的控件(即自定義控件)中,只需重寫OnApplyTemplate,然後使用FindName,但當ContentTemplate被觸發器切換時,該重寫不會觸發(...至少不是用於UserControl。我還沒有用自定義控件測試過這個功能。)

現在我已經嘗試將Loaded事件連接到交換模板中的控件,該模板在代碼隱藏中觸發,然後我只是存儲'發件人'在一個類級變量中。但是,當我嘗試通過訂閱Unloaded事件來清除該值時,由於tempalte被換出,因此不會觸發該事件,因此在有機會調用該事件並使控件從屏幕上靜默卸載之前解除該連接,但我仍然在代碼隱藏方面有這樣的參考。

爲了模擬OnApplyTemplate功能,我正在考慮訂閱ContentTemplateChanged通知,並且只是使用VisualTreeHelper來查找我想要的控件,但是我想知道是否有更好的方法,因此這篇文章。

任何想法?

僅供參考,這裏有一個非常簡潔的控制例子。在這個例子中,如果IsEditing爲真,我想找到名爲'FindMe'的文本框。如果IsEditing是假的,這意味着的ContentTemplate不會被換的,我希望得到「空」 ......

<UserControl x:Class="Crestron.Tools.ProgramDesigner.Controls.EditableTextBlock" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:local="clr-namespace:Crestron.Tools.ProgramDesigner.Controls" 
    x:Name="Root"> 

    <UserControl.Resources> 

     <DataTemplate x:Key="EditModeTemplate"> 

      <TextBox x:Name="FindMe" 
       Text="{Binding Text, ElementName=Root}" /> 

     </DataTemplate> 

     <Style TargetType="{x:Type local:EditableTextBlock}"> 
      <Style.Triggers> 

       <Trigger Property="IsEditing" Value="True"> 
        <Setter Property="ContentTemplate" Value="{StaticResource EditModeTemplate}" /> 
       </Trigger> 

      </Style.Triggers> 
     </Style> 

    </UserControl.Resources> 

    <TextBlock x:Name="TextBlock" 
     Text="{Binding Text, ElementName=Root}" /> 

</UserControl> 

Aaaaaaand GO!

M

回答

2

不幸的是,沒有更好的方法。您可以覆蓋OnContentTemplateChanged,而不是連接到事件。

您將需要使用DataTemplate.FindName方法來獲取實際元素。該鏈接有一個如何使用該方法的例子。

如果使用OnContentTemplateChanged,則需要延遲對FindName的調用,因爲它不會立即應用於基礎ContentPresenter。喜歡的東西:

protected override void OnContentTemplateChanged(DataTemplate oldContentTemplate, DataTemplate newContentTemplate) { 
    base.OnContentTemplateChanged(oldContentTemplate, newContentTemplate); 

    this.Dispatcher.BeginInvoke((Action)(() => { 
     var cp = FindVisualChild<ContentPresenter>(this); 
     var textBox = this.ContentTemplate.FindName("EditTextBox", cp) as TextBox; 
     textBox.Text = "Found in OnContentTemplateChanged"; 
    }), DispatcherPriority.DataBind); 
} 

或者,您可以將處理程序附加到LayoutUpdated事件的用戶控件的,但是這可能往往比你想火。這也將處理隱式DataTemplates的情況。

事情是這樣的:

public UserControl1() { 
    InitializeComponent(); 
    this.LayoutUpdated += new EventHandler(UserControl1_LayoutUpdated); 
} 

void UserControl1_LayoutUpdated(object sender, EventArgs e) { 
    var cp = FindVisualChild<ContentPresenter>(this); 
    var textBox = this.ContentTemplate.FindName("EditTextBox", cp) as TextBox; 
    textBox.Text = "Found in UserControl1_LayoutUpdated"; 
} 
+0

啊哈!不知道OnContentTemplateChanged覆蓋。這應該夠了吧。有一件事,在你的鏈接的代碼示例中,對於列表框,它表示'IsSynchronizedWithCurrentItem設置爲True以使其工作'與代碼行'ListBoxItem myListBoxItem =(ListBoxItem)(myListBox.ItemContainerGenerator.ContainerFromItem(myListBox.Items.CurrentItem ));」你知道他們爲什麼不使用'myListBox.SelectedItem'嗎?那麼我認爲你不必使用'CurrentItem',對嗎?或者我錯過了什麼? – MarqueIV 2011-04-15 15:17:27

+0

@MarqueIV - 是的,好像SelectedItem會和Items.CurrentItem一樣,但是你必須測試它。如果你不使用ListBox,那麼你不需要那樣做。看起來你正在使用TextBlock,這是否正確?它應該是一個ContentControl,不是嗎? – CodeNaked 2011-04-15 15:20:40

+0

奇怪的事情,我注意到...當IsEditing是錯誤的(即我還沒有替換模板)ContentTemplate返回'null'。我認爲它至少會選擇UserControl的默認「模板」(即USerControl的主要元素),但似乎並非如此。所以看起來我將不得不檢查ContentTemplate是否爲空,如果不是,請使用它,但如果是這樣,請轉到常規的COntrol.FindName方法。這只是一個猜測,但我必須測試。 (順便說一句,這就是爲什麼我不打算接受這一點,我在技術上還沒有完成。) – MarqueIV 2011-04-15 15:58:33

相關問題