ListView Column Size to Change with Content - c#

I have a ListView where some columns contains TextBox controls for the user to enter text. However, if the entered text is too long it will be limited by the current width of the column. Is there a way to automatically resize the width of the column when text is entered overflowing the current size of the column?
<ListView x:Name="List1"
Grid.Column="1"
Margin="0 0 5 20"
Background="Transparent"
Foreground="White"
BorderThickness="0"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Stretch"
ScrollViewer.VerticalScrollBarVisibility="Auto"
ScrollViewer.HorizontalScrollBarVisibility="Auto"
ItemsSource="{Binding Path=Stations}">
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}" BasedOn="{StaticResource {x:Type ListViewItem}}">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</ListView.ItemContainerStyle>
<ListView.View>
<GridView AllowsColumnReorder="False">
<!-- Location -->
<GridViewColumn Header="Location">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBox>
<TextBox.Style>
<Style TargetType="{x:Type TextBox}" BasedOn="{StaticResource {x:Type TextBox}}">
<Setter Property="Text" Value="{Binding Location, Mode=TwoWay}"/>
<Setter Property="Width" Value="Auto"/>
<Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Self}, Path=Text}"/>
</Style>
</TextBox.Style>
</TextBox>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>

You could try to handle the TextChanged event and measure the desired size of the TextBox:
private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
{
TextBox textBox = (TextBox)sender;
textBox.Measure(new Size(Double.PositiveInfinity, Double.PositiveInfinity));
if (textBox.DesiredSize.Width > col.Width)
col.Width = textBox.DesiredSize.Width;
}
XAML:
<GridViewColumn Width="100" x:Name="col">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBox TextChanged="TextBox_TextChanged">
<TextBox.Style>
<Style TargetType="{x:Type TextBox}" BasedOn="{StaticResource {x:Type TextBox}}">
<Setter Property="Text" Value="{Binding Location, Mode=TwoWay}"/>
<Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Self}, Path=Text}"/>
</Style>
</TextBox.Style>
</TextBox>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>

Related

Double left click on listview WPF

I would bind an ICommand on left double click event on item in the ListView.
I tried the solution below but doesn't work properly, the Execute function is called when I click the ListView not the item in the list. Someone has any suggestion ?
<ListView x:Name="history_list_view" HorizontalAlignment="Left" Height="210" Margin="25,194,0,0"
VerticalAlignment="Top" Width="656" Background="#FF2F2B2B" Foreground="White"
ItemsSource="{Binding Items}"
SelectedItem="{Binding SelectedItem}">
<ListView.InputBindings >
<MouseBinding Gesture="LeftDoubleClick" Command="{Binding SelectedItemCommand}" CommandParameter="{Binding SelectedItem}"/>
</ListView.InputBindings>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#7f8c8d"/>
<Setter Property="BorderThickness" Value="0" />
</Trigger>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="#7f8c8d"/>
<Setter Property="BorderThickness" Value="0" />
</Trigger>
</Style.Triggers>
</Style>
</ListView.ItemContainerStyle>
<ListView.View>
<GridView >
<GridView.ColumnHeaderContainerStyle>
<Style TargetType="{x:Type GridViewColumnHeader}">
<Setter Property="HorizontalContentAlignment" Value="Left" />
</Style>
</GridView.ColumnHeaderContainerStyle>
<GridViewColumn Header="Lot" Width="170" DisplayMemberBinding="{Binding Lot}"/>
<GridViewColumn Header="Code" Width="160" DisplayMemberBinding="{Binding Code}"/>
<GridViewColumn Header="Rev" Width="80" DisplayMemberBinding="{Binding Rev}"/>
<GridViewColumn Header="User" Width="140" DisplayMemberBinding="{Binding User}"/>
<GridViewColumn Header="Date" Width="100" DisplayMemberBinding="{Binding Date}"/>
</GridView>
</ListView.View>
</ListView>
Either add an EventSetter for the MouseDoubleClick event in the ItemContainerStyle and invoke the command from the event handler in the code-behind, or use an attached behaviour to execute the command:
public static class DoubleClickBehavior
{
public static ICommand GetCommand(ListViewItem obj) => (ICommand)obj.GetValue(CommandProperty);
public static void SetCommand(ListViewItem obj, ICommand value) => obj.SetValue(CommandProperty, value);
public static readonly DependencyProperty CommandProperty =
DependencyProperty.RegisterAttached("Command", typeof(ICommand), typeof(DoubleClickBehavior), new PropertyMetadata(null, OnCommandChanged));
private static void OnCommandChanged(object sender, DependencyPropertyChangedEventArgs e)
{
ListViewItem lvi = (ListViewItem)sender;
lvi.MouseDoubleClick += Lvi_MouseDoubleClick;
}
private static void Lvi_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
ListViewItem lvi = (ListViewItem)sender;
ICommand command = GetCommand(lvi);
if (command != null)
command.Execute(null);
}
}
XAML:
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="local:DoubleClickBehavior.Command"
Value="{Binding DataContext.SelectedItemCommand, RelativeSource={RelativeSource AncestorType=ListView}}" />
</Style>
</ListView.ItemContainerStyle>
Another simple solution is to override the ControlTemplate of the ListBoxItem to set the InputBindings on it:
<ListView>
<ListView.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<Border.InputBindings>
<MouseBinding MouseAction="{x:Static MouseAction.LeftDoubleClick}"
Command="{Binding RelativeSource={RelativeSource AncestorType=ListView}, Path=DataContext.SelectPageCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=ListView}, Path=SelectedItem}" />
</Border.InputBindings>
<ContentPresenter />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListView.ItemContainerStyle>
</ListView>
Alternatively turn the complete item into a Button:
<ListView>
<ListView.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Button Content="{TemplateBinding Content}"
Command="{Binding RelativeSource={RelativeSource AncestorType=ListView}, Path=DataContext.SelectedItemCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=ListView}, Path=SelectedItem}" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListView.ItemContainerStyle>
</ListView>
You surely want to style the items to remove the button look.

