locked
Get the Elements of a XAML of a Template Control RRS feed

  • Question

  • I have this XAML:

    <ItemsControl ItemsSource="{Binding Infos}">
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <StackPanel Orientation="Horizontal" Margin="0, 0, 0, 5">
                    <uc:GradientLabel Value="{Binding Label}" />
                    <TextBlock Text="{Binding Value}" Margin="5, 2, 0, 0" />
                    <TextBox Width="150" Height="22" Padding="2" MaxLength="20" Text="{Binding Value}" Visibility="Collapsed" />
                    <Button Style="{StaticResource ImageButton}" Click="EditField" Margin="5, 0, 0, 0">
                        <Image Width="18" Source="{StaticResource EditIcon}" Style="{StaticResource MouseOverStyle}" />
                    </Button>
                </StackPanel>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <StackPanel Orientation="Vertical" Margin="5" />
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
    </ItemsControl>

    There's a TextBlock displaying values of some user-managed info, and a TextBox appears when they click the Button so they can edit that info.

    After the user finishes editing and he clicks save, I validate all the fields, and I want to reset the fields to their original state (TextBlock visible, TextBox hidden) but display the TextBox on fields that failed validation.

    I need to get the XAML elements inside this ItemsControl programatically, how can I do this?

    One option would be to move everything to my ModelView but since there's stuff that are strictly related to visual only, I want to leave it in the XAML or XAML.cs only.

    Friday, May 8, 2015 9:24 PM

