How to retrieve VisualChild from ControlTemplate

时光怂恿深爱的人放手 提交于 2019-12-11 03:56:40

问题


I have a WPF application in .NET 3.5 SP1 which is using TabControl.
In that we have TabItems, which in turn have their Styles to determine currently displayed items.
Let's say we have a TabItem named Books, now Books will have three stages of display:
1. Loading results,
2. Displaying results,
3. Displaying no results - i.e. nothing found.

<TabControl>
    <TabItem Header="Books"/>
    <TabItem Header="DVD's"/>
    ...
</TavControl>

Now I have 5 TabItems which let's say represent "DVD's", "Blu-Rays", "CD's", "Books" and "Comics".

<TabItem Header="Books">
    <Control>
        <Control.Resources>
            <Style TargetType="Control">
               <Setter Property="Template">
                   <Setter.Value>
                       <ControlTemplate TargetType="Control">
                           <ListView ItemsSource="{Binding Books}"/>
                       </ControlTemplate>
                   </Setter.Value>
               </Setter>
            <Style.Triggers>
                <!-- Assign different Visuals depending on the current state of the app, i.e. Loading, No results, results found
                <DataTrigger .../>
            </Style.Triggers>
            </Style>
        </Control.Resources>
    </Control>
</TabItem>

Underneath the TabItem I have a TextBlock to display number of currently found results:

<TextBlock>
    <TextBlock.Text>
        <MultiBinding StringFormat="Displaying {0} records for your Shop ({1})" Converter="{StaticResource tstMVC}">
            <Binding ElementName="Tc" Path="SelectedValue"/>
            <Binding Path="ShopId" FallbackValue="Liverpool"/>
        </MultiBinding>
    </TextBlock.Text>
</TextBlock>  

Converter is there for me to check what values are passed in the MultiBinding.
ISSUE:
When user selects a tab item I would like to display current number of the items displayed but I can't locate the ListView in the Control, as that's the Current Content of the TabItem.
I have tried TabControl.SelectedItem, SelectedValue and still can't find Current ItemsSource.Count.
Thanks in advance


UPDATE:
I have tried both of the solutions big thanks to @Sheridan and @pushpraj!
Unfortunately I haven't used either of them, instead I used ListView inside of the TabItem and then accessed it with this code:

<TabControl Name="Tc">
    <TabItem><ListView ItemsSource="{Binding Books}"/></TabItem>
    ...
</TabControl>

<TextBlock Text="{Binding ElementName=Tc, Path=SelectedItem.Content.Items.Count}"/>

This way Content of my TextBlock changes every time user selects different Tab.
P.S. Nevertheless I wouldn't have done it without evaluation of both answers.


回答1:


if you have separate collection classes for all you entities you may use the following approach

define data templates for your collection classes

eg

<DataTemplate DataType="{x:Type l:BookCollection}">
    <ListView ItemsSource="{Binding}" />
</DataTemplate>

xaml

<TabControl x:Name="Tc">
    <TabItem Header="Books"
             Content="{Binding Books}" />
    <TabItem Header="DVD's"
             Content="{Binding DVDs}" />
</TabControl>

or if you do not have separate collections then use DataTemplate as follows

<TabControl  x:Name="Tc">
    <TabControl.ItemTemplate>
        <DataTemplate>
            <ListView ItemsSource="{Binding}" />
        </DataTemplate>
    </TabControl.ItemTemplate>
    <TabItem Header="Books"
             Content="{Binding Books}" />
    <TabItem Header="DVD's"
             Content="{Binding DVDs}" />
</TabControl>

then the binding to get the selected tab's items count will be

<TextBlock Text="{Binding SelectedItem.Content.Count, ElementName=Tc}" />



回答2:


In WPF, we generally work with data elements rather than UI elements. By that, I mean that it is customary to data bind data elements from our code behind or view models to the UI elements in the views, UserControls and Windows.

Therefore, if you have a data collection property named Books in your code behind or view model, then you can simply refer to that collection to find out how many items are in it, rather than trying to find it via the UI controls:

<ListView ItemsSource="{Binding Books}" />

You could even expose the count of items as a separate property and data bind to it directly:

public ObservableCollection<Book> Books
{
    get { return books; }
    set 
    { 
        books = value; 
        NotifyPropertyChanged("Books"); 
        NotifyPropertyChanged("BookCount");
    }
}

public int BookCount
{
    get { return Books.Count; }
}

UPDATE >>>

In response to your latest comment, you can find out how to access UI elements from within a ControlTemplate from the How to: Find ControlTemplate-Generated Elements page on MSDN. In short though, you need to access the element that has the ControlTemplate applied (the relevant TabItem in your case) and then you can use the FrameworkTemplate.FindName Method to find the internally declared elements. Take this example from the linked page:

// Finding the grid that is generated by the ControlTemplate of the Button
Grid gridInTemplate = (Grid)myButton1.Template.FindName("grid", myButton1);

// Do something to the ControlTemplate-generated grid
MessageBox.Show("The actual width of the grid in the ControlTemplate: "
    + gridInTemplate.GetValue(Grid.ActualWidthProperty).ToString());


来源:https://stackoverflow.com/questions/25844872/how-to-retrieve-visualchild-from-controltemplate

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!