Binding to data breaking on VirtualizingStackPanel - c#

First of all, a disclaimer, I'm working with .net 3.5's virtualizingstackpanel. If you get different behavior in future versions let me know. It's relatively simple to set up a test case with listviews that you can test this against.
I have an itemcontainer style in a virtualizingstackpanel that binds the property IsSelected to the viewmodel.
When I Select an unselected item in the view model that is off screen, and then scroll to that item, both the datacontext (viewmode) and the actual listviewitem have the IsSelected Property as true (expected behavior). The trigger is properly applied to the listviewitem highlighting it.
However, when I deselect the datacontext for an item that is not in view and then scroll down until the item is in view, upon reaching the item and creating it, the item's datacontext now has IsSelected = true and the listviewitem's IsSelected property is also true, so the listviewitem ends up with a selection rectangle from the trigger(incorrect behavior).
It's almost as if the ListViewItem's Properties are all restored on creation of the item ( this makes sense to me, but then they should bind the datacontext's values to the item afterward).
But that doesn't seem to be happening. Moreover, after failing to deselect the item and scrolling back to find it selected. If i then select/desect it, the binding has no effect on the item.
I can see no logical reason why it would work when selecting an item in the viewmodel that is offscreen and not when I Deselect an item offscreen. In both cases, the newly created item should need to be rebound to the current value of the viewmodel. However, one works and the other doesn't.
Edit:
Ah ok, so I just can't use recycling mode and binding it seems. Thanks #devhedgehog. Would give you the bounty but you need an answer. I swear I tried that earlier, but maybe I was not handling click events in the bound listview before so that I was breaking the binding on physical selection or something. I do remember attempting both modes at one point, but probably had something else interfering so it wouldn't work. Anyway it works now.
Since you mentioned it, I would like to preferably avoid keeping unnecessary code and inherit from virtualizingstackpanel instead of virtualizing panel. But I want to be able to set the horizontal scroll extent, which requires me to reimplement Iscrollinfo. However, I was having trouble getting virtualizingstackpanel to interact nicely with iscrollinfo.
<ListView
x:Name="TestLV"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
Background="Green"
ItemsSource="{Binding Path=AddedItems, Mode=OneWay}"
SnapsToDevicePixels="True"
VirtualizingStackPanel.VirtualizationMode="Recycling"
VirtualizingStackPanel.IsVirtualizing="true"
ScrollViewer.IsDeferredScrollingEnabled="False"
Grid.Column ="4"
MouseDown="TestLV_MouseDown"
>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="IsSelected" Value="{Binding Path=IsSelected, Mode=OneWay}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListViewItem}">
<Grid
x:Name="SignalGrid"
Background="Transparent"
>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Border
Name="Bd"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Padding="{TemplateBinding Padding}"
SnapsToDevicePixels="true">
<ContentPresenter
x:Name="PART_Header"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
/>
</Border>
<ItemsPresenter
x:Name="ItemsHost"
Grid.Row="1"
Grid.Column="0"
/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected"
Value="false">
<Setter
TargetName="SignalGrid"
Property="Background"
Value="Transparent"
/>
</Trigger>
<Trigger Property="IsSelected"
Value="true">
<Setter
TargetName="SignalGrid"
Property="Background"
Value="Navy"
/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
<!--<Components:VirtualizingTilePanel
ChildSize="{Binding Path=GraphHeight}"
/>-->
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.Template>
<ControlTemplate>
<Grid >
<ScrollViewer
>
<ItemsPresenter />
</ScrollViewer>
</Grid>
</ControlTemplate>
</ListView.Template>
<!--Template Defining the layout of items in this treeview-->
<ListView.Resources>
<HierarchicalDataTemplate
ItemsSource ="{Binding Path = bits}"
DataType="{x:Type ViewModels:BusViewModel}"
>
<Grid>
<Components:CenteredTextBlock
x:Name="CommentTextBlock"
Foreground="Black"
BorderThickness="{Binding RelativeSource ={RelativeSource AncestorType={x:Type ListView}}, Path=BorderThickness}"
BorderBrush="{Binding RelativeSource ={RelativeSource AncestorType={x:Type ListView}}, Path=BorderBrush}"
HorizontalAlignment="Stretch"
Height="{Binding ElementName=graph_viewer, Path=GraphHeight, Mode=OneWay}"
>
<Components:CenteredTextBlock.MainText>
<MultiBinding Converter="{StaticResource StringConcatConverter}">
<Binding Path="Alias" />
<Binding Path="SignalValueAtPrimaryMarker" />
</MultiBinding>
</Components:CenteredTextBlock.MainText>
</Components:CenteredTextBlock>
</Grid>
</HierarchicalDataTemplate>
<DataTemplate
DataType="{x:Type ViewModels:BitViewModel}"
>
<Grid>
<Components:CenteredTextBlock
Foreground="Black"
BorderThickness="{Binding RelativeSource ={RelativeSource AncestorType={x:Type ListView}}, Path=BorderThickness}"
BorderBrush="{Binding RelativeSource ={RelativeSource AncestorType={x:Type ListView}}, Path=BorderBrush}"
HorizontalAlignment="Stretch"
Height="{Binding ElementName=graph_viewer, Path=GraphHeight, Mode=OneWay}">
<Components:CenteredTextBlock.MainText>
<MultiBinding Converter="{StaticResource StringConcatConverter}">
<Binding Path="Alias" />
<Binding Path="SignalValueAtPrimaryMarker" />
</MultiBinding>
</Components:CenteredTextBlock.MainText>
</Components:CenteredTextBlock>
</Grid>
</DataTemplate>
<DataTemplate
DataType="{x:Type ViewModels:SelectableItemViewModel}"
>
<Grid>
<Components:CenteredTextBlock
Foreground="Red"
BorderThickness="{Binding RelativeSource ={RelativeSource AncestorType={x:Type ListView}}, Path=BorderThickness}"
BorderBrush="{Binding RelativeSource ={RelativeSource AncestorType={x:Type ListView}}, Path=BorderBrush}"
HorizontalAlignment="Stretch"
Height="{Binding ElementName=graph_viewer, Path=GraphHeight, Mode=OneWay}"
MainText="{Binding Path = FullName}"
/>
</Grid>
</DataTemplate>
</ListView.Resources>
</ListView>

It seems strange that this question actually appears to have been answered, yet is listed as not having an answer. So I will post dev hedgehog's comment/answer here.
Please use standard VirtualizingStackPanel without your custom measure
logic. There is nothing you added special in your logic that
VirtualizingStackPanel cant do. And RecyclingMode should not be
Recycle but instead leave it out or change to Standard.

Related

WPF switch between horizontal and vertical split

Can you suggest a good way to make a switch between horizontal and vertical split in wpf? I have two areas in my interface. Want them to be divided by a draggable separator and to have a button to switch between horisontal and vertical split. I tried to do that with AvalonDock, but for some reason that didn't work. Here's my question on it, nobody answered yet. prev. question
Maybe another library, or a simple way of doing that with standard GridSplitter?
I just ran into a similar problem. Here is how I solved it, thanks to some good ideas here:
<ContentControl>
<ContentControl.Resources>
<BoolConverter x:Key="BoolToLayoutConverter" TrueValue="templateHorizontal" FalseValue="templateVertical"/>
<BoolConverter x:Key="BoolToLayoutCharacterConverter" TrueValue="—" FalseValue="|"/>
<DataTemplate x:Key="mainTable">
<StackPanel>
<Label Content="MainTable goes here"/>
<ToggleButton Content="{Binding LayoutHorizontal, Converter={StaticResource BoolToLayoutCharacterConverter}}"
IsChecked="{Binding LayoutHorizontal}"/>
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="childTables">
<Label Content="ChildTables go here"/>
</DataTemplate>
</ContentControl.Resources>
<ContentControl.Style>
<Style TargetType="ContentControl">
<Style.Triggers>
<DataTrigger Binding="{Binding LayoutHorizontal}" Value="False">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="10"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<ContentPresenter Grid.Column="0" ContentTemplate="{StaticResource mainTable}"/>
<GridSplitter Grid.Column="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
<ContentPresenter Grid.Column="2" ContentTemplate="{StaticResource childTables}"/>
</Grid>
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
<DataTrigger Binding="{Binding LayoutHorizontal}" Value="True">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="5"/>
<RowDefinition/>
</Grid.RowDefinitions>
<ContentPresenter Grid.Row="0" ContentTemplate="{StaticResource mainTable}"/>
<GridSplitter Grid.Row="1" Height="10" VerticalAlignment="Stretch" HorizontalAlignment="Stretch"/>
<ContentPresenter Grid.Row="2" ContentTemplate="{StaticResource childTables}"/>
</Grid>
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
Where BoolConverter is an IValueConverter. And the code behind:
private bool _layoutHorizontal = true;
public bool LayoutHorizontal
{
get { return _layoutHorizontal; }
set
{
_layoutHorizontal = value;
NotifyPropertyChanged();
}
}
I don't know of any reasonably clean way to redefine grid rows and columns of a grid at runtime. You would need some code that redefines the RowDefinitions and ColumnDefinitions, as well as possibly updating Grid.Row and Grid.Column attached properties of the children of the grid. I am not sure how well a grid responds to such a reconfiguration. You might need to invalidate some things manually. I suspect the library you tried to use did not implement all of the steps necessary to reconfigure the grid, or perhaps they tried and found that it did not work.
However, it should be relatively straightforward to swap out one preconfigured grid for another. Put both grids in the same place and set the visibility of the one not currently in use to collapsed.

How to get ControlTemplate not to override DataTemplate?

Similar to gmail's Select button, I wanted to create a ComboBox for the ListView which allows the user to quickly select entries of their choosing (ex. All, None, Read, Unread). However, the selected value would display a tri-state CheckBox equivalent to All, Some, or None of the entries being selected. I succeeded in doing so. Below is the xaml for an example Window utilizing this feature(*):
<Window x:Class="WPFTest.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:wpfTest="clr-namespace:WPFTest.ViewModels"
xmlns:prism="clr-namespace:Microsoft.Practices.Prism.Mvvm;assembly=Microsoft.Practices.Prism.Mvvm.Desktop"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WPFTest.Models"
mc:Ignorable="d" Title="WPF Test" Height="221.256" Width="605"
d:DataContext="{d:DesignInstance wpfTest:MainWindowViewModel, IsDesignTimeCreatable=True}"
prism:ViewModelLocator.AutoWireViewModel="True" WindowStartupLocation="CenterScreen">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary
Source="/WPFTest;Component/Resources/Resources.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Grid>
<ListView
ItemsSource="{Binding Entries}"
SelectedValue="{Binding SelectedEntry}"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
ScrollViewer.CanContentScroll="True"
SelectionMode="Single"
Margin="10">
<ListView.View>
<GridView>
<GridViewColumn Width="Auto">
<GridViewColumn.CellTemplate>
<DataTemplate>
<CheckBox
IsChecked="{Binding Selected}"
Command="{Binding DataContext.RowSelectedCommand, RelativeSource={RelativeSource AncestorType={x:Type ListBox}}}"
HorizontalAlignment="Center"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
<GridViewColumnHeader>
<ComboBox
ItemsSource="{Binding Options.Items}"
SelectedValue="{Binding Options.SelectedItem}"
ItemTemplateSelector="{DynamicResource itemTemplateSelector}"
HorizontalAlignment="Stretch"
Margin="0,0,0,0"
VerticalAlignment="Stretch"
Width="44"
Height="34"
FontSize="20"
VerticalContentAlignment="Top"
HorizontalContentAlignment="Left">
<ComboBox.Resources>
<DataTemplate x:Key="selectedTemplate">
<TextBlock
x:Name="displayText"
Text="{Binding DataContext.Options.SelectedDisplay, RelativeSource={RelativeSource AncestorType={x:Type ListBox}}}"
FontSize="20"
Height="22"/>
</DataTemplate>
<DataTemplate x:Key="dropDownTemplate">
<TextBlock Text="{Binding}" FontSize="12"/>
</DataTemplate>
<local:ComboBoxItemTemplateSelector
x:Key="itemTemplateSelector"
SelectedTemplate="{StaticResource selectedTemplate}"
DropDownTemplate="{StaticResource dropDownTemplate}"/>
</ComboBox.Resources>
</ComboBox>
</GridViewColumnHeader>
</GridViewColumn>
<GridViewColumn
Width="Auto"
DisplayMemberBinding="{Binding Type, Mode=OneWay}"
Header="Type"/>
</GridView>
</ListView.View>
</ListView>
</Grid>
</Window>
Recently I was asked to make a style for every ComboBox on our screens - changing their backgrounds. Because I'm on Windows8, setting the Background alone isn't enough. Using this tutorial I was able to create the ControlTemplate to get the correct behavior, with one minor error fix:
<MultiTrigger.Conditions>
<Condition Property="IsGrouping" Value="True"/>
<!-- Comment out the following, it throws an error. -->
<!--<Condition>
<Condition.Value>
<sys:Boolean>False</sys:Boolean>
</Condition.Value>
</Condition>-->
</MultiTrigger.Conditions>
And usage:
<Style
TargetType="{x:Type ComboBox}">
...
<Setter
Property="Template"
Value="{StaticResource ComboBoxStyle1}" />
</Style>
This successfully styles the ComboBox Background. However, revisiting the former screen, I noticed that this breaks my gmail-like display.
How can I get this ControlTemplate and the dynamic DataTemplate to cooperate?
(*) ViewModels and Models can be provided if necessary for a solution. Or see full working example.
I'm not sure why you doing so complicated ComboBox - you defined ItemTemplateSelector twice...
Ok - here is my 2 cents: ComboBox is lookless control. It based on ControlTemplate target type = ComboBox. Inside ComboBox ControlTemplate you will find ContentPresenter. Whatever coming into Content of ContentPresenter could be styled with DataTemplate. Generally - when you define DataTemplate it wrap only ContentPresenter or ItemsPresenter for range based controls - not the whole ControlTemplate obviously.
So if you want to change 'Selected' template for ComboBox is ok but all other data should be defined via DataTemplate for this type {x:Type local:SomeType} that will be used by ComboBox.
Also - consider using #galakt suggestion: use Style with TargetType - it easy to read, refactor, find, understand...
Color the Background
As per the OP, following this tutorial, you generate the ControlTemplate.
To do this, you can right-click on the ComboBox element in design mode
in Visual Studio 2012 or 2013 and select the “Edit template” option
and then the “Edit a copy…” option.
Again note the bug fix from the OP. Changing the background can then be done by:
<Border
x:Name="templateRoot"
BorderBrush="#FFACACAC"
BorderThickness="{TemplateBinding BorderThickness}"
SnapsToDevicePixels="True"
Background="{StaticResource MyComboBackgroundBrush}"> <!-- option 1 -->
<!-- <Border.Background> -->
<!-- option 2 -->
<!-- <LinearGradientBrush EndPoint="0,1" StartPoint="0,0">
<GradientStop Color="#FFF0F0F0" Offset="0"/>
<GradientStop Color="#FFE5E5E5" Offset="1"/>
</LinearGradientBrush>-->
<!-- option 3 -->
<!-- <SolidColorBrush Color="Yellow"/> -->
<!-- </Border.Background>
<Border x:Name="splitBorder" BorderBrush="Transparent" BorderThickness="1" HorizontalAlignment="Right" Margin="0" SnapsToDevicePixels="True" Width="{DynamicResource {x:Static SystemParameters.VerticalScrollBarWidthKey}}">
<Path x:Name="Arrow" Data="F1M0,0L2.667,2.66665 5.3334,0 5.3334,-1.78168 2.6667,0.88501 0,-1.78168 0,0z" Fill="#FF606060" HorizontalAlignment="Center" Margin="0" VerticalAlignment="Center"/>
</Border> -->
</Border>
Interact with the DataTemplate
In the resource file, following the above instructions, you declared:
<ControlTemplate x:Key="ComboBoxControlTemplateBasic" TargetType="{x:Type ComboBox}">
...
<ContentPresenter
x:Name="contentPresenter"
ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}"
... />
...
</ControlTemplate>
Create a copy of it with the following change, where selectedTemplate is the key of the DataTemplate you want to cooperate with the ControlTemplate:
<ControlTemplate x:Key="ComboBoxControlTemplateHeader" TargetType="{x:Type ComboBox}">
...
<ContentPresenter
x:Name="contentPresenter"
ContentTemplate="{DynamicResource selectedTemplate}"
... />
...
</ControlTemplate>
Declare the appropriate styles:
<Style
TargetType="{x:Type ComboBox}">
<Setter Property="Template"
Value="{StaticResource ComboBoxControlTemplateBasic}" />
<!-- other generic style setters -->
</Style>
<Style
x:Key="ComboBoxHeader"
x:Name="ComboBoxHeader"
TargetType="{x:Type ComboBox}"
BasedOn="{StaticResource {x:Type ComboBox}}">
<Setter Property="Template"
Value="{StaticResource ComboBoxControlTemplateHeader}" />
<Setter Property="ItemTemplateSelector"
Value="{DynamicResource itemTemplateSelector}"/>
<!-- other specific style setters -->
</Style>
Results
Every ComboBox shares the same look.
The dynamic use of DataTemplate selectedTemplate allows each ListView header gmail-like ComboBox to have the same look with unique binding values.
Take for example the OP ComboBox header plus the following ComboBox with the same item source:
<ComboBox
HorizontalAlignment="Left"
Margin="10,152,0,0"
VerticalAlignment="Top"
Width="120"
ItemsSource="{Binding Options.Items}"
SelectedValue="{Binding Options.SelectedItem}"/>
<GridViewColumnHeader>
<ComboBox
ItemsSource="{Binding Options.Items}"
SelectedValue="{Binding Options.SelectedItem}"
Style="{StaticResource ComboBoxHeader}">
<ComboBox.Resources>
<DataTemplate x:Key="selectedTemplate">
<TextBlock
x:Name="displayText"
Text="{Binding DataContext.Options.SelectedDisplay, RelativeSource={RelativeSource AncestorType={x:Type ListBox}}}"
FontSize="20"
Height="22"/>
</DataTemplate>
<DataTemplate x:Key="dropDownTemplate">
<TextBlock Text="{Binding}" FontSize="12"/>
</DataTemplate>
<local:ComboBoxItemTemplateSelector
x:Key="itemTemplateSelector"
SelectedTemplate="{StaticResource selectedTemplate}"
DropDownTemplate="{StaticResource dropDownTemplate}"/>
</ComboBox.Resources>
</ComboBox>
</GridViewColumnHeader>

