In my Explorer view that displays a list of files, I have tried right-aligning the Size column as follows. I have the following resources:
<UserControl.Resources>
<converters:FileSizeConverter x:Key="FileSizeConverter" />
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
<DataTemplate x:Key="SizeTemplate">
<TextBlock HorizontalAlignment="Right" Text="{Binding Size, Converter={StaticResource FileSizeConverter}}" />
</DataTemplate>
</UserControl.Resources>
FileSizeConverter is just an IValueConverter that converts numeric file sizes into string format with B, KB, MB etc. I then have the following column:
<GridViewColumn CellTemplate="{StaticResource SizeTemplate}" Header="Size" Width="80" />
This displays the file size correctly converted, e.g. 8,2 KB, but still left-aligned. I have followed the example in the Microsoft Docs article How to: Change the Horizontal Alignment of a Column in a ListView, so what could be wrong here?
There are several complications here.
If you run Snoop and take a look at what you end up with in your view you'll find the textblock is hosted in a gridviewrowpresenter.
This is applying a bunch of code to lay out it's contents.
If you really want to know exactly how that works then you could pick through the source code here https://referencesource.microsoft.com/#PresentationFramework/src/Framework/System/Windows/Controls/GridViewRowPresenter.cs
If you're more interested in how to get this to work as you want then there are two things to do:
1) Make the columns stretch:
<ListView.Resources>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</ListView.Resources>
2) Either put the textblock in a container that can fill that space and allow the textblock to align right. Or, more simply. Set TextAlignment on your textblock.
<DataTemplate x:Key="SizeTemplate">
<TextBlock TextAlignment="Right"
To right align the text in a ListView GridViewColumn:
Set the ListViewItem.HorizontalContentAlignment="Stretch" so the column cells inherit it
Set up a CellTemplate for the column you want to right-align with a TextBlock with its HorizontalAlignment="Right"
You can't use DataMemberBinding on the column, otherwise the CellTemplate will be ignored
For example:
<ListView Name="_priceListListView" ItemsSource="{Binding PriceList}" >
<!-- How to set ListViewItem.HorizontalContentAlignment="Stretch" -->
<ListView.Resources>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</ListView.Resources>
<ListView.View>
<GridView>
<GridView.Columns>
<!-- Left-aligned text column -->
<GridViewColumn Header="Item" DisplayMemberBinding="{Binding Path=Item}" />
<!-- Right-aligned numeric column -->
<GridViewColumn Header="Price" >
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Price, StringFormat={}{0:C}}" HorizontalAlignment="Right" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView.Columns>
</GridView>
</ListView.View>
</ListView>
Related
I have found how to create ToolTip for DataGridColumnHeader in WPF (and make sure that the Header is wrapped).
<Style x:Key="StartingTabToolTipHeaderStyle" TargetType="DataGridColumnHeader">
<Setter Property="ToolTip" Value="{x:Static r:Resource.StartingTabToolTip}"/>
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock TextWrapping="Wrap" Text="{Binding}"/>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
I can use this style in this way:
<DataGridComboBoxColumn Header="{x:Static r:Resource.StartingTab}" SelectedItemBinding="{Binding StartingTab}"
HeaderStyle="{StaticResource StartingTabToolTipHeaderStyle}">
This solution is not nice because I need to create a separate style for each header because the tooltip is hardwired in the style. I would like a general style which can be used in this way:
<DataGridComboBoxColumn Header="{x:Static r:Resource.StartingTab}" SelectedItemBinding="{Binding StartingTab}"
MyToolTip="{x:Static r:Resource.StartingTabToolTip}">
Any idea how to do it? May be with attached property?
You can set the ToolTipService.ToolTip attached property on each column and bind your values to it, because it is not used on columns.
<DataGridComboBoxColumn Header="{x:Static r:Resource.StartingTab}"
SelectedItemBinding="{Binding StartingTab}"
HeaderStyle="{StaticResource StartingTabToolTipHeaderStyle}"
ToolTipService.ToolTip="{x:Static r:Resource.StartingTabToolTip}">
Then you can bind this text on the column in your header style using a RelativeSource binding.
<Style x:Key="StartingTabToolTipHeaderStyle" TargetType="{x:Type DataGridColumnHeader}">
<Setter Property="ToolTip" Value="{Binding Column.(ToolTipService.ToolTip), RelativeSource={RelativeSource Self}}"/>
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock TextWrapping="Wrap" Text="{Binding}"/>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
Now you only have a single style. If you want to avoid setting the style on each column, make it an implicit style by omitting the x:Key. Then it will be applied to all matching types in scope.
<Style TargetType="{x:Type DataGridColumnHeader}">
You could use DataGridTemplateColumn
<DataGrid AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTemplateColumn Header="yourHeader" HeaderStyle={StaticResource HeaderStyle1}">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<!--you can add any control such as ComboBox, Button etc-->
<TextBlock Text="{Binding Message}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<!--Editing cell template is not mandatory, only if you want allow user to edit a particular cell, shown here only for reference-->
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<!--When the user double clicks on this cell, it changes the control to TextBox (from TextBlock - see above) to allow for user input-->
<TextBox Text="{Binding Message}"/>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
<!--Second Column-->
<DataGridTemplateColumn Header="secondHeader" HeaderStyle={StaticResource HeaderStyle2}">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox ItemSource="{Binding SomeSource}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
This might seem a bit much for each column. But it gives you far more control & better scalability for future complex changes. Its somewhat trivial work put in during the initial dev phase.
Atleast thats my opinion since after the success of the concept, several further inputs/changes & new requirements come in to improve user interactivity with specific rules & logic on how/when the user can interact with UI Elements especially when altering data.
Edit -
Your style can be exactly like you made i.e. targeting DataGridColumnHeader.
I have a silverlight datagrid whose row height and font are to be reduced because more number of rows are to be displayed in it without increasing the total height.
I have set its row height to 15 and font size to 10. All its content is visible clearly, but when I type in some text in one of its cells, the typed content is not visible as I am typing it. When I am done typing the text and press , I can see it.
This is how it looks when I am typing some text in one of its cells:
How can I fix this?
Edit: The relevant part from the .xaml:
<sdk:DataGrid AutoGenerateColumns="False" Height="171" HorizontalAlignment="Left" Name="StockistClaimDetailsGrid" VerticalAlignment="Top" Width="756" Margin="0,20,0,0" RowHeight="15">
<sdk:DataGrid.Columns>
<sdk:DataGridTemplateColumn Header="" Width="30" >
<sdk:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox Height="16" x:Name="CheckBox" Checked="CheckBox_Checked" IsChecked="{Binding IsChecked, Mode=TwoWay}" IsEnabled="{Binding CheckBoxEnabled, Mode=TwoWay}" Tag="{Binding}" />
</DataTemplate>
</sdk:DataGridTemplateColumn.CellTemplate>
</sdk:DataGridTemplateColumn>
<sdk:DataGridTextColumn x:Name="Remarks" Binding="{Binding Remarks}" Header="Remarks" IsReadOnly="False" Width="300" FontSize="10" />
</sdk:DataGrid.Columns>
</sdk:DataGrid>
The DataGrid uses two different TextBox instances for just displaying a cell's content and for editing it. So I guess the text style settings got applied to the displaying TextBox but not to the editing TextBox.
So, how to fix this?
I found the property EditingElementStyle on msdn. This seems to do the trick.
Just assign the same TextBoxStyle.
<Style x:Key="SmallTextBoxStyle" TargetType="TextBox">
<Setter Property="FontSize" Value="10"/>
</Style>
...
<sdk:DataGridTextColumn EditingElementStyle="{StaticResource SmallTextBoxStyle}" FontSize="10"/>
[Edit] I just found out that the ElementStyle must be applyable to TextBlock. So you cannot reused the Style, but you can duplicate the style and set the TaregtType accordingly. Or just omit the ElementStyle and set the FontSize on the column as seen above.
<Style x:Key="SmallTextBlockStyle" TargetType="TextBlock">
<Setter Property="FontSize" Value="10"/>
</Style>
<Style x:Key="SmallTextBoxStyle" TargetType="TextBox">
<Setter Property="FontSize" Value="10"/>
</Style>
...
<sdk:DataGridTextColumn
ElementStyle="{StaticResource SmallTextBlockStyle}"
EditingElementStyle="{StaticResource SmallTextBoxStyle}"/>
I have a ListView in XAML which displays its source collection in a GridView (with columns) fashion.
However I intend to use the same ListView to display the source collection may be in a grid of images, or some card view.
I want the ListView to change itself based on a ComboBox selection. So say for ComboBox value 1 ListView should display a GridView, for value 2 ListView should display card view.
Currently my ListView specifies a GridView set as its View property:
<ListView ItemsSource="{Binding PersonList}" Width="450" HorizontalAlignment="Right" IsSynchronizedWithCurrentItem="True">
<ListView.View>
<GridView>
<GridViewColumn Header="FirstName" DisplayMemberBinding="{Binding FirstName}" />
<GridViewColumn Header="LastName" DisplayMemberBinding="{Binding LastName}" />
<GridViewColumn Header="Ip Address" DisplayMemberBinding="{Binding Path=IpAddress}" />
</GridView>
</ListView.View>
</ListView>
I would like to know how can I change the ListView to display different views based on ComboBox triggers.
ListView View is a DependencyProperty of type ViewBase. So, what you can do is create your own custom views and can set it via DataTrigger on combobox selected item.
Microsoft already has sample available for it online which you can download from here.
OR
May be you can define two separate ListView's in resources as two seperate DataTemplates.
<Window.Resources>
<DataTemplate x:Key="GridViewTemplate">
<ListView/> <!-- GridView -->
</DataTemplate>
<DataTemplate x:Key="CardViewTemplate">
<ListView/> <!-- CardView -->
</DataTemplate>
</Window.Resources>
and have one ContentControl in place and you can swap its Content based on selected value in combobox.
<ContentControl>
<ContentControl.Style>
<Style TargetType="ContentControl">
<Setter Property="Content"
Value="{StaticResource GridViewTemplate}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding SelectedValue}" Value="Value2">
<Setter Property="Content"
Value="{StaticResource CardViewTemplate}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
Now I have ListView and in one column has:
<GridViewColumn CellTemplateSelector="{StaticResource messagerEditorTemplateSelector}"/>
And everything is fine: cell is filled with content based on item. But now I want to place in this cell 2 controls: for one template must be selected based on binding and other is control with name, say TimeRangeView. But I cannot understand how it can be implemented? So I must have code like:
<GridViewColumn>
<DataTemplate>
<StackPanel>
<SomeControlWhichSupportTemplateSelector ... />
<views:TimeRangeView ... />
</StackPanel>
</DataTemplate>
</GridViewColumn>`
Which control should I use for template? I've found only listbox but it must be bound to collection. Of course, I could bind like:
<ListBox ItemsSource="{Binding Converter=ItemToCollectionConverter}" />
but it doesn't look elegant. May be there is another way to do it?
You can use a ContentControl and set its ContentTemplateSelector property:
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<StackPanel>
<ContentControl ContentTemplateSelector="{StaticResource messagerEditorTemplateSelector}" />
<views:TimeRangeView ... />
</StackPanel>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
Note that for Binding to work within your ContentControl, you will have to set the Content property to whichever object is used in the Bindings of the DataTemplate returned by your selector.
So that's for option 1.
You can also just use DataTriggers:
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<StackPanel>
<ContentControl Content="{Binding MyBoundObject}">
<ContentControl.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=MyBoundObject.ABooleanProperty}" Value="True">
<Setter Property="ContentControl.ContentTemplate" Value="{StaticResource myFirstTemplate}" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=MyBoundObject.ABooleanProperty}" Value="False">
<Setter Property="ContentControl.ContentTemplate" Value="{StaticResource mySecondTemplate}" />
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
<views:TimeRangeView ... />
</StackPanel>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
Which is what i do and it works like a charm =)
I have a listView with a gridview presentation and TextBlocks in each column. I would like to make the selected line editable by replacing the textblocks with TextBoxes and ComboBoxes when the user clicks on an edit button. I tried to do this by setting styles that toggles the visibility of the controls like this :
<Style x:Name="ItemDisplayStyle" TargetType="{x:Type TextBlock}" x:Key="ItemDisplayStyle">
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="Visibility" Value="{Binding Path=dislayMode}" />
</Style>
<Style x:Name="ItemEditStyle" TargetType="{x:Type FrameworkElement}" x:Key="ItemEditStyle">
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="Visibility" Value="{Binding Path=editMode}" />
</Style>
displayMode and editMode are Visibility properties set in the code-behind.
And lower in the xaml :
<GridViewColumn Header="Date de début" Width="80">
<GridViewColumn.CellTemplate>
<DataTemplate>
<Grid>
<TextBlock Margin="-6,0"
HorizontalAlignment="Stretch" TextAlignment="Center"
Text="{Binding Path=DateDebut, Mode=TwoWay}"
Style="{StaticResource ItemDisplayStyle}" />
<TextBox x:Name="tbDateDebut" Margin="-6,0"
HorizontalAlignment="Stretch" HorizontalContentAlignment="Center"
Text="{Binding Path=DateDebut, Mode=TwoWay}"
Style="{StaticResource ItemEditStyle}" />
</Grid>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
My problem is that changing 'editMode' and 'displayMode' in the code-behind does not seem to be detected at the UI level.
Also, even if I get this to work, I don't have any idea of how to apply it only to the selected line.
I can do this separately by binding the visibility value with the ListView so that when the user selects a line, she/he gets the editable controls on that line but I really want to allow this only when they click on a button.
Have you Refreshed the contents of the Grid after making changes? You can use the Grid.GetColumn method and send the sender object i.e. edit button (which I suppose would be separate for each column) and then probably use the VisualTreeHelper to get the textbox and combobox in that column.
Also, why don't you use the 'IsReadOnly' property of the TextBox instead of replacing the TextBlock? Make it true or false as per your requirement.