ListViewItem Context Menu Get Data from ListViewItem - wpf

I'm trying to create a context menu, so that when you right click the ListViewItem, the user is presented with a list of options. The problem is; I can't get the refrenced item linked to the ListViewItem in the Click event.
I think it may be because I'm putting the ContextMenu in the wrong place in my XAML. I have been searching and playing around for ages, but think it could have something to do with the DataTemplate I'm using where the examples weren't in templates.
<ListView Margin="0" Name="FileImagesListView" VerticalAlignment="Top" Grid.Row="0">
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<EventSetter Event="Mouse.MouseEnter" Handler="MouseEnterPicFileListItem" />
<EventSetter Event="Mouse.MouseLeave" Handler="MouseLeavePicFileListItem"/>
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate>
<Border BorderBrush="{Binding Path=BorderBrushColourID, Converter={StaticResource BorderColourConverter}}" BorderThickness="3" CornerRadius="2">
<StackPanel FlowDirection="LeftToRight" Orientation="Vertical" Margin="3">
<Grid>
<TextBlock TextAlignment="Center" Text="{Binding Path=TimeAgo}" Margin="0,7" ></TextBlock>
<Label Style="{StaticResource CircularLabel}" HorizontalAlignment="Right" Height="35" Margin="0,-8,0,0" Content="{Binding Path=MatchedCount}" Visibility="{Binding Path=MatchedCount, Converter={StaticResource VisibleIfGreaterThanOne}}" ></Label>
</Grid>
<Image Name="FilePic" Height="Auto" Width="160" Source="{Binding Path=BitmapPicture}"></Image>
</StackPanel>
</Border>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Margin="3" Orientation="Horizontal"></StackPanel>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
</ListView>

Usually you can get the data item by just calling myListViewItem.DataContext and casting it into whatever it should be.
private void ListViewItem_Click(object sender, EventArgs e)
{
ListViewItem item = sender as ListViewItem;
if (item == null) return;
MyDataItem = item.DataContext as MyDataItem;
// Do whatever here
}
As a side note, WPF ContextMenus do not share the same VisualTree as your application, so trying to bind them to your main UI works differently. Its hard to tell if that is related to your problem because I see no ContextMenu or Click event in your question.
Edit
If your ContextMenu is on a ListBoxItem, then you need to refer to your ContextMenu's PlacementTarget to get the ListBoxItem that the ContextMenu is attached to

Related

Border that bulges near mouse pointer

