I want to drag data from a ListView and drop it in a TreeView(the draging works fine). I use DataBinding and ItemTemplate to fill the TreeView.
<TreeView ItemsSource="{Binding Groups}" Name="tvGroups" AllowDrop="True"
Drop="tvDrop" DragOver="tvDragOver">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Participants}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}" />
<Button Tag="{Binding .}" Click="Button_Click_2">
<Image Source="Resources/cross.png" />
</Button>
</StackPanel>
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" >
<TextBlock Text="{Binding Alias}" />
<Button Tag="{Binding .}" Name="btnDeleteParticipants" Click="btnParticipants_Click" >
<Image Source="Resources/cross.png" />
</Button>
</StackPanel>
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
private void tvDrop(object sender, DragEventArgs e)
{
if (e.Effects == DragDropEffects.Copy &&
e.Data.GetDataPresent(typeof(Participant)))
{
Participant data = e.Data.GetData(typeof(Participant)) as Participant;
}
}
A Participant is dragged from the ListView to the TreeView. Now I need to find the Group. Any ideas where to get the right Group from the TreeView?
I would simply set the Drop="tvDrop" and DragOver="tvDragOver" on the StackPanel in the HierarchicalDataTemplate's ItemTemplate.
This way
1) You don't have any risk of getting an event when something is dropped out of a group
2) You can safely cast the Sender to a FrameworkElement and get the DataContext and cast it to your class.
You can also set a different handler on the treeview itself if you need to support dragging out of the groups.
Related
I am struggling to bind my user-defined tooltip to the items of a TreeView.
This is the XAML inside the TreeView: as you can see I bind my HierarchicalDataTemplate to the GroupWrapper (simple Class that exposes the properties Name and Children) and the DataTemplate to the DocWrapper (again, simple Class that has properties such as Name, Icon, BsonContent).
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type tmistruct:GroupWrapper}" ItemsSource="{Binding Children}">
<TextBlock Text="{Binding Name}" />
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type tmistruct:DocWrapper}" >
<StackPanel Orientation="Horizontal">
<Image Source="{Binding Icon}"/>
<TextBlock Text="{Binding Name}" Tag="{Binding BsonContent}" VerticalAlignment="Center" Style="{StaticResource TreeViewTextboxItemStyle}"/>
<StackPanel.ToolTip>
<local:MyDocTooltip
NameField="{Binding Name}"/>
</StackPanel.ToolTip>
</StackPanel>
</DataTemplate>
</TreeView.Resources>
It all works great! The Tree is automatically populated with the correct text and icons for all nodes BUT... my user-defined tooltip doesn't get the memo. The tooltip shows, but the name field is not populated. Note that if I replace
<local:MyDocTooltip
NameField="{Binding Name}"/>
with:
<local:MyDocTooltip
NameField="TESTSTRING"/>
The test string is correctly displayed in the Tooltip.
This is what my user-defined tooltip looks like:
namespace MyControls
{
/// <summary>
/// Interaction logic for MyDocTooltip.xaml
/// </summary>
public partial class MyDocTooltip : UserControl
{
public MyDocTooltip()
{
InitializeComponent();
DataContext = this;
}
public static readonly DependencyProperty NameFieldProperty = DependencyProperty.Register("NameField", typeof(string), typeof(MyDocTooltip));
public string NameField
{
get { return (string)GetValue(NameFieldProperty); }
set { SetValue(NameFieldProperty, value); }
}
}
}
I guess something to do with my tooltip's data context being set to its own ViewModel? I've been trying with relative references but with no luck. Any help appreciated! Cheers.
OK my whole setup was wrong. (Thanks #ASh)
For those out there who are struggling with similar issues:
Each user control should not set its DataContext property!
This should be inherited.
What you do inside each user control, is to bind each element to its UserControl ancestor.
So, inside MyDocTooltip.xaml each element will be defined like this (bound to NameField property):
<TextBlock Text="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}},Path=NameField}"/>
Then I do the same in the TreeView (which is also inside a user control):
<TreeView x:Name="treeViewCollection" Grid.Row="2" Grid.ColumnSpan="2" ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}},Path=FilteredGroups, UpdateSourceTrigger=PropertyChanged}" >
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type tmistruct:GroupWrapper}" ItemsSource="{Binding Children}">
<TextBlock Text="{Binding Name}" />
</HierarchicalDataTemplate>
<DataTemplate x:Name="TreeViewDataTemplate" DataType="{x:Type tmistruct:DocWrapper}" >
<StackPanel Orientation="Horizontal">
<Image Source="{Binding Icon}"/>
<TextBlock Text="{Binding Name}" Tag="{Binding BsonContent}" VerticalAlignment="Center"/>
<StackPanel.ToolTip>
<local:MyDocTooltip
NameField="{Binding Name}"
/>
</StackPanel.ToolTip>
</StackPanel>
</DataTemplate>
</TreeView.Resources>
</TreeView>
And it all seems to work fine. Please do let me know if I'm still doing it wrong. Thanks for the help.
I have a comboBox that is styled to have rows of text blocks and check boxes to allow user to check a list of different things. What I am attempting to do is disable selection of a row in the the combobox.
My View Code:
<ComboBox ItemsSource="{Binding HeaderList}"
IsSelected="False"
HorizontalAlignment="Left"
Height="10"
x:Name="ComboBox">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding IsChecked}"/>
<TextBlock Text="{Binding HeaderName}"/>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Here is a picture of my Drop Down:
Here is a picture of what I don't want:
I want to disable the ability for the user to select a specific row, but keep the ability for them to select various check boxes in the drop down. Is there a way to style this?
I had the same issue, i solved it by using a ToggleButton in combination with a Popup (instead of using a ComboBox)
Note: Not tested
<ToggleButton x:Name="filterButton" />
<Popup x:Name="popup"
AllowsTransparency="True"
StaysOpen="False"
PlacementTarget="{Binding ElementName=filterButton}"
IsOpen="{Binding ElementName=filterButton,Path=IsChecked,Mode=TwoWay}">
<ItemsControl ItemsSource="{Binding HeaderList}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding IsChecked}"/>
<TextBlock Text="{Binding HeaderName}"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Popup>
You can try to set the selectedindex of your combobox to -1 in the selectionchanged event
private void myComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
myComboBox.SelectedIndex = -1;
}
<ListView x:Name="listview" ScrollViewer.HorizontalScrollBarVisibility="Visible" ScrollViewer.ZoomMode="Enabled"
ItemsSource="{Binding YourCollection}" DoubleTapped="listview_DoubleTapped" Tapped="listview_Tapped" SelectionChanged="listview_SelectionChanged"
GotFocus ="StackPanel_GotFocus" IsItemClickEnabled="True" ItemClick="ListView_ItemClick"
Margin="162,539,-103,11" Style="{StaticResource ListViewStyle1}" ScrollViewer.VerticalScrollBarVisibility="Disabled" Grid.RowSpan="2">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" Height="130" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate >
<DataTemplate>
<StackPanel Orientation="Vertical" Height="130" Width="192" >
<StackPanel Orientation="Horizontal">
<Image Source="{Binding Image}" Height="108" Width="192" HorizontalAlignment="Center" VerticalAlignment="Center" />
</StackPanel>
<StackPanel Orientation="Vertical" >
<TextBlock Text="{Binding Name}" TextAlignment="Center" Height="22" Width="192" Foreground="White" FontFamily="Assets/GothamLight.ttf#GothamLight"/>
</StackPanel>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Need to set the focus or selection to first item of the listview items. The listview contains an array of items, where the focus needs to be targeting first item during start and then retain the last chosen item.
There are multiple options here depending on your coding style. It looks like you're using code behind from our event handlers and binding to a view model class with YourCollection property, so I'll give you both examples. :)
Using code-behind
Update your XAML file to handle the Loaded event and name your ListView:
<Page Loaded="Page_Loaded">
...
<ListView Name="MyListView" ItemsSource="{Binding YourCollection}">
...
</ListView>
Then add the following code your Page_Loaded handler:
private void Page_Loaded(object sender, RoutedEventArgs e)
{
if (MyListView.Items.Count > 0)
MyListView.SelectedIndex = 0;
}
Using view model
Provide a SelectedItem property in your view model (wherever you are defining YourCollection):
private YourItem_selectedItem = null;
public Dumb SelectedItem
{
get { return _selectedItem; }
set { SetProperty<YourItem>(ref _selectedItem, value); }
}
Then bind your ListView to the selected item, as you did with your ItemsSource:
<ListView Name="MyListView" ItemsSource="{Binding YourCollection}" SelectedItem="{Binding SelectedItem, Mode=TwoWay}">
Finally, just set your SelectedItem after you've loaded your collection items.
This method also has the benefit of replacing your SelectionChanged and ItemClick events. You won't need them because the control changes SelectedItem by default in those situations.
I found a further solution that does not require the Page_Loaded handler nor the property SelectedItem in the ViewModel.
<ListView Name="yourCollectionListView"
ItemsSource="{Binding YourCollection}"
SelectedItem="{Binding RelativeSource={RelativeSource Self}, Path=ItemSource[0]}"/>
Of course you should ensure the list has at least one item. With VisualState.StateTriggers you can hide the ListView if it is empty.
<VisualStateManager.VisualStateGroups>
<VisualStateGroup>
<VisualState.StateTriggers>
<StateTrigger IsActive="{Binding YourCollection.Count, Converter={StaticResource EqualToParam}, ConverterParameter={StaticResource Zero}}"/>
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="yourCollectionListView.Visibility" Value="Collapsed" />
</VisualState.Setters>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
Define the zero value in the page's resources:
<x:Int32 x:Key="Zero">0</x:Int32>
EDIT
It is even possible to achieve this by binding the following to the SelectedItem property:
SelectedItem="{Binding Path=[0]}"
i am completly new to WPF and need your help. I followed many tutorials but nothing works.
I have two ObserveableLists L1 and L2 to bind and I want to archiev:
On depth 1 - The first List and for each child list 2.
L1.1
L2.1
L2.2
L2.3
L1.2
L2.1
L2.2
L2.3
L1.3
L2.1
L2.2
L2.3
Update 16.02.07 - 16:44
My first try:
<TreeView Grid.Column="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Grid.Row="1" Margin="10" ItemsSource="{Binding orderCities}">
<TreeView.Resources>
<DataTemplate DataType="{x:Type model:city}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding name}" />
</StackPanel>
</DataTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding products}" DataType="{x:Type model:Product}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding name}" />
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
My second try: Defining two DataTemplates how to handle my types:
<UserControl.Resources>
<HierarchicalDataTemplate DataType="{ x:TypeExtension model:city }" ItemsSource="{Binding orderCities}">
<StackPanel>
<TextBlock Text="{Binding name}" />
<!-- Here embed Product Type (Dont know how)-->
</StackPanel>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{ x:TypeExtension model:Product }" ItemsSource="{Binding products}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding name}" />
<TextBlock Text=" - " />
<TextBox Text="1" />
</StackPanel>
</HierarchicalDataTemplate>
</UserControl.Resources>
And testet both templates and got the right design.
<TreeView ItemsSource="{Binding orderCities}" />
<TreeView ItemsSource="{Binding orderCities}" />
The treeview alone won't help you here. You have to actually create the data you want to display, i.e. the treeview won't "multiply" the two lists.
Each element in the first list may of course return the same instance of the second list in its children-property. You might want to have look at this codeproject article, it's rather old, but gives a nice introduction on how to use the wpf treeview.
In my application using DataTemplate mechanism I insert into ListBox item another listbox. But is it possible that when selected one listboxitem in parent listbox, the focus may be on another parent listboxitem's child (see picture)
How to do: if one of child listbox in focus (one item from them selected), then parent listboxitem being selected to? Using binding or templating
<DataTemplate x:Key="NotesListBoxDataTemplate" DataType="Note">
<StackPanel Orientation="Vertical">
<StackPanel Orientation="Horizontal">
<TextBox Text="{Binding Path=Title, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"></TextBox>
<my:DatePicker Height="25" Name="datePicker1" Width="115" xmlns:my="clr-namespace:Microsoft.Windows.Controls;assembly=WPFToolkit"
SelectedDate="{Binding LastEdit,
Mode = TwoWay}" />
</StackPanel>
<TextBox Text="{Binding Path=Content, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"></TextBox>
<StackPanel Orientation="Horizontal">
<ListBox Name="ImagesListBox" SelectedIndex="{Binding Mode=OneWayToSource, Source={StaticResource progParameters}, Path=SelectedImage, UpdateSourceTrigger=PropertyChanged}" ItemsSource="{Binding Path=Images}" ItemTemplate="{StaticResource NotesListBoxImagesTemplate}" Style="{StaticResource HorizontalListBox}">
</ListBox>
<StackPanel Orientation="Vertical">
<Button Name="AddImageButon" Content="+" Click="AddImageButon_Click"></Button>
<Button Name="RemoveImageButon" Content="-" Click="RemoveImageButon_Click"></Button>
</StackPanel>
</StackPanel>
</StackPanel>
</DataTemplate>
On the parent ListBox set property IsSynchronizedWithCurrentItem to true, then in the inner ListBoxes set the SelectedItem property to "{Binding SelectedItem ElementName=lbParent}".
Consider using a Converter to help you get specific data that is not accessible thru xaml or if you need to do some caculations.
Hope this helps.