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>
Related
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>
I have a grid view with 4 columns.
<ListView Grid.Row="0" Name="CallHistory" ItemsSource="{Binding CallDetails}" ItemContainerStyle="{DynamicResource ListViewItemStyle}" SelectionChanged="CallHistory_OnSelectionChanged" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="#FF474747" Foreground="White">
<ListView.View>
<GridView ColumnHeaderContainerStyle="{DynamicResource ListColumnHeaderStyle}">
<GridViewColumn Width="150" Header="Year" DisplayMemberBinding="{Binding Year}" />
<GridViewColumn Width="85" Header="Time(sec)" DisplayMemberBinding="{Binding Duration.TotalSeconds, StringFormat={}{0:F2}}" />
<GridViewColumn Width="200" Header="Caller" DisplayMemberBinding="{Binding CallerName}"/>
<GridViewColumn Width="Auto" Header="Receipient" DisplayMemberBinding="{Binding ReceipientName}" />
</GridView>
</ListView.View>
</ListView>
Added a style to it in App.xaml
<Style TargetType="{x:Type GridViewColumnHeader}" x:Key="ListColumnHeaderStyle">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GridViewColumnHeader}">
<Border BorderThickness="0,0,0,1" BorderBrush="WhiteSmoke" Background="Transparent">
<TextBlock x:Name="ContentHeader" Text="{TemplateBinding Content}" Padding="5,5,5,0" Width="{TemplateBinding Width}" TextAlignment="Left" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="OverridesDefaultStyle" Value="True" />
<Setter Property="Foreground" Value="White" />
<Setter Property="FontFamily" Value="Segoe UI" />
<Setter Property="FontSize" Value="12" />
</Style>
With the above code, the user won't be able to resize the columns anymore. If I comment the "Template" and "OverridesDefaultStyle" setters in the style, I will get the non-styled GridView which I can resize. I want to keep the style along with column resize feature. Please let me know what I need to do for it.
You forgot to add the PART_HeaderGripper when you overrode the GridViewColumnHeader's ControlTemplate:
<Style TargetType="{x:Type GridViewColumnHeader}" x:Key="ListColumnHeaderStyle">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GridViewColumnHeader}">
<Grid>
<Border BorderThickness="0,0,0,1" BorderBrush="WhiteSmoke" Background="Transparent">
<TextBlock x:Name="ContentHeader" Text="{TemplateBinding Content}" Padding="5,5,5,0" Width="{TemplateBinding Width}" TextAlignment="Left" />
</Border>
<Thumb x:Name="PART_HeaderGripper"
HorizontalAlignment="Right"
Margin="0"
/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="OverridesDefaultStyle" Value="True" />
<Setter Property="Foreground" Value="White" />
<Setter Property="FontFamily" Value="Segoe UI" />
<Setter Property="FontSize" Value="12" />
</Style>
Full ListView's Style and Template for reference
Another WPF DataGrid-related question.
I have a DataGrid whose rows are grouped, and each group is underneath an Expander control. There are two columns per row. By default, I have all the expanders opened. The second column's width is set to "*", and row headers have already been turned off.
When I close all the expanders, the layout changes the tiniest bit, shifting the expander controls to the left a little. When I open any one of the expanders, they shift the tiniest bit to the right. I also notice that when an expander is open, the column headers show an additional column on the right side, which makes the horizontal scroll bar appear.
How can I adjust the datagrid declaration to make this additional column not exist/appear when an expander is open?
<Grid>
<ScrollViewer VerticalScrollBarVisibility="Auto"
HorizontalScrollBarVisibility="Disabled" >
<DataGrid AutoGenerateColumns="False"
ItemsSource="{Binding Path=MyCollection}"
GridLinesVisibility="None"
CanUserAddRows="False"
CanUserDeleteRows="False"
CanUserReorderColumns="False"
CanUserSortColumns="False"
CanUserResizeColumns="False"
CanUserResizeRows="False"
HeadersVisibility="Column">
<DataGrid.Resources>
<ResourceDictionary>
<Style x:Key="{x:Type DataGridCell}" TargetType="{x:Type DataGridCell}">
<Setter Property="Background" Value="{x:Null}" />
<Setter Property="BorderBrush" Value="{x:Null}" />
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="{x:Null}" />
<Setter Property="Foreground" Value="Black" />
<Setter Property="BorderBrush" Value="{x:Null}" />
</Trigger>
</Style.Triggers>
</Style>
<Style x:Key="{x:Type DataGridRow}" TargetType="{x:Type DataGridRow}">
<Setter Property="Margin" Value="4" />
<Setter Property="Background" Value="{x:Null}" />
<Setter Property="BorderBrush" Value="{x:Null}" />
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="{x:Null}" />
<Setter Property="Foreground" Value="Black" />
<Setter Property="BorderBrush" Value="{x:Null}" />
</Trigger>
</Style.Triggers>
</Style>
</ResourceDictionary>
</DataGrid.Resources>
<DataGrid.GroupStyle>
<GroupStyle>
<GroupStyle.Panel>
<ItemsPanelTemplate>
<DataGridRowsPresenter />
</ItemsPanelTemplate>
</GroupStyle.Panel>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Expander Margin="4"
IsExpanded="True">
<Expander.Header>
<StackPanel Orientation="Horizontal"
Margin="4">
<TextBlock FontWeight="Bold"
FontSize="14"
Text="{Binding Path=Name}" />
</StackPanel>
</Expander.Header>
<ItemsPresenter />
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</DataGrid.GroupStyle>
<DataGrid.Columns>
<DataGridTemplateColumn Width="Auto">
...
</DataGridTemplateColumn>
<DataGridTemplateColumn Width="*">
...
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</ScrollViewer>
</Grid>
The DataGrid itself has a built in scrollviewer so the outside one isn't needed. I think it is the DataGrid's scrollviewer that is causing your issue. Try getting rid of the outside ScrollViewer and add the attributes to the DataGrid, like this:
<DataGrid AutoGenerateColumns="False"
ScrollViewer.VerticalScrollBarVisibility="Auto"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
...
>
...
</DataGrid>
If you need the outside scrollviewer for some reason, keep it but still disable the DataGrid's HorizontalScrollBarVisibility.
This is part hack, part real answer I think.
Hack part: In order to get the horizontal scroll bar to not show up, I simply disabled it in the DataGrid declaration. Then, to ensure that the column didn't get shaved off a little on the right side, I set row margin and padding in the style I used to turn off selection color.
Real part: Setting the vertical scrollbar to always show works properly as long as the height of the second row of the outtermost grid is set to "*" (asterisk). This ensures the DataGrid will use the proper amount of space when an expander is opened and there are too many rows to show.
The final DataGrid declaration looks like this:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Label Content="Import Data Mappings"
FontWeight="Bold"
HorizontalAlignment="Center"
Margin="4"
Padding="4"
Grid.Row="0" />
<DataGrid AutoGenerateColumns="False"
VerticalScrollBarVisibility="Visible"
HorizontalScrollBarVisibility="Disabled"
ItemsSource="{Binding Path=MyGroupedCollection, Mode=OneWay}"
HeadersVisibility="Column"
Grid.Row="1"
... >
<DataGrid.Resources>
<ResourceDictionary>
<Style x:Key="{x:Type DataGridCell}" TargetType="{x:Type DataGridCell}">
<Setter Property="Background" Value="White" />
<Setter Property="BorderBrush" Value="{x:Null}" />
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="White" />
<Setter Property="Foreground" Value="Black" />
<Setter Property="BorderBrush" Value="{x:Null}" />
</Trigger>
</Style.Triggers>
</Style>
<Style x:Key="{x:Type DataGridRow}" TargetType="{x:Type DataGridRow}">
<Setter Property="Margin" Value="0,4,0,4" />
<Setter Property="Background" Value="White" />
<Setter Property="BorderBrush" Value="{x:Null}" />
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="White" />
<Setter Property="Foreground" Value="Black" />
<Setter Property="BorderBrush" Value="{x:Null}" />
</Trigger>
</Style.Triggers>
</Style>
</ResourceDictionary>
</DataGrid.Resources>
<DataGrid.GroupStyle>
<GroupStyle>
<GroupStyle.Panel>
<ItemsPanelTemplate>
<DataGridRowsPresenter />
</ItemsPanelTemplate>
</GroupStyle.Panel>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Expander Margin="4"
IsExpanded="True">
<Expander.Header>
<StackPanel Orientation="Horizontal"
Margin="4">
<TextBlock FontWeight="Bold"
FontSize="14"
Text="{Binding Path=Name}" />
</StackPanel>
</Expander.Header>
<ItemsPresenter />
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</DataGrid.GroupStyle>
<DataGrid.Columns>
<DataGridTemplateColumn Width="Auto">
<DataGridTemplateColumn.HeaderTemplate>
...
</DataGridTemplateColumn.HeaderTemplate>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock .../>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Width="*">
<DataGridTemplateColumn.HeaderTemplate>
...
</DataGridTemplateColumn.HeaderTemplate>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<DockPanel LastChildFill="True">
<Button Style="{DynamicResource ResourceKey=MyButtonStyle}"
Content="Button Text"
Command="{Binding Path=MyButtonCommand}"
DockPanel.Dock="Left"/>
<ComboBox Margin="4"
Padding="4"
SelectedItem="{Binding Path=MySelectedItem, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
ItemsSource="{Binding Path=MyAvailableFields}"
DockPanel.Dock="Left">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock VerticalAlignment="Center"
HorizontalAlignment="Center"
Text="{Binding Path=MyDisplayName}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</DockPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
I have a ListView which is having GridView as its View.
See the sample code below:
<ListView ItemsSource="{Binding Path=Employees}" >
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="Focusable" Value="False"/>
</Style>
</ListView.ItemContainerStyle>
<ListView.Resources>
<Style TargetType="{x:Type CheckBox}" x:Key="DataGridCheckBox">
<Setter Property="HorizontalAlignment" Value="Center" />
<Setter Property="HorizontalContentAlignment" Value="Center" />
<Setter Property="IsEnabled" Value="True" />
<Setter Property="Margin" Value="4" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="Width" Value="{Binding RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type GridViewColumn}},Path=ActualWidth}" />
</Style>
</ListView.Resources>
<ListView.View>
<GridView x:Name="EmployeesGridView">
<GridViewColumn Header="IsEligible">
<GridViewColumn.CellTemplate>
<DataTemplate>
<CheckBox Style="{StaticResource DataGridCheckBox}" IsChecked="{Binding Path=IsSelected}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Name" >
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock>
<TextBlock>
<TextBlock Text="{Binding Path=Employee.Name}"/>
</TextBlock>
</TextBlock>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Age">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Age}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
How can I export this data to CSV file. I am not getting any property by which I can get Rows present in GridView.
How can I do this. Please suggest.
You can create a SelectedPerson property in your ViewModel and bind ListView.SelectedItem to this property.
I'm trying to get multiple lines in a listView I currently have the following XAML:
<ListView x:Name="listTasks" Margin="0,0,1.5,0" Background="{x:Null}" BorderBrush="{x:Null}" BorderThickness="0" Foreground="White" ItemsSource="{Binding TasksCollection}" ScrollViewer.HorizontalScrollBarVisibility="Disabled" ScrollViewer.VerticalScrollBarVisibility="Auto">
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<Style.Triggers>
<Trigger Property="IsSelected"
Value="True">
<Setter Property="Background"
Value="{x:Null}" />
<Setter Property="BorderBrush"
Value="{x:Null}" />
</Trigger>
<Trigger Property="IsMouseOver"
Value="True">
<Setter Property="Background"
Value="{x:Null}" />
<Setter Property="BorderBrush"
Value="{x:Null}" />
</Trigger>
</Style.Triggers>
<Setter Property="Height" Value="50px" />
<Setter Property="Focusable" Value="false" />
</Style>
</ListView.ItemContainerStyle>
<ListView.Resources>
<Style x:Key="CustomHeaderStyle" TargetType="{x:Type GridViewColumnHeader}">
<Setter Property="Visibility" Value="Collapsed" />
</Style>
</ListView.Resources>
<ListView.View>
<GridView ColumnHeaderContainerStyle="{DynamicResource CustomHeaderStyle}">
<GridViewColumn Header="">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock>
<TextBlock.Text>
<MultiBinding StringFormat=" Module: {0} Conditions: {1}|{2}|{3} for {4} minutes">
<Binding Path="Module"/>
<Binding Path="FirstValue"/>
<Binding Path="SecondValue"/>
<Binding Path="ThirdValue"/>
<Binding Path="Minutes"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
This outputs something like:
What I am trying to achieve is something like this (ignore the different colored background):
I can't seem to bring my head around how I would do this, any ideas?
Thanks
You can use a vertical StackPanel in order to put a series of TextBlock elements one on top of another:
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Module, StringFormat='Module: {0}'}"/>
<TextBlock Text="{Binding FirstValue, StringFormat='Module: {0}'}"/>
...
</StackPanel>
</DataTemplate>
Or you can format everything in one `TextBlock':
<DataTemplate>
<TextBlock>
<Run Text="Module: "/><Run Text="{Binding Module}"/><LineBreak/><Run Text="Conditions: "/><Run Text="{Binding FirstValue}"/><LineBreak/>
...
</TextBlock>
</DataTemplate>