Need help create wpf listview custom control - c#

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

Related

Make element visible when Validation.HasError is true

I have the following xaml markup:
<GroupBox Margin="10" Padding="20" Header="Case 5 - Custom Error Object">
<GroupBox.DataContext>
<local:ViewModel4/>
</GroupBox.DataContext>
<StackPanel>
<Label Name="AdornerElement" Style="{StaticResource ResourceKey=AdornerElementStyle}"/>
<TextBox Text="{Binding Path=UserName, UpdateSourceTrigger=PropertyChanged, ValidatesOnNotifyDataErrors=True}"
Margin="0 10"
Validation.ValidationAdornerSite="{Binding ElementName=AdornerElement}">
</TextBox>
</StackPanel>
</GroupBox>
and the following style:
<Style x:Key="AdornerElementStyle" TargetType="Label">
<Setter Property="Visibility" Value="Collapsed"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Label">
<StackPanel Orientation="Horizontal" Background="LightCoral">
<Image Source="/clipart.png" Width="24" Margin="10"/>
<ItemsControl ItemsSource="{Binding ElementName=AdornerElement, Path=(Validation.ValidationAdornerSiteFor).(Validation.Errors)}"
VerticalAlignment="Center">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=ErrorContent.ValidationMessage}"
Style="{StaticResource CustomErrorTypeStyle}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding ElementName=AdornerElement, Path=Validation.HasError}" Value="True">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Everything works fine except Trigger.
If I set up the property "Visibility" to "Visible" initially, then I can see that error messages are shown correctly.
If I use style shown before, then the Label remains to be collapsed.
Please, help me to use triggers correctly to achieve the final result.
This is not how Validation.ValidationAdornerSite works. This attached property just defines the element that will be adorned with the validation error template. By default this is the element that is validating, the Binding.Target to be more precise.
When you set Validation.ValidationAdornerSite the binding engine will automatically set the attached Validation.ValidationAdornerSiteFor property to reference the element that Validation.ValidationAdornerSite was originally set on.
This means that Validation.HasError and other related attached properties are always set on the Binding.Target and not on the adorner site.
That's why your triggers don't work: they are triggering on the Label instead of the TextBox (where the validation/binding error is registered).
To fix it, the DataTrigger must get the Validation.HasErrors attached property value of the Validation.ValidationAdornerSiteFor attached property, which references the original Binding.Target (the TextBox).
<Style x:Key="AdornerElementStyle"
TargetType="Label">
<Setter Property="Visibility"
Value="Collapsed" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Label">
<StackPanel Orientation="Horizontal"
Background="LightCoral">
<Image Source="/clipart.png"
Width="24"
Margin="10" />
<ItemsControl ItemsSource="{Binding ElementName=AdornerElement, Path=(Validation.ValidationAdornerSiteFor).(Validation.Errors)}"
VerticalAlignment="Center">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding ErrorContent.ValidationMessage}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding ElementName=AdornerElement, Path=(Validation.ValidationAdornerSiteFor).(Validation.HasError)}"
Value="True">
<Setter Property="Visibility"
Value="Visible" />
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Remarks
It's not clear why you are using a Label to display the error messages. You could simply define an error template, which is a ControlTemplate that is referenced by the attached Validation.ErrorTemplate property. This would simplify your code significantly.
The following example creates the exact same visual error feedback but without the hassle for an additional Label and associated triggers to manage the visibility of the error messages. All this is all handled by the WPF binding engine.
<Window>
<Window.Resources>
<!-- The Validation.Errors property is the DataContext of this template -->
<ControlTemplate x:Key="ErrorTemplate">
<StackPanel>
<Border BorderBrush="Red"
BorderThickness="1"
Background="LightCoral">
<StackPanel Orientation="Horizontal">
<Image Source="/clipart.png"
Width="24"
Margin="10" />
<ItemsControl ItemsSource="{Binding}"
VerticalAlignment="Center">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding ErrorContent.ValidationMessage}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</Border>
<Border BorderBrush="Transparent"
BorderThickness="1"
HorizontalAlignment="Left">
<!-- Placeholder for the Binding.Target -->
<AdornedElementPlaceholder x:Name="AdornedElement" />
</Border>
</StackPanel>
</ControlTemplate>
</Window.Resource>
<StackPanel>
<TextBox Text="{Binding Path=UserName, UpdateSourceTrigger=PropertyChanged, ValidatesOnNotifyDataErrors=True}"
Validation.ErrorTemplate="{StaticResoucre ErrorTemplate}" />
</StackPanel>
</Window>

Show ContextMenu of element in ListBox

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>

How to select listview item when clicking button