How to bind text alignment in GridViewColumn

I'm using MVVM in a WPF application. I've a UserControl whose DataContext is set to a ViewModel. The UserControl is a table with 3 columns.
This is the following ListView/GridView:
<DockPanel>
<ListView DockPanel.Dock="Top" ItemsSource="{Binding Items}" ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ctrl:ListViewLayoutManager.Enabled="True">
<ListView.View>
<GridView>
<GridViewColumn Header="{Binding ColumnHeader1}"
CellTemplate="{StaticResource cellTemplateColumn1}">
<GridViewColumn.HeaderContainerStyle>
<Style BasedOn="{StaticResource {x:Type GridViewColumnHeader}}" TargetType="GridViewColumnHeader">
<Setter Property="TextBlock.TextAlignment" Value="{Binding Column1HAlignment}" />
</Style>
</GridViewColumn.HeaderContainerStyle>
</GridViewColumn>
<GridViewColumn Header="{Binding ColumnHeader2}"
CellTemplate="{StaticResource cellTemplateColumn2}">
<GridViewColumn.HeaderContainerStyle>
<Style BasedOn="{StaticResource {x:Type GridViewColumnHeader}}" TargetType="GridViewColumnHeader">
<Setter Property="TextBlock.TextAlignment" Value="{Binding Column2HAlignment}" />
</Style>
</GridViewColumn.HeaderContainerStyle>
</GridViewColumn>
<GridViewColumn Header="{Binding ColumnHeader3}"
CellTemplate="{StaticResource cellTemplateColumn3}"
ctrl:RangeColumn.IsFillColumn="True">
<GridViewColumn.HeaderContainerStyle>
<Style BasedOn="{StaticResource {x:Type GridViewColumnHeader}}" TargetType="GridViewColumnHeader">
<Setter Property="TextBlock.TextAlignment" Value="{Binding Column3HAlignment}" />
</Style>
</GridViewColumn.HeaderContainerStyle>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
</DockPanel>
This is achieved by the following resources:
<Style TargetType="GridViewColumnHeader">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="GridViewColumnHeader">
<TextBlock Text="{TemplateBinding Content}" Margin="5" Width="{TemplateBinding Width}">
</TextBlock>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="FontWeight" Value="Bold" />
<Setter Property="FontSize" Value="14" />
</Style>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="VerticalAlignment" Value="Stretch" />
<Setter Property="VerticalContentAlignment" Value="Center" />
</Style>
<DataTemplate x:Key="cellTemplateColumn1">
<fa:ImageAwesome Icon="{Binding IconType}" Foreground="{Binding Status}" Width="10" />
</DataTemplate>
<DataTemplate x:Key="cellTemplateColumn2">
<TextBlock Text="{Binding SerialNumber, StringFormat='0'}" />
</DataTemplate>
<DataTemplate x:Key="cellTemplateColumn3">
<TextBlock Text="{Binding Action}" TextWrapping="Wrap" />
</DataTemplate>
The text alignments of each column is settable through the view model. I can do this easily for the headers:
<GridViewColumn.HeaderContainerStyle>
<Style BasedOn="{StaticResource {x:Type GridViewColumnHeader}}" TargetType="GridViewColumnHeader">
<Setter Property="TextBlock.TextAlignment" Value="{Binding Column1HAlignment}" />
</Style>
</GridViewColumn.HeaderContainerStyle>
But how can I pass the same parameter (Column1HAlignment) to align the cell's TextBlock contents?
Note the alignment of the 2nd column (where header is #) in the image above. The header is right aligned through data binding. I want the same binding for each cell content.
You could bind the HorizontalAlignment property of the element in the CellTemplate to the Column1HAlignment source property using a {RelativeSource}:
<DataTemplate x:Key="cellTemplateColumn2">
<TextBlock Text="{Binding SerialNumber, StringFormat='0'}"
HorizontalAlignment="{Binding DataContext.Column1HAlignment,
RelativeSource={RelativeSource AncestorType=ListView}}"/>
</DataTemplate>