Using A WrapPanel in a TreeView

I want to display data with level of detail, so i use a TreeView, but each detail is quite short, so i would like to use a WrapPanel (horizontal) to have many details per line.
Something like :
This is an unexpanded item
This is The Header of an expanded item
Info 1 Info 2 Info 3 Info 4
Info 5 Info 6 Info 7
So i tried defining TreeViewItem's Template but i could not get the wrappanel to
wrap. I have only one info per line, when info's datatemplate width is 100 and TreeView
is 500. i tried setting Width of WrapPanel, ItemsWidth, are other things with no success.
Any idea ?
EDIT : i finally got this to work with a 'simpler' solution. Still it seems that we
have to define the WrapPanel's Width, which make the solution less generic.
Here's the solution i came to : just defining, in a style, the ItemsPanel used in a
TreeViewItem :
<Style TargetType="TreeViewItem">
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal"
Width="520"
HorizontalAlignment="Stretch"
Margin="0"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
IsItemsHost="True"
/>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
</Style>
And i still let the not working solution here, for completeness sake.
(Why wouldn't it work ???)
<Style TargetType="TreeViewItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TreeViewItem">
<Grid Margin="2" Width="500">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ContentPresenter Name="PART_Header"
ContentSource="Header"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
!!!! this is the wrapanel not wrapping
<ListBox Name="AllItems" Grid.Row="1" >
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ItemsPresenter />
</ListBox>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsExpanded" Value="False">
<Setter
TargetName="AllItems"
Property="Visibility"
Value="Collapsed" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
EDIT : i finally got this to work with a 'simpler' solution. Still it seems that we
have to define the WrapPanel's Width, which make the solution less generic.
(Maybe a binding of the width (but which ?) would solve this)
Here's the solution i came to : just defining, in a style, the ItemsPanel used in a
TreeViewItem :
<Style TargetType="TreeViewItem">
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal"
Width="520"
HorizontalAlignment="Stretch"
Margin="0"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
IsItemsHost="True"
/>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
</Style>
You must set
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
to your
<TreeView/>
not for
<WrapPanel/>
example:
<TreeView x:Name="fieldTreeView" Grid.Row="1" Margin="5,0,5,0" Background="Beige"
ItemsSource="{Binding Source={StaticResource bla}}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<TreeView.Resources>
<DataTemplate DataType="{x:Type Model:bla}">
<StackPanel Orientation="Vertical">
<Label Content="{Binding Name}"/>
<TextBox Text=""/>
</StackPanel>
</DataTemplate>
</TreeView.Resources>
<TreeView.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</TreeView.ItemsPanel>
</TreeView>
This solution works for me.
Try
Disable the scroll bars
Widen the WrapPanel and see the visual impact, is it effected?
Make a color border/background to track the actual size of the WrapPanel
These will help you trace the problem

ListBox , DataTemplate and Triggers

i got a DataTemplate for a listboxitem and i want to create a triger , so when a user click an item the background will change and also the label
my code:
<Window.Resources>
<Style x:Key="RoundedItem" TargetType="ListBoxItem">
<EventSetter Event="MouseDoubleClick" Handler="listViewItem_MouseDoubleClick" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Border Name="ItemBorder" CornerRadius="10" BorderBrush="Black" BorderThickness="1" Margin="1" Background="Transparent">
<Label Name="ItemLabel" Foreground="Red" >
<ContentPresenter />
</Label>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter TargetName="ItemBorder" Property="Background" Value="DeepSkyBlue" />
<Setter TargetName="ItemLabel" Property="Foreground" Value="Orange" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<DataTemplate x:Key="TitleTemplate" DataType="models:Title" >
<StackPanel>
<Image Source="{Binding ThumbFilePath}" Width="50" HorizontalAlignment="Center"/>
<Label Content="{Binding Name}" HorizontalAlignment="Center" />
<TextBlock Text="{Binding Description}" HorizontalAlignment="Center" TextWrapping="Wrap" Padding="5,5,5,5"/>
</StackPanel>
</DataTemplate>
</Window.Resources>
What happend is that the TextBlock change his color and not the label..
anyone know why ?
Thanks.
The TextBlock inherits the Foreground definition from its parents in the visual tree. The Label, on the other hand, defines the Foreground in its default style.
Your approach is "non-WPF-like" - you shouldn't wrap the ContentPresenter in a Label control.
The right approach depends on whether you want all text in the item to change its Foreground, or just the label?
[In both cases, there's no apparent benefit to using a Label in the data template - so I'll assume that the label is changed to TextBlock.]
If the answer to the above question is that all text should be changed: in the ControlTemplate of the ListBoxItem, in the trigger for IsSelected, from the seccond setter remove TargetName="ItemLabel" so the final setter is:
<Setter Property="Foreground" Value="Orange" />
This will change the foreground of the item that will affect the foreground of both TextBlocks in the data template.
If you want to affect just one of the TextBlocks:
1. remove the setter for the foreground from the control template
2. add a trigger to your data template:
<DataTemplate>
<StackPanel>
<Image .../>
<TextBlock x:Name="Text01" ..../>
<TextBlock x:Name="Text02" ..../>
</StackPanel>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding IsSelected, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBoxItem}}}" Value="True">
<Setter TargetName="Text01" Property="Foreground" Value="Orange"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
Side note: if you have to use Label control in your data template, bind its Foreground property to the Foreground of the list box item, like so:
<Label Foreground="{Binding Foreground, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBoxItem}}}"....../>
If this doesn't help, it means that your list box item inherits its foreground, so use:
<Label Foreground="{Binding TextElement.Foreground, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBoxItem}}}"....../>
I want to tack on to this that I was experiencing a similar problem where I'd added a ListBox.ItemTemplate to my ListBox, and the styling then did not apply to the text anymore.
What I was doing was trying to display a list of languages (CultureInfo) for the user to select from, however I wanted the native names to display, not the English name. For some reason, not all languages have their native names capitalized in their CultureInfo, and NativeName is the only instance of their name, so I needed to apply a function to the CultureInfo.NativeName to capitalize the names myself. To accomplish this, I added the ItemTemplate with a Data Template inside, on which I applied a converter.
<ListBox IsSynchronizedWithCurrentItem="True" VerticalAlignment="Center" MinHeight="200" x:Name="cbLanguages"
ItemsSource="{Binding Path=SupportedCultures, Mode=OneWay, Source={StaticResource CultureResourcesDS}}"
FontSize="24" HorizontalAlignment="Stretch" Width="300" Margin="10"
Style="{DynamicResource ListBoxTemplate}" ItemContainerStyle="{DynamicResource ListBoxItemStyle}">
<ListBox.ItemTemplate>
<DataTemplate>
<Label Content="{Binding Converter={StaticResource NativeNameConverter}}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
After a while of searching, I came across XAMeLi's answer here, and changed the Label I'd put in the DataTemplate to a TextBox, and the ListBoxItemStyle I'd created worked again.
Basically, Labels and TextBoxes have different traits that can be exploited, or can cause annoying issues in this case. Here's a good explanation with some examples of the differences: http://joshsmithonwpf.wordpress.com/2007/07/04/differences-between-label-and-textblock/

