I want to select a node of tree view on right click. I am using MVVM pattern and don't want to achieve this in code behind. Here is my XAML for tree view.
<TreeView Margin="5,0,0,5" ItemsSource="{Binding TreePads}">
<TreeView.ItemContainerStyle >
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsSelected" Value="{Binding DataContext.IsSelected, Mode=TwoWay, RelativeSource={RelativeSource Self}}" />
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}" >
<MenuItem IsEnabled="{Binding RenameMenuEnabled}" Header="Rename" Command="{Binding RenameCommand}" />
</ContextMenu>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
</Trigger>
</Style.Triggers>
</Style>
</TreeView.ItemContainerStyle >
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type vm:TreePad}" ItemsSource="{Binding Members, Mode=TwoWay}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding PadName}"/>
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.Resources>
You could define a DependencyProperty. Below I have shared a sample app which uses a dependency property to achieve this.
TreeViewExtension.cs
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
namespace WpfApplication1
{
public static class TreeViewExtension
{
public static readonly DependencyProperty SelectItemOnRightClickProperty = DependencyProperty.RegisterAttached(
"SelectItemOnRightClick",
typeof(bool),
typeof(TreeViewExtension),
new UIPropertyMetadata(false, OnSelectItemOnRightClickChanged));
public static bool GetSelectItemOnRightClick(DependencyObject d)
{
return (bool)d.GetValue(SelectItemOnRightClickProperty);
}
public static void SetSelectItemOnRightClick(DependencyObject d, bool value)
{
d.SetValue(SelectItemOnRightClickProperty, value);
}
private static void OnSelectItemOnRightClickChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
bool selectItemOnRightClick = (bool)e.NewValue;
TreeView treeView = d as TreeView;
if (treeView != null)
{
if (selectItemOnRightClick)
treeView.PreviewMouseRightButtonDown += OnPreviewMouseRightButtonDown;
else
treeView.PreviewMouseRightButtonDown -= OnPreviewMouseRightButtonDown;
}
}
private static void OnPreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
TreeViewItem treeViewItem = VisualUpwardSearch(e.OriginalSource as DependencyObject);
if (treeViewItem != null)
{
treeViewItem.Focus();
e.Handled = true;
}
}
public static TreeViewItem VisualUpwardSearch(DependencyObject source)
{
while (source != null && !(source is TreeViewItem))
source = VisualTreeHelper.GetParent(source);
return source as TreeViewItem;
}
}
}
XAML:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mvvmhelper="clr-namespace:WpfApplication1"
Title="MainWindow" Height="350" Width="525">
<Grid>
<TreeView mvvmhelper:TreeViewExtension.SelectItemOnRightClick="true">
<TreeViewItem Header="One"/>
<TreeViewItem Header="Two"/>
<TreeViewItem Header="Three"/>
<TreeView.ContextMenu>
<ContextMenu>
<MenuItem Header="Menu1"/>
<MenuItem Header="Menu2"/>
</ContextMenu>
</TreeView.ContextMenu>
</TreeView>
</Grid>
</Window>
You can use interactivity
xmlns:interactive="http://schemas.microsoft.com/expression/2010/interactivity"
The xaml:
<TreeView x:Name="TreeView" HorizontalAlignment="Left" Height="373" Margin="13,15,0,0" VerticalAlignment="Top" Width="373" Background="#29292f"
Foreground="White" BorderBrush="Transparent" BorderThickness="0" ItemsSource="{Binding EmtRoot}" FontSize="24" >
<interactive:Interaction.Triggers>
<interactive:EventTrigger EventName="SelectedItemChanged">
<interactive:InvokeCommandAction Command="{Binding SelectItemCommand}" CommandParameter="{Binding ElementName=TreeView,Path=SelectedItem}"/>
</interactive:EventTrigger>
</interactive:Interaction.Triggers>
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type models:EmtRootModel}" ItemsSource="{Binding Entities}">
<TextBlock Text="Root" Foreground="White" ContextMenu="{StaticResource ContextMenuLevel0}"/>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type models:EntityModel}" ItemsSource="{Binding Slides}">
<TextBlock Text="{Binding EntityName}" Foreground="White" ContextMenu="{StaticResource ContextMenuLevel1}"/>
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type models:SlideModel}">
<StackPanel Orientation="Horizontal" ContextMenu="{StaticResource ContextMenuLevel2}">
<StackPanel.InputBindings>
<MouseBinding MouseAction="RightClick"
Command="{Binding ElementName=Emt,Path=DataContext.TreeViewRightClickCommand}" CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=TreeViewItem}}"/>
</StackPanel.InputBindings>
<TextBlock Text="{Binding SlideName}" Foreground="White">
<TextBlock.InputBindings>
<MouseBinding MouseAction="LeftDoubleClick"
Command="{Binding ElementName=Emt,Path=DataContext.SlideDoubleClickCommand}" CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=TextBlock},Path=DataContext}"/>
</TextBlock.InputBindings>
</TextBlock>
<CheckBox IsChecked="{Binding IsChecked}" Foreground="White" />
</StackPanel>
</DataTemplate>
</TreeView.Resources>
</TreeView>
And in the viewModel:
private void ExecuteTreeViewRightClickCommand(object obj)
{
((TreeViewItem)obj).IsSelected = true;
SelectedSlide = ((TreeViewItem)obj).Header as SlideModel;
}
Related
i want drag on treevieew one and drop in treeview tow
TreeView one
<TreeView Name="tvMain" FontSize="14" Grid.Row="2" Background="#FFF9F9F9" >
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<EventSetter Event="MouseDoubleClick" Handler="TreeView_MouseDoubleClick" />
<EventSetter Event="MouseRightButtonDown" Handler="TreeView_MouseRightClick" />
</Style>
</TreeView.ItemContainerStyle>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate >
<StackPanel Orientation="Horizontal">
<Image DragDrop.DragOver="Image_DragOver" Margin="2" Width="14" Height="14" Source="{Binding Path=ImageUrl}" HorizontalAlignment="Right" VerticalAlignment="Center" ></Image>
<TextBlock Margin="2" Text="{Binding Path=Name}" MinWidth="280" Width="{Binding Path=SizeOfName}" VerticalAlignment="Center" />
<TextBlock Name="lb_params" Text="{Binding Params}" Width="{Binding Path=SizeParams}" VerticalAlignment="Center" TextAlignment="Center"/>
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
TreeView tow
<TreeView Name="tvMain" FontSize="14" Grid.Row="2" Background="#FFF9F9F9" >
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<EventSetter Event="MouseDoubleClick" Handler="TreeView_MouseDoubleClick" />
<EventSetter Event="MouseRightButtonDown" Handler="TreeView_MouseRightClick" />
</Style>
</TreeView.ItemContainerStyle>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate >
<StackPanel Orientation="Horizontal">
<Image Margin="2" Width="14" Height="14" Source="{Binding Path=ImageUrl}" HorizontalAlignment="Right" VerticalAlignment="Center" ></Image>
<TextBlock Margin="2" Text="{Binding Path=Name}" Width="{Binding Path=SizeOfName}" VerticalAlignment="Center" />
<TextBlock Name="lb_params" Text="{Binding Params}" Width="{Binding Path=SizeParams}" VerticalAlignment="Center" TextAlignment="Center"/>
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
i have tow treeview
i want drag in treeview one and drop in treeview tow in wpf?
To enable Drag and Drop feature in TreeView control:
Set TreeView two control’s property AllowDrop="True".
Declare three events in TreeView control, i.e. “MouseDown”, “MouseMove”,
“DragOver” and “Drop” events.
<treeview.itemcontainerstyle>
<style targettype="{x:Type TreeViewItem}">
<EventSetter Event="TreeViewItem.DragOver" Handler="treeView_DragOver"/>
<EventSetter Event="TreeViewItem.Drop" Handler="treeView_Drop"/>
<EventSetter Event="TreeViewItem.MouseMove" Handler="treeView_MouseMove"/>
<EventSetter Event="TreeViewItem.MouseDown" Handler="treeView_MouseDown"/>
</style>
</treeview.itemcontainerstyle>
And define event handlers for all the events in your xaml.cs file
Code for drop might help you.
private void treeView_MouseMove(object sender, MouseEventArgs e)
{
try
{
e.Effects = DragDropEffects.None;
e.Handled = true;
TreeViewItem TargetItem = GetNearestContainer
(e.OriginalSource as UIElement);
if (TargetItem != null && draggedItem != null )
{
_target = TargetItem;
e.Effects = DragDropEffects.Move;
} } catch (Exception) { }}
I get this error :
System.Windows.Data Error: 40 : BindingExpression path error: 'OpenPopupCommand' property not found on 'object' ''String' (HashCode=62725275)'. BindingExpression:Path=OpenPopupCommand; DataItem='String'
when I added a parameter to my command:
OpenPopupCommand = new RelayParamCommand((e) => PopupVisibility(FilterButton) );
VM:
private void PopupVisibility(object sender)
{
Console.WriteLine(sender.ToString());
PopupVisible ^= true;
}
Think is that I added Filter Button to Datagrid Headers which are generated automatically. Now I want to open popup when button is clicked. But think is that is not working, because i have to pass button by buttons x:Name to Popup PlacementTarget parameter.
<Page.DataContext>
<PDB:UsersViewModel x:Name="vm"/>
</Page.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<!--Page Header info content-->
<Grid Grid.Row="0">
<StackPanel Orientation="Horizontal">
<TextBlock Margin="2" Text="{Binding ElementName=userPage, Path=Name}"/>
<TextBlock Margin="2" Text="{Binding SelectedUser.Name}"/>
<TextBlock Margin="2" Text="{Binding ElementName=myGrd, Path=CurrentColumn.DisplayIndex}"/>
<Button x:Name="mybtn"
Content="{Binding Filters.Count, Mode=OneWay}"
Visibility="{Binding Filters.Count, Converter={Wpf:VisibilityConverter}}"
/>
</StackPanel>
</Grid>
<!--Datagrid content-->
<DataGrid x:Name="myGrd"
SelectionMode="Single"
SelectionUnit="Cell"
CurrentItem="{Binding SelectedUser, Mode=TwoWay}"
CurrentColumn="{Binding CurrentColumn, Mode=TwoWay}"
IsReadOnly="True"
Grid.Row="1"
ItemsSource="{Binding FilteredUserList}"
AutoGenerateColumns="True"
CanUserAddRows="False"
>
<DataGrid.Resources>
<!--Popup-->
<ContextMenu x:Key="ContextMenu">
<ContextMenu.Items>
<MenuItem Header="Filter by Selection" Command="{Binding IncludeCommand, Source={x:Reference vm}}"/>
<MenuItem Header="Filter exclude Selection" Command="{Binding ExcludeCommand, Source={x:Reference vm}}"/>
<MenuItem Header="Remove all Filters" Command="{Binding RemoveAllFiltersCommand, Source={x:Reference vm}}" Visibility="{Binding Filters.Count, Source={x:Reference vm}, Converter={Wpf:VisibilityConverter}}"/>
</ContextMenu.Items>
</ContextMenu>
<!--Custom Datagrid header View-->
<Style TargetType="DataGridColumnHeader" x:Name="FilterHeader">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<StackPanel>
<TextBox Margin="0,0,0,10" Width="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGridColumnHeader}}, Path=Width}" />
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding}" HorizontalAlignment="Center"/>
<Button Name="FilterButton"
Content="[-F-]"
Command="{Binding OpenPopupCommand}"
CommandParameter="{Binding ElementName=FilterButton}"/>
<Popup Name="MyPopup"
StaysOpen="False"
Placement="Right"
IsOpen="{Binding PopupVisible}"
PlacementTarget="{Binding FilterButton}">
<Border Background="White" BorderBrush="Black" Padding="5" BorderThickness="2" CornerRadius="5">
<StackPanel Orientation="Vertical">
</StackPanel>
</Border>
</Popup>
</StackPanel>
</StackPanel>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</DataGrid.Resources>
<DataGrid.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="ContextMenu" Value="{StaticResource ContextMenu}"/>
</Style>
</DataGrid.CellStyle>
</DataGrid>
</Grid>
By this approach i want to pass clicked button as parameter to VM and bind it to Popup PlacementTarget parameter. What i am doing wrong? Can i pass clicked button parameter? I know i break mvvm rules when passing view to vm, but how to achieve what i want, when i don't want to define every column in datagrid. Thank you
Your bindings have the DataContext of the DataGridColumnHeader as source which is the value of DataGridColumn.Header. This value in your case is a string and not your expected view model. This is the reason why your bindings doesn't resolve and you get the error message (which is exactly telling you this).
To fix the binding you have to find the next parent element that has the required DataContext, which is I assume the DataGrid:
<Button Name="FilterButton"
Content="[-F-]"
Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=DataGrid}, Path=DataContext.OpenPopupCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource Self}}"/>
When checking your code I can see the command which is bound to the Button is toggling the PopupVisible property. I therefore suggest to remove this view related code from the view model and replace the Button with a ToggleButton which binds directly to the Popup.IsOpen:
<!--Custom Datagrid header View-->
<Style TargetType="DataGridColumnHeader">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<StackPanel>
<TextBox Margin="0,0,0,10"
Width="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGridColumnHeader}}, Path=Width}" />
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding}"
HorizontalAlignment="Center" />
<ToggleButton Name="FilterButton"
Content="[-F-]" />
<Popup Name="MyPopup"
StaysOpen="False"
Placement="Right"
IsOpen="{Binding ElementName=FilterButton, Path=IsChecked}"
PlacementTarget="{Binding ElementName=FilterButton}">
<Border Background="White"
BorderBrush="Black"
Padding="5"
BorderThickness="2"
CornerRadius="5">
<StackPanel Orientation="Vertical">
</StackPanel>
</Border>
</Popup>
</StackPanel>
</StackPanel>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
To get the cell values of all rows contained in a single column where the column header value is the parameter and maps to a property name requires reflection. You need to bind a ListView to the resulting column value collection which uses an ItemTemplate to add a CheckBox to the item. The final version should be as follows:
<!--Custom Datagrid header View-->
<Style TargetType="DataGridColumnHeader">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<StackPanel>
<TextBox Margin="0,0,0,10"
Width="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGridColumnHeader}}, Path=Width}" />
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding RelativeSource={RelativeSource Self}}"
HorizontalAlignment="Center" />
<ToggleButton Name="FilterButton"
Content="[-F-]"
Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=DataGrid}, Path=DataContext.(UsersViewModel.GenerateFilterViewItemsCommand)}"
CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=DataGridColumnHeader}, Path=Content}" />
<Popup Name="MyPopup"
StaysOpen="False"
Placement="Right"
IsOpen="{Binding ElementName=FilterButton, Path=IsChecked}"
PlacementTarget="{Binding ElementName=FilterButton}">
<Border Background="White"
BorderBrush="Black"
Padding="5"
BorderThickness="2"
CornerRadius="5">
<ListView
ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=DataGrid}, Path=DataContext.(UsersViewModel.FilterViewItems)}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding IsIncluded, Mode=OneWayToSource}"
Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=DataGrid}, Path=DataContext.(UsersViewModel.IncludeItemCommand)}"
CommandParameter="{Binding}" />
<ContentPresenter Content="{Binding CellValue}" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Border>
</Popup>
</StackPanel>
</StackPanel>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
To map the filter list to the actual items requires an additional type to hold the information:
Predicate.cs
public class FilterPredicate
{
public FilterPredicate(int rowIndex, object cellValue, object columnKey)
{
this.RowIndex = rowIndex;
this.CellValue = cellValue;
this.ColumnKey = columnKey;
}
public int RowIndex { get; set; }
public object CellValue { get; set; }
public bool IsIncluded { get; set; }
public object ColumnKey { get; set; }
}
You also need to add a collection for the filter view and one for the included item indices to the view model:
UsersViewModel.cs
public class UsersViewModel
{
public UsersViewModel()
{
this.FilterViewItems = new ObservableCollection<FilterPredicate>();
this.IncludedItemsIndex = new Dictionary<object, List<int>>();
}
// The binding source for the Popup filter view
public ObservableCollection<FilterPredicate> FilterViewItems { get; set; }
private Dictionary<object, List<int>> IncludedItemsIndex { get; set; }
public ICommand GenerateFilterViewItemsCommand => new RelayParamCommand((param) =>
{
var columnHeader = param as string;
this.IncludedItemsIndex.Remove(columnHeader);
this.FilterViewItems.Clear();
for (var rowIndex = 0; rowIndex < this.FilteredUserList.Count; rowIndex++)
{
var data = this.FilteredUserList.ElementAt(rowIndex);
var columnValue = data.GetType()
.GetProperty(columnHeader, BindingFlags.Public | BindingFlags.Instance)?
.GetValue(data);
if (columnValue != null)
{
this.FilterViewItems.Add(new FilterPredicate(rowIndex, columnValue, columnValue));
}
}
});
public ICommand IncludeItemCommand => new RelayParamCommand((param) =>
{
var predicate = param as FilterPredicate;
if (predicate.IsIncluded)
{
if (!this.IncludedItemsIndex.TryGetValue(predicate.ColumnKey, out List<int> includedIndices))
{
includedIndices = new List<int>();
this.IncludedItemsIndex.Add(predicate.ColumnKey, includedIndices);
}
includedIndices.Add(predicate.RowIndex);
}
else
{
if (this.IncludedItemsIndex.TryGetValue(predicate.ColumnKey, out List<int> includedIndices))
{
includedIndices.Remove(predicate.RowIndex);
}
}
// Apply the filter
CollectionViewSource.GetDefaultView(this.FilteredUserList).Filter =
data => this.IncludedItemsIndex.Values
.SelectMany(indices => indices)
.Contains(this.FilteredUserList.IndexOf(data as FilteredUser));
});
}
Since you are autogenerating columns, I strongly suspect it is trying to find a OpenPopupCommand in one of the string properties on the items for FilteredUserList.
Try The Following :
Give you'r Page or DataGrid a Name, And change your Binding to
<MenuItem Command="{Binding Path=DataContext.IncludeCommand, Source={x:Reference myPage}}"/>
I have two DataTemplate for my EvenementViewModel, one for read only (LectureEvenement) and the second for write (EditeurEvenement).
The template is selected depending of the EvenementViewModel state. These template are used within a TabControl.
From the code behind (Button click), I would like to get the FlowDocument that is in the read only DataTemplate. The goal is to print the FlowDocument.
I need help to get the handle on the FlowDocument that is in the selected tab of the TabControl.
Any suggestion?
There is some part of the XAML code.
<Window.Resources>
<DataTemplate x:Key="EditeurEvenement" DataType="{x:Type local:EvenementViewModel}">
<ScrollViewer Name="Conteneur" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<StackPanel MaxWidth="1000">
<Border BorderThickness="5" BorderBrush="#55CDD7E1">
<StackPanel Background="#55CDD7E1">
<!-- Centrale -->
<StackPanel DockPanel.Dock="Top" Margin="0,0,0,5" HorizontalAlignment="Left">
<TextBlock VerticalAlignment="Center"><Run Text="Centrale :" FontWeight="Bold"/></TextBlock>
<ComboBox MinWidth="160" ItemsSource="{Binding ListeCentrale}" DisplayMemberPath="Nom" SelectedItem="{Binding Centrale}"/>
</StackPanel>
...
</StackPanel>
</Border>
</StackPanel>
</ScrollViewer>
</DataTemplate>
<DataTemplate x:Key="LectureEvenement" DataType="{x:Type local:EvenementViewModel}">
<FlowDocumentScrollViewer VerticalScrollBarVisibility="Auto">
<FlowDocument FontFamily="Sergoe UI" FontSize="12px" Name="FdEvenement">
<Paragraph>
<!-- Centrale -->
<TextBlock Margin="0,0,0,5"><Run Text="Centrale : " FontWeight="Bold"/><Run Text="{Binding Centrale.Nom, Mode=OneWay}"/></TextBlock>
<LineBreak/>
...
</Paragraph>
</FlowDocument>
</FlowDocumentScrollViewer>
</DataTemplate>
<DataTemplate DataType="{x:Type local:EvenementViewModel}">
<ContentControl Content="{Binding}">
<ContentControl.Style>
<Style TargetType="{x:Type ContentControl}">
<Setter Property="ContentTemplate" Value="{StaticResource LectureEvenement}"></Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding EstModifiable}" Value="True">
<Setter Property="ContentTemplate" Value="{StaticResource EditeurEvenement}"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
</DataTemplate>
</Window.Resources>
<TabControl x:Name="TcEvenements" ItemsSource="{Binding ListeOngletOuvert}" SelectedItem="{Binding OngletSelectionne}">
<TabControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding TitreTab}" Margin="0,0,5,0" VerticalAlignment="Center"/>
<Button CommandParameter="{Binding DataContext, RelativeSource={RelativeSource AncestorType={x:Type Window}}}" Command="{Binding LancerFermer}">
<Image Source="Icone/icons8-delete-23.png" Width="15"/>
</Button>
</StackPanel>
</DataTemplate>
</TabControl.ItemTemplate>
</TabControl>
The FlowDocumentScrollViewer element is a visual child of the TabControl whenever your template is applied. You can then get a reference to it using the VisualTreeHelper class:
private void Button_Click(object sender, RoutedEventArgs e)
{
FlowDocumentScrollViewer fdsv = FindVisualChild<FlowDocumentScrollViewer>(TcEvenements);
if (fdsv != null)
{
FlowDocument fd = fdsv.Document;
//...
}
}
private T FindVisualChild<T>(Visual visual) where T : Visual
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(visual); i++)
{
Visual child = (Visual)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 like to Binding the datagrid parent from Behavior (pnlDgSubFooter) placed in DataGrid/GroupStyle/ContainerStyle/Style[GroupItem]/Setter[template]/ControlTemplate/Expander/Header/DockPanel/StackPanel
But the Binding can't leave of DockPanel.
in ElementName, there is only "btnExpandAll" and himself.
In Behavior, the LeDataGrid property return alway null (except if i bind on . or btnExpandAll)
<DataGrid Grid.Row="0" x:Name="dgMain" AutoGenerateColumns="False" SelectionUnit="FullRow" LoadingRow="dgMain_LoadingRow" MouseDown="dgMain_MouseDown" Sorting="dgMain_Sorting"
CanUserReorderColumns="False" CanUserResizeColumns="True" CanUserResizeRows="False" CanUserSortColumns="True" CanUserAddRows="False"
Style="{StaticResource dg}" RowStyle="{StaticResource dgRow}" CellStyle="{StaticResource dgCell}" ColumnHeaderStyle="{StaticResource dgColHeader}" RowHeaderStyle="{StaticResource dgRowHeader}"
ItemsSource="{Binding NotifyOnSourceUpdated=True, Source={StaticResource cvsElmts}}" HorizontalAlignment="Left" >
<!--DataGrid.DataContext><Binding Source="{StaticResource tblUsers}"/></DataGrid.DataContext-->
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<mvvm:EventToCommand Command="{Binding SendCommand, Mode=OneWay}" CommandParameter="{Binding SelectedItem, ElementName=dgMain}" PassEventArgsToCommand="False"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<DataGrid.GroupStyle>
<GroupStyle>
<!-- Style for groups under the top level. -->
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Margin" Value="0,0,0,5"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Expander IsExpanded="{Binding DataContext.ExpandedAll, Mode=OneWay, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}" BorderThickness="1,1,1,5">
<Expander.Style>
<Style TargetType="{x:Type Expander}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsBottomLevel}" Value="True">
<Setter Property="Margin" Value="8,0,0,0" />
</DataTrigger>
</Style.Triggers>
</Style>
</Expander.Style>
<Expander.Header>
<DockPanel>
<Button Name="btnExpandAll" Command="{Binding DataContext.ExpandedAllCommand, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}" Click="btnExpandAll_Click" ToolTip="{x:Static resx:resMain.lblExpandAll}" BorderThickness="0">
<TextBlock Grid.Column="0" Text="" FontFamily="{StaticResource FntSymbol}" Foreground="{StaticResource scbBlack}" FontSize="12" VerticalAlignment="Center" HorizontalAlignment="Center"/>
</Button>
<TextBlock FontWeight="Bold" Text="" Margin="5,0,0,0"><Run Text="{Binding Path=Name, Mode=OneWay}" /><Run Text=" ("/><Run Text="{Binding Path=ItemCount, Mode=OneWay}" /><Run Text=" éléments)."/></TextBlock>
<StackPanel Name="pnlDgSubFooter" HorizontalAlignment="Left" Orientation="Horizontal" >
<i:Interaction.Behaviors>
<Classes:BehaviorWithCommand LeDataGrid="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}}" LeGroupe="{Binding .}" />
</i:Interaction.Behaviors>
</StackPanel>
</DockPanel>
</Expander.Header>
<Expander.Content>
<Border BorderThickness="1" BorderBrush="{StaticResource scbGrey3}">
<ItemsPresenter />
</Border>
</Expander.Content>
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
....
The Behavior:
public class BehaviorWithCommand : Behavior<FrameworkElement> { // StackPanel
public static readonly DependencyProperty LeDataGridProperty = DependencyProperty.Register(nameof(LeDataGrid), typeof(object), typeof(BehaviorWithCommand), new PropertyMetadata(null));
public static readonly DependencyProperty LeGroupeProperty = DependencyProperty.Register(nameof(LeGroupe), typeof(object), typeof(BehaviorWithCommand), new PropertyMetadata(null));
public object LeDataGrid {
get { return (object)GetValue(LeDataGridProperty); }
set { SetValue(LeDataGridProperty, value); }
}
public object LeGroupe {
get { return (object)GetValue(LeGroupeProperty); }
set { SetValue(LeGroupeProperty, value); }
}
protected override void OnAttached() {
base.OnAttached();
((Panel)AssociatedObject).Children.Clear();
var dg = new DataGrid();
dg.Columns.Add(new DataGridTextColumn());
dg.Columns.Add(new DataGridTextColumn());
dg.Columns.Add(new DataGridTextColumn());
//var dg = (DataGrid)LeDataGrid;
foreach (var item in dg.Columns) {
var g = new Grid() { MinWidth = 10 };
g.SetBinding(Grid.WidthProperty, new System.Windows.Data.Binding("ActualWidth") { Source = item }); // dhHeadName DataGridColumn
var t = new TextBox() { Margin = new Thickness(1, 0, 1, 0), Background = new SolidColorBrush(Color.FromRgb(200, 200, 200)), FontWeight = FontWeights.Bold, FontSize = 9, IsReadOnly = true };
t.Text = "Coucou";
g.Children.Add(t);
((Panel)AssociatedObject).Children.Add(g);
}
}
protected override void OnDetaching() {
base.OnDetaching();
}
}
This is working:
<StackPanel Grid.ColumnSpan="2" Grid.Row="1" Name="pnlDgSubFooter" HorizontalAlignment="Left" Orientation="Horizontal" Margin="-20, 0, 0, 0">
<i:Interaction.Behaviors>
<Classes:BehaviorWithCommand LeDataGrid="{Binding ., ElementName=dgMain}" LeGroupe="{Binding .}" />
</i:Interaction.Behaviors>
</StackPanel>
It's just that designer don't resolve it.
Regards.
<HierarchicalDataTemplate x:Key="SchemTemplate">
<TextBlock Text="{Binding Path=path}" />
</HierarchicalDataTemplate>
<HierarchicalDataTemplate x:Key="SchemesTemplate">
<TreeViewItem TextBlock.FontStyle="Italic"
Header="{Binding name}"
ItemsSource="{Binding schemes}"
ItemTemplate="{StaticResource SchemTemplate}" />
</HierarchicalDataTemplate>
<DataTemplate x:Key="ProjectTemplate">
<TreeView>
<TreeViewItem Header="{Binding Path=name}"
ItemsSource="{Binding schemes}"
ItemTemplate="{StaticResource SchemesTemplate}">
</TreeViewItem>
</TreeView>
</DataTemplate>
using:
<ListBox Style="{StaticResource ListProjectsStyle}"
x:Name="projects"
Grid.Row="0" Grid.Column="0"
ItemsSource="{Binding projects_models, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
ItemTemplate="{StaticResource ProjectTemplate}"
SelectedItem="{Binding active_project, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
</ListBox>
When I click right mouse button over TreeView then ListBox's SelectedItem changed, but left click does this. Left click puts the focus on TreeView or expand TreeView items. How can I change focus on ListBox when focus is on TreeView?
Solution:
<ListBox.Resources>
<Style TargetType="{x:Type ListBoxItem}" BasedOn="{StaticResource ListBoxItem}">
<EventSetter Event="ListBoxItem.PreviewMouseLeftButtonDown"
Handler="ListBoxItem_Select" />
</Style>
</ListBox.Resources>
private void ListBoxItem_Select(object sender, MouseEventArgs e)
{
var mouseOverItem = sender as ListBoxItem;
if (mouseOverItem != null&& e.LeftButton == MouseButtonState.Pressed)
{
mouseOverItem.IsSelected = true;
}
}