ListBoxItem ContextMenu only when ListBox Multiple Items Selected

How can I disable the listboxitem context menu when none or only one item is selected?
ListBox has a SelectedItems property, but it is read only and you cannot bind to it.
<ListBox ItemsSource="{Binding Items}" SelectionMode="Extended">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu>
<MenuItem Header="GOGO" />
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}" HorizontalAlignment="Center" VerticalAlignment="Center" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
This should work:
<ListBox ItemsSource="{Binding Items}" SelectionMode="Extended">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu>
<MenuItem Header="GOGO" />
</ContextMenu>
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding SelectedItems.Count, RelativeSource={RelativeSource AncestorType={x:Type ListBox}}}" Value="0">
<Setter Property="ContextMenu" Value="{x:Null}" />
</DataTrigger>
<DataTrigger Binding="{Binding SelectedItems.Count, RelativeSource={RelativeSource AncestorType={x:Type ListBox}}}" Value="1">
<Setter Property="ContextMenu" Value="{x:Null}" />
</DataTrigger>
</Style.Triggers>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}" HorizontalAlignment="Center" VerticalAlignment="Center" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Adding two DataTriggers checking whether the SelectedItems.Count is 0 or 1 in which case it sets the ContextMenu to {x:Null}.

Context menu on Treeview

How can I have a context menu in a DataTrigger on treeview? The code below does not trigger the context menu eg I want the menu on "Symbols" as well. Although I have a context menu on HierarchicalDataTemplate which works fine but only on child elements. The root on the treeview does not have a menu
<HierarchicalDataTemplate x:Key="NameTemplate" ItemsSource="{Binding Path=ChildPlanner}">
<HierarchicalDataTemplate.ItemContainerStyle>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding IsFolder}" Value="True">
<Setter Property="TreeViewItem.ContextMenu" Value="{StaticResource AddNewSymbol}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</HierarchicalDataTemplate.ItemContainerStyle>
<StackPanel Orientation="Horizontal" Margin="2">
<TextBlock Text="{Binding Path=Name}" FontWeight="Bold">
</TextBlock>
</StackPanel>
</HierarchicalDataTemplate>
<TreeView Name="SymbolsTreeView">
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsFolder}" Value="True">
<Setter Property="ContextMenu" Value="{StaticResource AddNewSymbol}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TreeView.ItemContainerStyle>
<TreeViewItem Header="Symbols" IsExpanded="True" ItemsSource="{Binding PlannerTreeList}" ItemTemplate="{StaticResource NameTemplate}"/>
</TreeView>
Imagine my tree is
Symbols
Current
Menu1Folder
Menu2Folder
Menu2Item
Menu2AnotherItem
Current1Item
The HierarchicalDataTemplate's menu works for menu1folder onwards which is ok. But I want it to work for Current1Item, Current and Symbols. Since Current1Item is not a folder, there should be no menu for it but Current and Symbols are folders
<TreeView.Resources>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="ContextMenu" Value="{StaticResource AddNewSymbol}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsFolder,RelativeSource={RelativeSource Self}}" Value="False">
<Setter Property="ContextMenu" Value="{x:Null}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TreeView.Resources>
Edit - Try this new code. I am using a converter to show and hide the contextmenu based on your property. It works with my sample code. Let me know if you want my sample code.
<Grid>
<Grid.Resources>
<BooleanToVisibilityConverter x:Key="VisibilityConverter" />
<ContextMenu x:Key="MenuOne" Visibility="{Binding IsFolder,Converter={StaticResource VisibilityConverter}}">
<MenuItem Header="Add Folder" Command="{Binding AddFolderCommand}"/>
<MenuItem Header="Add Item" Command="{Binding AddItemCommand}"/>
</ContextMenu>
</Grid.Resources>
<TreeView Name="SymbolsTreeView" ItemsSource="{Binding Items}">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type local:MyTreeViewItem}" ItemsSource="{Binding Items}">
<ContentControl>
<TextBlock Text="{Binding Name}"/>
</ContentControl>
</HierarchicalDataTemplate>
<Style TargetType="TreeViewItem">
<Setter Property="ContextMenu" Value="{StaticResource MenuOne}"/>
<Setter Property="IsExpanded" Value="True"/>
</Style>
</TreeView.Resources>
</TreeView>
</Grid>

