WPF - How to have certain ListView items span columns? - wpf

I have a set of data that I'd like to present via a WPF ListView in this way:
Column1 Column2 Column3
--GroupName1--
Item1 part2 part3
Item2 part2 part3
--GroupName2--
Item3 part2 part3
Item4 long_text_in_both_columns
Item5 part2 part3
--GroupName1--
Item6 part2 part3
Item7 long_text_in_both_columns
--GroupName3--
Item8 part2 part3
I am starting by working with this basic sample: http://msdn.microsoft.com/en-us/library/ms771309(VS.90).aspx
Item4 and Item7 above have long text that I would like to span the remaining columns (ignoring what the original column headings were for). How can I do this?
I already have some XAML setup with a DataTrigger to replace the default GridViewRowPresenter with a custom TextBlock, but this isn't quite what I'm looking for. I need the data in column 1 to be displayed normally and the width of the first column recognized.

Here's how I ended up solving this with a proper ListView:
<ListView.ItemContainerStyle >
<Style TargetType="ListViewItem">
<Style.Triggers>
<DataTrigger Binding="{Binding ShowAcrossColumns}" Value="True">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListViewItem}">
<Grid>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{Binding ElementName=myListView, Path=View.Columns[0].Width}" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Padding="6,3,6,3" Text="{Binding Column1Text}" />
<TextBlock Grid.Column="1" Padding="6,3,6,3" Text="{Binding ColumnSpanningText}" />
</Grid>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListViewItem}">
<Grid>
<GridViewRowPresenter />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListView.ItemContainerStyle>
The key is that the DataTriggered Template does not use GridViewRowPresenter, and instead fakes the use of it with its own grid. Some of the padding/margins had to be guessed at to sort of match what GridViewRowPresenter uses internally. The other tricky part was binding the internal Grid columns to the overall ListView column widths. Resizing columns then works as expected.

I think for this, you use ListBox instead of ListView and use its ItemTemplate to split each columns.
<ListBox>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Text1}"/>
<TextBlock Text="{Binding Text2}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
In this way you will have chance to put Trigger in or based on some data, you could make one control invisible and show the long text.
:)

I have a similar problem, where I want some lines to have a multi-column-spanning item that starts in the 5th (of nine) columns. I implemented a variation on #PeteVasi's answer, with two main changes:
You want to bind to ActualWidth rather than Width, or it doesn't work right when e.g. columns are auto-sized.
You can use RelativeSource to avoid specifying myListView by name (which makes it more suitable for use in a style resource).
To do this, change the Width assignment in the ColumnDefinition entries from this:
Width="{Binding ElementName=myListView, Path=View.Columns[0].Width}"
to this:
Width="{Binding RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType={x:Type ListView}}, Path=View.Columns[0].ActualWidth}"
Further, you don't need to use a nested <Grid>, and if you want to keep the mouse-over and selection highlighting effects, you need to clone the default style.
I experimented with a minor variation on the answer that uses GridViewRowPresenter, and played with a bad idea (watching for column resize events and using them to set a property to which a column width was bound). The various experiments, including a fully-styled version, can be found in the DisasmUiTest project on github.

Related

Why in my WPF view is the Content text of my Label control not visible?