Inside my listview I´ve created a control template. Inside this template I made a button, so that every item inside mit listview has a button. This button represents an link which opens the directory that is shown. When I click the button, my program opens the explorer as I expect it. But when I click the button of an item that is not selected it opens the path of the selected item. So my question is, how can I change the selected item when I click the button inside my listview.
Here is how it looks like:
As you can see "R1" is selected, but I click on the link of "R3". What happens is, that C:\Temp\Folder1 gets opened, because "R1" is still the selecteditem. What I wish to is that C:\Temp\Folder3 gets opend. I think the trick should be that "R3" gets selected when clicking the button that represents the link. Does anyone know how to that?
This is my XAML-Code:
<ListView Grid.Row="1" ItemsSource="{Binding MyCollection}" FontSize="20" SelectedItem="{Binding SelectedMember, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Margin="7,10,7,0" x:Name="ListView1" SelectionChanged="ListView1_SelectionChanged">
<ListView.View>
<GridView>
<GridViewColumn Header="Header 1" Width="150"/>
<GridViewColumn Header="Header 2" Width="120"/>
<GridViewColumn Header="Header 3" Width="120"/>
<GridViewColumn Header="Header 4" Width="560"/>
<GridViewColumn Header="Header 5" Width="100"/>
</GridView>
</ListView.View>
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}" BasedOn="{StaticResource {x:Type ListBoxItem}}">
<EventSetter Event="MouseDoubleClick" Handler="ListViewItem_MouseDoubleClick"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150"/>
<ColumnDefinition Width="120"/>
<ColumnDefinition Width="120"/>
<ColumnDefinition Width="560"/>
<ColumnDefinition Width="100"/>
</Grid.ColumnDefinitions>
<ContentPresenter Grid.Column="0" Content="{Binding ID}" HorizontalAlignment="Center"/>
<ContentPresenter Grid.Column="1" Content="{Binding Name}" HorizontalAlignment="Center"/>
<ContentPresenter Grid.Column="2" Content="{Binding Description}" HorizontalAlignment="Center"/>
<Button Grid.Column="3" Content="{Binding Path}" Style="{StaticResource Link}" Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.GoToPathCommand }" HorizontalAlignment="Left" Margin="5,0,0,0" IsEnabled="{Binding ButtonEnabled}"/>
<ContentPresenter Grid.Column="4" Content="{Binding Owner}" HorizontalAlignment="Center"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="Foreground" Value="DarkBlue"/>
</Trigger>
</Style.Triggers>
</Style>
</ListView.ItemContainerStyle>
</ListView>
Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.GoToPathCommand }"
This Command will jump to the Cmd of the ViewModel. I assume that the Command will check which item is selected and it will access it's property Path.
Avoiding this behavior, You can create a Command that will take a parameter and set it from Binding:
<Button Content="{Binding Path}"
Command="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type Window}}, Path=DataContext.GoToPathCommand }"
CommandParameter="{Binding Path}"/>
Now the command itself must be adjusted:
public MyCmd : ICommand
{
public void Execute(object parameter)
{
string path = (string) parameter;
//Open the path via explorer
}
}
Try adding below trigger too in the ListViewItem Style
<Style.Triggers>
<Trigger Property="IsKeyboardFocusWithin" Value="True">
<Setter Property="IsSelected" Value="True"/>
</Trigger>
</Style.Triggers>

how to display the label which have two categorys with background and foreground

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>

DataGridColumnHeader with StackPanel, bind content of TextBlock/Label to ColumnName, dynamic grid

I have a dynamically generated DataGrid bound to a DataTable property in my ViewModel.
I have AutoGenerateColumnHeaders = true, and it's working fine. However, I'm using a DataTemplate to cover the Header with a StackPanel containing a Label and Button. I cannot seem to figure out how to bind the Label Content to the DataGridColumnHeader. I have tried with and without FindAncestor, but I believe the following is the closest to where I need to be...Question is on the Label Content="{}"
<local:UserControlViewBase.Resources>
<Style TargetType="DataGridColumnHeader">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Border BorderBrush="Black" BorderThickness="1">
<StackPanel Width="Auto" Orientation="Horizontal">
<Label Content="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type local:UserControlViewBase}},Path=DataContext.TestList.ColumnName}" Padding="12,0,12,0" HorizontalAlignment="Center" VerticalAlignment="Center" />
<Button Content="Ok" Padding="12,0,12,0" HorizontalAlignment="Center" VerticalAlignment="Center" />
</StackPanel>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</local:UserControlViewBase.Resources>
//local:UserControlViewBase is just a UserControl with some extra bells and whistles added.
I'm fairly new to WPF and I'm assuming I'm just missing something with the binding - I'm still learning.
Thanks.
This is what I did to get it to work. I had to change the findancestor to look for the DataGridColumnHeader instead of the user control. I then was able to access the Column.Header property:
<local:UserControlViewBase.Resources>
<Style TargetType="DataGridColumnHeader">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Border BorderBrush="Black" BorderThickness="1">
<StackPanel Width="Auto" Orientation="Horizontal">
<Label Width="75" Content="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type DataGridColumnHeader}},Path=Column.Header}" Padding="12,0,12,0" HorizontalAlignment="Center" VerticalAlignment="Center" />
<Button Content="Ok" Padding="12,0,12,0" HorizontalAlignment="Center" VerticalAlignment="Center" />
</StackPanel>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</local:UserControlViewBase.Resources>

Categories

Resources