I have a ListView populated with ListViewItems, like navigation bar style menu options.
I would like to create the effect of the ListViewItem border "bulging" like in the Mail application on Win10 as shown in this picture
The border "bulges" (fisheye?) as the mouse moves left and right along the ListViewItem. It's a nice effect that I'd like to replicate but am struggling to even get started on how to tackle this.
My list items are bound to a content control which is templated with a ControlTemplate. The ItemsControl ItemTemplate is a DataTemplate containing an image and a Textbox wrapped inside a button control, like this .... (although I suspect how it's created is probably not relevant to my question)
<ContentControl Content="{Binding Path=MenuModel.AllMenuItems}"
x:Name="menuCtrl1"
Template="{StaticResource MenuListItemControlTemplate}"
VerticalAlignment="Top" />
...
<ControlTemplate x:Key="MenuListItemControlTemplate" TargetType="ContentControl">
<Border Background="Transparent" >
<ScrollViewer Margin="{StaticResource DefaultNoMargin}" HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
<ItemsControl ItemsSource="{TemplateBinding Content}"
ItemTemplate="{StaticResource MenuListItemDataTemplate}" />
</ScrollViewer>
</Border>
</ControlTemplate>
...
<DataTemplate x:Key="MenuListItemDataTemplate" DataType="local:SingleMenuItem">
<Button Command="{Binding MenuCommand}" Style="{StaticResource MenuButtonStyle}" >
<StackPanel Orientation="Horizontal" >
<Image Source="{Binding MenuIconSource}" Style="{StaticResource MenuItemImage}" />
<TextBox Text="{Binding DisplayText}" Style="{StaticResource MenuItemStyle}" />
</StackPanel>
</Button>
</DataTemplate>
Any tips on how to achieve it or even where to start would be gratefully received.

WPF TabControl: ItemContainerStyle and ItemTemplateSelector

I am not able to find an answer to my question somewhere. Maybe you guys can help me.
I am using WPF and have a TabControl which uses an ItemTemplateSelector. The ItemsSource of the TabControl is an ObservableCollection of Strings. Based on the string, the template for the TabItem is choosen. Works fine so far.
Only problem I have now, is that I want to use a custom style on my TabItems. So I tried the ItemContainerStyle property, but this doesn't work. When I set the ItemContainerStyle, the ItemTemplateSelector doesn't fire anymore. I am not using a ContentTemplateSelector as I don't need this one on this solution as the content of the Tabs is always the same.
So my question is: How can I define the style of the TabItems, when I use an ItemTemplateSelector?
Here is some code:
TabControl on Usercontrol:
<TabControl TabStripPlacement="Right"
ItemsSource="{Binding loadedPalettes, UpdateSourceTrigger=PropertyChanged}"
Style="{StaticResource StyleTabControl}"
ItemTemplateSelector="{StaticResource TabTemplateSelector}"
Height="Auto"
SelectionChanged="paletteSelectionChanged"
SelectedIndex="{Binding selPaletteIndex}"
Width="Auto"
Margin="0,5,0,0">
<TabControl.ContentTemplate>
<DataTemplate>
<local:tabDataGrid />
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
TemplateSelector Class
Public Class SSITabTemplateSelector
Inherits DataTemplateSelector
Public Overrides Function SelectTemplate(item As Object, container As DependencyObject) As DataTemplate
Dim element As FrameworkElement
element = TryCast(container, FrameworkElement)
If element Is Nothing Then Return Nothing
If container Is Nothing Then Return Nothing
Select Case item
Case "Search"
Return TryCast(element.FindResource("searchTabItem"), DataTemplate)
'Case "TabSwitch"
' Return TryCast(element.FindResource("TextItem"), DataTemplate)
Case Else
Return TryCast(element.FindResource("normalTabItem"), DataTemplate)
End Select
Return Nothing
End Function
End Class
DataTemplates for the TabItems
<DataTemplate x:Key="normalTabItem">
<StackPanel Name="Panel"
Orientation="Horizontal">
<TextBlock Text="{Binding}"
Background="Transparent">
<TextBlock.LayoutTransform>
<RotateTransform Angle="270" />
</TextBlock.LayoutTransform>
</TextBlock>
<ContentPresenter x:Name="ContentSite"
VerticalAlignment="Center"
HorizontalAlignment="Center"
Content="{Binding Content}" />
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="searchTabItem">
<StackPanel Name="Panel"
Orientation="Horizontal">
<TextBlock Text="blabla"
Background="AliceBlue">
<TextBlock.LayoutTransform>
<RotateTransform Angle="270" />
</TextBlock.LayoutTransform>
</TextBlock>
<ContentPresenter x:Name="ContentSite"
VerticalAlignment="Center"
HorizontalAlignment="Center"
Content="{Binding Content}" />
</StackPanel>
</DataTemplate>
Adding the answer, according the comment. You can write something like this.
<TabControl>
<TabControl.Resources>
<Style TargetType="TabItem">
<Setter Property="Header" Value="MyHeader"/>
</Style>
</TabControl.Resources>
</TabControl>
Solution:
Yeah, it was that simple. bars222 had the right answer. Just use <TabControl.Resources> and add the styling.
Thanks!

WPF Popup inside a DataTemplate

I need a WPF Popup inside a DataTemplate, something like this:
<ScrollViewer>
<ItemsControl ItemsSource="{Binding Collection}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<TextBox Name="MyTextboxBrief" Text="{Binding TextBrief}"/>
<Popup PlacementTarget="{Binding ElementName=MyTextboxBrief}" Placement="Center">
<TextBox Name="MyTextboxVerbose" Text="{Binding TextVerbose}"/>
</Popup>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
But, this Popup should behave like this:
it must scroll together with relevant ItemsControl item
when app window is minimized - it shouldn't stay visible on desktop
it will be taller than ItemsControl, its content mustn't be clipped, but it mustn't change ItemsControl height
it will be wider than relevant ItemsControl item - but it shouldn't shift other ItemsControl items to the left or to the right
I have a strong feeling that I should somehow use ComboBox template - but I don't understand how to get it's Popup behavior
I don't think that Popup will help you here, nor ComboBox. See if this helps you any further:
<DataTemplate>
<Grid>
<TextBox Name="MyTextboxBrief" Text="{Binding TextBrief}" />
<!-- You might want to bind visibility against
some kind of property -->
<Canvas >
<Canvas.RenderTransform>
<!--In case you want to move-->
<TranslateTransform Y="-5" />
</Canvas.RenderTransform>
<Border Width="100" Height="20" Background="Black">
<TextBlock Text="Test" />
</Border>
</Canvas>
</Grid>
</DataTemplate>

ListBox not using its ItemTemplate

What in the world is wrong with this ListBox? It is showing items as plain strings, not using the template I have provided:
<ListBox>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Ellipse Width="20" Height="20" Fill="LightBlue" />
<TextBlock Text="{TemplateBinding Content}" Foreground="Red" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.Items>
<ListBoxItem>html</ListBoxItem>
<ListBoxItem>head</ListBoxItem>
<ListBoxItem>body</ListBoxItem>
<ListBoxItem>table</ListBoxItem>
<ListBoxItem>tr</ListBoxItem>
<ListBoxItem>td</ListBoxItem>
</ListBox.Items>
</ListBox>
From the Remarks section in the ItemTemplate MSDN page:
When you set an ItemTemplate on an ItemsControl, the UI is generated
as follows (using the ListBox as an example):
1.During content generation, the ItemsPanel initiates a request for the ItemContainerGenerator to create a container for each data item.
For ListBox, the container is a ListBoxItem. The generator calls back
into the ItemsControl to prepare the container.
2.Part of the preparation involves the copying of the ItemTemplate of the ListBox to be the ContentTemplate of the ListBoxItem.
3.Similar to all ContentControl types, the ControlTemplate of a ListBoxItem contains a ContentPresenter. When the template is applied,
it creates a ContentPresenter whose ContentTemplate is bound to the
ContentTemplate of the ListBoxItem.
4.Finally, the ContentPresenter applies that ContentTemplate to itself, and that creates the UI.
These steps are apparently not executed when you create ListBoxItem instances directly in XAML. It is however not strictly necessary to bind the ItemSource property. You may also directly set items like this:
<ListBox xmlns:sys="clr-namespace:System;assembly=mscorlib">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Ellipse Width="20" Height="20" Fill="LightBlue" />
<TextBlock Text="{TemplateBinding Content}" Foreground="Red" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.Items>
<sys:String>html</sys:String>
<sys:String>head</sys:String>
<sys:String>body</sys:String>
<sys:String>table</sys:String>
<sys:String>tr</sys:String>
<sys:String>td</sys:String>
</ListBox.Items>
</ListBox>
public class MyViewModel
{
public List<String> Items
{
get { return new List<String> { "html", "head", "body","table","tr","td" }; }
}
}
//This can be done in the Loaded event of the page:
DataContext = new MyViewModel();
Your XAML
<ListBox Margin="20" ItemsSource="{Binding Items}">
<ListBox.ItemTemplate>
<DataTemplate>
<Ellipse Width="20" Height="20" Fill="LightBlue" />
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>

Performance degredation with custom ListBox ControlTemplate

Using a template for a custom control deriving from ListBox causes filtering of ItemSource to become slow. The filtering is done in the get of the ItemSource that the control is bound to. This problem is not present when a normal ListBox is used, so why should it be any different for a custom ListBox?
Filtering:
public IEnumerable<LibraryViewModel> Libraries {
get {
if (!string.IsNullOrEmpty(this.LibrarySearchString))
return _libraries.Where(lib => IsLibraryMatch(lib, this.LibrarySearchString));
else
return _libraries.OrderBy(lib => !lib.IsFavourite);
}
}
Using the control:
<con:FilterListBox Grid.Row="1"
ItemsSource="{Binding Libraries}"
SelectedItem="{Binding SelectedLibrary}"
ItemTemplate="{StaticResource
LibraryItemTemplate}"
SearchString="{Binding LibrarySearchString, Mode=TwoWay}"
IsSearching="False"
Margin="4"/>
The control template:
<Style x:Key="{x:Type con:FilterListBox}" TargetType="{x:Type con:FilterListBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type con:FilterListBox}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<DockPanel Grid.Row="0">
<TextBlock Text="Search"
VerticalAlignment="Center"/>
<TextBox Text="{Binding RelativeSource={RelativeSource TemplatedParent},
Path=SearchString,
UpdateSourceTrigger=PropertyChanged}"
Margin="4,0,0,0"/>
</DockPanel>
<ScrollViewer Grid.Row="1" CanContentScroll="True">
<StackPanel IsItemsHost="True"
HorizontalAlignment="Stretch"/>
</ScrollViewer>
<TextBlock Grid.Row="1"
Text="Searching..."
HorizontalAlignment="Center"
VerticalAlignment="Center"
Visibility="{Binding RelativeSource={RelativeSource TemplatedParent},
Path=IsSearching,
Converter={StaticResource CollapsedIfFalseConverter}}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Thanks for any help.
The slow behavior of your FilterListBox may come with an Virtualization issue. You replaced the ItemsHost of the ListBox with a simple StackPanel. By default, the ListBox uses a VirtualizingStackPanel, which virtualizes the Items whenever possible. See the default ListBox Template as a reference. If you have a simple StackPanel as ItemsPresenter, the ListBox has to re-render every item when your filter changes. Depending on the number of items, this can cause your slow behavior. Try to use the default itemshost instead. You should also know, that virtualization is only possible with 'simple' items (same Height for every item basically).

Resources