I have the following markup:
<StackPanel Grid.Row="0" Orientation="Horizontal">
<StackPanel Orientation="Horizontal" Visibility="{Binding OrgListVisibility}">
<Label Content="Org:" />
<ComboBox ItemsSource="{Binding OrgSelectList, NotifyOnSourceUpdated=True}" SelectedValuePath="Key" DisplayMemberPath="Value" SelectedItem="{Binding OrgId}" />
</StackPanel>
<StackPanel Orientation="Horizontal" Visibility="{Binding BranchListVisibility}">
<TextBlock Text="Branch:" Style="{StaticResource FormLabel}" />
<ComboBox x:Name="BranchList" ItemsSource="{Binding BranchSelectList}" SelectedValuePath="Key" DisplayMemberPath="Value" SelectedItem="{Binding BranchId}" />
</StackPanel>
</StackPanel>
Yet when I run the app, only the text from the TextBlock is visible, and not that of the Label. The latter is in the Visual Tree, with a TextBlock deep down, but that is as far as I can see.
AS REQUESTED: Here is the style for FormLabel:
<Style TargetType="TextBlock" x:Key="FormLabel">
<Setter Property="Height" Value="20" />
<Setter Property="Margin" Value="10" />
<Setter Property="TextAlignment" Value="Right" />
<Setter Property="VerticalAlignment" Value="Center" />
</Style>
A SIMILAR PROBLEM:
I found an almost similar problem with a combobox when I bound it to a collection of instances of a generic class. The items' text simply did not show, but they were present in the comboboxes. Selecting on the one by knowing the position of my sought item correctly cascaded to the 2nd combobox, which had visible items, and I could see the correct but invisible item had been selected.
As soon as I change the item source to a list of non-generic objects, the items in the dropdown were visible again.
The code looks fine and as you have mentioned in the comments section that it takes layout space then it may very well happen that the color of your label and the background color of the containing layout be same.
To troubleshoot this, try giving some different background and foreground colors e.g. red or blue to the Label. Hope this helps
Ctrl+Q -> Live Visual Tree
Then hit the "pick element" button and select your label. Check the following properties:
Visibility
Opacity
Content
Also check the child elements of the Label. Setting the Content should result in a tree like this:
If a default style has changed the control template, you might not see the TextBlock as a child here. Also drill into the TextBlock and make sure it has the right Text property, then make sure it and all its parents have the right Opacity and Visibility . Also make sure that the inner TextBlock has space allocated to it by selecting it and turning on the highlighting feature in the live visual tree window.
Can you try this code to see if it works?
<Grid Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0"
Orientation="Horizontal" Visibility="{Binding OrgListVisibility}">
<Label Content="Org:" />
<ComboBox ItemsSource="{Binding OrgSelectList, NotifyOnSourceUpdated=True}"
SelectedValuePath="Key"
DisplayMemberPath="Value"
SelectedItem="{Binding OrgId}" />
</StackPanel>
<StackPanel Grid.Column="1"
Orientation="Horizontal" Visibility="{Binding BranchListVisibility}">
<TextBlock Text="Branch:" Style="{StaticResource FormLabel}" />
<ComboBox x:Name="BranchList"
ItemsSource="{Binding BranchSelectList}"
SelectedValuePath="Key"
DisplayMemberPath="Value"
SelectedItem="{Binding BranchId}" />
</StackPanel>
</Grid>
The Label would take up layout space while not being visible when its Visibility == Hidden. You should check and make sure that your application does not define a global style (one with no Key) for TargetType="Label" where this value could be set:
<Style TargetType="Label"> !!!note that this has no 'Key' associated
[...]
<Setter Property="Visibility" Value="Hidden" />
[...]
</Style>
This would not need to be in the same xaml file in order to be automatically applied, you should check the global dictionary or any other ResourceDictionary linked in the file.
I had the same problem. It turned out that the label Height was too small. Increased the height and its content became visible.

DevExpress NavBarItem won't data bind properly in ContentTemplate that uses a WPF TreeView

I'm trying to organize my NavBarItem elements in each NavBarGroup into a tree view, using the following code:
<dxn:NavBarControl DataContext="{Binding}" ItemsSource="{Binding Bars}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" >
<dxn:NavBarControl.Resources>
<Style TargetType="dxn:NavBarGroup">
<Setter Property="Header" Value="{Binding DisplayText}"/>
<Setter Property="Content" Value="{Binding MenuItems}"/>
<Setter Property="DisplaySource" Value="Content"/>
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<TreeView x:Name="MenuView" ItemsSource="{Binding}" >
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Path=ChildItems}" >
<ContentControl>
<dxn:NavBarItem
DataContext="{Binding}"
Content="{Binding ItemText}" PreviewMouseUp="MenuItemOnPreviewMouseUp" Initialized="FrameworkContentElement_OnInitialized" />
</ContentControl>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</dxn:NavBarControl.Resources>
<dxn:NavBarControl.View>
<dxn:ExplorerBarView/>
</dxn:NavBarControl.View>
</dxn:NavBarControl>
The groups are binding correctly, and I am only populating one group, "Personnel" for debugging reasons, with the mini-hierarchy:
"Personnel"
--"Employees"
----"Contractors"
------"Time-sheets"
I get the correct structure in the treeview, but each item only has the text that would result from calling NavBarItem.ToString(). When I handle the NavBarItem.Initialized even, the sender argument, being a NavBarItem actually has the correct value in its Content property, so the binding isn't all broken, but I don't know what is, and am seeking help here.
First of all it is necessary to notice that NavBarGroup and NavBarItem are non-visual elements. Thus if you use them as the ContentPresenter content only simple strings will be shown (just like you see in your case).
The second thing is that multi-level hierarchy is not supported in NavBar (only group level and item level).
So if you need to show more than two levels you can use TreeView as you used in your sample but put TreeViewItems to the content template instead of putting NavBarItems; If you need only two levels you can use NavBarControl.ItemsSource and NavBarGroup.ItemsSource properties to populate layout.