Answers

  • >>I need to get the XAML elements inside this ItemsControl programatically, how can I do this?

    Once the visual elements that you define in the template has been created and added to the visual tree you can find them using a visual tree helper method. You should give the elements that you want to be able to find an x:Name in the template to be able to identify them in the code:

    <ItemsControl ItemsSource="{Binding Infos}" Loaded="infos_Loaded">
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <StackPanel Orientation="Horizontal" Margin="0, 0, 0, 5">
                    <uc:GradientLabel Value="{Binding Label}" />
                    <TextBlock Text="{Binding Value}" Margin="5, 2, 0, 0" />
                    <TextBox Width="150" Height="22" Padding="2" MaxLength="20" Text="{Binding Value}" Visibility="Collapsed" />
                    <Button x:Name="button" Style="{StaticResource ImageButton}" Click="EditField" Margin="5, 0, 0, 0">
                        <Image Width="18" Source="{StaticResource EditIcon}" Style="{StaticResource MouseOverStyle}" />
                    </Button>
                </StackPanel>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <StackPanel Orientation="Vertical" Margin="5" />
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
    </ItemsControl>
    private void infos_Loaded(object sender, RoutedEventArgs e)
            {
                ItemsControl ic = sender as ItemsControl;
                int index = 0; //first item in the ItemsControl (the Infos collection)
                ContentPresenter item = ic.ItemContainerGenerator.ContainerFromIndex(index) as ContentPresenter;
                //find the button:
                Button button = FindVisualChildren<Button>(item).FirstOrDefault(b => b.Name == "button");
                if (button != null)
                {
                    //do something with the Button...
                }
            }
    
            private static IEnumerable<T> FindVisualChildren<T>(DependencyObject depObj) where T : DependencyObject
            {
                if (depObj != null)
                {
                    for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
                    {
                        DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
                        if (child != null && child is T)
                        {
                            yield return (T)child;
                        }
    
                        foreach (T childOfChild in FindVisualChildren<T>(child))
                        {
                            yield return childOfChild;
                        }
                    }
                }
            }
    

    Note that any UI elements with the Visibility property set to Collapsed are never added to the visual tree so there is no way to get a reference to these (because they don't exist). The DataTemplate is just a template based on which the actual elements are created at runtime.


    Please remember to close your threads by marking helpful posts as answer and then start a new thread if you have a new question. Please don't ask several questions in the same thread.

    • Proposed as answer by Xavier Xie-MSFT Monday, May 11, 2015 3:13 AM
    • Marked as answer by Barry Wang Tuesday, May 19, 2015 2:22 AM
    Saturday, May 9, 2015 8:15 AM

All replies

  • MVVM is a passive view pattern.

    I would prefer to have any logic in the ViewModel and the view just binding to the result of that logic - that way setting Visibility.Collapsed or Visible.

    I';d much rather see binding than:

    Visibility="Collapsed" />

    .

    But it's your project.

    I'm not 100% sure I understand the requirement what with mention of an original state. 

    You can use a trigger or converter based on whether a field has errors or not.

    This will return true or false:

    "{Binding ElementName=yourElementName, Path=(Validation.HasError)} 

    So for example you can do:

            <StackPanel Orientation="Horizontal" Margin="0, 0, 0, 5">
                <TextBlock Name="blok" Text="{Binding Value}" Margin="5, 2, 0, 0" >
                    <TextBlock.Style>
                        <Style TargetType="TextBlock">
                            <Style.Triggers>
                                <DataTrigger Binding="{Binding Path=(Validation.HasError), ElementName=box}" Value="true">
                                    <Setter Property="Visibility" Value="Visible"/>
                                </DataTrigger>
                                <DataTrigger Binding="{Binding Path=(Validation.HasError), ElementName=box}" Value="false">
                                    <Setter Property="Visibility" Value="Collapsed"/>
                                </DataTrigger>
                            </Style.Triggers>
                        </Style>
                    </TextBlock.Style>
                </TextBlock>
                <TextBox Name="box" Width="150" Height="22" Padding="2" MaxLength="20" Text="{Binding Value}"   />
            </StackPanel>

    I've just done one of them there, not sure I got that the right way round... but it ought to give you the idea.


    Hope that helps.

    Technet articles: Uneventful MVVM; All my Technet Articles

    Saturday, May 9, 2015 7:55 AM
  • >>I need to get the XAML elements inside this ItemsControl programatically, how can I do this?

    Once the visual elements that you define in the template has been created and added to the visual tree you can find them using a visual tree helper method. You should give the elements that you want to be able to find an x:Name in the template to be able to identify them in the code:

    <ItemsControl ItemsSource="{Binding Infos}" Loaded="infos_Loaded">
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <StackPanel Orientation="Horizontal" Margin="0, 0, 0, 5">
                    <uc:GradientLabel Value="{Binding Label}" />
                    <TextBlock Text="{Binding Value}" Margin="5, 2, 0, 0" />
                    <TextBox Width="150" Height="22" Padding="2" MaxLength="20" Text="{Binding Value}" Visibility="Collapsed" />
                    <Button x:Name="button" Style="{StaticResource ImageButton}" Click="EditField" Margin="5, 0, 0, 0">
                        <Image Width="18" Source="{StaticResource EditIcon}" Style="{StaticResource MouseOverStyle}" />
                    </Button>
                </StackPanel>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <StackPanel Orientation="Vertical" Margin="5" />
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
    </ItemsControl>
    private void infos_Loaded(object sender, RoutedEventArgs e)
            {
                ItemsControl ic = sender as ItemsControl;
                int index = 0; //first item in the ItemsControl (the Infos collection)
                ContentPresenter item = ic.ItemContainerGenerator.ContainerFromIndex(index) as ContentPresenter;
                //find the button:
                Button button = FindVisualChildren<Button>(item).FirstOrDefault(b => b.Name == "button");
                if (button != null)
                {
                    //do something with the Button...
                }
            }
    
            private static IEnumerable<T> FindVisualChildren<T>(DependencyObject depObj) where T : DependencyObject
            {
                if (depObj != null)
                {
                    for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
                    {
                        DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
                        if (child != null && child is T)
                        {
                            yield return (T)child;
                        }
    
                        foreach (T childOfChild in FindVisualChildren<T>(child))
                        {
                            yield return childOfChild;
                        }
                    }
                }
            }
    

    Note that any UI elements with the Visibility property set to Collapsed are never added to the visual tree so there is no way to get a reference to these (because they don't exist). The DataTemplate is just a template based on which the actual elements are created at runtime.


    Please remember to close your threads by marking helpful posts as answer and then start a new thread if you have a new question. Please don't ask several questions in the same thread.

    • Proposed as answer by Xavier Xie-MSFT Monday, May 11, 2015 3:13 AM
    • Marked as answer by Barry Wang Tuesday, May 19, 2015 2:22 AM
    Saturday, May 9, 2015 8:15 AM