Problem:
I have a ListBox in which I display my data using DataBinding. The data is displayed as a custom view SystemEnvironmentElementView.
I want to allow the user to rename single elements. This should happen in the ViewModel of the desired element.
I created a ContextMenu in the View that displays my data. However when I right click on my element the ContextMenu is not displayed. I also encountered the problem that on my PreviewMouseDown event handler I don't get the view as OriginalSource in the EventArgs but a Border.
Question:
How do I get the ContextMenu of my element to show?
ListBox Code:
<customControls:ListBoxNoDragSelection ItemsSource="{Binding Elements}" Background="{DynamicResource BG}" SelectedItem="{Binding SystemEnviromentAnalysis.SelectedElement}"
BorderThickness="0" x:Name="ListBoxNoDragSelection" SelectedValue="{Binding SelectedElement}">
<i:Interaction.Behaviors>
<dragAndDrop:FrameworkElementDropBehaviorWithMousePos />
</i:Interaction.Behaviors>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<Canvas Background="{DynamicResource MyVisualBrush}"
Height="Auto" Width="Auto" PreviewMouseLeftButtonDown="UIElement_OnPreviewMouseLeftButtonDown"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="Canvas.Left" Value="{Binding Element.X}"/>
<Setter Property="Canvas.Top" Value="{Binding Element.Y}"/>
<Setter Property="Canvas.ZIndex" Value="{Binding Element.Z}"/>
<Setter Property="Height" Value="Auto"/>
<Setter Property="Width" Value="Auto"/>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<systemEnvironment:SystemEnvironmentElementView/>
</DataTemplate>
</ListBox.ItemTemplate>
</customControls:ListBoxNoDragSelection>
SystemEnviromentElementView:
<UserControl x:Class="TestApp.Editor.Views.SystemEnvironment.SystemEnvironmentElementView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Height="Auto" Width="Auto">
<UserControl.ContextMenu>
<ContextMenu>
<MenuItem Header="Rename"/>
</ContextMenu>
</UserControl.ContextMenu>
<StackPanel>
<Image Height="{Binding Element.Height}" Width="{Binding Element.Width}" Grid.Row="0" Source="{Binding Element.Icon}" IsHitTestVisible="False"/>
<Label Height="Auto" Width="Auto" Grid.Row="1" HorizontalAlignment="Center" VerticalAlignment="Center" IsHitTestVisible="False"
Content="{Binding Element.Name}"/>
</StackPanel>
Related
I want to create a WPF custom control inherit ListView. I know how to build the listview in a normal WPF window but in custom control's Generic.xaml I don't know how to do it.
Can you help me transform the listview below into custom control's Generic.xaml format please?
Thanks.
<ListView x:Name="listView"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ScrollViewer.VerticalScrollBarVisibility="Auto"
SelectionMode="Single"
ItemContainerStyle="{DynamicResource MyListViewItemContainerStyle}"
ItemsSource="{TemplateBinding ItemsSource}" >
<ListView.Resources>
<DataTemplate x:Key="FirstColumnDataTemplate" >
<Border BorderBrush="#FFABADB3" BorderThickness="1,0,1,1" Margin="-6,0,-6,0">
<Grid Margin="6,2,6,2">
<ContentPresenter ContentTemplate="{Binding FirstColumnItemTemplate, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=local:CustomControl1}}"
Focusable="False"
RecognizesAccessKey="True"/>
</Grid>
</Border>
</DataTemplate>
<DataTemplate x:Key="SecondColumnDataTemplate">
<Border BorderBrush="#FFABADB3" BorderThickness="0,0,1,1" Margin="-6,0,-6,0">
<Grid Margin="6,2,6,2">
<ContentPresenter ContentTemplate="{Binding SecondColumnItemTemplate, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=local:CustomControl1}}"
Focusable="False"
RecognizesAccessKey="True"/>
</Grid>
</Border>
</DataTemplate>
<Style x:Key="MyListViewItemContainerStyle" TargetType="{x:Type ListViewItem}">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="VerticalContentAlignment" Value="Stretch" />
<Setter Property="Margin" Value="0,-4,0,0"/>
</Style>
</ListView.Resources>
<ListView.View>
<GridView AllowsColumnReorder="False">
<GridViewColumn x:Name="FirstColumn"
Header="{TemplateBinding FirstColumnHeader}"
CellTemplate="{DynamicResource FirstColumnDataTemplate}"/>
<GridViewColumn x:Name="SecondColumn"
Header="{TemplateBinding SecondColumnHeader}"
CellTemplate="{DynamicResource SecondColumnDataTemplate}"/>
</GridView>
</ListView.View>
</ListView>
FirstColumnHeader, SecondColumnHeader are string dependency properties. FirstColumnItemTemplate, SecondColumnItemTemplate are DataTemplate dependency properties of the custom control.
The custom control itself inherit ListView like:
public class CustomControl1 : ListView
I need some advice. We noticed unusual behavior within the ScrollViewer. I have a StackPanel with whom I am more items including ListBox when the StackPanel placed in a ScrollViewer, when loading data to the listbox, a program for a brief moment freezes. When I am alone ListBox but everything works normally, no freezing of the program.
Here is my code:
<ScrollViewer VerticalScrollBarVisibility="Auto">
<StackPanel x:Name="tStack" >
<Grid Height="300">
</Grid>
<Grid Height="300">
</Grid>
<ListBox x:Name="ListBox1" ItemsSource="{Binding AlbumsCvs.View, IsAsync=True}"
Style="{StaticResource ListBoxAlbumsTracksStyles}"
VirtualizingStackPanel.IsVirtualizing="True"
VirtualizingPanel.IsVirtualizingWhenGrouping="True"
VirtualizingStackPanel.VirtualizationMode="Recycling" >
<ListBox.GroupStyle>
<GroupStyle ContainerStyle="{StaticResource AlbumsHeader}" />
</ListBox.GroupStyle>
</ListBox>
</StackPanel>
</ScrollViewer>
<Style x:Key="ListBoxAlbumsTracksStyles" TargetType="{x:Type ListBox}">
<Setter Property="Padding" Value="0,0,0,0" />
<Setter Property="Margin" Value="0,0,0,0" />
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="Background" Value="Transparent"/>
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto" />
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Hidden" />
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate >
<DockPanel>
<Border Background="#00000000"
Height="36"
Width="{Binding Path=ActualWidth, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ScrollContentPresenter}}}">
<DockPanel>
<TextBlock x:Name="TrackNumber"
DockPanel.Dock="Left" Margin="2,0,5,0"
Text="{Binding TrackNumber}"
VerticalAlignment="Center"
FontSize="13"
MinWidth="17"
Foreground="Black"/>
<DockPanel>
<TextBlock DockPanel.Dock="Left"
Text="{Binding TrackTitle}"
TextAlignment="Left"
FontSize="13"
VerticalAlignment="Center"
HorizontalAlignment="Stretch"
TextTrimming="CharacterEllipsis"
Margin="0,0,2,0"/>
<TextBlock DockPanel.Dock="Right"
Text="{Binding Duration}"
VerticalAlignment="Center"
HorizontalAlignment="Stretch"
TextTrimming="CharacterEllipsis"
Margin="0,0,10,0"
FontSize="13" TextAlignment="Right"/>
</DockPanel>
</DockPanel>
</Border>
</DockPanel>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- GroupItem -->
<Style x:Key="AlbumsHeader" TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Border Width="{Binding Path=ActualWidth, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ScrollContentPresenter}}}" Background="#00000000">
<StackPanel Margin="0,0,0,15">
<StackPanel>
<TextBlock Text="{Binding AlbumName}"
DataContext="{Binding Items}"
Margin="0,5,0,0"
HorizontalAlignment="Stretch"
FontSize="20"
FontWeight="Light"
TextTrimming="CharacterEllipsis"
Foreground="Black"/>
<TextBlock Text="{Binding IdAlbum}"
DataContext="{Binding Items}"
Margin="0,0,0,10"
HorizontalAlignment="Stretch"
TextTrimming="CharacterEllipsis"
Foreground="Black"/>
</StackPanel>
<ItemsPresenter HorizontalAlignment="Stretch"/>
</StackPanel>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
code behind:
private async Task AlbumsArtistInformation()
{
if (string.IsNullOrEmpty(ArtistName))
return;
ObservableCollection<AlbumsArtistCollections> _albumsArtistCollections =
new ObservableCollection<AlbumsArtistCollections>();
try
{
var search = await spotifyDataService.GetArtists(ArtistName);
if (search == null) throw new ArgumentNullException(nameof(search));
foreach (var _artist in search.Artists.Items.Take(1))
{
this.IdArtist = _artist.Id;
}
var _artistAlbum = await spotifyDataService.GetArtistsAlbumsAsync(this.IdArtist, AlbumType.All);
if (_artistAlbum == null) throw new ArgumentNullException(nameof(_artistAlbum));
_albumsArtistCollections = _artistAlbum;
}
finally
{
// Unbind to improve UI performance
Application.Current.Dispatcher.Invoke(() =>
{
this.Albums = null;
this.AlbumsCvs = null;
});
Application.Current.Dispatcher.Invoke(() =>
{
this.Albums = _albumsArtistCollections;
});
Application.Current.Dispatcher.Invoke(() =>
{
// Populate CollectionViewSource
this.AlbumsCvs = new CollectionViewSource { Source = this.Albums };
//Group by Album if needed
this.AlbumsCvs.GroupDescriptions.Add(new PropertyGroupDescription("IdAlbum"));
});
}
}
Does anyone know how to solve this problem.
Vertically oriented StackPanel provides an unbounded available space for the the ListBox, when it calls MeasureOverride(Size availableSize) method, during layout. Therefore, the ListBox (which by default uses virtualization) should create the whole items and this is why you program freezes for a moment.
Therefore, use a DockPanel instead:
<DockPanel x:Name="tStack" LastChildFill="True" >
<Grid DockPanel.Dock="Top" Height="300">
</Grid>
<Grid DockPanel.Dock="Top" Height="300">
</Grid>
<ListBox DockPanel.Dock="Bottom" x:Name="ListBox1" ItemsSource="{Binding AlbumsCvs.View, IsAsync=True}"
Style="{StaticResource ListBoxAlbumsTracksStyles}"
VirtualizingStackPanel.IsVirtualizing="True"
VirtualizingPanel.IsVirtualizingWhenGrouping="True"
VirtualizingStackPanel.VirtualizationMode="Recycling" >
<ListBox.GroupStyle>
<GroupStyle ContainerStyle="{StaticResource AlbumsHeader}" />
</ListBox.GroupStyle>
</ListBox>
</DockPanel>
LastChildFill is true by default. The ListBox should be the last element, in order to fill the space.
As another option, you can set the Height of the ListBox and put the DockPanel in a ScrollViewer or you can consider a Grid with splitters as another option.
I'm using MahApps.Metro 1.3.0 and have a problem with a smaller clickable area of controls at the edge of the window, but if I move them away from the edge of the window the clickable area goes back to normal.
Is there any way to fix this?
My scrollviewer xaml:
<ScrollViewer x:Name="ScrollViewer" VerticalScrollBarVisibility="Visible" HorizontalScrollBarVisibility="Auto">
<ItemsControl x:Name="DocumentList" ItemsSource="{Binding Source={StaticResource Documents}}" BorderBrush="{x:Null}" ScrollViewer.CanContentScroll="False" Margin="0,0,0,1">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock>
<Hyperlink Command="{Binding DataContext.OpenCommand, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}" CommandParameter="{Binding UNID}">
<InlineUIContainer>
<TextBlock Text="{Binding DisplayName}" />
</InlineUIContainer>
</Hyperlink>
</TextBlock>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Expander Header="{Binding Name}" IsExpanded="False">
<ItemsPresenter />
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</ItemsControl.GroupStyle>
</ItemsControl>
</ScrollViewer>
And my window xaml:
<Controls:MetroWindow x:Class="MainWindow"
Name="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:data="clr-namespace:System.Windows.Data;assembly=PresentationFramework"
xmlns:scm="clr-namespace:System.ComponentModel;assembly=WindowsBase"
xmlns:Controls="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"
BorderBrush="{DynamicResource AccentColorBrush}" BorderThickness="1 0 1 1"
Title="" Height="318" Width="526"
ShowIconOnTitleBar="False"
ResizeMode="NoResize"
Cursor="{Binding Cursor}"
NonActiveWindowTitleBrush="{StaticResource InactiveBlueColorBrush}">
The design image is
This codes is my wrote,but the background is larger then my expection,i hope the data change when i click one category,the selected label highlight
my codes is
<DataTemplate x:Key="varietyListViewTemplate" x:DataType="local:Category" >
<TextBlock Text="{x:Bind title}" FontSize="12" HorizontalAlignment="Center" TextWrapping="Wrap" VerticalAlignment="Center" />
</DataTemplate>
<Style x:Key="varietyListViewStyle" TargetType="ListView">
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="varietyListViewItemStyle" TargetType="ListViewItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListViewItem">
<ListViewItemPresenter
Foreground="#979797"
Background="#ffffff"
SelectedForeground="#ffffff"
SelectedBackground="#fd8c00"
PressedBackground="#fd8c00"
SelectedPressedBackground="#fd8c00"
SelectedPointerOverBackground="#fd8c00"
Margin="2"
/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- PointerOverBackground="#fd8c00"
SelectedForeground="#ffffff"
PointerOverForeground="#ffffff" -->
</Page.Resources>
<Grid>
<Grid Background="#FFFFFF">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<ListView x:Name="varietyListView" Grid.Row="0"
ItemsSource="{x:Bind Path=Categorys}" ItemTemplate="{StaticResource varietyListViewTemplate}"
IsItemClickEnabled="True" SelectionMode="Single" Style="{StaticResource varietyListViewStyle}" ItemContainerStyle="{StaticResource varietyListViewItemStyle}"
ItemClick="varietyListView_ItemClick" >
</ListView>
<ListView x:Name="varietyListView2" Grid.Row="0"
ItemsSource="{x:Bind Path=Categorys}" ItemTemplate="{StaticResource varietyListViewTemplate}"
IsItemClickEnabled="True" SelectionMode="Single" Style="{StaticResource varietyListViewStyle}" ItemContainerStyle="{StaticResource varietyListViewItemStyle}"
ItemClick="varietyListView2_ItemClick" Margin="0,0,0,50">
</ListView>
<Frame x:Name="varietyFrame" Grid.Row="1"/>
</Grid>
<Grid Name="listImage">
</Grid>
</Grid>
That is because the ListViewItem’s size is larger than actual size of the text. By checking the default template of ListViewItem, you will find the MinWidth and MinHeight properties are specified as following.
<Setter Property="MinWidth" Value="{ThemeResource ListViewItemMinWidth}"/>
<Setter Property="MinHeight" Value="{ThemeResource ListViewItemMinHeight}"/>
<x:Double x:Key="ListViewItemMinWidth">88</x:Double>
<x:Double x:Key="ListViewItemMinHeight">44</x:Double>
To find the default template, checking ListViewItem styles and templates or in generic.xaml (typical in C:\Program Files (x86)\Windows Kits\10\DesignTime\CommonConfiguration\Neutral\UAP\10.0.10240.0\Generic) by searching "Default style for Windows.UI.Xaml.Controls.ListViewItem".
To work around this issue, we can try to make the ListViewItem has same size as the actual text by setting ListViewItem's MinWidth and MinHeight to "0" and use Margin to control the spacing between them. So apply the following code should get what you want:
<Style x:Key="varietyListViewItemStyle" TargetType="ListViewItem">
<Setter Property="MinWidth" Value="0" />
<Setter Property="MinHeight" Value="0" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListViewItem">
<ListViewItemPresenter
Foreground="#979797"
Background="#ffffff"
SelectedForeground="#ffffff"
SelectedBackground="#fd8c00"
PressedBackground="#fd8c00"
SelectedPressedBackground="#fd8c00"
SelectedPointerOverBackground="#fd8c00"
Margin="20"
/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I recommend that you nest Grid to ListviewItem, and then you can change the backgroud of the listitems or just the background of the Grid, and of course you can shape the size of this Grid.
For example:
<ListView x:Name="varietyListView" Grid.Row="0" >
<ListView.ItemTemplate >
<DataTemplate>
<Grid>
<TextBlock Text="{Binding ...}"/>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
To summarize before posting my XAML, this form is very simple for now. There is a ListBox, two buttons and a context menu for the ListBox.
If the right mouse button is clicked in the ListBox, and NO ELEMENT is selected, my context menu, performs the ADD operation, which right now is just simply popping up a message box.
When an element is selected, such as MODIFY, my bindings do not work. So I am assuming, after much reading, that I have an inheritance problem somewhere. I have tried using DataContext, RelativeSource, etc...and still no joy.
Here is my XAML
<Window x:Class="FracasReportSettings.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ignore="http://www.ignore.com"
mc:Ignorable="d ignore"
Height="402"
Width="578"
Title="FRACAS Ticket Value Modification"
DataContext="{Binding Main, Source={StaticResource Locator}}">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Skins/MainSkin.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Grid x:Name="LayoutRoot">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="91*"/>
<ColumnDefinition Width="194*"/>
</Grid.ColumnDefinitions>
<TextBlock FontSize="36"
FontWeight="Bold"
Foreground="Purple"
Text="{Binding PageTitle}"
VerticalAlignment="Center"
HorizontalAlignment="Center"
TextWrapping="Wrap" Margin="266,27,10,9" Width="112" Grid.Column="1" />
<StackPanel HorizontalAlignment="Left" Height="311" Margin="40,22,0,0" VerticalAlignment="Top" Width="111">
<Button Content="Detection Method" Margin="10,10,10,10"
Command="{Binding MyBinding}"
CommandParameter="DetectionMethod"/>
<Button Content="Button" Margin="10,0,10,0"/>
</StackPanel>
<ListBox
Name="AdminList"
ItemsSource="{Binding Names}"
Height="302" Width="231" Margin="151,22,0,0"
HorizontalAlignment="Left" VerticalAlignment="Top" Grid.ColumnSpan="2"
SelectedItem="{Binding SomeName}"
>
<ListBox.Resources>
<Style TargetType="ListBoxItem">
<Style.Triggers>
<Trigger Property="IsKeyboardFocusWithin" Value="True">
<Setter Property="IsSelected" Value="True"/>
</Trigger>
</Style.Triggers>
</Style>
</ListBox.Resources>
**<-- THIS WORKS FINE -->
<ListBox.ContextMenu>
<ContextMenu>
<MenuItem Header="Add"
Command="{Binding RCContentMenu}">
</MenuItem>
</ContextMenu>
</ListBox.ContextMenu>**
<--MY ERROR IS IN HERE-->
**<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu DataContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBox}}, Path=DataContext}">
<MenuItem Header="Modify"
Command="{Binding Path=ModifyElementCommand}"
CommandParameter="{Binding Path=SelectedItem}"/>
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>**
</ListBox>
</Grid>
The ModifyElementCommand is sitting in a ViewModel named MainViewModel.
The error I receive from the output window is:
System.Windows.Data Error: 4 : Cannot find source for binding with
reference 'RelativeSource FindAncestor,
AncestorType='System.Windows.Controls.ListBox', AncestorLevel='1''.
BindingExpression:Path=DataContext; DataItem=null; target element is
'ContextMenu' (Name=''); target property is 'DataContext' (type
'Object')
Which I know means that the dependency cannot be found.
What should I do to fix this?
Following my comment here is the XAML:
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu DataContext="{Binding RelativeSource={RelativeSource Self},Path=PlacementTarget.Tag.DataContext}">
<MenuItem Header="Modify"
Command="{Binding Path=ModifyElementCommand}"
CommandParameter="{Binding Path=SelectedItem}"/>
</ContextMenu>
</Setter.Value>
</Setter>
<Setter Property="Tag" Value="{Binding ElementName=AdminList}" />
</Style>
</ListBox.ItemContainerStyle>