Designing a custom edit control similar to hotmail style "To" box

I am desiging a custom control for use in my application which simulates a hotmail style "To" textbox which lets the user enter semicolon delimited strings. The control behaves like a textbox, on each ener press or entering semicolon a box(a textblock infact) gets created containing text entered which can be manipulated individually.
The control is implemented by means of a listview with its ItemPresenter set to a WrapPanel
The XAML looks like below:
<ListView x:Name="col" ItemContainerStyle="{StaticResource ContainerStyle}">
<ListView.GroupStyle>
<GroupStyle>
<GroupStyle.Panel>
<ItemsPanelTemplate>
<local:MyWrapPanel Orientation="Horizontal" MinWidth="400"/>
</ItemsPanelTemplate>
</GroupStyle.Panel>
</GroupStyle>
</ListView.GroupStyle>
</ListView>
<Style TargetType="{x:Type ListViewItem}" x:Key="ContainerStyle">
<Setter Property="ContentTemplate" Value="{StaticResource BoxView}" />
<Style.Triggers>
<Trigger Property="Tag" Value="Edit">
<Setter Property="ContentTemplate" Value="{StaticResource BoxViewEdit}" />
</Trigger>
</Style.Triggers>
</Style>
The control template for the listviewitems (boxes) looks like:
<DataTemplate x:Key="BoxView">
<Border BorderThickness="1" BorderBrush="Brown" Background="Beige" Margin="1,1,1,0" CornerRadius="6" >
<StackPanel Orientation="Horizontal">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock Grid.Column="0" Grid.Row="0" Margin="5,5,5,0" Height="20" Text="{Binding XPath=''}"/>
<DockPanel Grid.Column="1" Grid.Row="0">
<Image Source="edit8.png" MouseLeftButtonUp="edit_MouseLeftButtonUp" ToolTip="Edit" Margin=" 10,0,0,0"></Image>
<Image Source="cancel8.png" MouseLeftButtonUp ="cancel_MouseLeftButtonUp" ToolTip="Remove" Margin=" 5,0,5,0"></Image>
</DockPanel>
</Grid>
</StackPanel>
</Border>
</DataTemplate>
Actually the listview is data bound to a set of xmlnodes having common parent.
The look and feel of the control has to be given such that it looks like a text box.
Now, I have the listview items bound to the xml nodes of an xml document, to show the textbox I add an empty xmlnode in the document and change the control template of the coressponding listviewitem so that it shows as a textbox.
On pressing enter in the text box a new xml node gets appended to the underlying xml at second last position containing innertext set to the textbox text(last positon is dummy node)
The template used for textbox is:
<DataTemplate x:Key="BoxViewEdit">
<TextBox Margin="0,5,5,0" Background="White" MaxWidth="400" BorderThickness="1" Text="{Binding XPath=''}"/>
</DataTemplate>
But the idea of adding an empty dummy xmlnode in the underlying source xml so that i could simulate editing of the listview seems hacky. Is there any cleaner way of doing this.
What i want is to wrap the text box as shown in figure with the contents of the wrappanel seamlessly without adding it to the wrappanel itself by means of creating a dummy xmlnode.
I had the same issue and found some solutions; you can check out the responses and comments here.

Fill up DataGrid with empty rows