Styling individual cells in a listview

On the internet I found many examples of styling a complete column or complete row in a listview.
I need to be able to dynamically style individual cells in the listview. How can I access the properties of individual items in a row?
If you've got a finite number of properties in your data objects that you want to use to style your items, you can create data templates and styles, and use data triggers to switch between them. I've used something like this to alter the appearance of data objects in a list based on if they are "active/inactive" and to create a collapsed/expanded version of the object based on whether it's selected or not.
You can also use converters (built-in or custom) to get some effects easily. For example, I used a built-in boolean to visibility converter to hide/unhide the combobox/textblock in my TaskSelectedTemplate based on if the object's IsActive member.
<DataTemplate x:Key="TaskSelectedTemplate">
<Grid Margin="4">
...
<Border Grid.Row="0" Grid.Column="0" Grid.RowSpan="4" Margin="0 0 4 0"
BorderThickness="0"
CornerRadius="2">
<Border.Background>
<MultiBinding Converter="{StaticResource ActiveToColor}">
<Binding Path="."/>
<Binding Path="IsActive"/>
<Binding Path="IsPaused"/>
</MultiBinding>
</Border.Background>
</Border>
<StackPanel Grid.Row="0" Grid.Column="1"
Orientation="Horizontal"
Margin="0 2">
<ComboBox ItemsSource="{Binding Source={StaticResource TaskTypes}}"
SelectedItem="{Binding Type}"
Text="{Binding Type}"
Visibility="{Binding IsActive, Converter={StaticResource BoolToVis}}"/>
<TextBlock Text="{Binding Type}"
FontWeight="Bold"
Visibility="{Binding IsActive, Converter={StaticResource InvBoolToVis}}"/>
<TextBlock Text=" task"/>
</StackPanel>
...
</Grid>
</DataTemplate>
<DataTemplate x:Key="TaskNotSelectedTemplate">
<Grid Margin="4">
...
<Border Grid.Row="0" Grid.Column="0" Grid.RowSpan="4" Margin="0 0 4 0"
BorderThickness="0"
CornerRadius="2">
<Border.Background>
<MultiBinding Converter="{StaticResource ActiveToColor}">
<Binding Path="."/>
<Binding Path="IsActive"/>
<Binding Path="IsPaused"/>
</MultiBinding>
</Border.Background>
</Border>
<TextBlock Grid.Row="0" Grid.Column="1"
Text="{Binding Type}"/>
<TextBlock Grid.Row="0" Grid.Column="2"
TextAlignment="Right">
<Run Text="{Binding Length.TotalMinutes, StringFormat='0', Mode=OneWay}"/>
<Run Text=" min"/>
</TextBlock>
<TextBlock Grid.Row="0" Grid.Column="3"
TextAlignment="Right">
<Run Text="{Binding TimesPerformed, Mode=OneWay}"/>
<Run Text=" tasks"/>
</TextBlock>
</Grid>
</DataTemplate>
<Style x:Key="ContainerStyle" TargetType="{x:Type ListBoxItem}">
<!--this part changes the selected item highlight color-->
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<Border Name="Border">
<ContentPresenter />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="true">
<Setter TargetName="Border"
Property="Background" Value="#2000BFFF">
</Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<!--this part causes selected task to expand-->
<Setter Property="ContentTemplate" Value="{StaticResource TaskNotSelectedTemplate}"/>
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="ContentTemplate" Value="{StaticResource TaskSelectedTemplate}"/>
</Trigger>
</Style.Triggers>
</Style>
For more complex scenarios, you might want to look at DataTemplateSelector. I've never used it, but it seems like it might be ideal if you've got a lot of data templates to juggle.
Generally speaking, you shouldn't need this. Assuming you are using GridView, you should be able to use CellTemplate or CellTemplateSelector of your GridViewColumns.
If you really want to access specific cells, I think there is no clean way, you'd be better using DataGrid (from .Net 4 or WPF toolkit for .Net 3.5). With that, you can do something like this:
((TextBlock)datagrid.Columns[1].GetCellContent(m_specificItem)).Background = Brushes.Red

Categories

Resources