Wpf How to Access Control from Datatemplate

WPF How to access control from DataTemplate

First of all, I can't even find the relation between the Resource (ShowAsExpanded) and the usage inside the ContentPresenter. But for the moment, let's assume that the DynamicResource should point to ShowAsExpanded.

You can't and shouldn't access the combobox via code. You should bind the datacontext to the grid that uses the style. If you don't want to do that, you will have to find the content at runtime and search for the child combobox.

How do I access a control inside a XAML DataTemplate?

The problem you are experiencing is that the DataTemplate is repeating and the content is being generated by the FlipView. The Name is not exposed because it would conflict with the previous sibling that was generated (or the next one that will be).

So, to get a named element in the DataTemplate you have to first get the generated item, and then search inside that generated item for the element you want. Remember, the Logical Tree in XAML is how you access things by name. Generated items are not in the Logical Tree. Instead, they are in the Visual Tree (all controls are in the Visual Tree). That means it is in the Visual Tree you must search for the control you want to reference. The VisualTreeHelper lets you do this.

Now, how to do it?

I wrote an article on this because it is such a recurring question: http://blog.jerrynixon.com/2012/09/how-to-access-named-control-inside-xaml.html but the meat of the solution is a recursive method that looks something like this:

public void TestFirstName()
{
foreach (var item in MyFlipView.Items)
{
var _Container = MyFlipView.ItemContainerGenerator
.ContainerFromItem(item);
var _Children = AllChildren(_Container);

var _FirstName = _Children
// only interested in TextBoxes
.OfType<TextBox>()
// only interested in FirstName
.First(x => x.Name.Equals("FirstName"));

// test & set color
_FirstName.Background =
(string.IsNullOrWhiteSpace(_FirstName.Text))
? new SolidColorBrush(Colors.Red)
: new SolidColorBrush(Colors.White);
}
}

public List<Control> AllChildren(DependencyObject parent)
{
var _List = new List<Control>();
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++)
{
var _Child = VisualTreeHelper.GetChild(parent, i);
if (_Child is Control)
_List.Add(_Child as Control);
_List.AddRange(AllChildren(_Child));
}
return _List;
}

The key issue here is that a method like this gets all the children, and then in the resulting list of child controls you can search for the specific control you want. Make sense?

And now to answer your question!

Because you specifically want the currently selected item, you can simply update the code like this:

if (MyFlipView.SelectedItem == null)
return;
var _Container = MyFlipView.ItemContainerGenerator
.ContainerFromItem(MyFlipView.SelectedItem);
// then the same as above...

How do I access a control of a DataTemplate

Try this piece of code to reach to a control inside ContentPresenter:

    public static FrameworkElement GetControlByName(DependencyObject parent, string name)
{
int count = VisualTreeHelper.GetChildrenCount(parent);
for (var i = 0; i < count; ++i)
{
var child = VisualTreeHelper.GetChild(parent, i) as FrameworkElement;
if (child != null)
{
if (child.Name == name)
{
return child;
}
var descendantFromName = GetControlByName(child, name);
if (descendantFromName != null)
{
return descendantFromName;
}
}
}
return null;
}

How to find control from datatemplate of tabitem wpf

Building on @mm8 approach, the following solution will find the ListBox by name instead of by type:

XAML

<TabControl x:Name="tabControl1" SelectionChanged="tabControl1_SelectionChanged">
<TabItem x:Name="tab1" Header="ABC">
<TabItem.ContentTemplate>
...

Code

private void tabControl1_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
Dispatcher.BeginInvoke(new Action(() => TabItem_UpdateHandler()));
}

void TabItem_UpdateHandler()
{
ContentPresenter myContentPresenter = tabControl1.Template.FindName("PART_SelectedContentHost", tabControl1) as ContentPresenter;
if (myContentPresenter.ContentTemplate == tab1.ContentTemplate)
{
myContentPresenter.ApplyTemplate();
var lb1 = myContentPresenter.ContentTemplate.FindName("listBox", myContentPresenter) as ListBox;
}
}

how to access a control within Data Template from code behind?

You should be able to access your control using the FrameworkTemplate.FindName method... first, get the ContentPresenter from one of the ListBoxItems:

ContentPresenter contentPresenter = FindVisualChild<ContentPresenter>(yourListBoxItem);

Then get the DataTemplate from the ContentPresenter:

DataTemplate yourDataTemplate = contentPresenter.ContentTemplate;

Then get the MediaElement from the DataTemplate:

MediaElement yourMediaElement = yourDataTemplate.FindName("vidList", contentPresenter) 
as MediaElement;
if (yourMediaElement != null)
{
// Do something with yourMediaElement here
}

Please see the FrameworkTemplate.FindName Method page on MSDN for more information.

How to get value of XAML Control from DataTemplate

You can get the Button's parent (i.e. StackPanel), and then its parent's parent (i.e. Grid), and then go down and find the TextBox.

But... Don't do this. What if you changed the hierarchy, or the type of the Panel?

Since you already know the type (i.e. Ausstattung) of the DataContext of your data template, you should create another property say TextValue and have it two-way bound with the TextBox. Then, you can either get its value from a CommandParameter if you use Button's Command, or in code-behind -

private void ButtonBase_Click(object sender, RoutedEventArgs e)
{
var button = (ButtonBase)sender;
var dataContext = (Ausstattung)button.DataContext;
var value = dataContext.TextValue;
}

Your class needs to implement INotifyPropertyChanged. After that, create a new property like this -

using System.ComponentModel;
using System.Runtime.CompilerServices;
using App1.Annotations;

namespace App1
{
public class Ausstattung : INotifyPropertyChanged
{
private string _textValue;
public string TextValue
{
get => _textValue;
set
{
_textValue = value;
OnPropertyChanged();
}
}

public event PropertyChangedEventHandler PropertyChanged;

[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}

In your xaml, do this -

<TextBox Text="{x:Bind TextValue, Mode=TwoWay}" Grid.Column="3" Foreground="White" FontSize="14" x:Name="txtAnzahl" PlaceholderText="{x:Bind Anzahl}" TextChanged="TextBox_OnTextChanged" Width="50" HorizontalAlignment="Center" VerticalAlignment="Center"/>


Related Topics



Leave a reply



Submit