I have a DataGrid bound to a DataTable. I want the DataGrid to always have at least ten (empty) rows, also if there are not enough real data-items (the data comes in little by little).
One approach would be to easily add ten empty rows to the DataTable at initialization. But when a real data-item comes in, I can't easily add the row, I have to find the first empty row to overwrite it, what is not very handy.
So someone knows a smarter/built-in way to achieve this?
It's gonna be mess, no matter from what side it's approached. I'd say your best bet (provided that your grid cells content won't be wrapped) is to use a visual brush (with lines) as your DataGrid's background.
UPDATE 1 - XAML
It's alwast there, the trick is to use MinHeight, which will produce a vision of blank items thanks to tiled background. If your grid will be populated with the real data the background will expand, rendering more lines.
One thing I didn't try is how it'll handling scrolling.
Here's an example:
<Grid>
<Grid.Resources>
<VisualBrush x:Key="StripesBrush" TileMode="Tile" Viewport="0,0,5,20"
Viewbox="0,0,10,10" ViewportUnits="Absolute"
ViewboxUnits="Absolute">
<VisualBrush.Visual>
<Line X1="0" X2="10000" Y1="0" Y2="0" Stroke="DarkGray"/>
</VisualBrush.Visual>
</VisualBrush>
</Grid.Resources>
<DataGrid x:Name="g" AutoGenerateColumns="False"
GridLinesVisibility="None"
MinHeight="100"
Height="100"
VerticalAlignment="Top"
Background="{StaticResource StripesBrush}">
<DataGrid.Columns>
<DataGridTextColumn Header="A" Width="Auto" Binding="{Binding Item1}">
<DataGridTextColumn.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="Background" Value="Transparent"></Setter>
</Style>
</DataGridTextColumn.CellStyle>
</DataGridTextColumn>
<DataGridTextColumn Header="B" Width="Auto" Binding="{Binding Item2}" />
</DataGrid.Columns>
</DataGrid>
</Grid>
if its just for layout, you can simply add a second datagrid with 10 empty rows under your real datagrid. or you take the aproach Dmitry posted and use visual brush

DataGrid column header text wrapping in silverlight

I am using a DataGrid in my silverlight project and I want one column to wrap it's header text. I know using a style for the header might be the answer but I want to know if there is a wrap property for a datagrid column header?
Here is my code:
<data:DataGrid x:Name="gridViewResources"
AutoGenerateColumns="False"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
ItemsSource="{Binding OpportunityResourceDetailList, Mode=TwoWay}" IsReadOnly="True">
<data:DataGrid.Columns>
<data:DataGridTemplateColumn Header="#" Width="Auto">
<data:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding PositionLevel.FullPositionAndLevelName}" />
</DataTemplate>
</data:DataGridTemplateColumn.CellTemplate>
</data:DataGridTemplateColumn>
Thanks!
I haven't found a way to get Silverlight to automatically wrap the header (in much the same way as the TextWrapping property of a TextBlock does). I suspect that it's not possible due to a limitation of the DataGridColumn.Header property:
Use discretion when using objects as header content; not all Silverlight objects are suitable for use within the limited presentation surface that appears for headers.
However, you can 'manually' wrap header text. If you put a newline in the header text, the header text will be broken over two lines at that point. (In XAML, you use the character entity
.) For example, the following header text appears split over three lines:
<sdk:DataGridTextColumn Header="ABCD
EFGH
IJKL" />
There is no specific property on a DataGridColumn to support this but once you have created the right style it is just as easy - simply set the HeaderStyle property for the specific column.
Create a Style resource with a TargetType of DataGridColumnHeader and set the ContentTemplate property to be a DataTemplate containing a TextBlock with the TextWrapping property set to Wrap. I've enclosed the TextBlock within a Grid panel to keep this closer to the default DataTemplate used by the ContentPresenter. Apply this style to a specific column using the HeaderStyle property or to the whole DataGrid using the ColumnHeaderStyle property.
Note that you will need to constrain the width of the Column to something less than the header text for the wrapping to take affect unless you specifically restrict the width of the TextBlock in the DataTemplate.
<Style x:Key="CustomDataGridColumnHeaderStyle" TargetType="sdk:DataGridColumnHeader" BasedOn="{StaticResource DefaultDataGridColumnHeaderStyle}">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Grid>
<TextBlock Text="{Binding}" TextWrapping="Wrap" />
</Grid>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
...
<sdk:DataGrid >
<sdk:DataGrid.Columns>
<sdk:DataGridTextColumn Header="Header Name" Binding="{Binding Xxx}" Width="80" HeaderStyle="{StaticResource CustomDataGridColumnHeaderStyle}"/>
</sdk:DataGrid.Columns>
</sdk:DataGrid>
here is my code
<DataGrid.ColumnHeaderStyle>
<Style TargetType="ContentControl">
<Setter Property="HorizontalContentAlignment" Value="Center"></Setter>
<Setter Property="ContentTemplate" >
<Setter.Value >
<DataTemplate>
<TextBlock Text="{Binding}" TextWrapping="Wrap" FontWeight="Bold" TextAlignment="Center" LineHeight="20"></TextBlock></DataTemplate>
</Setter.Value>
</Setter>
</Style>
`enter code here`</DataGrid.ColumnHeaderStyle>

Resources