0

I am trying to access textblocks and textboxes in a listview, but cannot get them in C# code because they are inside an ItemTemplate and DataTemplate. Here is a sample of the XAML code:

<ListView x:Name="VehicleList" HorizontalAlignment="Center" Height="120" Margin="0" VerticalAlignment="Center" Width="1119" Background="{ThemeResource CheckBoxDisabledForegroundThemeBrush}" SelectionChanged="VehicleList_SelectionChanged">
        <ListView.ItemTemplate>
            <DataTemplate>
                <Grid x:Name="VehicleGrid" Height="52" Width="1117" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="20,0,0,0">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="318*"/>
                        <ColumnDefinition Width="425*"/>
                        <ColumnDefinition Width="366*"/>
                    </Grid.ColumnDefinitions>
                    <TextBlock x:Name="Year" Grid.Column="0" TextWrapping="Wrap" Text="{Binding Year}" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0,20,0,0" Height="52" Grid.ColumnSpan="1" TextAlignment="Center" Width="321" FontSize="26.667"/>
                    <TextBlock x:Name="Make" Grid.Column="1" TextWrapping="Wrap" Text="{Binding Make}" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0,20,0,0" Height="52" Grid.ColumnSpan="1" TextAlignment="Center" Width="425" FontSize="26.667" />
                    <TextBlock x:Name="Model" Grid.Column="2" TextWrapping="Wrap" Text="{Binding Model}" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0,20,0,0" Height="52" Grid.ColumnSpan="1" TextAlignment="Center" Width="366" FontSize="26.667"/>
                    <TextBox x:Name="AddYear" Grid.Column="0" TextWrapping="Wrap" Text="TextBlock" HorizontalAlignment="Center" VerticalAlignment="Center" Height="52" Grid.ColumnSpan="1" TextAlignment="Center" Width="321" FontSize="26.667" Visibility="Collapsed"/>
                    <TextBox x:Name="AddMake" Grid.Column="1" TextWrapping="Wrap" Text="TextBlock" HorizontalAlignment="Center" VerticalAlignment="Center" Height="52" Grid.ColumnSpan="1" TextAlignment="Center" Width="425" FontSize="26.667" Visibility="Collapsed"/>
                    <TextBox x:Name="AddModel" Grid.Column="2" TextWrapping="Wrap" Text="TextBlock" HorizontalAlignment="Center" VerticalAlignment="Center" Height="52" Grid.ColumnSpan="1" TextAlignment="Center" Width="366" FontSize="26.667" Visibility="Collapsed"/>
                </Grid>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>

Is there anyway to get the items inside the DataTemplate?

Lotzi11
  • 477
  • 1
  • 13
  • 26

6 Answers6

2

Your problem is generic to all XAML flavors, same in WPF and Silverlight. The problem is your DataTemplate. Keep in mind that XAML will inject the contents of your DataTemplate once for each item in your list. That means your names can only exist within the scope of an instance of your DataTemplate.

If you have do code behind for your template, you might do better by creating a UserControl. See below for an example:

<!-- most boiler plate code skipped -->
<UserControl x:Class="MyProject.VehicleListItem">
    <Grid x:Name="VehicleGrid" Height="52" Width="1117" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="20,0,0,0">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="318*"/>
            <ColumnDefinition Width="425*"/>
            <ColumnDefinition Width="366*"/>
        </Grid.ColumnDefinitions>
        <TextBlock x:Name="Year" Grid.Column="0" TextWrapping="Wrap" Text="{Binding Year}" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0,20,0,0" Height="52" Grid.ColumnSpan="1" TextAlignment="Center" Width="321" FontSize="26.667"/>
        <TextBlock x:Name="Make" Grid.Column="1" TextWrapping="Wrap" Text="{Binding Make}" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0,20,0,0" Height="52" Grid.ColumnSpan="1" TextAlignment="Center" Width="425" FontSize="26.667" />
        <TextBlock x:Name="Model" Grid.Column="2" TextWrapping="Wrap" Text="{Binding Model}" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0,20,0,0" Height="52" Grid.ColumnSpan="1" TextAlignment="Center" Width="366" FontSize="26.667"/>
        <TextBox x:Name="AddYear" Grid.Column="0" TextWrapping="Wrap" Text="TextBlock" HorizontalAlignment="Center" VerticalAlignment="Center" Height="52" Grid.ColumnSpan="1" TextAlignment="Center" Width="321" FontSize="26.667" Visibility="Collapsed"/>
        <TextBox x:Name="AddMake" Grid.Column="1" TextWrapping="Wrap" Text="TextBlock" HorizontalAlignment="Center" VerticalAlignment="Center" Height="52" Grid.ColumnSpan="1" TextAlignment="Center" Width="425" FontSize="26.667" Visibility="Collapsed"/>
        <TextBox x:Name="AddModel" Grid.Column="2" TextWrapping="Wrap" Text="TextBlock" HorizontalAlignment="Center" VerticalAlignment="Center" Height="52" Grid.ColumnSpan="1" TextAlignment="Center" Width="366" FontSize="26.667" Visibility="Collapsed"/>
    </Grid>
</UserControl>

That let's you do everything you need to do from a UserControl that can be instantiated, has it's own code behind, etc. You can get that working exactly how you want, and then reference it when you need it in your list like this:

