I would to know if there is a mean to get the treview parent of a TreeViewItem programmactically or a turn around examples in WPF.
The goal is to have the DataContext of the TreeView in order to execute command from the ViewModel.
Any help would be appreciated.
Here, the xaml code. The AddDisplayProperty is part of the data context of the treeview.
My other problem is how to find the TreeViewItem from the TextBlock of the DataTemplate.
<TreeView ItemsSource="{Binding DisplayProperties, Mode=OneWay}" MinHeight="20" AllowDrop="True">
<i:Interaction.Behaviors>
<local:FrameworkElementCommandDropBehavior DropCommand="{Binding AddDisplayPropertyCommand}" DropType="{x:Type local:SearchProperty}"/>
</i:Interaction.Behaviors>
<TreeView.Resources>
<DataTemplate DataType="{x:Type local:SearchProperty}">
<TextBlock Margin="5.0" Text="{Binding Path=LongDescription, Mode=OneWay}" AllowDrop="True">
<i:Interaction.Behaviors>
<local:FrameworkElementCommandDropBehavior DropCommand="[Binding AddDisplayPropertyCommand}" DropType="{x:Type local:SearchProperty}" DropParameters="{Binding}"/>
</i:Interaction.Behaviors>
</TextBlock>
</DataTemplate>
</TreeView.Resources>
</TreeView>
I have used this code at some point in time, you just have to call ParentofType(treeviewItem) and it will give you the first Treeview it finds up the chain or null.
public T ParentOfType<T>(DependencyObject element) where T : DependencyObject
{
if (element == null)
return default (T);
else
return Enumerable.FirstOrDefault<T>(Enumerable.OfType<T>((IEnumerable) GetParents(element)));
}
public IEnumerable<DependencyObject> GetParents( DependencyObject element)
{
if (element == null)
throw new ArgumentNullException("element");
while ((element = GetParent(element)) != null)
yield return element;
}
private DependencyObject GetParent(DependencyObject element)
{
DependencyObject parent = VisualTreeHelper.GetParent(element);
if (parent == null)
{
FrameworkElement frameworkElement = element as FrameworkElement;
if (frameworkElement != null)
parent = frameworkElement.Parent;
}
return parent;
}
I would check out this link. I think it explains how to do it, from code behind.
The datacontext in the example can be accessed using parent.DataContext
Related
I am using Windows.UI.Xaml.Controls.TreeView along w/ Windows.UI.Xaml.Controls.TreeViewItem with Selection="Multiple".
<TreeView Name="DessertTree" SelectionMode="Multiple" ItemsSource="{x:Bind DataSource}" ItemInvoked="{x:Bind OnSelectionChanged, Mode=OneTime}">
<TreeView.ItemTemplate>
<DataTemplate x:DataType="local:Item">
<TreeViewItem ItemsSource="{x:Bind Children}" Content="{x:Bind Name}"/>
</DataTemplate>
</TreeView.ItemTemplate>
</TreeView>
I cannot seem to find a way to have an event fired for a state change of the checkbox associated with the TreeViewItem. Does anyone have any information on this? I see that the TreeView in WPF has a trigger for change of the checkbox state, so I would think the same functionality is available.
The only trigger I found that is somewhat similar is ItemInvoked, but that does not register selection events on the checkbox, only if the label is clicked.
You should be able to handle the Tapped event for the TreeViewItem, find the CheckBox in the visual tree and examine its IsChecked property:
private void OnTreeViewItemTapped(object sender, TappedRoutedEventArgs e)
{
TreeViewItem tvi = (TreeViewItem)sender;
CheckBox chk = FindVisualChild<CheckBox>(tvi);
bool? isChecked = chk.IsChecked;
}
private static T FindVisualChild<T>(DependencyObject visual) where T : DependencyObject
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(visual); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(visual, i);
if (child != null)
{
T correctlyTyped = child as T;
if (correctlyTyped != null)
return correctlyTyped;
T descendent = FindVisualChild<T>(child);
if (descendent != null)
return descendent;
}
}
return null;
}
I want the parent of a node that is selected as TreeViewItem
I have a Person class with 2 fields. Name(String) and Children(List of string)
This is my xaml code
<Grid x:Name="gridView" Margin="10">
<TreeView Name="treeView1" TreeViewItem.Selected="TreeViewItem_OnItemSelected" ItemsSource="{Binding}">
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsSelected" Value="{Binding IsSelected, Source=Check, Mode=TwoWay}" />
</Style>
</TreeView.ItemContainerStyle>
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type loc:Person}" ItemsSource="{Binding Children}" >
<TextBlock Text="{Binding Name}" />
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
</Grid>
this is my code behind.
I set the item source to a list of Person objects.
void set()
{
if (treeView1.Items.IndexOf(treeView1.SelectedItem) != -1)
{
//is a parent
//returns -1 for children
Person selected = (Person)treeView1.SelectedItem;
int index = search(selected);
TreeViewItem parent = treeView1.Tag as TreeViewItem;
setSelected(parent,index);
}
else
{
//is a child
TreeViewItem child = treeView1.Tag as TreeViewItem; //returns the selected node
TreeViewItem parent = child.Parent as TreeViewItem; //returns null
}
}
private void TreeViewItem_OnItemSelected(object sender, RoutedEventArgs e)
{
treeView1.Tag = e.OriginalSource;
int ind = 0;
foreach (var _item in treeView1.Items)
{
if (_item == treeView1.SelectedItem)
{
selectedIndex = ind;
break;
}
ind++;
}
}
In the else part, The child.Parent always returns null. I tried other methods but none of them return a TreeViewItem instead they return DependencyObject or ItemsControl.
I also tried ContainerFromItem method but it only works for direct children(parent) and not the children of the parent.
Please help
You could use the VisualTreeHelper.GetParent() method:
https://msdn.microsoft.com/de-de/library/system.windows.media.visualtreehelper.getparent(v=vs.110).aspx
Example Code:
private TreeViewItem FindParentTreeViewItem(object child)
{
try
{
var parent = VisualTreeHelper.GetParent(child as DependencyObject);
while ((parent as TreeViewItem) == null)
{
parent = VisualTreeHelper.GetParent(parent);
}
return parent as TreeViewItem;
}
catch (Exception e)
{
//could not find a parent of type TreeViewItem
return null;
}
}
The while loop is needed, as the visual parent of a tree view item isn't its parent tree view item as can be seen in this WPF Tree Visualizer:
WPF Tree Visualizer showing tree view
ItemsSource is typically a collection of DependencyObjects which you can bind to. It doesn't reflect any information about UI. What you are looking for is actually the ItemContainer of each Item which is a part of WPF UI.
In most cases you can access a UIElement from another without any problem. But in my experience as the UI gets complicated by Styles, Templates and hierarchical items, some UIElements will be hard to find.
I suggest the following way:
Implement a Parent property in your ViewModel (Person Class) with type of Person and initialize it in the constructor of Person, Then you can get both the parent Person and the parent TreeViewItem like this:
var parentPerson = (treeView1.SelectedItem as Person).Parent;
var parentNode = treeView1.ItemContainerGenerator.ContainerFromItem(parentPerson);
I was wonder if any one knows how to change the visibility of a listbox within a DataTemplate when a sibling is clicked. The DataTemplate is being used on a listbox. The following is an example of the xaml I'm using:
<DataTemplate x:Key="Template1">
<StackPanel Margin="110,0,0,0">
<TextBlock Text="{Binding Name}" />
<TextBlock Name="ShowHide" Text="Hide" Tap="ShowHide_Tap" />
<ListBox Name="Listbox1" ItemsSource="{Binding SecondList}" Visibility="Visible" ItemTemplate="{StaticResource Template2}"/>
</StackPanel>
</DataTemplate>
The following is my attempt but I can't use the FindName
private void ShowHide_Click(object sender, System.Windows.Input.GestureEventArgs e)
{
var item = sender as TextBlock;
ListBox Listbox = null;
if (item != null)
{
ContentPresenter templateParent = GetFrameworkElementByName<ContentPresenter>(item);
DataTemplate dataTemplate = templateParent.ContentTemplate;
if (dataTemplate != null && templateParent != null)
{
Listbox = templateParent.FindName("Listbox1") as ListBox;
}
if (Listbox != null)
{
MessageBox.Show(String.Format("ERROR!"));
}
else
Listbox.Visibility = Visibility.Collapsed;
}
}
private static T GetFrameworkElementByName<T>(FrameworkElement referenceElement) where T : FrameworkElement
{
FrameworkElement child = null;
for (Int32 i = 0; i < VisualTreeHelper.GetChildrenCount(referenceElement); i++)
{
child = VisualTreeHelper.GetChild(referenceElement, i) as FrameworkElement;
System.Diagnostics.Debug.WriteLine(child);
if (child != null && child.GetType() == typeof(T))
{ break; }
else if (child != null)
{
child = GetFrameworkElementByName<T>(child);
if (child != null && child.GetType() == typeof(T))
{
break;
}
}
}
return child as T;
}
If any one has any insights they would be much appreciated,
Thanks.
It so happens that the Blend SDK provides functionality for this -- you can even use XAML only, no code behind. Just use an EventTrigger (on the "Tap" event) along with a ChangePropertyAction. Here's how it looks:
<TextBlock Name="ShowHide" Text="Hide" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="Tap">
<ei:ChangePropertyAction TargetName="Listbox1"
PropertyName="Visibility" Value="Collapsed" />
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBlock>
<ListBox Name="Listbox1" ItemsSource="{Binding SecondList}" Visibility="Visible" />
Note that this requires you to add references to the following Extensions:
System.Windows.Interactivity
Microsoft.Expression.Interactions
Reference them in XAML with the namespaces:
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
Welcome to StackOverflow!
Generally speaking this is not the way to use WPF, especially if you're using DataTemplates. The purpose of the view in WPF is to display the view model and fire off user events, nothing more. By changing the Data Template at run-time you're effectively storing the state of the view inside the view itself. This runs totally against the grain of how WPF was designed to be used.
To do this properly your view model (i.e. the class with the SecondList property) should have an extra property called something like ListBoxVisibility and you would bind your listbox's Visibility member to that. An even cleaner method is to use a bool and then use a converter in the view to convert it from type bool to type Visibility. Either way the view model should also have a property of type ICommand (e.g. OnButtonPressedCommand) that the button invokes when the user presses it. The handler for OnButtonPressedCommand, which should also be in the view model, then sets ListBoxVisible or whatever to the value you want which then propagates through to the list box. Doing things this way keeps good separation of concerns and means the view model can be created and its visibility-changing behavior unit tested independently without having to create the view itself.
I have a problem in WPF of programatically accessing a textbox in a DataGridTemplateColumn.CellEditingTemplate, when the cell is selected and in editing mode.
Here is the XAML of my DataGrid:
<DataGrid x:Name="OrderLinesGrid"
Style="{StaticResource DataGridStyle}"
SelectionMode="Single"
SelectionUnit="Cell"
ItemsSource="{Binding OrderLines}">
<DataGrid.Columns>
<DataGridTemplateColumn x:Name="NumberColumn"
Header="Varenr."
MinWidth="100">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Number}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<TextBox Text="{Binding Number}" />
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
How can I access that TextBox when the cell is selected? Here is a image showing the visual tree of the DataGrid if that can help you:
I have tried the following in a DataGridCell GotFocus event, but without luck. It simply returns NULL because it is not found.
private void DataGridCellGotFocus(object sender, RoutedEventArgs e)
{
var cell = sender as DataGridCell;
var textBox = FindChild<TextBox>(cell, null);
}
Where the FindChild method is the following:
/// <summary>
/// Finds a Child of a given item in the visual tree.
/// </summary>
/// <param name="parent">A direct parent of the queried item.</param>
/// <typeparam name="T">The type of the queried item.</typeparam>
/// <param name="childName">x:Name or Name of child. </param>
/// <returns>The first parent item that matches the submitted type parameter.
/// If not matching item can be found,
/// a null parent is being returned.</returns>
public static T FindChild<T>(DependencyObject parent, string childName)
where T : DependencyObject
{
// Confirm parent and childName are valid.
if (parent == null) return null;
T foundChild = null;
int childrenCount = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < childrenCount; i++)
{
var child = VisualTreeHelper.GetChild(parent, i);
// If the child is not of the request child type child
T childType = child as T;
if (childType == null)
{
// recursively drill down the tree
foundChild = FindChild<T>(child, childName);
// If the child is found, break so we do not overwrite the found child.
if (foundChild != null) break;
}
else if (!string.IsNullOrEmpty(childName))
{
var frameworkElement = child as FrameworkElement;
// If the child's name is set for search
if (frameworkElement != null && frameworkElement.Name == childName)
{
// if the child's name is of the request name
foundChild = (T)child;
break;
}
}
else
{
// child element found.
foundChild = (T)child;
break;
}
}
return foundChild;
}
I suspect it has something to do with the DataTemplate but I need some suggestions on how to select the TextBox child element?
I think you you should avoid using VisualTreeHelper as much as possible. If i understood, you can encapsulate your login within CellEditingCommand
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<TextBox Text="{Binding Number}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="GotFocus">
<i:InvokeCommandAction Command="{Binding CellEditingCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
Also you can use Behaviors
UPD:
<DataTemplate>
<TextBox Text="{Binding Number}">
<Interactivity:Interaction.Triggers>
<Interactivity:EventTrigger EventName="Loaded">
<TriggerActions:TakeFocusAction />
</Interactivity:EventTrigger>
</Interactivity:Interaction.Triggers>
</TextBox>
</DataTemplate>
and trigger action
public class TakeFocusAction : TriggerAction<UIElement>
{
protected override void Invoke(object parameter)
{
AssociatedObject.Focus();
}
}
ContentPresenter presenter = e.Column.GetCellContent(e.Row);
TextBox textBox = presenter.ContentTemplate.FindName("nameOfYourTextBox", presenter) as TextBox;
I think you should handle PreparingCellForEdit: Sth
void MainDataGrid_PreparingCellForEdit(object sender, DataGridPreparingCellForEditEventArgs e)
{
TextBox tb = e.Column.GetCellContent(e.Row) as TextBox;
}
See: How to know while user editing the WPF DataGrid Cell is empty?
I have a datatemplate which contains a grid and inside the grid I have a combobox.
<DataTemplate x:Key="ShowAsExpanded">
<Grid>
<ComboBox Name ="myCombo" Grid.Column="1" HorizontalAlignment="Stretch" VerticalAlignment="Top" Margin="5"
IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding}"
ItemTemplate="{StaticResource MyItems}">
<ComboBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
</ItemsPanelTemplate>
</ComboBox.ItemsPanel>
</ComboBox>
</Grid>
</DataTemplate>
I then I have a grid that refers to that template through styling.
<Grid>
<ContentPresenter Name="_contentPresenter" Style="{DynamicResource StyleWithCollapse}" Content="{Binding}" />
</Grid>
How can I access through code behing the myCombo to basically set its DataContext?
Three ways which I know of.
1.Use FindName
ComboBox myCombo =
_contentPresenter.ContentTemplate.FindName("myCombo",
_contentPresenter) as ComboBox;
2.Add the Loaded event to the ComboBox and access it from there
<ComboBox Name ="myCombo" Loaded="myCombo_Loaded" ...
private void myCombo_Loaded(object sender, RoutedEventArgs e)
{
ComboBox myCombo = sender as ComboBox;
// Do things..
}
3.Find it in the Visual Tree
private void SomeMethod()
{
ComboBox myCombo = GetVisualChild<ComboBox>(_contentPresenter);
}
private T GetVisualChild<T>(DependencyObject parent) where T : Visual
{
T child = default(T);
int numVisuals = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < numVisuals; i++)
{
Visual v = (Visual)VisualTreeHelper.GetChild(parent, i);
child = v as T;
if (child == null)
{
child = GetVisualChild<T>(v);
}
if (child != null)
{
break;
}
}
return child;
}
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.
you need to use FindName. check out http://msdn.microsoft.com/en-us/library/system.windows.frameworktemplate.findname.aspx