Border not lining up on a TextBlock

I am working on a chat program, and I would like to have a single line separate every line of text.
Here is the XAML I am using:
<ScrollViewer Grid.Row="0" Name="ScrollBar">
<ListView HorizontalContentAlignment="Stretch"
BorderThickness="0"
IsTabStop="False"
Name="AllItems"
Background="#E4E4E4"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
PreviewMouseWheel="AllItems_PreviewMouseWheel"
ItemsSource="{Binding Messages}">
<ListView.ItemTemplate>
<DataTemplate>
<Border BorderBrush="Black"
BorderThickness="0, 1, 0, 0">
<TextBlock
FontSize="16"
TextWrapping="Wrap"
FontFamily="Arial"
Margin="0"
Initialized="ChatLine_Initialized"
VerticalAlignment="Center" />
</Border>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ScrollViewer>
I'm also setting the background of each line in alternating colors (this is an event handler for TextBlock's Initialize event):
static int s_curr;
void ChatLine_Initialized(object sender, EventArgs e)
{
int curr = Interlocked.Increment(ref s_curr);
if ((curr % 2) == 0)
((TextBlock)sender).Background = (SolidColorBrush)(new BrushConverter().ConvertFrom("#C8C8C8"));
else
((TextBlock)sender).Background = (SolidColorBrush)(new BrushConverter().ConvertFrom("#E4E4E4"));
}
The problem I'm running into is that the darker lines do not fill down to the border.
For example, here is how it renders:
How do I get the darker background to fill down to the border line?
Remove the border of your textblock and apply a style for the ListBox Items, this works,
<Grid>
<ScrollViewer Grid.Row="0" Name="ScrollBar">
<ListView HorizontalContentAlignment="Stretch"
BorderThickness="0"
IsTabStop="False"
Name="AllItems"
Background="#E4E4E4"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ItemsSource="{Binding Messages}">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListViewItem">
<Border Name="Border" BorderBrush="Black" BorderThickness="1">
<ContentPresenter />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock
FontSize="16"
TextWrapping="Wrap"
FontFamily="Arial"
Text="{Binding Name}"
Initialized="ChatLine_Initialized"
/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ScrollViewer>
</Grid>
Output:
There's no need to create a ListView wrapped in a ScrollViewer and then set each alternate row a different color. A DataGrid has all this functionality built in.
This can be tweaked, but is styled to represent your screen-shot:
<DataGrid HorizontalContentAlignment="Stretch"
BorderThickness="0"
IsTabStop="False"
Name="AllItems"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ItemsSource="{Binding Messages}"
AlternationCount="2"
HeadersVisibility="None">
<DataGrid.CellStyle>
<Style TargetType="{x:Type DataGridCell}">
<Setter Property="Foreground" Value="Black" />
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="{x:Null}" />
<Setter Property="BorderBrush" Value="{x:Null}" />
</Trigger>
</Style.Triggers>
</Style>
</DataGrid.CellStyle>
<DataGrid.RowStyle>
<Style TargetType="{x:Type DataGridRow}">
<Style.Triggers>
<Trigger Property="AlternationIndex" Value="0">
<Setter Property="Background" Value="#E4E4E4" />
</Trigger>
<Trigger Property="AlternationIndex" Value="1">
<Setter Property="Background" Value="#C8C8C8" />
</Trigger>
</Style.Triggers>
</Style>
</DataGrid.RowStyle>
</DataGrid>

Categories

Resources