<ListView x:Name="VehicleList" HorizontalAlignment="Center" Height="120" Margin="0" VerticalAlignment="Center" Width="1119" Background="{ThemeResource CheckBoxDisabledForegroundThemeBrush}" SelectionChanged="VehicleList_SelectionChanged">
    <ListView.ItemTemplate>
        <DataTemplate>
            <myProject:VehicleListItem/>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

The ListView will assign a vehicle item to the DataContext of your user control and everything will work just like you designed it to.

There is a way to get at the visual tree in code behind but it is very convoluted. Essentially you need to use the ListView.ItemContainerGenerator and call ContainerFromItem(dataItem) (ref) and then walk down the visual tree you get from that. It's not only a serious pain to do, there's no guarantee all of the API will be accessible from WPF to UWP or SilverLight. The cleanest solution is to break up the code into independent pieces.


Another solution, which probably is even more clean that what I proposed is to take advantage of your bound objects. ListView.SelectedItem returns your object that is bound to the DataTemplate. Just get the values from that object. If you have a ViewModel that includes properties for AddYear, AddMake, and AddModel then it makes a lot of the work easier to do since you aren't dealing with XAML at all.

Berin Loritsch
  • 11,400
  • 4
  • 30
  • 57
  • There is not a x:Class in UserControl. Did you mean something else? Also for the SelectedItem option, don't you need to select the item when before you go to that event? – Lotzi11 Feb 17 '16 at 21:23
  • I didn't include the boilerplate which has the xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" that's standard in every XAML document you create. If you do "New User Control..." then VisualStudio will add those namespaces for you automatically – Berin Loritsch Feb 17 '16 at 22:06
0

If it's okay for you to do this on a button-click; you could use the CommandParameter or traverse the VisualTree like these two links suggest:

How to get text from TextBox inside ListViewItem's DataTemplate

How to get the value out of a textbox in XAML?

Community
  • 1
  • 1
Tobias Kullblikk
  • 226
  • 1
  • 12
0

UserControl

It sounds like a UserControl is what you are looking for.

ProjectAdd UserControl

It should be easy since you already have the XAML design. The code behind will look something like this:

public class VehicleView : UserControl
{
    // Year
    public static readonly DependencyProperty YearProperty =
        DependencyProperty.Register("Year", typeof(int), typeof(VehicleView),
            new PropertyMetadata());

    public int Year
    {
        get { return (int)GetValue(YearProperty); }
        set { SetValue(YearProperty, value); }
    }

    // Make
    public static readonly DependencyProperty MakeProperty =
        DependencyProperty.Register("Make", typeof(string), typeof(VehicleView),
            new PropertyMetadata("None"));

    public string Make
    {
        get { return (string)GetValue(MakeProperty); }
        set { SetValue(MakeProperty, value); }
    }

    // Model
    public static readonly DependencyProperty ModelProperty =
        DependencyProperty.Register("Model", typeof(string), typeof(VehicleView),
            new PropertyMetadata("None"));

    public string Model
    {
        get { return (string)GetValue(ModelProperty); }
        set { SetValue(ModelProperty, value); }
    }

    public VehicleView()
    {
        InitializeComponent();
    }
}

Binding words the same way. Just name the UserControl with x:Name. Something like:

<UserControl x:Name="vehicleview"
             x:Class="MyProjectClass"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             mc:Ignorable="d">

    <Grid>
        <!-- Here's where your ListView goes. -->
        <TextBox Text="{Binding ElementName=vehicleview, Path=Model}"/>
    <Grid>

</UserControl>
Tyler Pantuso
  • 835
  • 8
  • 16
0

As i can see you have used bindings.

On VehicleList_SelectionChanged you can do the following

List<YouClass-Model> selectedItems = VehicleList.SelectedItems.Cast<YouClass-Model>().ToList();

or if you use ItemClicked EventHandler

 YouClass-Model itemClicked = (YouClass-Model)e.ClickedItem)

That way you can load the binded data. ex itemClicked.Model

Also make sure that you binded your data correctly

Data binding overview

INotifyPropertyChanged.PropertyChanged

Stamos
  • 3,938
  • 1
  • 22
  • 48
0

To get the UI control from the index or data, use listView.ContainerFromIndex or listView.ContainerFromItem. This is the accepted way in UWP.

James Esh
  • 2,219
  • 1
  • 24
  • 41
0

try this code:

private void myListView_ContainerContentChanging(ListViewBase sender, ContainerContentChangingEventArgs args)
    {
        ListItemData data = args.Item as ListItemData;
        ListViewItem a = args.ItemContainer as ListViewItem;
        var b = a.ContentTemplateRoot as Grid;  
        var txtBlock =b.FindName("txtTitle") as TextBlock;
        txtBlock.Text = data.Title;
        TextRange textRange = new TextRange()
        {
            StartIndex = 1,
            Length = 3
        };
        TextHighlighter highlighter = new TextHighlighter()
        {
            Background = new SolidColorBrush(Colors.Yellow),
            Ranges = { textRange }
        };
        txtBlock.TextHighlighters.Add(highlighter);
    }
yu ma
  • 1
  • 1
    Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Dec 27 '21 at 16:09